|
最近在看俄罗斯方块的游戏,看到一个大神写的俄罗斯方块代码,非常非常优秀,拿出来解析给大家看看,也希望大家自己尝试运行试试,从中能得到一些启发。 #先了解下俄罗斯方块的几个形状一共分成 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}};- x7 z$ ^! x6 S t9 Pint 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}}; 我们分析这段代码 /***********擦除显示*************/6 r/ n. w4 S3 N4 [9 a8 v/ oint Display(int x, int y, int CAC, int Mode)1 @5 e* u4 o8 N B I# l {+ U( L: ]% B/ e$ I4 T0 c K o. B for(j=0;j<=3;j++){& P+ r' z* L* c' P0 P0 f 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;' g! a% E9 S( R: z2 V0 Q } P[j]=CAC&0xF 取到的是 4个bit,然后通过判断这 4个bit决定输出方块的位置。 我们拿 0这个方块的数据0x4859 对应的二进制 0B0100100001011001来做个例子。 0100 ----> 对应坐标(1,0)1000 ----> 对应坐标(2,0) 0101 ----> 对应坐标(1,1)! k# c: M t8 X; M; n: R 1001 ----> 对应坐标(2,1) 通过这个坐标,我们会输出这样一个方块
再举个例子 我们拿 Z这个方块的数据0x0156 对应的二进制 0B0000000101010110来做个例子。 0000 ----> 对应坐标(0,0)0001 ----> 对应坐标(0,1)* c' D1 \ Q: E1 D: M9 H { 0101 ----> 对应坐标(1,1)& j2 L9 ^9 Y" U) X A& y 0110 ----> 对应坐标(1,2)
显示方块没有问题了
上面的代码中,如果mode等于0的话,也就是把某个方块从显示中擦除掉。7 x. q: U' O' ^9 P9 }1 x% V
我们用devc++写的程序,需要知道几个细节。 #构建固定行列的窗口正常情况,我们显示cmd是一个默认值的黑框「如下图」。 实际上,我们可以用代码来固定cmd的框框大小。 默认代码输出
示例代码: #include <conio.h>: `- W% l& I. Z. i#include <stdio.h> #include <stdlib.h> #include <time.h>0 s+ ?# |% H* Z$ f$ A% { #include <Windows.h>( [4 o" ~/ O5 S9 q+ R/ C 2 B: T9 S* i8 G! h3 i /**********Main主函数***********/ int main()7 \7 o" A4 f& o2 I8 l% j% ?+ Z) \, L {5 G% [: g0 O) u8 G+ _ system("color F1&mode con cols=35 lines=25"); getchar(); return 0;4 \. D3 A6 m5 w1 R } 程序输出:
#延迟函数的设计 跟其他不同的是,这个程序的延迟没有使用usleep,我觉得这也是非常值得称赞的地方,不过空跑cpu,哈哈,是有那么一点调皮。 把延迟放到键值获取的函数中。 /**********按键获取**************/int Getkey(int N,int T) {0 @! f/ [: n }" B; d6 a! s int start=clock();. y/ t1 L6 r0 z! A1 o if(KEY_V==115){return 115;} do{4 [' v8 t! f, e if(kbhit()){ KEY_V=(int)(getch()); if(KEY_V<97){KEY_V+=32;} return KEY_V;0 J" k4 s& G8 l0 @7 f }8 B( s! j1 l; C o6 H! T# I+ e2 e for(i=0;i<=N;i++); }while((clock()-start)<T);- }8 Q& V' G( e6 Z( j. l- N9 Q dy=1; return -1; }& A5 w* W' t: X 就先简单讲解下这些,至于得分机制,碰撞检测,消除的代码,还没有想好怎么讲明白,需要再花时间剖析剖析。 #试玩一下
#include <conio.h> #include <stdio.h>+ g4 u4 Q6 w6 Y t* b #include <stdlib.h> #include <time.h>4 v- g" O H* N #include <Windows.h>' ~+ e: u: b( x+ Z ~1 t8 L+ h /**********初始化参数************/( K% N- c9 [8 [ 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}};( D& Z9 ?2 I2 G+ i; x( [: z( Q5 P 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}}; - M3 R2 S# U2 [ /**********光标位置函数**********/ void Pos(int x,int y) {4 P5 a! b8 y, @! K6 T) l2 j( u9 F n COORD pos;$ D, D6 d7 m6 |& J% H, Z) |* a HANDLE hOutput; pos.X=2*x; pos.Y=y;& k1 _' p! J8 J( {! e' n. p hOutput=GetStdHandle(STD_OUTPUT_HANDLE);& y. t. M, T0 B, r N$ P SetConsoleCursorPosition(hOutput,pos);3 \* @, O) Y& F }& J1 U. n( c9 ]6 x void HideCursor() { CONSOLE_CURSOR_INFO cursor_info={1,0}; SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cursor_info);) W) I7 J% d: m1 h( T1 e } * z! p8 u" ?7 B. i9 H7 ] /**********初始化界面************/ void CreatUI()7 Y, E4 Q4 Z6 ^# M {. A' u* O$ E8 M8 M int i,j,BOUNDARY; printf("┏━━━━━━━━━━┓\n"); for(j=1;j<=20;j++) { if (j==3){printf("┃ ┃LEVEL:1\n");} j7 a ~4 n; k$ ?: a else if(j==5){printf("┃ ┃SCORE:0\n");} else if(j==7){printf("┃ ┃NEXT \n");} else {printf("┃ ┃\n");} } printf("┗━━━━━━━━━━┛\n");2 N E5 I' ^& R& T B) B printf(" CopyRight@2016~2018 BY HAPPY\n"); for(j=1;j<=21;j++){# k$ s! r8 z# s6 X! W for(i=0;i<=11;i++){9 e8 _2 H5 _! S8 L1 s. v' j BOUNDARY=i*(i-11)*(j-21); if(BOUNDARY==0){3 z' d# g% O0 E, I. f POINT_V[j]=1; }else{& [% }' } f6 B/ m POINT_V[j]=0;$ z% S2 h( `- i# q) u+ i5 B } } }! B; \# X- V+ L } 7 L/ u+ @( w9 E: r4 g% u3 \ /**********按键获取**************/ int Getkey(int N,int T)1 N4 Z/ M& Y' J$ e8 l { int start=clock();) k6 k4 _3 G* o% X if(KEY_V==115){return 115;} do{ if(kbhit()){3 T0 i8 N% B; r+ l2 n6 X" P KEY_V=(int)(getch());& i" b8 C! T; h if(KEY_V<97){KEY_V+=32;} return KEY_V; }. f" [& `: v; a5 T: {( ]% m for(i=0;i<=N;i++); printf("%d\n",clock()-start);8 }' d* l& }9 l# } }while((clock()-start)<T); dy=1; return -1; }0 Z6 z) Z, F7 |% ] 7 y3 B2 E c+ K7 c3 D" A+ { /***********块体转置*************/( `; e6 j. S8 M ]& I7 a1 j2 m4 } int Rote(int S, int I)0 E# p1 `3 i) o {4 i( ^0 E, a& F. A: x7 _ return (F==0)?TGM[S][(I+4)%4]:SRS[S][(I+4)%4]; } - N* v/ ]5 k3 l$ ^) t /***********擦除显示*************/ int Display(int x, int y, int CAC, int Mode)5 Z8 v+ u# u3 w5 { {. }# f, x, Q3 i4 f for(j=0;j<=3;j++){& y# u% y6 b" @- u3 ^& ` P[j]=CAC&0xF, CAC>>=4;2 r& y, l9 N3 D6 P; X0 f 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; } /***********固化块体*************/( a9 C- Z. w& U9 ^) G int DoBlocks() { //~~~游戏结束 if(Y<2){& R% z v0 {8 l, p Pos(1,22);printf("GAME OVER!"); exit(0);, @% ]# n+ J; B, H1 H5 P% {3 V }$ ?) @8 `7 M: U* p x //~~~固化块体. Y1 J' r# I4 k 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;}) y6 I6 `. W; K8 @2 E# a POINT_V[P_X][P_Y]=1;4 }; i+ M L# H, {2 A+ ]2 t }. X: Y5 w8 o. ~- z" u `) v8 y //~~~关卡得分 for(j=Y;j<=POS_H_MAX;j++){ FLAG[2]=1; for(i=1;i<=10;i++){% V+ y6 D. R5 T% x& U' p! @$ b if(POINT_V[j]==0){FLAG[2]=0;}' u& w! f. I5 ` } if(FLAG[2]){/ r% L8 E. B. z6 m SCORE+=10,MARK[j]=1; if(SCORE==400){ SCORE=0,LEVEL+=1,T-=100;5 r6 B! f7 v6 Z" P' S FLAG[4]=1; } } } //~~~极品消行 for(j=20;j>=5;j--){ if(FLAG[4]){1 r3 U+ R ?/ N9 [# K: ` for(i=1;i<=10;i++){5 ~- R& _5 @* x/ F4 ?# S) O/ n POINT_V[j]=0;2 u9 Q: n6 h4 ^. V: f0 @ Pos(i,j);printf(" "); } }$ D4 o, S0 `; G* a1 u else if(MARK[j])$ h" e7 d0 l. i/ | { MARK[j]=0,J=j-1;7 s* ]: E9 M9 ]0 @$ w6 J for(N=1;N<=3;N++){ if(MARK[J]){J--;} } MARK[J]=1;- \/ d6 y# C0 t$ c8 N! T2 ?2 U for(i=1;i<=10;i++){ Pos(i,j); if(POINT_V[j]=POINT_V[J]){+ z v. q4 L `, u printf("■"); }else{ printf(" "); } }' T4 E( Z9 ~1 O5 T }& l* s, Z( P( | }8 V7 T6 T9 Z2 @9 ~ FLAG[4]=0; return 0; } 9 Z% E" @( @% {5 Z3 X /***********碰撞检测*************/ int CheckCollision() { for(j=0;j<=3;j++){) }- a2 `. N' M P_X=(P[j]>>2)+X+dx,P_Y=(P[j]&0x3)+Y+dy; if(POINT_V[P_X][P_Y]){0 U) f$ ?. U% G8 }# Y/ ?( ^2 }! L if(dx!=0){return 1;}) c) M' j; `5 x' k& R9 q if(dy){ DoBlocks();. P, O2 H- w4 I& \. I$ y Pos(12,3);printf("LEVEL:%-3d",LEVEL);2 {' w$ Y" ^: v J Pos(12,5);printf("SCORE:%-3d",SCORE);4 M9 P6 j; d' K5 G: x9 y return 2; } if(KEY_V==119){FLAG[0]=1;}, D/ l& [0 |: O$ ~5 Q } }+ c2 q; K! C3 T" y$ W6 p return 0;" @ e. y; H7 N1 o$ ~5 @+ n; S3 B" u }2 u0 n3 v) f3 O0 R& l4 Y, `; m unsigned 5 v8 ^( l# V: [ X /***********循环核心*************/, |/ n7 U$ `$ F1 O$ Q; [! O int GameCycle(int N, int T, int F) {5 h/ s- m# D& Y! a0 r2 X srand((unsigned)time(NULL));RU=rand()%7,RI=(rand()%4); while(1){ if(FLAG[3]){" C" O) }& j; W+ C- Q& H( S+ R. o* H Display(12,8,Rote(RU,RI),0); m# S) N g! u* | 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);+ R! ]- \, {' _6 z. V" P" J Display(X, Y,Rote(NU,NI),1);' r* X% w- C3 n, _3 a4 E; { } dx=0,dy=0; " }& _8 r$ r) D2 j KEY_V=Getkey(N,T);& K) j6 k! h# i( y if(KEY_V==119){5 P, ]1 c! ?$ r/ h NI++; Display(X,Y,Rote(NU,NI),2);% [! Q7 ~# F; R- b6 H# ?+ l }//旋W! b; w; B' C B+ j else if(KEY_V==115){dy= 1;}//下S7 H" O2 T* f" t' |$ U, f# h else if(KEY_V==97 ){dx=-1;}//左A else if(KEY_V==100){dx= 1;}//右D7 j2 w9 r0 d" ^; \ else if(KEY_V==112){getch(); }//暂停P4 w: }0 y: Y0 {! P; Q7 | 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;$ X% r8 x' S8 u5 J) | Display(X,Y,Rote(NU,NI),0); }9 l% m" ^! p' {: h6 S' v else if(KEY_V==119){ Display(X,Y,Rote(NU,NI-1),0);6 d0 B$ x4 F5 Z+ y }else{: R1 {" ]0 g# }; V4 C: O- z Display(X,Y,Rote(NU,NI),0);+ C; F- d5 w/ K6 W* \ }, Q! u; X' o2 z7 I Display(X+dx,Y+dy,Rote(NU,NI),1); X+=dx,Y+=dy;' ?9 M7 K! l7 M2 D } } }2 r5 @' c; M4 c# [3 _ return 0; } /**********Main主函数***********/ int main()2 ]: }4 E' h# i0 _, ]# n2 R6 W {3 j" J8 p1 ] M. n" p9 ? system("color F0&mode con cols=35 lines=25"); HideCursor(); e' p* Q- L6 Z2 [+ | CreatUI(); GameCycle(10,800,1);+ i( s" ^: V, L. C# w& b return 0; X# e& o2 u/ e- K% L }; O) ?2 x3 t/ P5 j6 { #后记 里面涉及的很多东西,我都没有完全总结出来,作者的巧妙非常令我佩服,后面应该还会有文章分析。 大家有什么想法或者自己的解读,可以留言,欢迎一起讨论,共同进步,探索代码里面的奥秘和乐趣。 方块消除6 r t N$ c# g* e$ E& Y5 s 准备下一个方块 得分机制" x) U. O5 f4 B1 e- w7 c0 R Game Over 判断 1 ^/ u: y3 z/ C2 d; n* W* i8 d" \& a3 e' g: b2 p |
微信公众号
手机版