|
贪吃蛇是个非常经典的游戏,用C语言来实现也是一个好玩的事情。这个游戏我写完后放在知乎,竟然点赞的人数超级多。我觉得大家喜欢,一个方面是因为写得简单,大家都能看得懂,一个可扩展性还是非常强的。 我试了说一下这个代码 核心的三个函数 menu();2 c- `/ L# [4 E3 Q; C2 nsetup(); draw();: `* n# z/ W9 K; l menu用来设置菜单,也就是我们一运行看到的那个。setup用来设置参数,我们需要设置高度和宽度,还有分数,食物的位置。draw也就是画,也就是画整个画面。 还有一个枚举类型 这个结构体用来设置蛇的几个状态,我觉得这个也是面向对象编程的一个思想,把蛇的状态都封装成一个枚举类型。 typedef enum{ STOP = 0, LEFT, RIGHT, UP, DOWN }Direction; 还有 /*判断贪吃蛇的长度*/void logic()- u v) l1 C- o) V" d 这个函数,这个函数应该是整个贪吃蛇的精髓了,要理解代码怎么把蛇给连接起来。用了点巧妙的东西。 来看这里面的关键代码 /*把上一个位置记下*/int lastX = tailX[0];$ Q; f( L4 g# R1 V1 i8 }$ @5 v int lastY = tailY[0];* A* X4 d& @: a* p! _( }& |. h+ ~. ?, _. M int last2X, last2Y; /*重新获取当前的位置*/ tailX[0]=x; tailY[0]=y;5 J$ p( y/ w& m' y( y6 t int i=0; /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/) W z, _7 v$ G for(i=1; i<ntail;i++) { last2X = tailX; last2Y = tailY; e8 U0 c' Q( s) `, ]/ o! G* b tailX=lastX; tailY=lastY;& X: Z5 ?8 c" @* n' t8 {7 R lastX = last2X; lastY = last2Y; } lastX lastY 用来存上一次的蛇头的位置。后面的 for 循环,通过tail 蛇的长度,把蛇上个状态给保存到数组tailX tailY里面。 完整代码 #include <stdio.h> #include <stdlib.h> #include <stdbool.h># f- w8 |# O8 t3 v2 R$ R #include <Windows.h> #include <time.h> #include <conio.h> /*键盘输入获取*/ ]& W3 y; @3 `5 h2 u3 w7 e bool gameOver;' n9 @& E( b+ \2 ~7 v: g bool stop = false;% P- J1 }4 w9 _: E& q1 B bool hit = false; /*游戏的边框大小*/; N( [$ |. K3 D/ h3 d2 { const int width = 50; const int height = 20; /*蛇的坐标,食物的坐标还有分数*/ int x,y,fruitX,fruitY,score;3 M2 @6 U' v6 x. y- w /*蛇每个点的坐标*// |* @7 k* b4 C2 s! Y/ E int tailX[200],tailY[200]; /*蛇的默认长度*/ int ntail=3; ) ~4 R0 m; Z+ Z' { typedef enum! b( d) g, k& f, B2 b# k2 P {$ J' L! L4 o, X, a STOP = 0, f, h, e8 O# D, F# k LEFT,' E5 `: I+ c# D* @3 d( x; W RIGHT,$ }. q$ K9 X3 c8 a' u UP,2 O4 U' O9 O$ [. R) B1 p, S6 q5 O DOWN+ o( }" {. S4 C9 y' c8 u; T }Direction; 9 F, Z! @9 M! F) l) i; P. z$ B" C Direction Dir; /*开始菜单*/ void menu() { int a; printf("------------------------------------------------------------------\n"); printf("| 贪吃蛇游戏 |\n");3 y* B6 `% ]: y; `+ v! s9 t printf("| 1) 新游戏 |\n"); printf("| 2) 开始边界 |\n");; J2 O0 q T! p5 d; q, [9 O( ` ] printf("| 3) 退出游戏 |\n");8 F. i( A- d. R( z6 P# i printf("------------------------------------------------------------------\n"); printf("---->请输入你的选择:"); scanf("%d", &a);4 {5 Z5 I, c' z" h/ ^ }$ y$ S1 G/ t8 ?8 K T" z# \ /*初始化状态*/ void setup() { gameOver = false; /*根据当前时间设置“随机数种子”*/ srand(time(NULL)); Dir = STOP;8 [& e2 J% ^8 h' S% B: W0 G# } /*贪吃蛇的位置,固定在中间*/ x= width/2;, K; {: r3 ?) a1 M y= height/2; /*食物的位置,位置是随机的*/& h8 J4 D0 U+ N7 }/ J( d" Z fruitX = rand()%width;: e1 e* k) ?4 k5 c) q1 d fruitY = rand()%height;- G3 o6 W+ e* e6 Y+ b" ^ score = 0;# L4 r' K2 T- a" [* { }4 Z3 K! h5 d9 n/ Q6 A: R6 F! K /*绘制界面*/ void draw() { if(stop == true) {. s' T1 l8 w; M( w: [ return;& P+ Q% t- S1 i- a }, m+ e8 p; A* E/ n% x; l system("cls");/*清除屏幕*/( P% U! S) `. a0 `9 }5 v& T printf("分数:%d",score);/ U0 V6 X; ?/ l; q9 d2 U4 x2 w% P printf("\n"); /*第一行*/ int i;4 b4 l6 ^" Z% m- A1 w1 U8 L for(i= 0 ;i<width+1;i++)6 ?% X1 C4 L7 W; t& q { printf("-"); }2 e8 u8 l' x; o printf("\n"); 2 W# E1 A/ b: d) A. v1 L /*画中间的画面*/7 O" B0 Y! V ]: \( z6 \- z0 V/ S int p; for(p= 0 ;p<height;p++)/*高度*/! K# C1 G# k+ }) w8 N+ ^ { int q; for(q= 0 ;q<width;q++)/*宽度*/ {, R* k; d! q; C1 U /*第一行最后已给字符*/: g' `9 O; n+ i' O# t if(q==0 || q==width-1) { printf("|");- N% v3 @/ z# v s3 q } ) z i* }1 c7 L+ c9 N) O# H if(p == fruitY && q == fruitX)/*食物的随机坐标*/& G: v& R- |$ \) W. O2 M { printf("O");! |5 I$ m& P7 ]! L" U! ?- [* I! F; Y% a } else( S' ?3 V. e6 M# n. a0 X {7 E, a3 ^6 F, v: G8 K int k=0;2 V$ a. g% i( }+ o bool print = false; /*贪吃蛇的长度 默认长度是 3*/ for(k=0;k<ntail;k++)" T1 H# P2 z5 x8 Y {; D/ O* ]( {- q5 a5 u* N if(tailX[k]==q && tailY[k]==p)2 B x" g( H( X+ C: m6 P; P) A {6 a# u8 s/ i& N* K( n. Y printf("*");9 } K, n+ |: S1 d+ y print = true; }5 ^6 ^+ H& d9 S7 M+ a( M4 w# W4 k } /*如果这个位置打印了 * 就不要打印空格了*/ if(!print). e( \" @; x n4 T {- t; c/ m# ?& F) I* T$ l" S Z. M printf(" "); }* T+ G6 |% x4 T6 f5 f } }: ]. A, } Y9 Y printf("\n"); }( z& g1 Q* ^) n2 h& p1 k7 ^ 8 O* S% V3 a6 B, C/ B" N5 m /*最后一行*/ int j;/ t2 H T8 M7 S$ {1 `/ v& @$ E for(j= 0 ;j<width+1;j++) {' u4 X; l/ G) o* e6 b9 a2 R printf("-"); } } /*按键输入控制*/ void input() {* i4 a4 s* L d if(_kbhit())# i0 A0 a0 w4 g/ x2 _/ e { /*获取键盘的输入字符*/$ R) P, L9 K, A1 I5 v5 O switch(_getch())$ c9 s2 W* u# V+ z$ ^% W4 K0 ] { j' X3 D, ~, n- g# f case '4': case 75:/*左键*/& Q# w* B( q+ | Dir = LEFT;- L, C z! C- J2 B* F* J! u' X hit= true; break;5 P1 N; w- ^/ V! x1 ?4 J9 k+ E" i* A case '8': case 72:/*上键*/$ T' c4 F- j# x* X; j S Dir = UP;4 w& F( v- L/ `+ d% T- g7 D hit= true; break;, s$ p1 K! A/ o# d% o- U case '6':5 m: O* i I+ T3 x' ?0 o6 @9 T case 77:/*右键*/ Dir = RIGHT;* u, m! T1 H+ F9 u0 @ hit= true; break;9 p7 i" q2 m+ q: N: p6 Q4 a' L5 M0 r case '2':3 I0 ?- T! X2 T# y$ c) @) K case 80:/*向下键盘键 */8 z& }* s1 R" ^6 y7 [) \7 Q Dir = DOWN;+ J3 x/ S3 N* s8 `1 N hit= true; Z( b. k0 f5 v% y V3 U n break; case 'x': case 27:/*ESE*/8 l! F, E; C* V0 q gameOver = true; break; case 32:/*空格 暂停键*/ stop = !stop; break;* I9 N+ X: s1 P# E3 f }( \' d% k; D( [5 B+ Q2 h } else if(!hit && stop == false)/*如果没有改变方向*/ {. j3 A- @1 w6 V p, h8 Z x++;" q* u3 }& p' X" Y; m! O6 @/ t } } /*判断贪吃蛇的长度*/) j: X$ [, T& ^" F* P: i3 ~1 u void logic() { if(stop == true) {* H/ v5 y6 d1 E) ]" Q return; }$ G- y3 A9 O b [+ U% q 6 K1 h) x6 ?2 M, M$ L+ ~1 Q' O /*把上一个位置记下*/1 ?, s' m# `* r int lastX = tailX[0];$ z3 }# ~: K. j4 T! Q, Z int lastY = tailY[0]; int last2X, last2Y; /*重新获取当前的位置*/ tailX[0]=x;& O( j" Y4 B& q tailY[0]=y;- ~; f( Q5 b) N; V int i=0;/ z9 G2 s& F) ~8 u; U( C /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/ for(i=1; i<ntail;i++) { last2X = tailX; last2Y = tailY; tailX=lastX;- Q1 ?/ _# z2 [( i4 I tailY=lastY;; R+ c# t% N+ q lastX = last2X;! p6 J3 I2 H2 L0 z+ K" n; h2 n+ ? lastY = last2Y;. |9 V9 }) h H! @9 l } /*根据方向来改变x y 的值*/- D! S/ p% M; S# b8 u7 d" V @ switch(Dir) { case UP:1 r e o+ E s y--; break; case DOWN: y++; break;; E2 |( s8 ^- V5 i0 U case LEFT:& @9 D3 o0 ~/ G8 X/ Z2 q* s% a+ { x--; break; case RIGHT: x++;9 S! g& l* ~1 G$ q8 X) V: Q break;1 u9 ?9 G* D3 d- z } if(x<0 || width<x || y<0 || height<y)9 j( B! ^% G6 l {* J/ M1 i1 f7 C gameOver = true; /*清除屏幕*/ system("cls");' G1 G6 n- |; V* k printf("------------------------------------------------------------------\n"); printf("| |\n"); printf("| |\n"); printf("| 游戏结束 |\n"); printf("| |\n"); printf("| |\n");# A' R$ Y$ I! l1 L% ^ printf("------------------------------------------------------------------\n"); } if(x==fruitX && y==fruitY)5 d2 X+ F+ m. a+ O8 N& B {8 ~6 b& d6 s: F% L0 x8 {) T! f5 x& H /*吃了一个食物,蛇的长度增加1*/ ntail++; score+=10;0 Y" R/ n7 K4 q) y- n2 { /*更新下一个食物的位置*/ fruitX = rand()%width; fruitY = rand()%height; }$ S( [8 V7 p5 |7 l7 h8 M } K+ V4 E `, p int main() { #if 0 while(1) {5 P d" w) o: E+ R printf("%d\n",_getch()); }! U4 ?+ d4 G; a+ J# V #endif menu(); setup();7 u: h% N, T2 z& r% _" Y* Y draw();- C% l4 j; w/ c, g, z /*循环画贪吃蛇的界面*/ while(!gameOver)& s' s5 y$ \ U" Z1 V {/ J g4 B5 _$ c% v9 } draw(); input(); logic(); Sleep(70);% S6 |) j7 l H5 F3 A9 o } return 0;% z) w8 Q$ T. |1 S5 Y! R9 A3 a }- F! a! K9 _; ? 上面这段代码直接在Dev C++上面应该是可以运行的,很多人在知乎上私信问我,为什么我的贪吃蛇执行不了呢,可能就是平台不同,少了这个头文件,少了那个头文件,但是你为什么不能跟我一样,用Dev C++呢,轻量级,简单。代码的精髓是什么?我认为精髓一定是思想,不是你写了多少行代码,用了什么高端的IDE。
我自认为我的注释已经写得不错了,所以就没有什么好说明的了吧,有不明白的把代码过一下,至于屏幕刷新这个东西,如果只是用时间刷新就会闪屏,所以出现了一个双缓存,把要显示的东西送到一个buff里面去,另一个buff用来显示,这样就可以保证不会出现闪屏。除了写贪吃蛇,可以用这个方法写其他小程序,挺有意思的。 在知乎上,发起了一个C语言 100 行代码之内实现贪吃蛇的问题。我觉得很不错,里面很多同学的回复都非常赞,特别是叶大神的回复。 http://www.zhihu.com/question/360814879/answer/1013986215 / A- S: W5 ^1 n! j& j% v# M |
| 谢谢分享! |
【经验分享】基于STM32的PS2遥控小车
STM32F7移植NES游戏
介绍几款可以在 Linux 系统中用命令行也能玩的游戏
Ratel——在命令行内进行简单的斗地主游戏
一个 C 程序——一次调用输出的井字棋游戏
STM32F446+OLED12864跑贪吃蛇游戏
DevBoy-stm32游戏机
【MCU实战经验】+基于stm32和LCD12864的贪吃蛇游戏制作,游戏分析(提供源码)
基于STM32与树莓派的游戏机
STM32实现的游戏手柄,具鼠标,键盘,手柄,摄像头功能
微信公众号
手机版