云寺手游网:做最专业最放心的安全下载站!
您当前所在位置:首页 > 游戏资讯 >

编程项目实战:解析一个C语言俄罗斯方块游戏,图文+代码透析

时间:2021-11-26 12:02:11 来源:云寺手游网


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

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

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

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

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


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

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


#写代码来显示这个方块

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

intTGM[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}}; intSRS[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}};

我们分析这段代码

/***********擦除显示*************/ intDisplay(intx,inty,intCAC,intMode) { 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("■");} elseif(Mode==0){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf("");} } return0; }

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主函数***********/ intmain() { system("colorF1&modeconcols=35lines=25"); getchar(); return0; }

程序输出:


#延迟函数的设计

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

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

/**********按键获取**************/ intGetkey(intN,intT) { intstart=clock(); if(KEY_V==115){return115;} do{ if(kbhit()){ KEY_V=(int)(getch()); if(KEY_V<97){KEY_V+=32;} returnKEY_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> /**********初始化参数************/ inti,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}; intTGM[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}}; intSRS[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}}; /**********光标位置函数**********/ voidPos(intx,inty) { COORDpos; HANDLEhOutput; pos.X=2*x; pos.Y=y; hOutput=GetStdHandle(STD_OUTPUT_HANDLE); SetConsoleCursorPosition(hOutput,pos); } voidHideCursor() { CONSOLE_CURSOR_INFOcursor_info={1,0}; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info); } /**********初始化界面************/ voidCreatUI() { inti,j,BOUNDARY; printf("┏━━━━━━━━━━┓n"); for(j=1;j<=20;j++){ if(j==3){printf("┃┃LEVEL:1n");} elseif(j==5){printf("┃┃SCORE:0n");} elseif(j==7){printf("┃┃NEXTn");} else{printf("┃┃n");} } printf("┗━━━━━━━━━━┛n"); printf("CopyRight@2016~2018BYHAPPYn"); 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; } } } } /**********按键获取**************/ intGetkey(intN,intT) { intstart=clock(); if(KEY_V==115){return115;} do{ if(kbhit()){ KEY_V=(int)(getch()); if(KEY_V<97){KEY_V+=32;} returnKEY_V; } for(i=0;i<=N;i++); printf("%dn",clock()-start); }while((clock()-start)<T); dy=1; return-1; } /***********块体转置*************/ intRote(intS,intI) { return(F==0)?TGM[S][(I+4)%4]:SRS[S][(I+4)%4]; } /***********擦除显示*************/ intDisplay(intx,inty,intCAC,intMode) { 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("■");} elseif(Mode==0){Pos((P[j]>>2)+x,(P[j]&0x3)+y);printf("");} } return0; } /***********固化块体*************/ intDoBlocks() { //~~~游戏结束 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(""); } } elseif(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; return0; } /***********碰撞检测*************/ intCheckCollision() { 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){return1;} if(dy){ DoBlocks(); Pos(12,3);printf("LEVEL:%-3d",LEVEL); Pos(12,5);printf("SCORE:%-3d",SCORE); return2; } if(KEY_V==119){FLAG[0]=1;} } } return0; } unsigned /***********循环核心*************/ intGameCycle(intN,intT,intF) { 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 elseif(KEY_V==115){dy=1;}//下S elseif(KEY_V==97){dx=-1;}//左A elseif(KEY_V==100){dx=1;}//右D elseif(KEY_V==112){getch();}//暂停P elseif(KEY_V==113){return0;}//退出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); } elseif(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; } } } return0; } /**********Main主函数***********/ intmain() { system("colorF0&modeconcols=35lines=25"); HideCursor(); CreatUI(); GameCycle(10,800,1); return0; } #后记

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

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

方块消除

准备下一个方块

得分机制

Game Over 判断


C语言是每个想要学习编程的小伙伴首要学习的语言~如果你也希望成为一个好的程序员,了解C语言更多知识,点击下方【了解更多】,接受牛人大牛们的指导,与志同道合的小伙伴一起快乐学习,共同进步~