这一次我们应用printf输出实现一个经典的小游戏—贪吃蛇,主要难点是小蛇数据如何存储、如何实现转弯的效果、吃到食物后如何增加长度。
1 构造小蛇
首先,在画面中显示一条静止的小蛇。二维数组canvas[High][Width]的对应元素,值为0输出空格,-1输出边框#,1输出蛇头@,大于1的正数输出蛇身*。startup()函数中初始化蛇头在画布中间位置(canvas[High/2][Width/2] = 1;),蛇头向左依次生成4个蛇身(for (i=1;i<=4;i++) canvas[High/2][Width/2-i] = i+1;),元素值分别为2、3、4、5。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <conio.h>
4 #include <windows.h>
5 //C语言自学网
6 #define High 20 // 游戏画面尺寸
7 #define Width 30
8
9 // 全局变量
10 int canvas[High][Width] = {0}; // 二维数组存储游戏画布中对应的元素
11 // 0为空格,-1为边框#,1为蛇头@,大于1的正数为蛇身*
12
13 void gotoxy(int x,int y) //光标移动到(x,y)位置
14 {
15 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
16 COORD pos;
17 pos.X = x;
18 pos.Y = y;
19 SetConsoleCursorPosition(handle,pos);
20 }
21
22 void startup() // 数据初始化
23 {
24 int i,j;
25
26 // 初始化边框
27 for (i=0;i<High;i++)
28 {
29 canvas[i][0] = -1;
30 canvas[i][Width-1] = -1;
31 }
32 for (j=0;j<Width;j++)
33 {
34 canvas[0][j] = -1;
35 canvas[High-1][j] = -1;
36 }
37
38 // 初始化蛇头位置
39 canvas[High/2][Width/2] = 1;
40 // 初始化蛇身,画布中元素值分别为2,3,4,5....
41 for (i=1;i<=4;i++)
42 canvas[High/2][Width/2-i] = i+1;
43 }
44
45 void show() // 显示画面
46 {
47 gotoxy(0,0); // 光标移动到原点位置,以下重画清屏
48 int i,j;
49 for (i=0;i<High;i++)
50 {
51 for (j=0;j<Width;j++)
52 {
53 if (canvas[i][j]==0)
54 printf(" "); // 输出空格
55 else if (canvas[i][j]==-1)
56 printf("#"); // 输出边框#
57 else if (canvas[i][j]==1)
58 printf("@"); // 输出蛇头@
59 else if (canvas[i][j]>1)
60 printf("*"); // 输出蛇身*
61 }
62 printf("\n");
63 }
64 }
65
66 void updateWithoutInput() // 与用户输入无关的更新
67 {
68 }
69
70 void updateWithInput() // 与用户输入有关的更新
71 {
72 }
73
74 int main()
75 {
76 startup(); // 数据初始化
77 while (1) // 游戏循环执行
78 {
79 show(); // 显示画面
80 updateWithoutInput(); // 与用户输入无关的更新
81 updateWithInput(); // 与用户输入有关的更新
82 }
83 return 0;
84 }
2 小蛇自动移动
实现小蛇的移动是贪吃蛇游戏的难点,下图列出了小蛇分别向右、向上运动后,对应二维数组元素值的变化,从中我们可以得出实现思路。
假设小蛇元素为54321,其中1为蛇头、5432为蛇身、最大值5为蛇尾。首先将所有大于0的元素加1,得到65432;将最大值6变为0,即去除了原来的蛇尾;再根据对应的移动方向,将2对应方向的元素由0变成1;如此即实现了小蛇的移动。小蛇向上移动的对应流程如图所示。
定义变量int moveDirection表示小蛇的移动方向,值1、2、3、4分别表示小蛇向上、下、左、右方向移动,小蛇移动实现在moveSnakeByDirection()函数中。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <conio.h>
4 #include <windows.h>
5 //C语言自学网
6 #define High 20 // 游戏画面尺寸
7 #define Width 30
8
9 // 全局变量
10 int moveDirection; // 小蛇移动方向,上下左右分别用1,2,3,4表示
11 int canvas[High][Width] = {0}; // 二维数组存储游戏画布中对应的元素
12 // 0为空格0,-1为边框#,1为蛇头@,大于1的正数为蛇身*
13
14 void gotoxy(int x,int y) //光标移动到(x,y)位置
15 {
16 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
17 COORD pos;
18 pos.X = x;
19 pos.Y = y;
20 SetConsoleCursorPosition(handle,pos);
21 }
22
23 // 移动小蛇
24 // 第一步扫描数组canvas所有元素,找到正数元素都+1
25 // 找到最大元素(即蛇尾巴),把其变为0
26 // 找到=2的元素(即蛇头),再根据输出的上下左右方向,把对应的另一个像素值设为1(新蛇头)
27 void moveSnakeByDirection()
28 {
29 int i,j;
30 for (i=1;i<High-1;i++)
31 for (j=1;j<Width-1;j++)
32 if (canvas[i][j]>0)
33 canvas[i][j]++;
34
35 int oldTail_i,oldTail_j,oldHead_i,oldHead_j;
36 int max = 0;
37
38 for (i=1;i<High-1;i++)
39 for (j=1;j<Width-1;j++)
40 if (canvas[i][j]>0)
41 {
42 if (max<canvas[i][j])
43 {
44 max = canvas[i][j];
45 oldTail_i = i;
46 oldTail_j = j;
47 }
48 if (canvas[i][j]==2)
49 {
50 oldHead_i = i;
51 oldHead_j = j;
52 }
53 }
54
55 canvas[oldTail_i][oldTail_j] = 0;
56
57 if (moveDirection==1) // 向上移动
58 canvas[oldHead_i-1][oldHead_j] = 1;
59 if (moveDirection==2) // 向下移动
60 canvas[oldHead_i+1][oldHead_j] = 1;
61 if (moveDirection==3) // 向左移动
62 canvas[oldHead_i][oldHead_j-1] = 1;
63 if (moveDirection==4) // 向右移动
64 canvas[oldHead_i][oldHead_j+1] = 1;
65 }
66
67 void startup() // 数据初始化
68 {
69 int i,j;
70
71 // 初始化边框
72 for (i=0;i<High;i++)
73 {
74 canvas[i][0] = -1;
75 canvas[i][Width-1] = -1;
76 }
77 for (j=0;j<Width;j++)
78 {
79 canvas[0][j] = -1;
80 canvas[High-1][j] = -1;
81 }
82
83 // 初始化蛇头位置
84 canvas[High/2][Width/2] = 1;
85 // 初始化蛇身,画布中元素值分别为2,3,4,5....
86 for (i=1;i<=4;i++)
87 canvas[High/2][Width/2-i] = i+1;
88
89 // 初始小蛇向右移动
90 moveDirection = 4;
91 }
92
93 void show() // 显示画面
94 {
95 gotoxy(0,0); // 光标移动到原点位置,以下重画清屏
96 int i,j;
97 for (i=0;i<High;i++)
98 {
99 for (j=0;j<Width;j++)
100 {
101 if (canvas[i][j]==0)
102 printf(" "); // 输出空格
103 else if (canvas[i][j]==-1)
104 printf("#"); // 输出边框#
105 else if (canvas[i][j]==1)
106 printf("@"); // 输出蛇头@
107 else if (canvas[i][j]>1)
108 printf("*"); // 输出蛇身*
109 }
110 printf("\n");
111 }
112 Sleep(100);
113 }
114
115 void updateWithoutInput() // 与用户输入无关的更新
116 {
117 moveSnakeByDirection();
118 }
119
120 void updateWithInput() // 与用户输入有关的更新
121 {
122 }
123
124 int main()
125 {
126 startup(); // 数据初始化
127 while (1) // 游戏循环执行
128 {
129 show(); // 显示画面
130 updateWithoutInput(); // 与用户输入无关的更新
131 updateWithInput(); // 与用户输入有关的更新
132 }
133 return 0;
134 }
3 玩家控制小蛇移动
这一步的实现比较简单,在updateWithInput()函数中按asdw键改变moveDirection的值,然后调用moveSnakeByDirection()实现小蛇向不同方向的移动,如图所示。
1 void updateWithInput() // 与用户输入有关的更新
2 //C语言自学网
3 {
4 char input;
5 if(kbhit()) // 判断是否有输入
6 {
7 input = getch(); // 根据用户的不同输入来移动,不必输入回车
8 if (input == 'a')
9 {
10 moveDirection = 3; // 位置左移
11 moveSnakeByDirection();
12 }
13 else if (input == 'd')
14 {
15 moveDirection = 4; // 位置右移
16 moveSnakeByDirection();
17 }
18 else if (input == 'w')
19 {
20 moveDirection = 1; // 位置上移
21 moveSnakeByDirection();
22 }
23 else if (input == 's')
24 {
25 moveDirection = 2; // 位置下移
26 moveSnakeByDirection();
27 }
28 }
29 }
4 判断游戏失败
当小蛇和边框或自身发生碰撞时,游戏失败,如图所示。
1 void moveSnakeByDirection()
2 //C语言自学网
3 {
4 int i,j;
5 for (i=1;i<High-1;i++)
6 for (j=1;j<Width-1;j++)
7 if (canvas[i][j]>0)
8 canvas[i][j]++;
9 int oldTail_i,oldTail_j,oldHead_i,oldHead_j;
10 int max = 0;
11 for (i=1;i<High-1;i++)
12 for (j=1;j<Width-1;j++)
13 if (canvas[i][j]>0)
14 {
15 if (max<canvas[i][j])
16 {
17 max = canvas[i][j];
18 oldTail_i = i;
19 oldTail_j = j;
20 }
21 if (canvas[i][j]==2)
22 {
23 oldHead_i = i;
24 oldHead_j = j;
25 }
26 }
27 canvas[oldTail_i][oldTail_j] = 0;
28 int newHead_i,newHead_j;
29 if (moveDirection==1) // 向上移动
30 {
31 newHead_i = oldHead_i-1;
32 newHead_j = oldHead_j;
33 }
34 if (moveDirection==2) // 向下移动
35 {
36 newHead_i = oldHead_i+1;
37 newHead_j = oldHead_j;
38 }
39 if (moveDirection==3) // 向左移动
40 {
41 newHead_i = oldHead_i;
42 newHead_j = oldHead_j-1;
43 }
44 if (moveDirection==4) // 向右移动
45 {
46 newHead_i = oldHead_i;
47 newHead_j = oldHead_j+1;
48 }
49
50 // 是否小蛇和自身撞,或者和边框撞,游戏失败
51 if (canvas[newHead_i][newHead_j]>0 || canvas[newHead_i][newHead_j]==-1)
52 {
53 printf("游戏失败!\n");
54 exit(0);
55 }
56 else
57 canvas[newHead_i][newHead_j] = 1;
58 }
5 吃食物增加长度
增加食物,二维数组canvas[High][Width]元素值为-2时,输出食物数值’F’,如图所示。当蛇头碰到食物时,长度加一。
实现思路和2中小蛇移动类似,只需保持原蛇尾,不将最大值变为0即可,下图为小蛇向上移动吃到食物的对应流程。
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <conio.h>
4 #include <windows.h>
5 //C语言自学网
6 #define High 20 // 游戏画面尺寸
7 #define Width 30
8
9 // 全局变量
10 int moveDirection; // 小蛇移动位置,上下左右分别用1,2,3,4表示
11 int food_x,food_y; // 食物的位置
12 int canvas[High][Width] = {0}; // 二维数组存储游戏画布中对应的元素
13 // 0为空格0,-1为边框#,-2为食物F,1为蛇头@,大于1的正数为蛇身*
14
15 void gotoxy(int x,int y) //光标移动到(x,y)位置
16 {
17 HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
18 COORD pos;
19 pos.X = x;
20 pos.Y = y;
21 SetConsoleCursorPosition(handle,pos);
22 }
23
24 // 移动小蛇
25 // 第一步扫描数组canvas所有元素,找到正数元素都+1
26 // 找到最大元素(即蛇尾巴),把其变为0
27 // 找到=2的元素(即蛇头),再根据输出的上下左右方向,把对应的另一个像素值设为1(新蛇头)
28 void moveSnakeByDirection()
29 {
30 int i,j;
31 for (i=1;i<High-1;i++)
32 for (j=1;j<Width-1;j++)
33 if (canvas[i][j]>0)
34 canvas[i][j]++;
35
36 int oldTail_i,oldTail_j,oldHead_i,oldHead_j;
37 int max = 0;
38
39 for (i=1;i<High-1;i++)
40 for (j=1;j<Width-1;j++)
41 if (canvas[i][j]>0)
42 {
43 if (max<canvas[i][j])
44 {
45 max = canvas[i][j];
46 oldTail_i = i;
47 oldTail_j = j;
48 }
49 if (canvas[i][j]==2)
50 {
51 oldHead_i = i;
52 oldHead_j = j;
53 }
54 }
55
56 int newHead_i,newHead_j;
57
58 if (moveDirection==1) // 向上移动
59 {
60 newHead_i = oldHead_i-1;
61 newHead_j = oldHead_j;
62 }
63 if (moveDirection==2) // 向下移动
64 {
65 newHead_i = oldHead_i+1;
66 newHead_j = oldHead_j;
67 }
68 if (moveDirection==3) // 向左移动
69 {
70 newHead_i = oldHead_i;
71 newHead_j = oldHead_j-1;
72 }
73 if (moveDirection==4) // 向右移动
74 {
75 newHead_i = oldHead_i;
76 newHead_j = oldHead_j+1;
77 }
78
79 // 新蛇头如果吃到食物
80 if (canvas[newHead_i][newHead_j]==-2)
81 {
82 canvas[food_x][food_y] = 0;
83 // 产生一个新的食物
84 food_x = rand()%(High-5) + 2;
85 food_y = rand()%(Width-5) + 2;
86 canvas[food_x][food_y] = -2;
87
88 // 原来的旧蛇尾留着,长度自动+1
89 }
90 else // 否则,原来的旧蛇尾减掉,保持长度不变
91 canvas[oldTail_i][oldTail_j] = 0;
92
93 // 是否小蛇和自身撞,或者和边框撞,游戏失败
94 if (canvas[newHead_i][newHead_j]>0 || canvas[newHead_i][newHead_j]==-1)
95 {
96 printf("游戏失败!\n");
97 Sleep(2000);
98 system("pause");
99 exit(0);
100 }
101 else
102 canvas[newHead_i][newHead_j] = 1;
103 }
104
105 void startup() // 数据初始化
106 {
107 int i,j;
108
109 // 初始化边框
110 for (i=0;i<High;i++)
111 {
112 canvas[i][0] = -1;
113 canvas[i][Width-1] = -1;
114 }
115 for (j=0;j<Width;j++)
116 {
117 canvas[0][j] = -1;
118 canvas[High-1][j] = -1;
119 }
120
121 // 初始化蛇头位置
122 canvas[High/2][Width/2] = 1;
123 // 初始化蛇身,画布中元素值分别为2,3,4,5....
124 for (i=1;i<=4;i++)
125 canvas[High/2][Width/2-i] = i+1;
126
127 // 初始小蛇向右移动
128 moveDirection = 4;
129
130 food_x = rand()%(High-5) + 2;
131 food_y = rand()%(Width-5) + 2;
132 canvas[food_x][food_y] = -2;
133 }
134
135 void show() // 显示画面
136 {
137 gotoxy(0,0); // 光标移动到原点位置,以下重画清屏
138 int i,j;
139 for (i=0;i<High;i++)
140 {
141 for (j=0;j<Width;j++)
142 {
143 if (canvas[i][j]==0)
144 printf(" "); // 输出空格
145 else if (canvas[i][j]==-1)
146 printf("#"); // 输出边框#
147 else if (canvas[i][j]==1)
148 printf("@"); // 输出蛇头@
149 else if (canvas[i][j]>1)
150 printf("*"); // 输出蛇身*
151 else if (canvas[i][j]==-2)
152 printf("F"); // 输出食物F
153 }
154 printf("\n");
155 }
156 Sleep(100);
157 }
158
159 void updateWithoutInput() // 与用户输入无关的更新
160 {
161 moveSnakeByDirection();
162 }
163
164 void updateWithInput() // 与用户输入有关的更新
165 {
166 char input;
167 if(kbhit()) // 判断是否有输入
168 {
169 input = getch(); // 根据用户的不同输入来移动,不必输入回车
170 if (input == 'a')
171 {
172 moveDirection = 3; // 位置左移
173 moveSnakeByDirection();
174 }
175 else if (input == 'd')
176 {
177 moveDirection = 4; // 位置右移
178 moveSnakeByDirection();
179 }
180 else if (input == 'w')
181 {
182 moveDirection = 1; // 位置上移
183 moveSnakeByDirection();
184 }
185 else if (input == 's')
186 {
187 moveDirection = 2; // 位置下移
188 moveSnakeByDirection();
189 }
190 }
191 }
192
193 int main()
194 {
195 startup(); // 数据初始化
196 while (1) // 游戏循环执行
197 {
198 show(); // 显示画面
199 updateWithoutInput(); // 与用户输入无关的更新
200 updateWithInput(); // 与用户输入有关的更新
201 }
202 return 0;
203 }
6 思考题
1. 增加道具,吃完可以加命或减速;
2. 尝试实现双人版贪吃蛇
感谢你的阅读,请用心感悟!希望可以帮到爱学习的你!!分享也是一种快乐!!!请接力。。。
来源:oschina
链接:https://my.oschina.net/u/4406269/blog/4313857