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

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

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

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

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

    menu();( f* a% O) U5 w0 ^! T& ~( ~& |
    setup();
& d  x/ E) \3 {+ Q  \$ o! f$ u    draw();

4 y$ k9 m$ g5 V6 i" ?

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

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

typedef enum
9 T9 G4 V9 o) i" Q1 n) b- e{
) t$ c1 q. e$ t" d% t1 a        STOP = 0,. W) l4 ]' U5 J* t- P5 a1 K  L
        LEFT,
7 V0 h3 e3 V4 G; D- W; m        RIGHT,! F; K' f) W- Q/ B
        UP,
( }0 r) W6 @7 h" V+ |4 f        DOWN
( B7 [- F/ Y( \7 q. ?- M}Direction;
: E! J5 {5 H8 k/ n# }. v

还有

/*判断贪吃蛇的长度*/
  s" f/ g- t  l/ rvoid logic()

* G/ c3 m8 Y8 ]7 u1 Y! [+ v( y

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

来看这里面的关键代码

/*把上一个位置记下*/
/ }$ E1 L* Y3 z0 A" P+ w- F; W    int lastX = tailX[0];
) n. p# X5 u" u2 r, `5 x    int lastY = tailY[0];
0 j2 g' N" o) {( i2 j4 n) E4 N    int last2X, last2Y;
! E4 D+ f! w1 a    /*重新获取当前的位置*/
6 ?0 t# f3 [* R* t, O" K$ l9 a% a    tailX[0]=x;
0 F: R9 d4 C2 U6 U1 H$ [    tailY[0]=y;, [+ u. k! g: x) b/ C5 [* c! \& y
    int i=0;
- e& q0 f/ S$ ~; \5 T3 P2 d    /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/
; ^. d, P: V! J$ |" d    for(i=1; i<ntail;i++); @+ l% r& C) z* x" g+ x
    {/ ]& p& ?9 E3 X8 T6 p
        last2X = tailX;
6 [$ d6 D& B5 f) ]        last2Y = tailY;
8 T1 z3 v. ~; ^; P8 ~        tailX=lastX;
; Y9 G- y6 b9 B7 j0 X        tailY=lastY;
: x: W3 j. C5 J* B% ~        lastX = last2X;
! c* J  u: C5 Y$ j" Y        lastY = last2Y;4 d5 k$ Q, E0 p0 W0 \5 f
    }
! F, x$ V  X1 {' e

lastX lastY 用来存上一次的蛇头的位置。后面的 for 循环,通过tail 蛇的长度,把蛇上个状态给保存到数组tailX tailY里面。

完整代码


# z7 i2 p/ P" W% H#include <stdio.h>5 B1 i1 |; r& P7 y, n
#include <stdlib.h>9 I( @' U7 \9 ~0 l, B
#include <stdbool.h>
. u3 K* M  p! e) A% S#include <Windows.h>
+ ?$ H7 C8 i0 h$ I) J4 B1 D  T#include <time.h>, {; A4 X" ~0 o0 p5 @7 @# x' S
#include <conio.h> /*键盘输入获取*/# g" I  n# K3 ^) Y1 y
( N  z5 `+ r7 O' U4 V) D0 I8 K* G
bool gameOver;
$ a, \* U' N+ Gbool stop = false;
# p/ D. C% P. N) X, {5 Y& \" {bool hit = false;  c+ C7 @7 x" J9 k% C) _( C* K2 K9 I
/*游戏的边框大小*/+ Z7 ^: ~  I, h. e; O
const int width = 50;
4 p: D7 x1 D2 ~( m* Econst int height = 20;
8 Y( K" e% [" @/*蛇的坐标,食物的坐标还有分数*/
' U- |& H  @/ ?/ |' m. s6 vint x,y,fruitX,fruitY,score;/ K& x7 t1 D, s& @* b
/*蛇每个点的坐标*/
5 V+ N8 m* J8 c; y0 F- dint tailX[200],tailY[200];
% W7 s$ ^% b" A8 P5 }4 m; g! {/*蛇的默认长度*/) ~+ h2 Q& y8 f( q; ]5 n: N: a
int ntail=3;$ z' r: I1 {1 [- G2 G+ O& r1 _
: @6 Q8 A4 }+ N3 M5 n% r% U1 H
typedef enum' x. u2 R8 N! d2 h7 O/ u' I! u
{
' i$ V; m0 G- u) W3 u* d" V7 r        STOP = 0,* s, `6 Y* s8 Y0 y$ r
        LEFT,
) D6 B  V$ ^8 R$ I; `        RIGHT,
' o: {. ^! J' c/ v( V5 j        UP,
! }$ N; F: i1 i" s3 u        DOWN
# Y9 [- @8 a0 k/ z6 h, D& T( g}Direction;
2 K0 Y) X8 Z! i, O% G6 x; V# ]* u: D1 \# o  w
Direction Dir;
( W6 w6 e9 b" T  C0 u8 D; P/*开始菜单*/( k  l3 U4 k, h- r0 K: C
void menu()
& @7 z8 f' y: N) f4 h4 S{
" h0 b& E7 n0 n    int a;) }% D9 ]/ T( o3 f) }2 f- `& h/ U
    printf("------------------------------------------------------------------\n");' C$ Q! B% n2 w8 \) G' c! E* ?
    printf("|                              贪吃蛇游戏                        |\n");
2 p( i$ Z7 `% D6 x6 C' N/ ]9 R7 e. r    printf("|                              1) 新游戏                         |\n");9 o0 }: c  ^, b8 ]  J8 k' v" Y
    printf("|                              2) 开始边界                       |\n");
* u$ `5 P% b9 e0 T, x    printf("|                              3) 退出游戏                       |\n");3 S! w- m+ [  U* ~5 n
    printf("------------------------------------------------------------------\n");
! _; p! k, j. d5 l( v, H; h    printf("---->请输入你的选择:");. V6 N6 G, X/ r& Z3 m1 F; r/ f9 ?
    scanf("%d", &a);; z) w, q6 L9 G( t' p. g
}
6 V! e# _4 E1 m7 F
, O5 V( X* Q$ a! w' ?/*初始化状态*/6 ]: M% `0 n  l
void setup()
. C- j' K: g" B* _$ w  C1 U9 a{. K" N" g, t* A
    gameOver = false;- M1 x3 T& p* x  j0 i
    /*根据当前时间设置“随机数种子”*/
2 K* X) H9 B. U) d) J) _# E* ]    srand(time(NULL));
, h: l$ y5 i/ _6 b    Dir = STOP;, `) B# a$ P3 S, K
    ; W. h0 i9 N/ m
    /*贪吃蛇的位置,固定在中间*/# z0 h, X: L% k3 l
    x= width/2;3 F% u0 ?/ E- I/ n
    y= height/2;* F2 |4 Y- U! C5 ?7 Z' i
    /*食物的位置,位置是随机的*/
0 V: ]$ g- N5 I% Q, M& j  P! t    fruitX = rand()%width;% W7 C. c; b2 W* j  m. h/ D
    fruitY = rand()%height;
/ E5 P9 G8 f2 }, v! W    score = 0;
! p( n% _, t% m' l& g0 D}
$ a; ]) n: _) l: W7 T" C/*绘制界面*/
/ @  A7 X5 e( ^6 Y; T# [8 q* z4 avoid draw()8 J. W' ~- s* r4 I; @
{
9 w. C6 Q" b" P; G2 D9 o1 O        if(stop == true)& V( o# z7 N+ ~: m; G( F2 k4 Z1 h
        {
: k9 A2 E3 X* L6 {1 ?9 q' z                return;
* w* k6 Z) C6 Y        }/ \1 h, Q4 q0 B' |8 h
    system("cls");/*清除屏幕*/
/ ]0 m) a9 d; ~# k" O    printf("分数:%d",score);
8 e! F, s* ?3 U% G/ O    printf("\n");
7 D4 V5 g' g- X  D    ; f' _2 R8 K0 l! _& c6 \. I
        /*第一行*/7 T: j% E. Z5 Z0 z- }
    int i;
6 u: [; M3 Q* Z# `    for(i= 0 ;i<width+1;i++)1 f4 `; P- x- O/ P' P
        {& H3 u$ [3 S$ C" R. ?' _& \
        printf("-");
8 W% j4 v) X" \7 c, Q8 f6 E    }
8 g2 X7 z3 Z) {9 B    printf("\n");0 G' X* Y4 c, M( }
   
! A+ U1 }5 ^; [9 N% R3 ~* o8 \    /*画中间的画面*/8 D1 U* Q# u, J% |4 }% K3 |; _! M
    int p;
  o# }5 F; h1 w. y, _& k    for(p= 0 ;p<height;p++)/*高度*/
$ Q5 C2 h4 v2 ~8 k! ?) F( h: F        {% [6 S, d% J& C7 w, h" z4 k4 @( B
        int q;
. L+ G; W* p4 F/ E/ x        for(q= 0 ;q<width;q++)/*宽度*/3 x3 H) g7 r' K- m- }
                {! d( ^3 x! j. j" Y' i2 \5 ~
                        /*第一行最后已给字符*/4 `% Z1 L) J. @( Z# x9 U" |, z
            if(q==0 || q==width-1)  b) a! j6 M6 ?
                        {4 ]0 ]  F4 l4 E' P
                printf("|");
