你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

用C语言来写个贪吃蛇游戏!

[复制链接]
gaosmile 发布时间:2020-5-1 21:52

贪吃蛇是个非常经典的游戏,用C语言来实现也是一个好玩的事情。这个游戏我写完后放在知乎,竟然点赞的人数超级多。我觉得大家喜欢,一个方面是因为写得简单,大家都能看得懂,一个可扩展性还是非常强的。

我试了说一下这个代码 核心的三个函数

    menu();
2 t8 X7 Z  t1 U9 N; S    setup();
" u" ]" m8 R# @/ B! V; \" g    draw();

6 M" K, k/ l, b% r1 n( b

menu用来设置菜单,也就是我们一运行看到的那个。setup用来设置参数,我们需要设置高度和宽度,还有分数,食物的位置。draw也就是画,也就是画整个画面。

还有一个枚举类型 这个结构体用来设置蛇的几个状态,我觉得这个也是面向对象编程的一个思想,把蛇的状态都封装成一个枚举类型。

typedef enum
4 F' n) ?# M$ N# w9 k{
4 a' O6 X6 G. X. Y5 w        STOP = 0,
0 z' B! ]3 _. |6 k; C0 w9 @( a        LEFT,
- Z- Y. ~& Y+ X! v0 i2 E' r        RIGHT,# N% c: v" P. H7 v
        UP,7 h* c/ s7 i1 u
        DOWN
9 ?* r! u, [6 W' V8 P! D5 v}Direction;
# m' I* O0 p% H  z/ @

还有

/*判断贪吃蛇的长度*/8 v3 }! w2 g$ ?, S
void logic()

' {. }7 W  E- H+ X' u

这个函数,这个函数应该是整个贪吃蛇的精髓了,要理解代码怎么把蛇给连接起来。用了点巧妙的东西。

来看这里面的关键代码

/*把上一个位置记下*/
, h: c( v/ I2 t- O2 F% z4 S2 h. ~    int lastX = tailX[0];; |- I0 _7 ]7 D
    int lastY = tailY[0];
( p) e5 }1 U# f3 u' C/ P# s) Y    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;
9 y( L, _$ H2 `5 \# b4 I# A    /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/
/ j' c0 {. Z4 i0 q    for(i=1; i<ntail;i++)% s- l% n$ i, w, s9 b7 o( O
    {
* e7 O' z  X0 U7 \        last2X = tailX;
0 x) q  P5 X, G        last2Y = tailY;
) I( Y9 X, c; V- i/ w- g" O( U8 C        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
    }

0 h8 o* A0 G5 ?+ [

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>
+ H: S9 R: k6 p4 u4 @& n9 {% W#include <time.h>
, F' @, X1 E  M- C#include <conio.h> /*键盘输入获取*/! g0 b9 h( ~4 @" s. l

" ]% o( x) b3 i, Z$ bbool gameOver;
& \: r. V; J7 gbool stop = false;
! }( |! g$ S. f* |& I4 u! zbool hit = false;
* j' n% B  w0 y4 ~0 u/*游戏的边框大小*/
4 o( b# @  L  Cconst 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
/*蛇的默认长度*/
/ d$ c( o3 l3 ]int ntail=3;$ b; |+ ~) T( x, Z, J/ p

  D- R, P$ I& y! C9 U% ^3 }typedef enum
! G, T. d/ d) t0 T4 D! f{
4 m9 Z/ I5 x* ], h0 _        STOP = 0,
5 [! E0 Y+ U3 R5 P1 G' ~) G        LEFT,
6 S, ?7 N5 P5 c! A$ ^3 e        RIGHT,
4 M! e5 v7 @7 W4 ?1 W# H6 r3 n        UP,
+ c, g0 V( h0 r2 G7 z. L( @        DOWN6 p6 R- S6 }) e% y
}Direction;
, F( v  L6 q& X3 h' V( w# h$ ~0 M9 m# r0 B0 f
Direction Dir;# b2 x/ T& J2 ~  ?: i
/*开始菜单*/
5 b5 v( ^8 j0 s3 i: }& J6 u* g. uvoid menu()
" {2 f3 H, Z0 k6 P1 t{
# L6 x0 q+ Z' T0 t    int a;
/ |! _) Q- G; b$ J. R* r    printf("------------------------------------------------------------------\n");+ f- x3 i" e- T7 V, }
    printf("|                              贪吃蛇游戏                        |\n");) ?* Y) o1 N) a6 n" t9 X+ q/ b
    printf("|                              1) 新游戏                         |\n");
! `3 y5 @% b9 j1 x    printf("|                              2) 开始边界                       |\n");
1 V) g" p- z( {    printf("|                              3) 退出游戏                       |\n");2 M$ Y' {6 u$ \. F7 A- j
    printf("------------------------------------------------------------------\n");
