|
贪吃蛇是个非常经典的游戏,用C语言来实现也是一个好玩的事情。这个游戏我写完后放在知乎,竟然点赞的人数超级多。我觉得大家喜欢,一个方面是因为写得简单,大家都能看得懂,一个可扩展性还是非常强的。 我试了说一下这个代码 核心的三个函数 menu();; g$ c5 }; O( y0 r7 @! msetup(); draw();" Y) t& l* \: w6 \) I menu用来设置菜单,也就是我们一运行看到的那个。setup用来设置参数,我们需要设置高度和宽度,还有分数,食物的位置。draw也就是画,也就是画整个画面。 还有一个枚举类型 这个结构体用来设置蛇的几个状态,我觉得这个也是面向对象编程的一个思想,把蛇的状态都封装成一个枚举类型。 typedef enum{" s" ?# Y7 W7 a9 ~ STOP = 0, LEFT,+ u$ m4 b1 g) R% u RIGHT,8 r" }- g; E4 k, A UP, DOWN }Direction; 还有 /*判断贪吃蛇的长度*/' }! z2 F* p& D) |# t1 {& m5 b evoid logic()) q$ K' y6 A7 q6 ~6 U9 o% L 这个函数,这个函数应该是整个贪吃蛇的精髓了,要理解代码怎么把蛇给连接起来。用了点巧妙的东西。 来看这里面的关键代码 /*把上一个位置记下*/1 X- Z& p( P% k1 |2 h1 Zint lastX = tailX[0];$ J$ }! `, f* Q% C5 a2 e; }! E int lastY = tailY[0]; int last2X, last2Y; /*重新获取当前的位置*/ tailX[0]=x; tailY[0]=y;7 X1 l- J, j! r3 ~- X7 l int i=0; /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/ for(i=1; i<ntail;i++)) \4 F% H6 z7 O* N, K6 L1 P { last2X = tailX;9 @- D1 G+ P; Z3 a) W last2Y = tailY; tailX=lastX;8 W/ q0 Z3 h, q! S tailY=lastY;; @- A8 r u& J, ^ lastX = last2X;' ?$ o% z- u" p$ n- B2 R lastY = last2Y; }0 I+ @5 k" ~: d3 x8 B% V lastX lastY 用来存上一次的蛇头的位置。后面的 for 循环,通过tail 蛇的长度,把蛇上个状态给保存到数组tailX tailY里面。 完整代码 " d' Y7 I, n$ |; O9 X$ h) v! W#include <stdio.h>! e# D/ Y6 t" _ _% K #include <stdlib.h> #include <stdbool.h> #include <Windows.h>1 q! a2 |- {7 X$ L #include <time.h>$ N$ B2 |# l% v) n #include <conio.h> /*键盘输入获取*/: W6 k. O- y. a ) y2 |0 v2 C+ D0 X0 Z* X$ }1 s bool gameOver; bool stop = false;$ \% S. w# G) W7 k' G3 y% x* B bool hit = false; /*游戏的边框大小*/$ k) [* X) a4 r const int width = 50;% ]' k+ _ t/ y const int height = 20; /*蛇的坐标,食物的坐标还有分数*/0 G0 N9 H {$ D9 F5 G. z int x,y,fruitX,fruitY,score; /*蛇每个点的坐标*/; t2 ]* @- G9 `2 m/ i# P. A5 W3 P3 L$ ` int tailX[200],tailY[200]; |% q6 P- i, `. n! z7 b7 X6 w3 y7 H /*蛇的默认长度*/: a* U4 K$ {( j3 {9 G: q int ntail=3;& |* q" V( T2 a3 F7 j typedef enum { STOP = 0, LEFT, RIGHT,, O" i* b6 S# b; O; g UP, DOWN }Direction;. N/ a" p- r# i1 G9 P+ R$ V& W Direction Dir;) S- L' s5 O: q8 i7 P. g' W /*开始菜单*/ _$ x. |% p- l: k+ Q/ H& x, r$ ^ void menu() { int a;1 h/ Y: e! _6 A& | printf("------------------------------------------------------------------\n");8 x; ^; m. L: P printf("| 贪吃蛇游戏 |\n");. f8 ?; m+ ?7 N# Z0 e( X9 Y printf("| 1) 新游戏 |\n");- n5 R+ P6 o& M1 X) |: R4 [: k printf("| 2) 开始边界 |\n"); printf("| 3) 退出游戏 |\n");. z0 _3 k4 a6 A7 } printf("------------------------------------------------------------------\n");+ J( g7 o. L, T3 Z8 z$ G printf("---->请输入你的选择:"); scanf("%d", &a); } /*初始化状态*/) W; g& m0 q) o; j8 F% h void setup(): W: i/ u) M& |- J {6 Q% K8 |, q0 ^, E& E8 r; q. n z6 c gameOver = false;) `3 Q3 T; h* ^3 X: ~ /*根据当前时间设置“随机数种子”*/& ]& N1 W- v4 b& M3 T/ y m srand(time(NULL)); Dir = STOP;) k/ P1 N& a2 s( E2 n! M7 @( X% _ /*贪吃蛇的位置,固定在中间*/- ^6 Q' l( f! ]2 x+ K x= width/2;3 {0 |& t9 E( j6 [" B3 ]; k4 s" r y= height/2;' u4 _& U8 U$ r+ W; f /*食物的位置,位置是随机的*/ fruitX = rand()%width; fruitY = rand()%height; score = 0;& C( f* d, r( k- ^3 Q. x9 a }' t# a* ~* S- ]. k /*绘制界面*/ void draw() { if(stop == true) {- N9 g& y8 q2 e D$ u return; } system("cls");/*清除屏幕*/% B, J1 R3 {9 f- j3 ?( Q4 ? a' s printf("分数:%d",score);& `( {+ }- Y7 y$ w8 r printf("\n");2 T, c9 S8 ~ J /*第一行*/. J& W! r+ B9 U8 W; R8 l int i; for(i= 0 ;i<width+1;i++), q; ]; ~. {; Q! N8 w3 w* U { printf("-");3 c5 ?- z( S' x& t/ |" F } printf("\n");1 m/ A# X& L8 l: x- s9 {: I & I8 |, i3 y* J /*画中间的画面*/ int p; for(p= 0 ;p<height;p++)/*高度*/6 \, \4 i" G, A* d' V( q { int q; for(q= 0 ;q<width;q++)/*宽度*/- }' H" k( H6 f9 p0 p) J/ V { /*第一行最后已给字符*// r+ C; |7 H. W/ L2 c if(q==0 || q==width-1) {$ i* o. r6 \" k: ?! r printf("|"); } % r. U: s3 Q; n O- g" e& c if(p == fruitY && q == fruitX)/*食物的随机坐标*/ {9 y% [- X* C. w8 A2 I$ m printf("O");) v" o. @8 ]- [/ o } else; p) U" L% C/ h0 X { int k=0; bool print = false; /*贪吃蛇的长度 默认长度是 3*/7 K4 A; q% \$ I' c for(k=0;k<ntail;k++) {6 O. Z: r/ d9 s+ U( f. N' o if(tailX[k]==q && tailY[k]==p)7 M% z4 t8 r/ n. d( Z# D { printf("*");. M6 [ E: p. ^* e( a5 u print = true; } }& S4 D0 b2 R+ H4 o8 o /*如果这个位置打印了 * 就不要打印空格了*/( Q) f4 q; q- x+ O if(!print)9 u- d3 M8 l* d { printf(" "); } } } printf("\n"); }# x& ]' ]* W3 B# _7 j$ B1 t' _% p : ~ g- ?7 r8 E/ M8 B+ v; |0 D2 z /*最后一行*/ int j; for(j= 0 ;j<width+1;j++) {& n( B. q" X6 U printf("-"); }: h& i0 j" `0 F3 H# k ( ~0 S4 B% i+ D0 V4 }+ g1 \ } /*按键输入控制*/& E. b) p9 j1 [5 h& m4 W' l% h void input() {6 I& v$ x* ~, r5 ^, V2 w5 P9 U if(_kbhit()) { /*获取键盘的输入字符*/; B) I2 s* y% I8 z" { switch(_getch()) { O$ P: |, \, ?, X( g! Y case '4': case 75:/*左键*/% ]1 q( _ F1 \& V! K: ?7 h Dir = LEFT; hit= true; break;; [) |. p/ `+ t% N6 j; U% N case '8':/ t- E2 }% s9 L! @- t& ^ case 72:/*上键*/ Dir = UP;! I, n( y) W* `9 Q7 R, g hit= true; break; case '6':7 O1 i! W2 `+ y# j9 ?) L case 77:/*右键*/$ M* P+ O3 k1 w Dir = RIGHT;6 ~1 E% _' n# [) S hit= true; break;3 c7 I1 u7 V- |2 l& u5 y4 \/ V; V2 ` case '2':' c/ Z4 ?' I5 }. l# U( S* e# {+ D case 80:/*向下键盘键 */ Dir = DOWN; hit= true;$ k& i* Y b0 Z break;/ r; V6 V0 E4 r8 a' g) R: P. t case 'x': case 27:/*ESE*/1 t: e, ^- k+ A gameOver = true;' w3 z6 ?9 r/ X break;0 {, U x8 Y+ M( }" l- k5 ` case 32:/*空格 暂停键*/$ _# r' Z: R9 f) t1 V, n stop = !stop; break; } }( Q! e* A0 b4 L% b c: o else if(!hit && stop == false)/*如果没有改变方向*/ { x++; } } /*判断贪吃蛇的长度*/ void logic() {5 | e- p) Q* m/ [6 ^: _ if(stop == true)4 |3 l" J6 R. Q8 n8 j2 A { return;, Z- j0 I3 f1 g9 `" ^* V }1 Y! G+ W. E7 a1 o# R! N /*把上一个位置记下*/4 _8 e, z3 f& o5 ^$ l int lastX = tailX[0];7 N" g6 H5 k0 u+ ?; G" R) v int lastY = tailY[0]; int last2X, last2Y;7 F/ h3 \# m5 I/ K" f: ]' N" L1 L /*重新获取当前的位置*/ tailX[0]=x;- e3 s& [: Q: A) W. B: Z tailY[0]=y;& c3 z% R5 j$ s3 s! w6 x int i=0; /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/ for(i=1; i<ntail;i++) {! h1 L% d4 V- K/ X: Z: v! Y last2X = tailX; last2Y = tailY;' b% A/ X1 ~- f6 I) ^7 l, H tailX=lastX;& q. \4 P; e- i& g: d tailY=lastY; lastX = last2X; lastY = last2Y; } /*根据方向来改变x y 的值*/2 F* G4 m- Y) C( H7 H switch(Dir) {6 b! v4 Z2 Z, ~+ _4 a2 Q8 W case UP:/ D9 m' d; O1 p% F y--;6 y- V5 {9 T- t: P1 P) V6 z break; case DOWN: y++;1 R! M; u' b" z% X break; case LEFT: x--; break;9 g* Z- n: y" c: x1 V case RIGHT: N2 a- V1 g7 U4 t1 q9 L$ ?" o x++;, C+ P f0 R2 g break; } if(x<0 || width<x || y<0 || height<y)6 B# t2 e& `) Q4 i; S {7 k, Y9 r6 \; |. {7 V6 S gameOver = true; /*清除屏幕*/2 ?- m% Z3 t! l8 @+ I: X, e system("cls");3 Z6 Q7 q" L% X4 N- j printf("------------------------------------------------------------------\n"); printf("| |\n");/ H3 I# D! ^5 ` W1 G8 G; c6 | printf("| |\n"); printf("| 游戏结束 |\n"); printf("| |\n"); printf("| |\n");. g5 P ?; u- `6 D j% A" B* o6 O1 |$ A printf("------------------------------------------------------------------\n"); } if(x==fruitX && y==fruitY); u" C0 P& e/ a( l2 i# N0 | {2 y0 j: Q' f6 m, }' Z0 W' V/ |$ c /*吃了一个食物,蛇的长度增加1*/8 Y% I \& T: ~ ntail++; score+=10; /*更新下一个食物的位置*// R( t9 ]- ?7 N" n3 d fruitX = rand()%width; fruitY = rand()%height;9 Z$ v3 H) o: w: A) j; T, B } }( Y% h% Y) Y! Q5 b* H2 m5 c0 l5 b1 } w int main()5 w4 [ M2 W) w9 t' V { #if 0& e! \9 t& S- v9 v! M0 B while(1)8 b) }3 v7 P) _! ~6 T {+ k8 p, ^' f/ {! ~ printf("%d\n",_getch());' K1 N; H5 A; C, F, y } #endif menu();3 r: ^/ }2 U/ F setup();! r: t# H+ f9 V& c draw(); /*循环画贪吃蛇的界面*/ while(!gameOver) {- p8 e2 p" [9 y( }6 v- u draw();. i7 e( N8 J0 t# X input(); logic();- N9 k4 m( g) d- L, u9 S: t" K5 M7 G Sleep(70);1 k. l- P4 E* v, W5 S# _ } 0 A0 z8 z2 K4 H/ l& e, e4 F return 0; }: z9 K# g4 \: v9 b5 } 上面这段代码直接在Dev C++上面应该是可以运行的,很多人在知乎上私信问我,为什么我的贪吃蛇执行不了呢,可能就是平台不同,少了这个头文件,少了那个头文件,但是你为什么不能跟我一样,用Dev C++呢,轻量级,简单。代码的精髓是什么?我认为精髓一定是思想,不是你写了多少行代码,用了什么高端的IDE。
我自认为我的注释已经写得不错了,所以就没有什么好说明的了吧,有不明白的把代码过一下,至于屏幕刷新这个东西,如果只是用时间刷新就会闪屏,所以出现了一个双缓存,把要显示的东西送到一个buff里面去,另一个buff用来显示,这样就可以保证不会出现闪屏。除了写贪吃蛇,可以用这个方法写其他小程序,挺有意思的。 在知乎上,发起了一个C语言 100 行代码之内实现贪吃蛇的问题。我觉得很不错,里面很多同学的回复都非常赞,特别是叶大神的回复。 http://www.zhihu.com/question/360814879/answer/1013986215 ( Y- ]4 ]5 x- l' ^# f |
| 谢谢分享! |
【经验分享】基于STM32的PS2遥控小车
STM32F7移植NES游戏
介绍几款可以在 Linux 系统中用命令行也能玩的游戏
Ratel——在命令行内进行简单的斗地主游戏
一个 C 程序——一次调用输出的井字棋游戏
STM32F446+OLED12864跑贪吃蛇游戏
DevBoy-stm32游戏机
【MCU实战经验】+基于stm32和LCD12864的贪吃蛇游戏制作,游戏分析(提供源码)
基于STM32与树莓派的游戏机
STM32实现的游戏手柄,具鼠标,键盘,手柄,摄像头功能
微信公众号
手机版