( E" N" |0 u9 j2 @1 v. q            }
1 Z1 h+ n; [: M4 @- ~; i            . l% y) [( Z* _) E
                        if(p == fruitY && q == fruitX)/*食物的随机坐标*/8 A/ R4 H6 |/ V& ]7 _8 |5 A4 @
                        {
) C- Z- P3 p3 e; E4 c5 E                printf("O");. @* ~5 i, y: K1 Q. _; V
            }
; @' C7 K" a; e2 d                        else
% G$ @( P9 z4 T2 K* G" _* q                        {
0 c7 M9 P* [# ~) T$ i: ?& s                    int k=0;
1 d9 r% b% E4 G2 }                    bool print = false;, m  |  o, [* y% ~: U% t
                    /*贪吃蛇的长度 默认长度是 3*/$ _7 Y  Q1 Q1 T  ~" o( t
                                        for(k=0;k<ntail;k++)
6 B# }% Z( [( d. g8 k4 S7 J                                        {
0 Y$ }" E( l" \4 w2 r6 T5 F                        if(tailX[k]==q && tailY[k]==p)
. o1 ?) t6 p' m# d( B& C                                                {
; F1 K& J) P, z- c. ^. Q, }                            printf("*");6 g: L( w$ k, U6 c
                            print = true;
, ?3 k9 Q9 x" j# Z2 B+ Y/ K                        }
( ?( P3 n3 ?9 }( ?                    }
/ V# T! z/ o8 }& |) P: x9 |                    /*如果这个位置打印了 * 就不要打印空格了*/* P# k6 \5 W# q$ {, ]' d" r. T) F
                    if(!print)
3 w& o% e# R6 l, ], e6 G1 V                                        {  R5 K& Y3 K) l9 B9 D' y( ^
                                                printf(" ");
+ k+ L6 ?' l& n                                        }
+ X  }, V/ @1 e/ X" ?2 |: G            }5 S. G( l+ Y0 f
        }8 p' r* ~' T; ~8 B* G% l. A
        printf("\n");; R5 I! X. T* Q  h% t
    }