5 R0 J2 b! U7 q    printf("---->请输入你的选择:");. W7 O, ^' p  O0 K
    scanf("%d", &a);3 M) B+ E# m# V: |; T
}
5 j- A3 m. ?: Q+ n/ C  A! \; n" ], N
/*初始化状态*/
' q% B5 ^- e' a" M3 y" cvoid setup()( a5 W) c5 A3 z9 X8 a/ _' j$ J
{
) A1 E. \" N! r    gameOver = false;) A& Z2 B8 r1 _8 t3 A
    /*根据当前时间设置“随机数种子”*/
2 r( B4 D3 c0 g* c5 [- w    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
    /*贪吃蛇的位置,固定在中间*/
, A& V  S- O  o: M    x= width/2;( \, u/ Y9 X+ V6 W& N8 @
    y= height/2;; t* U, B% B% }! k3 N
    /*食物的位置,位置是随机的*/
! ^/ c9 T$ }+ C; P  {+ c    fruitX = rand()%width;
8 m% \8 d+ I) O3 j    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
{
) E6 v6 H' P, r1 p2 I  U5 Y        if(stop == true)' t# ?! v6 o7 r7 d" O/ q2 ~3 ^
        {, ]0 u4 L' z1 ?; {: d4 v
                return;! I, v: f% \1 q+ S# `
        }
