zoukankan      html  css  js  c++  java
  • C语言俄罗斯方块游戏解析(图文+源码)

     
    以下文章来源于嵌入式Linux ,作者写代码的篮球球痴

    最近在看俄罗斯方块的游戏,看到一个大神写的俄罗斯方块代码,非常非常优秀,拿出来解析给大家看看,也希望大家自己尝试运行试试,从中能得到一些启发。

    #先了解下俄罗斯方块的几个形状

    一共分成 7 形状,有的形状有 4种状态,有的形状有1种状态。

    不管是多少种状态,一个方块需要一个2个字节来存储,也就是16bit来保存一个方块的信息。

    基于上面的理论,我们可以使用4x4的数组来保存方块的信息。

    注意,下面代码中的 "■" 占用的是2个字节。

    #写代码来显示这个方块

    方块数据,这两个数组其实是两种游戏模式的方块信息,我们只需要分析一种就可以了。

    int TGM[7][4]={{0x159D,0x89AB,0x159D,0x89AB},{0x126A,0x4856,0x159A,0x4526},{0x926A,0x456A,0x1592,0x0456},{0x4859,0x4859,0x4859,0x4859},{0x5926,0x0156,0x5926,0x0156},{0x4159,0x4596,0x1596,0x4156},{0x156A,0x4152,0x156A,0x4152}};
    int SRS[7][4]={{0x159D,0x89AB,0x26AE,0x4567},{0x0159,0x4856,0x159A,0x4526},{0x8159,0x456A,0x1592,0x0456},{0x4859,0x4859,0x4859,0x4859},{0x4815,0x459A,0x5926,0x0156},{0x4159,0x4596,0x1596,0x4156},{0x0459,0x8596,0x156A,0x4152}};
    
    

    我们分析这段代码

    /***********擦除显示*************/
    int Display(int x, int y, int CAC, int Mode)
    {
        for(j=0;j<=3;j++){
            P[j]=CAC&0xF, CAC>>=4;
            if     (Mode==1){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf("■");}
            else if(Mode==0){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf(" ");}
        }
        return 0;
    }
    

    P[j]=CAC&0xF 取到的是 4个bit,然后通过判断这 4个bit决定输出方块的位置。

    我们拿 0这个方块的数据0x4859 对应的二进制 0B0100100001011001来做个例子。

    0100  ----> 对应坐标(1,0)
    1000  ----> 对应坐标(2,0)
    0101  ----> 对应坐标(1,1)
    1001  ----> 对应坐标(2,1)
    

    通过这个坐标,我们会输出这样一个方块

    再举个例子

    我们拿 Z这个方块的数据0x0156 对应的二进制 0B0000000101010110来做个例子。

    0000  ----> 对应坐标(0,0)
    0001  ----> 对应坐标(0,1)
    0101  ----> 对应坐标(1,1)
    0110  ----> 对应坐标(1,2)
    

    显示方块没有问题了

    上面的代码中,如果mode等于0的话,也就是把某个方块从显示中擦除掉。

    我们用devc++写的程序,需要知道几个细节。

    #构建固定行列的窗口

    正常情况,我们显示cmd是一个默认值的黑框「如下图」。

    实际上,我们可以用代码来固定cmd的框框大小。

    默认代码输出

    示例代码:

    #include   <conio.h>
    #include   <stdio.h>
    #include  <stdlib.h>
    #include    <time.h>
    #include <Windows.h>
    
    /**********Main主函数***********/
    int main()
    {
        system("color F1&mode con cols=35 lines=25");
        getchar();
        return 0;
    }
    

    程序输出:

    #延迟函数的设计

    跟其他不同的是,这个程序的延迟没有使用usleep,我觉得这也是非常值得称赞的地方,不过空跑cpu,哈哈,是有那么一点调皮。

    把延迟放到键值获取的函数中。

    /**********按键获取**************/
    int Getkey(int N,int T)
    {
        int start=clock();
        if(KEY_V==115){return 115;}
        do{
            if(kbhit()){
                    KEY_V=(int)(getch());
                    if(KEY_V<97){KEY_V+=32;}
                    return KEY_V;
            }
            for(i=0;i<=N;i++);
        }while((clock()-start)<T);
        dy=1;
        return -1;
    }
    

     

    就先简单讲解下这些,至于得分机制,碰撞检测,消除的代码,还没有想好怎么讲明白,需要再花时间剖析剖析。

     

    #试玩一下

    #源码

    #include   <conio.h>
    #include   <stdio.h>
    #include  <stdlib.h>
    #include    <time.h>
    #include <Windows.h>
     
    /**********初始化参数************/
    int i,j,N,T,F,J,X,Y,dx,dy,  KEY_V,  Cache1,Cache2,NU,NI,RU,RI,  P_X,P_Y,POS_H_MAX,  LEVEL=1,SCORE=0,  P[4],  POINT_V[12][22],  MARK[21],  FLAG[5]={0,0,0,1,0};
    int TGM[7][4]={{0x159D,0x89AB,0x159D,0x89AB},{0x126A,0x4856,0x159A,0x4526},{0x926A,0x456A,0x1592,0x0456},{0x4859,0x4859,0x4859,0x4859},{0x5926,0x0156,0x5926,0x0156},{0x4159,0x4596,0x1596,0x4156},{0x156A,0x4152,0x156A,0x4152}};
    int SRS[7][4]={{0x159D,0x89AB,0x26AE,0x4567},{0x0159,0x4856,0x159A,0x4526},{0x8159,0x456A,0x1592,0x0456},{0x4859,0x4859,0x4859,0x4859},{0x4815,0x459A,0x5926,0x0156},{0x4159,0x4596,0x1596,0x4156},{0x0459,0x8596,0x156A,0x4152}};
     
    /**********光标位置函数**********/
    void Pos(int x,int y)
    {
            COORD pos;
            HANDLE hOutput;
            pos.X=2*x;
            pos.Y=y;
            hOutput=GetStdHandle(STD_OUTPUT_HANDLE);
            SetConsoleCursorPosition(hOutput,pos);
    }
     
    void HideCursor()
    {
            CONSOLE_CURSOR_INFO cursor_info={1,0};
            SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);
    }
     
    /**********初始化界面************/
    void CreatUI()
    {
            int i,j,BOUNDARY;
            printf("┏━━━━━━━━━━┓
    ");
            for(j=1;j<=20;j++) {
                    if     (j==3){printf("┃          ┃LEVEL:1
    ");}
                    else if(j==5){printf("┃          ┃SCORE:0
    ");}
                    else if(j==7){printf("┃          ┃NEXT   
    ");}
                    else         {printf("┃          ┃
    ");}
            }
            printf("┗━━━━━━━━━━┛
    ");
            printf(" CopyRight@2016~2018 BY HAPPY
    ");
            for(j=1;j<=21;j++){
                    for(i=0;i<=11;i++){
                            BOUNDARY=i*(i-11)*(j-21);
                            if(BOUNDARY==0){
                                    POINT_V[i][j]=1;
                            }else{
                                    POINT_V[i][j]=0;
                            }                
                    }
            }
    }
     
    /**********按键获取**************/
    int Getkey(int N,int T)
    {
        int start=clock();
        if(KEY_V==115){return 115;}
        do{
            if(kbhit()){
                    KEY_V=(int)(getch());
                    if(KEY_V<97){KEY_V+=32;}
                    return KEY_V;
            }
            for(i=0;i<=N;i++);
            printf("%d
    ",clock()-start);
        }while((clock()-start)<T);
        dy=1;
        return -1;
    }
     
    /***********块体转置*************/
    int Rote(int S, int I)
    {
        return (F==0)?TGM[S][(I+4)%4]:SRS[S][(I+4)%4];
    }
     
    /***********擦除显示*************/
    int Display(int x, int y, int CAC, int Mode)
    {
        for(j=0;j<=3;j++){
            P[j]=CAC&0xF, CAC>>=4;
            if     (Mode==1){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf("■");}
            else if(Mode==0){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf(" ");}
        }
        return 0;
    }
     
    /***********固化块体*************/
    int DoBlocks()
    {
            //~~~游戏结束
            if(Y<2){
                    Pos(1,22);printf("GAME OVER!");
                    exit(0);
            }
            //~~~固化块体
            POS_H_MAX=0, FLAG[3]=1;
            for(j=0;j<=3;j++){
                    P_X=(P[j]>>2)+X,P_Y=(P[j]&0x3)+Y;
                    if(POS_H_MAX<P_Y){POS_H_MAX=P_Y;}
                    POINT_V[P_X][P_Y]=1;
            }
            //~~~关卡得分
            for(j=Y;j<=POS_H_MAX;j++){
                    FLAG[2]=1;
                    for(i=1;i<=10;i++){
                            if(POINT_V[i][j]==0){FLAG[2]=0;}
                    }
                    if(FLAG[2]){
                            SCORE+=10,MARK[j]=1;
                            if(SCORE==400){
                                    SCORE=0,LEVEL+=1,T-=100;
                                    FLAG[4]=1;        
                            }
                    }
            }
            //~~~极品消行
            for(j=20;j>=5;j--){
                    if(FLAG[4]){
                            for(i=1;i<=10;i++){
                                    POINT_V[i][j]=0;
                                    Pos(i,j);printf(" ");
                            }
                    }
                    else if(MARK[j])
                    {
                            MARK[j]=0,J=j-1;
                            for(N=1;N<=3;N++){
                                    if(MARK[J]){J--;}
                            }
                            MARK[J]=1;
                            for(i=1;i<=10;i++){
                                    Pos(i,j);
                                    if(POINT_V[i][j]=POINT_V[i][J]){
                                            printf("■");
                                    }else{
                                            printf(" ");
                                    }
                            }
                    }
            }
            FLAG[4]=0;
            return 0;
    }
     
    /***********碰撞检测*************/
    int CheckCollision()
    {                                
        for(j=0;j<=3;j++){
            P_X=(P[j]>>2)+X+dx,P_Y=(P[j]&0x3)+Y+dy;
            if(POINT_V[P_X][P_Y]){
                if(dx!=0){return 1;}
                if(dy){
                    DoBlocks();
                    Pos(12,3);printf("LEVEL:%-3d",LEVEL);
                    Pos(12,5);printf("SCORE:%-3d",SCORE);
                    return 2;
                }
                if(KEY_V==119){FLAG[0]=1;}
            }
        }
        return 0;
    }
    unsigned 
    /***********循环核心*************/
    int GameCycle(int N, int T, int F)
    {
            srand((unsigned)time(NULL));RU=rand()%7,RI=(rand()%4);
            while(1){
                    if(FLAG[3]){
                            Display(12,8,Rote(RU,RI),0);
                            X=4,Y=1, NU=RU,NI=RI, RU=rand()%7,RI=(rand()%4), FLAG[3]=0,KEY_V=0;
                            Display(12,8,Rote(RU,RI),1);
                            Display(X, Y,Rote(NU,NI),1);
                    }
                    dx=0,dy=0;        
                    KEY_V=Getkey(N,T);
                    if(KEY_V==119){
                        NI++;
                        Display(X,Y,Rote(NU,NI),2);
                    }//旋W
                    else if(KEY_V==115){dy= 1;}//下S
                    else if(KEY_V==97 ){dx=-1;}//左A
                    else if(KEY_V==100){dx= 1;}//右D
                    else if(KEY_V==112){getch(); }//暂停P
                    else if(KEY_V==113){return 0;}//退出Q
                    if(dx!=0 || dy!=0 || KEY_V==119){
                        if(!CheckCollision()){
                                if(FLAG[0]){
                                        NI--,FLAG[0]=0;
                                        Display(X,Y,Rote(NU,NI),0);
                                }
                                else if(KEY_V==119){
                                        Display(X,Y,Rote(NU,NI-1),0);
                                }else{
                                        Display(X,Y,Rote(NU,NI),0);
                                }
                                Display(X+dx,Y+dy,Rote(NU,NI),1);
                                X+=dx,Y+=dy;
                        }
                    }
            }
            return 0;
    }
     
    /**********Main主函数***********/
    int main()
    {
            system("color F0&mode con cols=35 lines=25");
            HideCursor();
            CreatUI();
            GameCycle(10,800,1);
            return 0;
    }
    

    #后记

    里面涉及的很多东西,我都没有完全总结出来,作者的巧妙非常令我佩服,后面应该还会有文章分析。

    大家有什么想法或者自己的解读,可以留言,欢迎一起讨论,共同进步,探索代码里面的奥秘和乐趣。

    方块消除

    准备下一个方块

    得分机制

    Game Over 判断

  • 相关阅读:
    使用BitMap进行海量数据去重
    记一次std::process::Child使用过程中碰到的问题
    我的第一篇rust博客
    优秀编程习惯总结
    利用generator模拟协程完美解决异步回调问题
    polymer框架在代码中动态创建需要支持内容分发的自定义元素并挂载到文档中
    属于自己的完美web服务器完成
    web components折腾记
    内边距的妙用
    用js修改带!important的css样式
  • 原文地址:https://www.cnblogs.com/zm131417-/p/14358833.html
Copyright © 2011-2022 走看看