+ c, G6 ~6 x- O) P. U$ a: X# p$ r- V* [
        /*最后一行*/* k  g' Y0 I1 S3 U- q! g
    int j;
$ M& j3 W( A0 H8 ^    for(j= 0 ;j<width+1;j++)( A  a# r* o7 ^& e: Q
        {4 P) n* N% d" C
        printf("-");# [$ A/ l- B1 a7 y# t
    }- @& U, o# w, i# @; L

# ]9 [3 Z0 m9 W9 a: p}4 e# P% l" o, P; u
/*按键输入控制*/
* w' u  @9 v0 Z: _4 T( E$ s+ wvoid input()
) e( I8 U4 e8 T# H: }9 x# S{( D3 ^6 S: L" ~# p( A; E2 E
    if(_kbhit())
' B: K. ^1 ~6 j  U        {: ?- P$ }/ r; w
                /*获取键盘的输入字符*/) E: h8 s2 p4 _8 p2 u2 `* R* \. o
        switch(_getch()). d4 F3 Z  k" R
                {# ^) D6 c1 J/ Z" b
            case '4':3 m5 J; w9 Q) {' J/ q5 _( q% G
            case 75:/*左键*/
7 j3 q4 `* X1 M9 V: v' p  B                Dir = LEFT;9 [: G; R3 y3 {, d( @2 F
                hit= true;
2 P3 M5 l+ ~2 k' j* C9 J( q                break;, @& j' J, h2 K
            case '8':  n9 P& q7 J- a7 c3 k
            case 72:/*上键*/5 s# |& [" m; ~$ \& ?0 \# P- s
                Dir = UP;
& @, C. j1 P* B, Y: G                hit= true;
  p; k+ w* @! \$ }) c                break;