1 g( \5 {/ |! c6 i    system("cls");/*清除屏幕*/9 H# ]4 e9 h3 L/ }2 ?& f
    printf("分数:%d",score);" O" a' A' x! z. U
    printf("\n");
  d! }* @/ Q% G2 u  i4 W  ~    " 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
        {
0 B5 z$ G* Y- L, [! z        printf("-");
2 g" ^% G4 f* q  Y( [9 r    }7 ?( k0 w. i; d+ W
    printf("\n");
- \' I; Y" L- j2 t: a. I    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++)/*高度*/
) Y! A, d5 D' ]6 U; `        {+ 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" \
                        /*第一行最后已给字符*/
5 L0 O7 r* e) Q0 |. e            if(q==0 || q==width-1)
# B- T) d! E4 `# U+ p% ]                        {
/ ~( K' V& @7 U$ x                printf("|");
& u. S, R5 }. F1 q            }
1 s) G; M5 _8 G  v            
" F, s9 M5 r  d5 @                        if(p == fruitY && q == fruitX)/*食物的随机坐标*/
6 u6 f  _9 p. I" P" ]* ~! t                        {
4 c0 Z$ Y6 j( b$ f                printf("O");
( `' }. d! _' e            }
' [! b2 j; y0 e' d# h                        else
+ s; C0 j# j  v' ^8 ?! r- o1 d                        {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++)
$ X) A  s. J, u) Q+ G                                        {7 K# J/ P2 `1 O$ M! Y- k
                        if(tailX[k]==q && tailY[k]==p)
3 \2 [/ U3 y% a0 R/ E9 p                                                {
; f( V4 q! H, F! A/ r7 T) P                            printf("*");
0 J$ p, `; x3 o: u% B. C* f4 u- _                            print = true;4 f+ Q* @- e' z# m
                        }8 a8 V7 ^7 k+ M
                    }
" M' h3 R* s5 m% _  C2 E; M) q                    /*如果这个位置打印了 * 就不要打印空格了*/
7 n; k0 V( z5 F. J1 \5 T5 T0 U1 ^: m                    if(!print)
& y% ]3 |# C/ }& y$ G% v7 ?# k                                        {* ]; }# h2 m( M9 F
                                                printf(" ");3 Q4 o+ M' i% G# C) n# \( I6 K+ N- h
                                        }
8 r8 G0 K1 y1 Y" s3 D- i            }# R# T8 K* r7 s9 ^2 X1 H+ A
        }1 Q' f5 g# Y: q0 ~" v4 |( G
        printf("\n");; }9 }( Y6 M+ E3 ^
    }
; j5 j5 [4 ]9 g, A( j% l
6 q8 S  Q4 F5 c' K7 z        /*最后一行*/0 M+ X0 x+ g7 `2 g
    int j;
) n) ~; t) l0 l3 _    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

0 h7 M" ?: k( j}
' Y$ p  l/ |4 s" j6 K/*按键输入控制*/: 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())
9 S( Q: Y2 V* |; j- ^" M8 k# b- I+ p  S        {
  w( \- M2 `: N$ g& I" u                /*获取键盘的输入字符*/
6 r( w  L7 y+ W* J; g! x        switch(_getch()); h/ W( P" m; s) F0 Y
                {0 R, D2 I: K/ n& F( ~6 h
            case '4':
3 J- p0 W& h: ~6 \            case 75:/*左键*/8 Z5 @2 k7 V: y% V  W1 D
                Dir = LEFT;
2 k( C6 [/ h9 R9 O! ~* p. S8 t                hit= true;
# [- F; E# _7 I4 i8 K, E9 f( k# y                break;
8 \6 d+ [" R# ~& W            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':
( l  X/ ]* M' k8 n2 u& N, N9 N2 ?; U            case 77:/*右键*/
2 P7 ]9 m" ]9 J, ^7 v0 p: E                Dir = RIGHT;7 |' p+ S1 u1 Z0 {
                hit= true;% [% [& E& |! Q7 u6 U
                break;+ `/ x' O6 E1 c  ]& I5 o& @
            case '2':
  }3 |' A, c) X, S2 H            case 80:/*向下键盘键 */
- ?& u1 R3 L+ o7 ^' D" y$ y                Dir = DOWN;# P* U! B5 ?1 x4 e4 N$ n
                hit= true;
/ V6 F2 C; ^5 N7 U* ^                break;& s* d' E5 r4 a- P' |6 g
            case 'x':8 s/ v( P' I# v  f4 t9 I
            case 27:/*ESE*/
5 B9 \6 Z  i7 O" p- T' }                gameOver = true;
+ i' E. D" I8 |9 B  q                break;- ]* \' S5 T& u$ S0 @; L
            case 32:/*空格 暂停键*/
% L1 s( x* H+ v  A4 I                                stop = !stop;2 i* Y* i/ B; _: f
                                break;
; \$ j$ }0 m: Q0 g, p1 a8 h4 Y5 i        }
! O7 B; l3 ^" X, u/ P    }
6 O8 E0 n, |3 ]  e        else if(!hit && stop == false)/*如果没有改变方向*/
0 i/ O1 B1 G8 r" r" f( a1 e' e7 L        {
0 T0 Z8 T& b( d- P) Q+ P        x++;
) H) q" v" B1 Y  r    }
+ Q) P! F3 f0 o2 I2 R5 z) f/ W}
+ P* _) {- j- Q* O9 ?2 ]: ~/ h0 P8 o# Y/*判断贪吃蛇的长度*/
7 @7 U7 E0 C' e) bvoid logic()
) R0 `0 p7 I- r; n{) B; b- j/ a1 e7 w' `9 j6 N- t
        if(stop == true)
9 i9 H) B* |9 a: a- E        {+ B. }6 G" s, k& `  h$ ?9 H& _' g
                return;
