贪吃蛇是个非常经典的游戏,用C语言来实现也是一个好玩的事情。这个游戏我写完后放在知乎,竟然点赞的人数超级多。我觉得大家喜欢,一个方面是因为写得简单,大家都能看得懂,一个可扩展性还是非常强的。 我试了说一下这个代码 核心的三个函数 menu();setup();7 N9 @3 ] ?# W; w* G( P draw(); menu用来设置菜单,也就是我们一运行看到的那个。setup用来设置参数,我们需要设置高度和宽度,还有分数,食物的位置。draw也就是画,也就是画整个画面。 还有一个枚举类型 这个结构体用来设置蛇的几个状态,我觉得这个也是面向对象编程的一个思想,把蛇的状态都封装成一个枚举类型。 typedef enum{ STOP = 0,3 X2 Y& T' ?$ l2 p [' N( c3 B LEFT, RIGHT," Y2 M3 q& u, O% o$ f UP,( y# n7 S+ D2 ^ E9 ?& A' G DOWN }Direction; 还有 /*判断贪吃蛇的长度*/void logic()( o4 v3 L) }7 Q* K 这个函数,这个函数应该是整个贪吃蛇的精髓了,要理解代码怎么把蛇给连接起来。用了点巧妙的东西。 来看这里面的关键代码 /*把上一个位置记下*/- E$ L* c0 ~* \+ Y" J) Y! Jint lastX = tailX[0]; int lastY = tailY[0]; int last2X, last2Y; /*重新获取当前的位置*/3 N7 i. Y7 m. q5 F8 |. R4 b9 o tailX[0]=x;' {9 U" ~) a! b* l tailY[0]=y;' A+ ?) w5 j, D- |6 o, N# w int i=0; /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/9 R5 B6 p+ M! @/ r/ P4 e V for(i=1; i<ntail;i++)8 p; G, v5 |/ a: N4 H {! q' @) w4 W6 e, H( d6 j; \ last2X = tailX; last2Y = tailY;4 g/ }- r7 Z) O tailX=lastX; tailY=lastY; lastX = last2X;" T7 \. e7 V7 M9 P J lastY = last2Y; } lastX lastY 用来存上一次的蛇头的位置。后面的 for 循环,通过tail 蛇的长度,把蛇上个状态给保存到数组tailX tailY里面。 完整代码 - Y: _6 o$ b+ e9 y#include <stdio.h> #include <stdlib.h>! I( \; {# f& M! K# `5 I #include <stdbool.h> #include <Windows.h> #include <time.h> #include <conio.h> /*键盘输入获取*/ bool gameOver;! V9 }% T" |% C4 `) Q7 z bool stop = false; bool hit = false; /*游戏的边框大小*/ const int width = 50; const int height = 20;" D0 y6 P1 ~5 \$ E /*蛇的坐标,食物的坐标还有分数*/ int x,y,fruitX,fruitY,score; /*蛇每个点的坐标*/" _6 u8 G6 F2 ^7 k5 J8 `3 ? int tailX[200],tailY[200]; /*蛇的默认长度*/6 t) x7 e- p5 T/ b9 U int ntail=3; typedef enum { STOP = 0, m# ~* h& h0 F! K& q" w LEFT, RIGHT, UP,7 X8 o. G5 A$ n5 `3 _ DOWN$ Y6 L6 w$ b7 b, @) r3 X. L }Direction; Direction Dir;& C+ y W7 ^! {) h, a! C /*开始菜单*/ void menu() {2 c( B2 z: g" k0 Y* Y int a; printf("------------------------------------------------------------------\n"); printf("| 贪吃蛇游戏 |\n"); printf("| 1) 新游戏 |\n"); printf("| 2) 开始边界 |\n");- x! W$ Z) F% J# C9 y( ?. a" P printf("| 3) 退出游戏 |\n");3 a- \& S" X4 W {8 |1 b printf("------------------------------------------------------------------\n"); printf("---->请输入你的选择:");; e/ a p; `9 R5 k* h( f scanf("%d", &a);+ g8 F& c" ]' x, U* V, e: U) d+ S& | } 5 r) E, `+ I7 T" j$ H* v3 V /*初始化状态*/ void setup() { gameOver = false;: A. \# u9 ^% s( ]! l /*根据当前时间设置“随机数种子”*/- H+ h1 H4 C2 ~& ^- W( ]( U srand(time(NULL));- [( ` b0 |/ x Dir = STOP; /*贪吃蛇的位置,固定在中间*/$ \ ]8 n- o8 D1 |+ B# X x= width/2;1 _7 o# [! ^3 D, {* B* }, _6 K y= height/2; /*食物的位置,位置是随机的*/ fruitX = rand()%width;1 c) F( R8 s8 g, o fruitY = rand()%height;8 C4 e: @0 j% p+ ?8 ]* {( x1 T score = 0;' C! F. G: t& r' U8 H- c1 g G- f } /*绘制界面*/4 g' n. f& B8 X void draw()5 S% ~6 `/ |' j7 V {9 u) k& S4 e$ Q* Y$ a8 D if(stop == true) { return;- n( _6 L6 `! ]/ m } system("cls");/*清除屏幕*/" E* F* F+ p" D; w$ v printf("分数:%d",score); printf("\n");- u0 M4 G4 S# | /*第一行*/ int i; for(i= 0 ;i<width+1;i++) {5 w- o1 _7 }; h printf("-"); }3 ?" D& k7 S. o1 h4 U0 h* V printf("\n"); /*画中间的画面*/ int p;& \# B, K' [" U8 ~; k, e" E8 G for(p= 0 ;p<height;p++)/*高度*/7 O/ i* |7 D1 ^ { int q; for(q= 0 ;q<width;q++)/*宽度*/ {4 S: N7 e6 a; t; t& @* G. ] /*第一行最后已给字符*/ if(q==0 || q==width-1), i, G7 d A: [3 K, m! | { printf("|"); } [) G2 ^4 P) V2 v I# i. w) U& D if(p == fruitY && q == fruitX)/*食物的随机坐标*/8 e) z I# N3 l j: J1 @# X { printf("O");. r; ^% @) A& T }; F$ `2 h( ~3 d! q else { int k=0;- s# x) q- }' a7 Q' u, M bool print = false; /*贪吃蛇的长度 默认长度是 3*/9 Y& w+ R% r' l; K& Y9 M7 X for(k=0;k<ntail;k++) {( F% R9 r$ u/ J5 z" ` if(tailX[k]==q && tailY[k]==p) {; @; O: w: K3 G; h& z5 n4 @ printf("*"); print = true; } } /*如果这个位置打印了 * 就不要打印空格了*/ if(!print) { printf(" ");: A0 v! ^+ k/ Z- i } } }$ h# v) K& J% D3 ]8 K/ E$ a printf("\n");' F/ z9 L* I8 B I* G } 2 J5 A+ e, A6 ~9 K+ X9 x /*最后一行*/# ?* b4 R$ d2 z* y! H% L2 V int j;: [, \" n, N2 g% f, F8 ^ for(j= 0 ;j<width+1;j++) {7 k( ?7 y2 |8 j printf("-");9 E( f4 N m4 q! G+ g4 V } 5 w9 w: x6 P8 M* B$ j% n; } }5 R, L/ Q) q- U7 M. H6 }/ h /*按键输入控制*/ void input() {( O4 x1 ^" l/ U if(_kbhit())5 ]" N" }; l" F, [6 v+ {0 b { /*获取键盘的输入字符*/ z" |' L, i! u switch(_getch())1 M! B4 w3 `5 p! }+ x% g {* _- N: w3 V1 C7 n# Z% D* R X case '4': case 75:/*左键*/2 _9 u/ P$ Y! n* D& i Dir = LEFT;+ d0 `9 i8 M' ^. c" ]/ y hit= true; break; case '8': case 72:/*上键*/ Dir = UP; hit= true;1 L" H( }8 o* l# g* Q( z break; case '6': case 77:/*右键*/+ ?8 B( |+ q# g/ @ Dir = RIGHT;5 y* f/ `) ?2 h. t hit= true; break; case '2': case 80:/*向下键盘键 */ Dir = DOWN;( p7 |* u4 B/ @: L. m$ G hit= true; break; case 'x': case 27:/*ESE*/7 q7 z0 |" w# D; a& d gameOver = true;4 `1 r0 T9 o3 N, H- ? break;9 C6 i% _+ Y, D7 N case 32:/*空格 暂停键*/ stop = !stop; break; } }% Y# }# Q4 c) t) j- n; o$ N else if(!hit && stop == false)/*如果没有改变方向*/8 p( d, q$ A* A) C8 S {; G0 G. X' } z, [& b x++;+ \& h3 h" G: n2 Y0 C }3 E) |% G6 I2 Y }* W B" N6 H3 g /*判断贪吃蛇的长度*/( i1 u: a8 J# b `/ D* @ Z( M void logic()) A, r( G+ C- N7 r" u {7 N- D# e0 D% V" h4 I& n2 S3 { if(stop == true) { return; }: y l6 j0 `$ p% p 0 Y- o+ P& M1 p6 f /*把上一个位置记下*/ }4 j# ?4 t: U, v- T- ]) {+ n int lastX = tailX[0]; int lastY = tailY[0]; int last2X, last2Y; /*重新获取当前的位置*/ tailX[0]=x; tailY[0]=y; int i=0; /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/* M' [) c/ |. m for(i=1; i<ntail;i++) {& F2 N& h( j/ d! p y4 `4 e! v last2X = tailX; last2Y = tailY;4 }* F2 S( c- ^$ S6 u" D; N tailX=lastX; tailY=lastY; lastX = last2X; lastY = last2Y; } /*根据方向来改变x y 的值*/ switch(Dir) { case UP:# J) M6 N) `/ w3 f( @4 F7 D+ {6 ^ y--; break; case DOWN:/ B! ^# e8 i) I9 Q3 M y++;3 Q2 E% x9 B* W1 v" K! G# O8 j, M break; case LEFT: x--; break;; U3 @& M+ j0 z- v- K case RIGHT:+ A$ O7 M3 d2 ]- l x++; break; } if(x<0 || width<x || y<0 || height<y) {4 } m \$ t; [2 n7 z: e gameOver = true;: M% F D* j' _6 e+ E /*清除屏幕*/& Z0 _ c# m5 d4 b* p system("cls"); printf("------------------------------------------------------------------\n");6 f. t( R% A0 p5 x8 j0 } printf("| |\n"); m; A" q0 S% c9 \2 I9 U1 N0 F printf("| |\n"); printf("| 游戏结束 |\n"); printf("| |\n"); printf("| |\n"); printf("------------------------------------------------------------------\n"); } if(x==fruitX && y==fruitY) {: q6 l& A6 t; X5 h9 R. W /*吃了一个食物,蛇的长度增加1*/ ntail++;9 b+ p& R2 g {) u score+=10; /*更新下一个食物的位置*/# j# Y$ s4 ?6 r* T) Q& d fruitX = rand()%width; fruitY = rand()%height;. r) x' e, H1 B" {8 ^$ } }' d1 D( C/ o" g n6 p5 U } int main() {& G% e, K9 R; G1 [8 t; f #if 0; w+ O8 P2 Z' m while(1). [1 p- I1 F5 b {; V A0 L" T7 N) Q% B printf("%d\n",_getch());! ~4 M6 e# `. i6 y }, c" ~" \7 t# }& B #endif menu(); setup();3 O0 n/ Q! R3 |1 W0 _ draw(); /*循环画贪吃蛇的界面*/ while(!gameOver) { draw(); input(); logic(); Sleep(70); }, f+ @7 C% C0 y6 @ ' [* |; h5 l$ r! f) h8 e5 \' [! ] return 0; } 上面这段代码直接在Dev C++上面应该是可以运行的,很多人在知乎上私信问我,为什么我的贪吃蛇执行不了呢,可能就是平台不同,少了这个头文件,少了那个头文件,但是你为什么不能跟我一样,用Dev C++呢,轻量级,简单。代码的精髓是什么?我认为精髓一定是思想,不是你写了多少行代码,用了什么高端的IDE。 我自认为我的注释已经写得不错了,所以就没有什么好说明的了吧,有不明白的把代码过一下,至于屏幕刷新这个东西,如果只是用时间刷新就会闪屏,所以出现了一个双缓存,把要显示的东西送到一个buff里面去,另一个buff用来显示,这样就可以保证不会出现闪屏。除了写贪吃蛇,可以用这个方法写其他小程序,挺有意思的。 在知乎上,发起了一个C语言 100 行代码之内实现贪吃蛇的问题。我觉得很不错,里面很多同学的回复都非常赞,特别是叶大神的回复。 http://www.zhihu.com/question/360814879/answer/1013986215 |
谢谢分享! |