|
贪吃蛇是个非常经典的游戏,用C语言来实现也是一个好玩的事情。这个游戏我写完后放在知乎,竟然点赞的人数超级多。我觉得大家喜欢,一个方面是因为写得简单,大家都能看得懂,一个可扩展性还是非常强的。 我试了说一下这个代码 核心的三个函数 menu();( f* a% O) U5 w0 ^! T& ~( ~& |setup(); draw(); menu用来设置菜单,也就是我们一运行看到的那个。setup用来设置参数,我们需要设置高度和宽度,还有分数,食物的位置。draw也就是画,也就是画整个画面。 还有一个枚举类型 这个结构体用来设置蛇的几个状态,我觉得这个也是面向对象编程的一个思想,把蛇的状态都封装成一个枚举类型。 typedef enum{ STOP = 0,. W) l4 ]' U5 J* t- P5 a1 K L LEFT, RIGHT,! F; K' f) W- Q/ B UP, DOWN }Direction;: E! J5 {5 H8 k/ n# }. v 还有 /*判断贪吃蛇的长度*/void logic() 这个函数,这个函数应该是整个贪吃蛇的精髓了,要理解代码怎么把蛇给连接起来。用了点巧妙的东西。 来看这里面的关键代码 /*把上一个位置记下*/int lastX = tailX[0]; int lastY = tailY[0]; int last2X, last2Y; /*重新获取当前的位置*/ tailX[0]=x; tailY[0]=y;, [+ u. k! g: x) b/ C5 [* c! \& y int i=0; /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/ for(i=1; i<ntail;i++); @+ l% r& C) z* x" g+ x {/ ]& p& ?9 E3 X8 T6 p last2X = tailX; last2Y = tailY; tailX=lastX; tailY=lastY; lastX = last2X; lastY = last2Y;4 d5 k$ Q, E0 p0 W0 \5 f }! F, x$ V X1 {' e lastX lastY 用来存上一次的蛇头的位置。后面的 for 循环,通过tail 蛇的长度,把蛇上个状态给保存到数组tailX tailY里面。 完整代码 #include <stdio.h>5 B1 i1 |; r& P7 y, n #include <stdlib.h>9 I( @' U7 \9 ~0 l, B #include <stdbool.h> #include <Windows.h> #include <time.h>, {; A4 X" ~0 o0 p5 @7 @# x' S #include <conio.h> /*键盘输入获取*/# g" I n# K3 ^) Y1 y ( N z5 `+ r7 O' U4 V) D0 I8 K* G bool gameOver; bool stop = false; bool hit = false; c+ C7 @7 x" J9 k% C) _( C* K2 K9 I /*游戏的边框大小*/+ Z7 ^: ~ I, h. e; O const int width = 50; const int height = 20; /*蛇的坐标,食物的坐标还有分数*/ int x,y,fruitX,fruitY,score;/ K& x7 t1 D, s& @* b /*蛇每个点的坐标*/ int tailX[200],tailY[200]; /*蛇的默认长度*/) ~+ h2 Q& y8 f( q; ]5 n: N: a int ntail=3;$ z' r: I1 {1 [- G2 G+ O& r1 _ : @6 Q8 A4 }+ N3 M5 n% r% U1 H typedef enum' x. u2 R8 N! d2 h7 O/ u' I! u { STOP = 0,* s, `6 Y* s8 Y0 y$ r LEFT, RIGHT, UP, DOWN }Direction; 6 x; V# ]* u: D1 \# o w Direction Dir; /*开始菜单*/( k l3 U4 k, h- r0 K: C void menu() { int a;) }% D9 ]/ T( o3 f) }2 f- `& h/ U printf("------------------------------------------------------------------\n");' C$ Q! B% n2 w8 \) G' c! E* ? printf("| 贪吃蛇游戏 |\n"); printf("| 1) 新游戏 |\n");9 o0 }: c ^, b8 ] J8 k' v" Y printf("| 2) 开始边界 |\n"); printf("| 3) 退出游戏 |\n");3 S! w- m+ [ U* ~5 n printf("------------------------------------------------------------------\n"); printf("---->请输入你的选择:");. V6 N6 G, X/ r& Z3 m1 F; r/ f9 ? scanf("%d", &a);; z) w, q6 L9 G( t' p. g } /*初始化状态*/6 ]: M% `0 n l void setup() {. K" N" g, t* A gameOver = false;- M1 x3 T& p* x j0 i /*根据当前时间设置“随机数种子”*/ srand(time(NULL)); Dir = STOP;, `) B# a$ P3 S, K ; W. h0 i9 N/ m /*贪吃蛇的位置,固定在中间*/# z0 h, X: L% k3 l x= width/2;3 F% u0 ?/ E- I/ n y= height/2;* F2 |4 Y- U! C5 ?7 Z' i /*食物的位置,位置是随机的*/ fruitX = rand()%width;% W7 C. c; b2 W* j m. h/ D fruitY = rand()%height; score = 0; } /*绘制界面*/ void draw()8 J. W' ~- s* r4 I; @ { if(stop == true)& V( o# z7 N+ ~: m; G( F2 k4 Z1 h { return; }/ \1 h, Q4 q0 B' |8 h system("cls");/*清除屏幕*/ printf("分数:%d",score); printf("\n"); ; f' _2 R8 K0 l! _& c6 \. I /*第一行*/7 T: j% E. Z5 Z0 z- } int i; for(i= 0 ;i<width+1;i++)1 f4 `; P- x- O/ P' P {& H3 u$ [3 S$ C" R. ?' _& \ printf("-"); } printf("\n");0 G' X* Y4 c, M( } /*画中间的画面*/8 D1 U* Q# u, J% |4 }% K3 |; _! M int p; for(p= 0 ;p<height;p++)/*高度*/ {% [6 S, d% J& C7 w, h" z4 k4 @( B int q; for(q= 0 ;q<width;q++)/*宽度*/3 x3 H) g7 r' K- m- } {! d( ^3 x! j. j" Y' i2 \5 ~ /*第一行最后已给字符*/4 `% Z1 L) J. @( Z# x9 U" |, z if(q==0 || q==width-1) b) a! j6 M6 ? {4 ]0 ] F4 l4 E' P printf("|"); } . l% y) [( Z* _) E if(p == fruitY && q == fruitX)/*食物的随机坐标*/8 A/ R4 H6 |/ V& ]7 _8 |5 A4 @ { printf("O");. @* ~5 i, y: K1 Q. _; V } else { int k=0; bool print = false;, m | o, [* y% ~: U% t /*贪吃蛇的长度 默认长度是 3*/$ _7 Y Q1 Q1 T ~" o( t for(k=0;k<ntail;k++) { if(tailX[k]==q && tailY[k]==p) { printf("*");6 g: L( w$ k, U6 c print = true; } } /*如果这个位置打印了 * 就不要打印空格了*/* P# k6 \5 W# q$ {, ]' d" r. T) F if(!print) { R5 K& Y3 K) l9 B9 D' y( ^ printf(" "); } }5 S. G( l+ Y0 f }8 p' r* ~' T; ~8 B* G% l. A printf("\n");; R5 I! X. T* Q h% t } : X# p$ r- V* [ /*最后一行*/* k g' Y0 I1 S3 U- q! g int j; for(j= 0 ;j<width+1;j++)( A a# r* o7 ^& e: Q {4 P) n* N% d" C printf("-");# [$ A/ l- B1 a7 y# t }- @& U, o# w, i# @; L }4 e# P% l" o, P; u /*按键输入控制*/ void input() {( D3 ^6 S: L" ~# p( A; E2 E if(_kbhit()) {: ?- P$ }/ r; w /*获取键盘的输入字符*/) E: h8 s2 p4 _8 p2 u2 `* R* \. o switch(_getch()). d4 F3 Z k" R {# ^) D6 c1 J/ Z" b case '4':3 m5 J; w9 Q) {' J/ q5 _( q% G case 75:/*左键*/ Dir = LEFT;9 [: G; R3 y3 {, d( @2 F hit= true; break;, @& j' J, h2 K case '8': n9 P& q7 J- a7 c3 k case 72:/*上键*/5 s# |& [" m; ~$ \& ?0 \# P- s Dir = UP; hit= true; break; case '6':4 u. b" R4 v. Y case 77:/*右键*/# S! b8 A6 k, f: J& K9 F9 T Dir = RIGHT; hit= true; break;9 f$ m# c* Z1 G0 t' M# h- ?2 p case '2':' B3 V, t# h1 G$ c9 Y; R' p. y case 80:/*向下键盘键 */ Dir = DOWN; hit= true; break; case 'x': case 27:/*ESE*/ gameOver = true;; |4 ?. P" O% N/ |( i* R3 Z* Q0 L break; case 32:/*空格 暂停键*/ stop = !stop; break; } }$ Y. `/ a" @8 M* Q- F3 S' T4 d/ O else if(!hit && stop == false)/*如果没有改变方向*/9 o1 R' F$ T( T4 D1 u7 ~- O, ?( ` { x++; }; |! P; P. }: D* B! }0 k }, f5 p% L$ M* t5 w0 Z% \ /*判断贪吃蛇的长度*/; {+ d! M C K% _ void logic()4 z7 F" f+ r' _% r4 b {9 [/ }, H, m1 {3 l& g if(stop == true)* s: u/ G. U5 y8 J* n( }% O# u7 z) B { return; } /*把上一个位置记下*/ int lastX = tailX[0]; int lastY = tailY[0]; int last2X, last2Y; /*重新获取当前的位置*/3 F# M9 a/ k2 h' J' ]* Y: `4 k tailX[0]=x;6 {$ z, P5 C& C1 V) p0 B tailY[0]=y;; v- E6 J& t0 }9 j int i=0; /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/ for(i=1; i<ntail;i++) { last2X = tailX; last2Y = tailY;+ j4 \1 h2 e; t0 F' J Q tailX=lastX; tailY=lastY; lastX = last2X;6 z3 _) p, [* C( ]$ G9 `8 q1 s lastY = last2Y; }, p4 s& n' q3 A7 m! t /*根据方向来改变x y 的值*/4 K* X6 `" ]0 A: ]9 }! x( u switch(Dir) {# P/ B8 O& O0 n% \8 K7 Z case UP: y--; break;: P2 g; F2 R; S case DOWN:/ {2 d. [5 w: j y++; break;, b. ]5 R/ |7 g) P case LEFT: x--; break; case RIGHT:( A* a5 |0 y$ }' g x++; break; } if(x<0 || width<x || y<0 || height<y) { gameOver = true;: F3 }2 l; T8 j7 w, q( B /*清除屏幕*/ system("cls");9 ~. d1 ?3 {; u) R printf("------------------------------------------------------------------\n"); printf("| |\n");* Y6 c# `6 W A, w; j' ` printf("| |\n"); printf("| 游戏结束 |\n");/ V7 t% C- e' b printf("| |\n");" g1 d8 a2 M) K8 r5 e- i* M printf("| |\n");8 l6 H& Z) c7 Y7 ?* b T( E printf("------------------------------------------------------------------\n");$ T5 ^1 L$ J5 ~ C( A* V } if(x==fruitX && y==fruitY)7 n5 F8 U3 w0 Q2 S9 C { /*吃了一个食物,蛇的长度增加1*/ ntail++;6 O! D7 n& p* x9 |2 \( g score+=10; /*更新下一个食物的位置*/ fruitX = rand()%width;, F6 q3 M/ t" M$ L% x* s) Z fruitY = rand()%height;. J3 j) z" j: f* F; P; J; d( q1 a& t }. [1 Y2 K$ U1 H' O; a7 W } int main() {1 u* `, F& w2 u0 P0 \* h3 {; a #if 0 while(1) { printf("%d\n",_getch());( f3 i6 A: J. h, K3 i& ?$ O } #endif menu(); setup(); draw(); /*循环画贪吃蛇的界面*/) I8 A- v3 ]% m( T, X0 Q( | while(!gameOver) { draw();5 }, T& u9 w0 \ input(); logic();$ X+ |: d4 B% G9 y; V% V2 h Sleep(70); }& J& d. H- }) q$ v3 v# C. O - P! }, j1 w' B, u( d2 W return 0;5 P; D+ o' Z8 ]" A5 l }& C' ~$ m* c4 d4 U' W4 R 上面这段代码直接在Dev C++上面应该是可以运行的,很多人在知乎上私信问我,为什么我的贪吃蛇执行不了呢,可能就是平台不同,少了这个头文件,少了那个头文件,但是你为什么不能跟我一样,用Dev C++呢,轻量级,简单。代码的精髓是什么?我认为精髓一定是思想,不是你写了多少行代码,用了什么高端的IDE。
我自认为我的注释已经写得不错了,所以就没有什么好说明的了吧,有不明白的把代码过一下,至于屏幕刷新这个东西,如果只是用时间刷新就会闪屏,所以出现了一个双缓存,把要显示的东西送到一个buff里面去,另一个buff用来显示,这样就可以保证不会出现闪屏。除了写贪吃蛇,可以用这个方法写其他小程序,挺有意思的。 在知乎上,发起了一个C语言 100 行代码之内实现贪吃蛇的问题。我觉得很不错,里面很多同学的回复都非常赞,特别是叶大神的回复。 http://www.zhihu.com/question/360814879/answer/1013986215 6 @( K) \/ O& v |
| 谢谢分享! |
【经验分享】基于STM32的PS2遥控小车
STM32F7移植NES游戏
介绍几款可以在 Linux 系统中用命令行也能玩的游戏
Ratel——在命令行内进行简单的斗地主游戏
一个 C 程序——一次调用输出的井字棋游戏
STM32F446+OLED12864跑贪吃蛇游戏
DevBoy-stm32游戏机
【MCU实战经验】+基于stm32和LCD12864的贪吃蛇游戏制作,游戏分析(提供源码)
基于STM32与树莓派的游戏机
STM32实现的游戏手柄,具鼠标,键盘,手柄,摄像头功能
微信公众号
手机版