3 A9 F$ S- r* }$ O4 S! s        }$ R& K8 @9 o5 M- E( m
        
0 k- d0 n, I2 j9 r& |        /*把上一个位置记下*/
4 w4 U; m' g& |5 H% ?# A2 j% w    int lastX = tailX[0];
/ `6 s+ f% y% `    int lastY = tailY[0];
: P$ W7 k  ^' ]    int last2X, last2Y;. K7 E( S) J' _' g8 b# G
    /*重新获取当前的位置*/
/ B; m. \8 g) T7 }! Q1 d8 [        tailX[0]=x;
2 i" ]5 o/ r* l    tailY[0]=y;
8 l- d4 Y" r" D  w8 \' G    int i=0;
9 d% k7 M1 M$ i$ e8 q% [2 h5 H    /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/
8 j1 E7 R% M: W9 ]    for(i=1; i<ntail;i++)
* Z6 p6 {" K) s, t6 o. p        {3 O1 U; e# e. n* x! E
        last2X = tailX;
$ o" R, u$ ^0 i        last2Y = tailY;' z+ p: H, M3 s9 Z+ p3 p
        tailX=lastX;
: D6 N' b2 _3 i4 ^' d1 o. p        tailY=lastY;
) j" t2 J' |3 }+ A3 }        lastX = last2X;
% t9 S* W$ v+ A4 z        lastY = last2Y;
3 d9 g1 ^0 s/ a# @    }$ 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
        {
7 g/ s, a: l! r0 c6 Q8 [! n& k        case UP:/ }5 f% N) J8 D% x# s
            y--;! p% V/ B8 ^& y9 n5 g; t5 ~8 D
            break;
( w, ~: C* t9 T( H; f% w; q2 E        case DOWN:
! G7 o" x3 ]) ~5 W. W            y++;
- \2 O. y3 N, v7 J            break;
6 |$ ~# ?9 }" O- l8 Z6 t        case LEFT:
: ?' d: S2 {6 |8 w, x            x--;
  R+ C7 }! W/ \$ W- n: _            break;9 Q/ X5 e' ?$ [
        case RIGHT:, t3 Q5 u9 n' f
            x++;
4 V& K) X) s' n* B. T& a. E            break;- `' v" q& y# t( L
    }1 Z! @8 [) j0 T; k( S
    if(x<0 || width<x || y<0 || height<y)
2 P. E' B3 C2 I        {( d6 o/ j. d0 x) I7 ?
        gameOver = true;9 k0 @( B2 M+ S
        /*清除屏幕*/  A/ j# R5 G# x& E! ?' @
        system("cls");
- B0 X5 n; U. `6 M* j1 @! G        printf("------------------------------------------------------------------\n");
% E0 i( O  [' {) V            printf("|                                                                |\n");
5 ~: b" O3 ?% F1 }! J  K            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");
' l9 k1 K+ c+ I" @! a            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;
% D. C* Z- |0 t1 J  O        /*更新下一个食物的位置*/! @8 x& l# y7 Q& `# Q4 s  r. B( A
        fruitX = rand()%width;
. M8 ?; _& O/ {! e        fruitY = rand()%height;
; v: L4 o- A0 z; N1 d0 Q5 X    }
" ?% E7 l3 N' |! X; I}
6 n/ j2 k" x" F. |; gint main()
6 ^) K( h9 {$ K" Q. ~$ r{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
        {
$ j, J/ n3 [& P" O. F0 C                printf("%d\n",_getch());
3 g2 y+ S  x2 h+ ~        }
1 g9 C  b. @  ~/ y. i#endif  v; z( J+ A8 M6 h4 u
    menu();
8 q# Q% d9 c1 X2 q/ \3 ]& L$ i    setup();
1 U3 k) O' {  Q$ Q' z0 e6 @    draw();, P  B0 m  j: Z, r. a* u9 i
    /*循环画贪吃蛇的界面*/5 w% o9 X3 M! v$ j7 A
        while(!gameOver)
' F: R! X2 i1 v        {
  ^) r5 s, d5 u  v9 U1 y6 v4 M        draw();
- i4 V. ?- x3 P1 ^  t        input();) }" U8 {- g0 l
        logic();
- i9 c6 T; K$ _' I        Sleep(70);
' r% p8 l% A0 G    }  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。

微信图片_20200501214937.png

我自认为我的注释已经写得不错了,所以就没有什么好说明的了吧,有不明白的把代码过一下,至于屏幕刷新这个东西,如果只是用时间刷新就会闪屏,所以出现了一个双缓存,把要显示的东西送到一个buff里面去,另一个buff用来显示,这样就可以保证不会出现闪屏。除了写贪吃蛇,可以用这个方法写其他小程序,挺有意思的。

在知乎上,发起了一个C语言 100 行代码之内实现贪吃蛇的问题。我觉得很不错,里面很多同学的回复都非常赞,特别是叶大神的回复。

http://www.zhihu.com/question/360814879/answer/1013986215


' J, o- V8 a% ~2 t- c1 k5 f
收藏 2 评论1 发布时间:2020-5-1 21:52

举报

1个回答
李康1202 回答时间:2020-5-3 09:21:10
谢谢分享!
关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版