
贪吃蛇是个非常经典的游戏,用C语言来实现也是一个好玩的事情。这个游戏我写完后放在知乎,竟然点赞的人数超级多。我觉得大家喜欢,一个方面是因为写得简单,大家都能看得懂,一个可扩展性还是非常强的。 我试了说一下这个代码 核心的三个函数 menu();setup(); draw(); menu用来设置菜单,也就是我们一运行看到的那个。setup用来设置参数,我们需要设置高度和宽度,还有分数,食物的位置。draw也就是画,也就是画整个画面。 还有一个枚举类型 这个结构体用来设置蛇的几个状态,我觉得这个也是面向对象编程的一个思想,把蛇的状态都封装成一个枚举类型。 typedef enum{ STOP = 0, LEFT, RIGHT,# N% c: v" P. H7 v UP,7 h* c/ s7 i1 u DOWN }Direction;# m' I* O0 p% H z/ @ 还有 /*判断贪吃蛇的长度*/8 v3 }! w2 g$ ?, Svoid logic() 这个函数,这个函数应该是整个贪吃蛇的精髓了,要理解代码怎么把蛇给连接起来。用了点巧妙的东西。 来看这里面的关键代码 /*把上一个位置记下*/int lastX = tailX[0];; |- I0 _7 ]7 D int lastY = tailY[0]; int last2X, last2Y;/ e2 R" k5 P# S4 [( n& b /*重新获取当前的位置*/7 I- ^4 Z# p+ l# O tailX[0]=x;5 G9 X% u; G5 M, p) } tailY[0]=y;8 T" S s) N9 X: H: N) G+ m a int i=0; /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/ for(i=1; i<ntail;i++)% s- l% n$ i, w, s9 b7 o( O { last2X = tailX; last2Y = tailY; tailX=lastX;& z; P) w; n1 `5 |1 u tailY=lastY;0 C* K) K8 i# o7 v: ^7 j lastX = last2X;( G X `% V" z lastY = last2Y;6 k7 k) x m2 u } lastX lastY 用来存上一次的蛇头的位置。后面的 for 循环,通过tail 蛇的长度,把蛇上个状态给保存到数组tailX tailY里面。 完整代码 5 { H& ]. |: k0 O' I" [# x, C#include <stdio.h>' M+ I2 [6 ]: D/ A' [# R #include <stdlib.h>- {5 H4 E0 q1 @+ D% m7 M #include <stdbool.h>* L+ ^4 `+ t8 @) a) b# K8 K #include <Windows.h> #include <time.h> #include <conio.h> /*键盘输入获取*/! g0 b9 h( ~4 @" s. l bool gameOver; bool stop = false; bool hit = false; /*游戏的边框大小*/ const int width = 50;, @% n) a' L8 a const int height = 20;. L7 ~7 z. l+ Y8 w7 { /*蛇的坐标,食物的坐标还有分数*/ b0 \% X! G8 W! h2 @) J int x,y,fruitX,fruitY,score;& g8 {5 ~; K* R2 N5 s$ Y4 p1 H /*蛇每个点的坐标*/4 a2 ~0 q# J: N: S& e: _ int tailX[200],tailY[200];3 p/ W5 g- Y$ J; r0 v /*蛇的默认长度*/ int ntail=3;$ b; |+ ~) T( x, Z, J/ p typedef enum { STOP = 0, LEFT, RIGHT, UP, DOWN6 p6 R- S6 }) e% y }Direction; 0 M9 m# r0 B0 f Direction Dir;# b2 x/ T& J2 ~ ?: i /*开始菜单*/ void menu() { int a; printf("------------------------------------------------------------------\n");+ f- x3 i" e- T7 V, } printf("| 贪吃蛇游戏 |\n");) ?* Y) o1 N) a6 n" t9 X+ q/ b printf("| 1) 新游戏 |\n"); printf("| 2) 开始边界 |\n"); printf("| 3) 退出游戏 |\n");2 M$ Y' {6 u$ \. F7 A- j printf("------------------------------------------------------------------\n"); printf("---->请输入你的选择:");. W7 O, ^' p O0 K scanf("%d", &a);3 M) B+ E# m# V: |; T } + n/ C A! \; n" ], N /*初始化状态*/ void setup()( a5 W) c5 A3 z9 X8 a/ _' j$ J { gameOver = false;) A& Z2 B8 r1 _8 t3 A /*根据当前时间设置“随机数种子”*/ srand(time(NULL));; c7 ]/ s% }3 o' ?/ r! t Dir = STOP;) o( {; T- U) Y& X' H " u$ I# \6 J/ Y/ w5 [ E" _+ ^2 X /*贪吃蛇的位置,固定在中间*/ x= width/2;( \, u/ Y9 X+ V6 W& N8 @ y= height/2;; t* U, B% B% }! k3 N /*食物的位置,位置是随机的*/ fruitX = rand()%width; fruitY = rand()%height;: [) }% R7 u% M7 I) N$ B, V score = 0;; o `, t" u9 @: A }. D6 B4 K* [% x1 m2 p C /*绘制界面*/2 _1 d ~4 m+ M6 i9 c void draw(), S: q2 C8 p2 t { if(stop == true)' t# ?! v6 o7 r7 d" O/ q2 ~3 ^ {, ]0 u4 L' z1 ?; {: d4 v return;! I, v: f% \1 q+ S# ` } system("cls");/*清除屏幕*/9 H# ]4 e9 h3 L/ }2 ?& f printf("分数:%d",score);" O" a' A' x! z. U printf("\n"); " H9 p$ t! x% i /*第一行*/3 i5 s' h# z4 L q: [ int i;4 L9 N$ W! f' Q. @7 x' {$ o, K for(i= 0 ;i<width+1;i++)" j. J/ h' U; I2 U3 y2 W9 ^+ J { printf("-"); }7 ?( k0 w. i; d+ W printf("\n"); 0 v% w# g+ v5 X9 l5 j" m7 E( V /*画中间的画面*/ U% Z2 n# c6 C N int p;5 ]1 O; ^" G: j+ J for(p= 0 ;p<height;p++)/*高度*/ {+ M+ a" w8 J% p4 }0 ~ J, { int q;6 j8 b6 _4 o0 |9 e9 X4 h for(q= 0 ;q<width;q++)/*宽度*/* r) X: i" o: v9 y9 p# w& r {( W$ ^! O6 x' c. u z" \ /*第一行最后已给字符*/ if(q==0 || q==width-1) { printf("|"); } if(p == fruitY && q == fruitX)/*食物的随机坐标*/ { printf("O"); } else {6 s$ Y( U c+ k6 Y: Y int k=0;& H* p' d" }" J bool print = false;3 h$ y! u9 X) }! g /*贪吃蛇的长度 默认长度是 3*/# p- }- P$ ?$ P/ y3 `7 k/ c4 v9 u; i for(k=0;k<ntail;k++) {7 K# J/ P2 `1 O$ M! Y- k if(tailX[k]==q && tailY[k]==p) { printf("*"); print = true;4 f+ Q* @- e' z# m }8 a8 V7 ^7 k+ M } /*如果这个位置打印了 * 就不要打印空格了*/ if(!print) {* ]; }# h2 m( M9 F printf(" ");3 Q4 o+ M' i% G# C) n# \( I6 K+ N- h } }# R# T8 K* r7 s9 ^2 X1 H+ A }1 Q' f5 g# Y: q0 ~" v4 |( G printf("\n");; }9 }( Y6 M+ E3 ^ } /*最后一行*/0 M+ X0 x+ g7 `2 g int j; for(j= 0 ;j<width+1;j++)* t9 i" [, E6 O" o2 f7 P {9 n- W8 a: f/ ] printf("-");; [1 N2 |6 k- _' {" T' W9 x }; h+ p1 [3 S+ B9 G } /*按键输入控制*/: L+ u3 g" t! ]4 H* {5 r$ {! W4 h4 G void input()5 \% N- Q# K+ S {) J. u& b6 m: r5 ~& k& \ if(_kbhit()) { /*获取键盘的输入字符*/ switch(_getch()); h/ W( P" m; s) F0 Y {0 R, D2 I: K/ n& F( ~6 h case '4': case 75:/*左键*/8 Z5 @2 k7 V: y% V W1 D Dir = LEFT; hit= true; break; case '8':% w& k3 t+ r! Y3 L) ?- m6 @: @' w case 72:/*上键*/4 a& l) X2 C# c( s# [, c3 {4 x' U Dir = UP;* b" a' x& K0 r4 S3 H. G hit= true;1 [8 F. s8 ]* v( `& ? break;) E" e9 l. }/ V7 v: I+ b, { case '6': case 77:/*右键*/ Dir = RIGHT;7 |' p+ S1 u1 Z0 { hit= true;% [% [& E& |! Q7 u6 U break;+ `/ x' O6 E1 c ]& I5 o& @ case '2': case 80:/*向下键盘键 */ Dir = DOWN;# P* U! B5 ?1 x4 e4 N$ n hit= true; break;& s* d' E5 r4 a- P' |6 g case 'x':8 s/ v( P' I# v f4 t9 I case 27:/*ESE*/ gameOver = true; break;- ]* \' S5 T& u$ S0 @; L case 32:/*空格 暂停键*/ stop = !stop;2 i* Y* i/ B; _: f break; } } else if(!hit && stop == false)/*如果没有改变方向*/ { x++; } } /*判断贪吃蛇的长度*/ void logic() {) B; b- j/ a1 e7 w' `9 j6 N- t if(stop == true) {+ B. }6 G" s, k& ` h$ ?9 H& _' g return; }$ R& K8 @9 o5 M- E( m /*把上一个位置记下*/ int lastX = tailX[0]; int lastY = tailY[0]; int last2X, last2Y;. K7 E( S) J' _' g8 b# G /*重新获取当前的位置*/ tailX[0]=x; tailY[0]=y; int i=0; /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/ for(i=1; i<ntail;i++) {3 O1 U; e# e. n* x! E last2X = tailX; last2Y = tailY;' z+ p: H, M3 s9 Z+ p3 p tailX=lastX; tailY=lastY; lastX = last2X; lastY = last2Y; }$ s4 W. U) b) r5 C4 J' n7 a /*根据方向来改变x y 的值*/, b, Y3 r6 o4 u- h! p& m0 y$ V switch(Dir)8 u1 P2 l# ^% R2 f# J) b; X7 j6 c { case UP:/ }5 f% N) J8 D% x# s y--;! p% V/ B8 ^& y9 n5 g; t5 ~8 D break; case DOWN: y++; break; case LEFT: x--; break;9 Q/ X5 e' ?$ [ case RIGHT:, t3 Q5 u9 n' f x++; break;- `' v" q& y# t( L }1 Z! @8 [) j0 T; k( S if(x<0 || width<x || y<0 || height<y) {( d6 o/ j. d0 x) I7 ? gameOver = true;9 k0 @( B2 M+ S /*清除屏幕*/ A/ j# R5 G# x& E! ?' @ system("cls"); printf("------------------------------------------------------------------\n"); printf("| |\n"); printf("| |\n");" J$ k* t3 [, A7 i7 \( t" y printf("| 游戏结束 |\n");8 p; h0 C( Q1 y' a, M printf("| |\n");9 Q, }, S! T: }$ _, z* O printf("| |\n"); printf("------------------------------------------------------------------\n");. j+ |) Y# \8 O p% ] }2 f* ^( L- E9 m4 ~ if(x==fruitX && y==fruitY)0 u4 V3 u* y# s {6 C+ `$ m% t: c F+ G! ^4 g. F$ Y /*吃了一个食物,蛇的长度增加1*/6 E- O% x" Z" H4 N# a9 j ntail++;9 l; r" r* m$ v+ E; }4 d2 M7 ^ score+=10; /*更新下一个食物的位置*/! @8 x& l# y7 Q& `# Q4 s r. B( A fruitX = rand()%width; fruitY = rand()%height; } } int main() {0 g# F3 u. a3 H" I& q #if 0# ]9 [# j% O ^3 y5 G; ~ while(1): J4 v* E; P4 U6 M; s/ b h! X { printf("%d\n",_getch()); } #endif v; z( J+ A8 M6 h4 u menu(); setup(); draw();, P B0 m j: Z, r. a* u9 i /*循环画贪吃蛇的界面*/5 w% o9 X3 M! v$ j7 A while(!gameOver) { draw(); input();) }" U8 {- g0 l logic(); Sleep(70); } g6 L* e9 j8 ^! A0 ~ ' }1 ^ E, ]% F% c$ A: v return 0;' F G! }1 s% O/ h }' F1 ^% C2 K: n" C* h d/ P4 S. \- J 上面这段代码直接在Dev C++上面应该是可以运行的,很多人在知乎上私信问我,为什么我的贪吃蛇执行不了呢,可能就是平台不同,少了这个头文件,少了那个头文件,但是你为什么不能跟我一样,用Dev C++呢,轻量级,简单。代码的精髓是什么?我认为精髓一定是思想,不是你写了多少行代码,用了什么高端的IDE。 ![]() 我自认为我的注释已经写得不错了,所以就没有什么好说明的了吧,有不明白的把代码过一下,至于屏幕刷新这个东西,如果只是用时间刷新就会闪屏,所以出现了一个双缓存,把要显示的东西送到一个buff里面去,另一个buff用来显示,这样就可以保证不会出现闪屏。除了写贪吃蛇,可以用这个方法写其他小程序,挺有意思的。 在知乎上,发起了一个C语言 100 行代码之内实现贪吃蛇的问题。我觉得很不错,里面很多同学的回复都非常赞,特别是叶大神的回复。 http://www.zhihu.com/question/360814879/answer/1013986215 |
谢谢分享! |