|
贪吃蛇是个非常经典的游戏,用C语言来实现也是一个好玩的事情。这个游戏我写完后放在知乎,竟然点赞的人数超级多。我觉得大家喜欢,一个方面是因为写得简单,大家都能看得懂,一个可扩展性还是非常强的。 我试了说一下这个代码 核心的三个函数 menu();setup(); draw(); menu用来设置菜单,也就是我们一运行看到的那个。setup用来设置参数,我们需要设置高度和宽度,还有分数,食物的位置。draw也就是画,也就是画整个画面。 还有一个枚举类型 这个结构体用来设置蛇的几个状态,我觉得这个也是面向对象编程的一个思想,把蛇的状态都封装成一个枚举类型。 typedef enum' i7 G+ a3 d0 b8 i! n9 d, V$ Z4 e{ STOP = 0, LEFT,! f# V: y6 D( X; _$ z) d* k9 M7 c RIGHT, UP,$ K3 ~# U: x* u- m+ R1 ^ DOWN. P6 H# ~8 ~. p4 N& s5 h }Direction; 还有 /*判断贪吃蛇的长度*/0 b, c8 }1 q5 A5 Z d1 U4 kvoid logic() 这个函数,这个函数应该是整个贪吃蛇的精髓了,要理解代码怎么把蛇给连接起来。用了点巧妙的东西。 来看这里面的关键代码 /*把上一个位置记下*/8 r2 @8 i3 K$ m! J$ `( Z9 ?int lastX = tailX[0];% J, o0 E2 T& Z0 D1 |7 D int lastY = tailY[0];! C+ i) _7 w4 E0 R8 @" ]4 G int last2X, last2Y; /*重新获取当前的位置*/8 ]9 |: p; f6 n# I- o b tailX[0]=x;6 r6 n5 L3 v0 D- l8 U tailY[0]=y;( W0 _5 _: X' U6 ?, A int i=0;6 _6 S) ?7 g9 }; Z6 D& e /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/% P6 I9 b; W% Y7 d for(i=1; i<ntail;i++) {' R7 g; u$ y% t; _/ y last2X = tailX; last2Y = tailY;9 y. E7 `$ {- c( C5 u tailX=lastX;( f6 W' V0 K/ U* u tailY=lastY;# B& [) L4 h8 p9 V! W lastX = last2X;6 v5 h' s7 H3 p: \ lastY = last2Y; }, X' z# Z% T' W3 p) D( x% c# O lastX lastY 用来存上一次的蛇头的位置。后面的 for 循环,通过tail 蛇的长度,把蛇上个状态给保存到数组tailX tailY里面。 完整代码 * g7 n( n1 h8 F+ Z+ y#include <stdio.h> #include <stdlib.h>3 X2 x [4 U Z2 O, {. { #include <stdbool.h> #include <Windows.h> #include <time.h>/ a: Z% B. I! [; M- C2 T #include <conio.h> /*键盘输入获取*/2 m$ K, \: v- F 0 a \; O* i) u$ C) F4 r% n bool gameOver;( n6 v: C8 f+ t bool stop = false;3 O7 H9 W4 R& V1 o, g bool hit = false; /*游戏的边框大小*/8 _1 b9 ?7 W; L! j, U( n- E const int width = 50;$ m6 @; t* d. W8 n0 y/ } const int height = 20; /*蛇的坐标,食物的坐标还有分数*/ int x,y,fruitX,fruitY,score; /*蛇每个点的坐标*/ int tailX[200],tailY[200];/ f4 w) b6 t6 g+ N! O$ Q8 C /*蛇的默认长度*/ int ntail=3; 6 p# D- H: t7 E. z typedef enum {, K0 \7 X; I5 x. ? STOP = 0, LEFT, RIGHT,- w6 u; r& t |6 D f UP,8 T; Z& B# [" i o+ H) x I DOWN, c; \+ J$ N$ y. \6 B }Direction;& J$ Y) c1 C, p; s Direction Dir; /*开始菜单*// X0 v7 {* I/ | void menu()2 O, q; E9 `8 n7 J6 K) j { int a;" H& U% L6 V' G) e, y' y7 V printf("------------------------------------------------------------------\n"); printf("| 贪吃蛇游戏 |\n"); printf("| 1) 新游戏 |\n");6 f+ |! r9 ~% S$ m5 Y; x6 h printf("| 2) 开始边界 |\n");9 a/ u W1 T" B- q$ ~ printf("| 3) 退出游戏 |\n");" B7 H; `9 z/ I* u$ | printf("------------------------------------------------------------------\n"); printf("---->请输入你的选择:"); scanf("%d", &a); } /*初始化状态*/0 K: a' l0 M2 l2 d void setup() {/ U6 R1 _) j$ b6 ~0 l gameOver = false;. A! S) m- f- c3 a$ ^, g0 L /*根据当前时间设置“随机数种子”*/, }2 V, Y( E% `, S+ r srand(time(NULL));. R1 d6 ] U( b5 j/ r, R Dir = STOP; /*贪吃蛇的位置,固定在中间*/' x2 c5 \5 b2 q x= width/2;0 t7 |3 S/ @ z- |" a y= height/2;: z7 y5 U! |) T# d4 O" g+ p* s# e4 v /*食物的位置,位置是随机的*/ fruitX = rand()%width; fruitY = rand()%height; score = 0; } /*绘制界面*/ void draw()! e* j. w" a2 {. A3 I { if(stop == true)+ l* |3 n: i' A9 s {) d) g ]. b" \% u% y5 u/ P4 P return;! W0 A# M% s% A7 L6 o# k. X z }1 Z/ s% x1 y/ ~' F/ I system("cls");/*清除屏幕*/ printf("分数:%d",score); printf("\n"); /*第一行*/ C/ L& C; n6 ~, ]6 ?* R$ t int i; for(i= 0 ;i<width+1;i++) { printf("-"); } printf("\n");$ \4 V! K0 x, S9 g9 d) _ , G1 H! W: P/ r8 t, a* U8 _* V /*画中间的画面*/& }9 H9 W# P* e5 M int p;( s: p$ g( J' L3 w% j for(p= 0 ;p<height;p++)/*高度*/ {: C5 c1 C ]- Z: V, d! S int q; for(q= 0 ;q<width;q++)/*宽度*/- j) f: ~8 ]+ m { /*第一行最后已给字符*/' O9 p% v6 C3 {4 Y if(q==0 || q==width-1) { printf("|"); } if(p == fruitY && q == fruitX)/*食物的随机坐标*/, E+ I. ?8 ^5 }1 p5 i! F {! c+ Y/ [. m6 n s% l5 } printf("O"); }8 g' \9 s* V& F else) n1 U/ m9 M$ y! ` { int k=0;+ Z2 C' o; ]4 \& R4 m Y% ~- y5 K5 u bool print = false; /*贪吃蛇的长度 默认长度是 3*/ for(k=0;k<ntail;k++)& `+ G, G1 q$ y" C {) P! Z" p6 ~7 @; v3 a if(tailX[k]==q && tailY[k]==p)* x) `4 o' ?$ y8 T t { printf("*");% E5 @* o/ h! P; ` print = true; } } /*如果这个位置打印了 * 就不要打印空格了*/ if(!print)( G( {" C$ l9 c+ ^" z. f/ \' j { printf(" "); }# U: W6 B0 w) i X5 } } } printf("\n"); } /*最后一行*/ int j;; M' ^4 }/ u' R/ n for(j= 0 ;j<width+1;j++)# B& {5 f; b$ C* f { printf("-"); }/ ^0 D. n- k8 ^# [ } /*按键输入控制*/ void input() {: u" ^0 C4 z$ O/ w4 V if(_kbhit()) {3 c) q9 b8 k: y/ Y; C) m9 _. C, _4 e /*获取键盘的输入字符*/ switch(_getch()) { case '4': case 75:/*左键*/) `# b2 f, ~( |" z) R) e: g" I7 g2 @5 s Dir = LEFT; hit= true; break;4 u1 i, x- [# _6 q% a( `4 ~6 w case '8':3 D* v7 n/ i8 |3 y _ case 72:/*上键*/ Dir = UP; hit= true;( ^# W( U% n9 x1 s1 V3 H break; case '6':& O2 i, h8 y* s1 \ case 77:/*右键*/ Dir = RIGHT;9 g3 B. q) C5 l0 z8 X hit= true;! B# @) d( U+ V+ p break; case '2':: g$ W2 Q; j) F7 X case 80:/*向下键盘键 */ Dir = DOWN; hit= true;' e7 _1 o: J5 @ s, W; u- i2 S break; case 'x':, y/ t6 Q2 {: H case 27:/*ESE*/ gameOver = true; break; case 32:/*空格 暂停键*/3 S, D8 R4 s: D' b$ _8 @* O9 V stop = !stop;: T* W _" O5 f& z% A9 } break; }4 P- S( H# D+ D& q! @ }" q; W; B! d9 m else if(!hit && stop == false)/*如果没有改变方向*/6 I; k7 ]3 Z, u6 b" d1 x5 E3 d { x++;- v7 G k& n4 b9 ~9 m y; w } } /*判断贪吃蛇的长度*/ void logic() { if(stop == true) {2 [5 V8 n, c9 h( W2 O4 q return; } 1 ~- ?0 q) s! @5 T/ I) { /*把上一个位置记下*/ int lastX = tailX[0]; int lastY = tailY[0];6 z* m) l7 L& j9 u% D3 @9 ^ int last2X, last2Y; /*重新获取当前的位置*/4 X }! x- a) g3 Y5 Y7 y+ d tailX[0]=x; tailY[0]=y;# B) X8 O9 H- y3 a% r. j' `) ? int i=0; /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/ for(i=1; i<ntail;i++)" ^/ ~( T: S. D% ~" O# l! p { last2X = tailX;' y; J% U3 Q2 i/ e' L; M1 X last2Y = tailY;* A4 H' n. k! W) o tailX=lastX; tailY=lastY; lastX = last2X; lastY = last2Y;6 R7 W5 Q1 u6 J* E }8 Z: |1 R+ h' t; |# B; n /*根据方向来改变x y 的值*/ switch(Dir)3 x! c" C% a, [ { case UP: y--; break; case DOWN: y++; |! e# R6 l; Z$ G: W7 C0 { break; case LEFT:" K! u5 _( B" }0 Q x--; break;0 }* h" t+ S1 ]* o$ V: k case RIGHT:( \, K+ [2 @. C x++; break;, ?. v4 N) F" m5 H8 ~ } if(x<0 || width<x || y<0 || height<y)& }4 K; r4 ^/ d U { y! b$ W+ P2 y8 R# c( _+ ]- P gameOver = true;" p. {; A0 ^2 b1 ]3 D) j3 M' ^ /*清除屏幕*/. b& h9 r: }4 y% h' S0 Q system("cls");- j! o4 D* X5 X$ g printf("------------------------------------------------------------------\n");6 ]# p' E! M1 K8 i; c printf("| |\n"); J7 U& @4 g3 S: _ printf("| |\n");% C( O4 }, g9 `: b% x- H+ z printf("| 游戏结束 |\n"); printf("| |\n");; S4 g& ]- b4 h( Q Q printf("| |\n"); printf("------------------------------------------------------------------\n");6 o8 y: I8 F) X. K3 E% R! k$ s8 u } if(x==fruitX && y==fruitY) {# T$ o) a p% _7 H" ~6 ?! @ /*吃了一个食物,蛇的长度增加1*/4 f/ k) z' L" b ntail++;. z+ s6 u% g* [% `1 k) h0 h score+=10;; Z, o0 R a- {+ } /*更新下一个食物的位置*/* V+ s2 x; x4 I, w4 a$ k) k ^ fruitX = rand()%width;& } R3 `+ h1 C0 ]3 q fruitY = rand()%height;7 l5 ^( R: d5 T9 ]3 n } } int main() { #if 0 while(1)2 v2 ?, ^" k- r7 }* v8 A9 K { printf("%d\n",_getch()); } #endif3 V4 v% o& Z7 c' ?2 a3 W menu(); setup();# O5 I% a' ^2 E draw(); /*循环画贪吃蛇的界面*/ while(!gameOver) {/ S1 Z. ], ]' p. |/ z' l- W5 Q- X4 e draw();% h$ o( @0 ]$ k input();+ c& D. m! V$ k+ ]: a V6 u logic();, e5 }( r7 d O0 l1 S( u3 @- ?9 { Sleep(70);" i3 h6 J0 a* M5 [6 d# A }3 c0 e9 w' K( u8 y2 M g) U$ g7 @4 l8 T3 _ return 0;6 I0 |/ g/ W/ [ }+ s6 g2 `. w# G; a* b4 d 上面这段代码直接在Dev C++上面应该是可以运行的,很多人在知乎上私信问我,为什么我的贪吃蛇执行不了呢,可能就是平台不同,少了这个头文件,少了那个头文件,但是你为什么不能跟我一样,用Dev C++呢,轻量级,简单。代码的精髓是什么?我认为精髓一定是思想,不是你写了多少行代码,用了什么高端的IDE。
我自认为我的注释已经写得不错了,所以就没有什么好说明的了吧,有不明白的把代码过一下,至于屏幕刷新这个东西,如果只是用时间刷新就会闪屏,所以出现了一个双缓存,把要显示的东西送到一个buff里面去,另一个buff用来显示,这样就可以保证不会出现闪屏。除了写贪吃蛇,可以用这个方法写其他小程序,挺有意思的。 在知乎上,发起了一个C语言 100 行代码之内实现贪吃蛇的问题。我觉得很不错,里面很多同学的回复都非常赞,特别是叶大神的回复。 http://www.zhihu.com/question/360814879/answer/1013986215 9 V/ K/ @3 _' Q |
| 谢谢分享! |
【经验分享】基于STM32的PS2遥控小车
STM32F7移植NES游戏
介绍几款可以在 Linux 系统中用命令行也能玩的游戏
Ratel——在命令行内进行简单的斗地主游戏
一个 C 程序——一次调用输出的井字棋游戏
STM32F446+OLED12864跑贪吃蛇游戏
DevBoy-stm32游戏机
【MCU实战经验】+基于stm32和LCD12864的贪吃蛇游戏制作,游戏分析(提供源码)
基于STM32与树莓派的游戏机
STM32实现的游戏手柄,具鼠标,键盘,手柄,摄像头功能
微信公众号
手机版