2 t. f  B/ m5 N5 N7 t            case '6':4 u. b" R4 v. Y
            case 77:/*右键*/# S! b8 A6 k, f: J& K9 F9 T
                Dir = RIGHT;
; o( S& o* _) r                hit= true;
8 k4 Z2 x9 N) [! r* a                break;9 f$ m# c* Z1 G0 t' M# h- ?2 p
            case '2':' B3 V, t# h1 G$ c9 Y; R' p. y
            case 80:/*向下键盘键 */
* M; B4 O# M2 ]" c5 ]( }; K                Dir = DOWN;
+ V* L0 N& g5 G                hit= true;
4 u) r, F6 X+ Z                break;
; n; ?7 T7 E2 d; d; Y            case 'x':
' _, H7 \& F% Y- P* Z            case 27:/*ESE*/
# K; {# Z0 @" q3 |                gameOver = true;; |4 ?. P" O% N/ |( i* R3 Z* Q0 L
                break;
! ?- q- T3 [7 m4 {            case 32:/*空格 暂停键*/
) ~1 M( C  K% |! c  D/ j                                stop = !stop;
' {+ X, ~1 C& ^1 }4 ?- u5 w                                break;
# q# d& V& v8 J8 P        }
/ i0 W7 a, r- {$ R" J4 W8 i- U    }$ Y. `/ a" @8 M* Q- F3 S' T4 d/ O
        else if(!hit && stop == false)/*如果没有改变方向*/9 o1 R' F$ T( T4 D1 u7 ~- O, ?( `
        {
, v8 d9 v, e2 n& o) S8 O        x++;
/ f3 ^& U8 e7 C% g) z. }    }; |! P; P. }: D* B! }0 k
}, f5 p% L$ M* t5 w0 Z% \
/*判断贪吃蛇的长度*/; {+ d! M  C  K% _
void logic()4 z7 F" f+ r' _% r4 b
{9 [/ }, H, m1 {3 l& g
        if(stop == true)* s: u/ G. U5 y8 J* n( }% O# u7 z) B
        {
8 w. q3 |; x" F5 V8 b3 x2 {8 Z                return;
$ S' e& E- d3 Z        }
' H9 ?" `# b/ d% N2 U        
& ~% s5 ~3 x# V" ~" `  a. d        /*把上一个位置记下*/
# @3 w0 {: q. k6 K3 U% Z" D8 d    int lastX = tailX[0];
6 c+ l9 v/ `9 x9 C) Z2 y    int lastY = tailY[0];
5 z2 q. U4 ~$ E" M/ c    int last2X, last2Y;
% D1 Q! T+ O- {) r* G    /*重新获取当前的位置*/3 F# M9 a/ k2 h' J' ]* Y: `4 k
        tailX[0]=x;6 {$ z, P5 C& C1 V) p0 B
    tailY[0]=y;; v- E6 J& t0 }9 j
    int i=0;
1 b* {, x% V6 l    /*遍历整条蛇的长度 把 0 的位置空出来,其余蛇的位置往后面的空间移动*/
8 B! W% Y. [8 B% u; w( M' S    for(i=1; i<ntail;i++)
3 ]6 B3 T& Q3 b' Q' Z# ^        {
! \( s: V8 |+ v5 B        last2X = tailX;
; c: c" b) C' ?2 B2 K9 S- g/ ~! G, ~        last2Y = tailY;+ j4 \1 h2 e; t0 F' J  Q
        tailX=lastX;
  V( Y0 a6 }5 u9 K4 P2 C* X, n        tailY=lastY;
3 |. p; C5 I3 i% z# \+ g        lastX = last2X;6 z3 _) p, [* C( ]$ G9 `8 q1 s
        lastY = last2Y;
0 G2 }5 S3 X7 \8 x9 V    }, p4 s& n' q3 A7 m! t
    /*根据方向来改变x y 的值*/4 K* X6 `" ]0 A: ]9 }! x( u
    switch(Dir)
4 B1 W$ o5 @- F2 [: l* X        {# P/ B8 O& O0 n% \8 K7 Z
        case UP:
6 F1 R* v3 z+ W- B- N) |. e/ j            y--;
' K8 H+ x& x) c            break;: P2 g; F2 R; S
        case DOWN:/ {2 d. [5 w: j
            y++;
3 K$ z1 g& H: M! ]8 G  F            break;, b. ]5 R/ |7 g) P
        case LEFT:
3 T' N* F) x, C9 z            x--;
' F' ]; z" j4 W- L$ A            break;
5 |  ^" H6 {. }4 F; x3 Z$ ~        case RIGHT:( A* a5 |0 y$ }' g
            x++;
  m( _( g; N; d( S0 s6 B8 w+ T            break;
+ E5 d+ o1 u; Y% d    }
3 H; s- n( C% p! b% I  b2 ~    if(x<0 || width<x || y<0 || height<y)
3 Y- u2 f: |. s) o        {
' ~, L6 M  h: y- X) Y        gameOver = true;: F3 }2 l; T8 j7 w, q( B
        /*清除屏幕*/
1 h' R% a0 ]& Q4 ^, H2 V; \  a  l        system("cls");9 ~. d1 ?3 {; u) R
        printf("------------------------------------------------------------------\n");
0 n  Z$ l- Y' ~( F            printf("|                                                                |\n");* Y6 c# `6 W  A, w; j' `
            printf("|                                                                |\n");
- [8 U1 z3 B, n  R            printf("|                             游戏结束                           |\n");/ V7 t% C- e' b
            printf("|                                                                |\n");" g1 d8 a2 M) K8 r5 e- i* M
            printf("|                                                                |\n");8 l6 H& Z) c7 Y7 ?* b  T( E
            printf("------------------------------------------------------------------\n");$ T5 ^1 L$ J5 ~  C( A* V
    }
! Y3 D& S  w& @    if(x==fruitX && y==fruitY)7 n5 F8 U3 w0 Q2 S9 C
        {
$ t3 a# Q: l: h. Y  q                /*吃了一个食物,蛇的长度增加1*/
8 Y! i# A2 U5 P$ U6 v        ntail++;6 O! D7 n& p* x9 |2 \( g
        score+=10;
+ R" i  k. }  q        /*更新下一个食物的位置*/
) _9 l3 W7 ?, n' }0 Q- Z        fruitX = rand()%width;, F6 q3 M/ t" M$ L% x* s) Z
        fruitY = rand()%height;. J3 j) z" j: f* F; P; J; d( q1 a& t
    }. [1 Y2 K$ U1 H' O; a7 W
}
6 w4 z7 ?) i4 _6 H) t9 vint main()
) w; ^1 F" Y1 g0 {* z5 J6 m0 b{1 u* `, F& w2 u0 P0 \* h3 {; a
#if 0
+ J2 }( _, T. C* T+ g4 v7 s$ V        while(1)
" q7 I# U# ?( N& i# j        {
) e3 p1 Z1 U  \6 P  L7 Q, R8 _                printf("%d\n",_getch());( f3 i6 A: J. h, K3 i& ?$ O
        }
4 p8 ?! i# k4 d' f: L2 h#endif
( m3 J$ o, r( M    menu();
$ g! U* E* `: J' y1 \    setup();
+ f. P) e; h  D. {. f) g) a; z/ ~    draw();
8 C2 J, B: v  N2 r% M# ?    /*循环画贪吃蛇的界面*/) I8 A- v3 ]% m( T, X0 Q( |
        while(!gameOver)
8 \3 \0 {3 n+ W/ U$ U5 J9 T        {
7 g# d) _: |' z/ ^+ q. b8 b/ V: K        draw();5 }, T& u9 w0 \
        input();
& Y: c' i) f0 `, d+ T6 d        logic();$ X+ |: d4 B% G9 y; V% V2 h
        Sleep(70);
6 Q5 I4 G5 @! S5 \, [- }4 @    }& J& d. H- }) q$ v3 v# C. O
- P! }, j1 w' B, u( d2 W
    return 0;5 P; D+ o' Z8 ]" A5 l
}
& C' ~$ m* c4 d4 U' W4 R

上面这段代码直接在Dev C++上面应该是可以运行的,很多人在知乎上私信问我,为什么我的贪吃蛇执行不了呢,可能就是平台不同,少了这个头文件,少了那个头文件,但是你为什么不能跟我一样,用Dev C++呢,轻量级,简单。代码的精髓是什么?我认为精髓一定是思想,不是你写了多少行代码,用了什么高端的IDE。

微信图片_20200501214937.png

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

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

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

6 @( K) \/ O& v
收藏 2 评论1 发布时间:2020-5-1 21:52

举报

1个回答
李康1202 回答时间:2020-5-3 09:21:10
谢谢分享!

所属标签

相似技术帖

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版