贪吃蛇_C实现 (初学者) (o゚▽゚)o
我是刚入坑不久的,贪吃蛇算是我第一个用C写出来的小游戏
这个程序里并没有什么难懂的部分,大多都是刚开始学就接触到的,so 加油吧!
源代码在文章的末尾.
效果图就不上了 (〃'▽'〃)
首先,来确定我们所需的头文件
#include <time.h> #include <conio.h> #include <stdio.h> #include <stdlib.h> #include <windows.h>
<time.h> 这是我们在之后生成随机食物坐标时,用来以时间来生成随机数种子所需的头文件
<conio.h> 这个是gotoxy()函数所需的头文件
至于其他的应该都明白的.
接下来,定义我们需要的常量和全局变量
//常量
//蛇的行进状态 U:上 D:下 L:左 R:右 #define U 1 #define D 2
#define L 3 #define R 4
//全局变量的设定 int state; //当前蛇的状态 int wall_x=1,wall_y=1,wall_x1=97,wall_y1=25; //围墙的坐标 int speed =200; //蛇每一次运行的时间间隔 int snake_x=6,snake_y=5; //蛇头的坐标 int food_x,food_y; //食物的坐标 int path_list[30000][2]; //路径列表 int path_find_line=0; //路径记录到第几行 int point_food=0; //食物得到次数
上面的注释应该挺清楚的了,我用的是食物得到的次数来确定蛇身的长度.
因为我比较喜欢吧main()函数放在源文件中定义函数的前面,
所以我需要函数原型,来告诉编译器将在源文件中定义的函数的特征.(如图)
void wall_draw(); //制造围墙 int border(int,int); //边界限制 int snake_move(); //蛇的移动 int snake_tail_delete(); //删除蛇尾 int path_log(); //蛇的位移路径记录 int food_generate(); //食物生成 int food_get(int,int); //获得食物 int gotoxy(int,int); //移动光标 int rectangle(int,int,int,int); //建立矩形 int control(); //控制蛇的移动方向
然后,开始一个一个的分析定义的函数:
void wall_draw() { //确定窗口大小 system("mode con cols=100 lines=30"); //绘画矩形 rectangel(wall_x,wall_y,wall_x1,wall_y1); //打印标题 gotoxy(43,0); printf("***Snake***"); //回到光标区 gotoxy(0,29); }
其中的:
在命令行中的意思是:吧窗口改变成 行容纳100个字符 可以输出30列
(ps:效果是这样但是我描述的可能不太好,建议自己在命令提示符中尝试)
接下来的 rectangel()函数,是我自己写的,好像在哪个头文件里已经有了这个函数,但是我看不懂.
没办法只能自己打一个.
int rectangel(int x,int y,int x2,int y2) { gotoxy(x,y); printf("┌"); int i; for (i = 0; i < x2-x-1; i++) { printf("─"); } printf("┐"); for (i = 0; i < y2-y-1; i++) { gotoxy(x,y+i+1); printf("│"); gotoxy(x2,y+i+1); printf("│"); } gotoxy(x,y2); printf("└"); for (i = 0; i < x2-x-1; i++) { printf("─"); } printf("┘"); }
关于这个函数其实挺简单的,他要求输入的参数有 4个.也就是矩形的两个对顶点
把两个对顶点输入进去,就可以求出整个矩形的四个顶点,然后用gotoxy()函数来移动光标进行打印矩形
path_log()函数:
//蛇的移动路径记录 int path_log() { path_list[path_find_line][0]=snake_x; path_list[path_find_line][1]=snake_y; ++path_find_line; }
当执行该函数时,会把当前蛇头的坐标记录到path_list数组中.
snake_tail_delete()函数:
//删除蛇尾
int snake_tail_delete()
{
//查询蛇尾位置
int x,y;
x=path_list[path_find_line-point_food-1][0];
y=path_list[path_find_line-point_food-1][1];
gotoxy(x,y);
printf(" ");
path_list[path_find_line-point_food-1][0]=0;
path_list[path_find_line-point_food-1][1]=0;
//返回光标区
gotoxy(0,29);
return 0;
}
在这里我的想法是每次都删除蛇尾,只不过如果吃到食物的时候会去上一次的地方删除蛇尾.
解释的可能不怎么好.(简单来讲:假设蛇移动了十次,且吃到了两次食物,那么就可知道蛇尾是在已走路径中的第 path_find_line - point_food这里,即10-2)
border()函数:
在贪吃蛇中,蛇不能撞墙,也不能撞到自己的身体所以需要 border()函数来判断蛇是否撞墙,或者撞到自己的身体.
//是否生成在障碍物上 是:1 否:0 border(x,y)
int border(int x,int y)
{
//查询物体是否撞到蛇身
int i;
for (i = 0; i < path_find_line+1; i++)
{
if (x==path_list[i][0] && y==path_list[i][1])
{
return 1;
}
}
//查询物体是否撞到墙
if (wall_x>=x || wall_x1<=x || wall_y>=y || wall_y1<=y)
{
return 1;
}
return 0;
}
在这个函数中,是有返回值的.
判断蛇身就是在path_list数组中查询当前蛇头坐标是否在里面,是:撞到 否:没撞到
如果蛇撞墙,或者撞到了蛇身,返回1, 否则返回0.
food_generate()函数:
//食物的生成 成功:1 失败:0 int food_generate() { //初始化随机种子 srand((unsigned int)time(NULL)); int x,y; x=food_x; y=food_y; while (1) { //随机食物坐标 food_x=rand()%wall_x1+wall_x; food_y=rand()%wall_y1+wall_y; //查询食物是否生成位置错误 if (food_x%2 ==1) { continue; } if (food_x==x && food_y==y) { continue; } if (border(food_x,food_y)==1) { continue; } //打印食物 gotoxy(food_x,food_y); printf("\a⊕"); gotoxy(0,29); return 1; } return 0; }
唯一要讲的差不多就是食物的随机生成的问题了
用rand()函数取随机数.但是使用前需要初始化随机数种子
也就是这个:
food_get()函数:
//获得食物 food_get(snake_x,snake_y) int food_get(int x,int y) { if (x==food_x && y==food_y) { food_generate(); ++point_food; gotoxy(1,0); printf("Point:%4d",point_food); gotoxy(0,29); } }
简单讲:就是判断蛇头坐标是否和食物坐标相等.
相等 -> 生成一次食物 food_generate()
不相等 -> 不执行
snake_move()函数:
//蛇的移动 int snake_move() { snake_tail_delete(); if (state == U) { --snake_y; gotoxy(snake_x,snake_y); printf("█"); gotoxy(10,29); printf("Speed:%4d",speed); } if (state == D) { ++snake_y; gotoxy(snake_x,snake_y); printf("█"); gotoxy(10,29); printf("Speed:%4d",speed); } if (state == L) { --snake_x; --snake_x; gotoxy(snake_x,snake_y); printf("█"); gotoxy(10,29); printf("Speed:%4d",speed); } if (state == R) { ++snake_x; ++snake_x; gotoxy(snake_x,snake_y); printf("█"); gotoxy(10,29); printf("Speed:%4d",speed); } food_get(snake_x,snake_y); if (border(snake_x,snake_y)==1) { return 1; } path_log(); Sleep(speed); }
在游戏里贪吃蛇每隔一定的时间就要移动一格.
所以我们需要Sleep()函数.
(ps:Sleep的S一定要大写,C语言对大小写十分敏感)
然后在蛇每移动一次,就执行path_log()来记录移动的路径
之后判断是否吃到食物 food_get()
以及执行snake_tail_delete()来删除蛇尾
然后等待Sleep(speed);
最后就是控制蛇的移动啦 ε≡٩(๑>₃<)۶
control()函数:
//监听键盘 成功:1 失败:0 int control() { state = D; gotoxy(snake_x,snake_y); printf(" "); while(1) { if(GetAsyncKeyState(VK_UP) && state!=D) { state=U; } else if(GetAsyncKeyState(VK_DOWN) && state!=U) { state=D; } else if(GetAsyncKeyState(VK_LEFT)&& state!=R) { state=L; } else if(GetAsyncKeyState(VK_RIGHT)&& state!=L) { state=R; } else if(GetAsyncKeyState(VK_F1)) { if (speed<=20) { speed=10; } else { speed=speed-10; } } else if(GetAsyncKeyState(VK_F2)) { speed=speed+10; } if (snake_move()==1) { break; } } }
这个就是游戏的主循环了
在这里的时候,出现了GetAsyncKeyState()函数
(解释一下这个函数:执行他的时候会寻找是否在上次执行这个命令时是否按下键盘上的那个键,详细的去百度吧!)
源代码:
1 #include <time.h> 2 #include <conio.h> 3 #include <stdio.h> 4 #include <stdlib.h> 5 #include <windows.h> 6 7 //蛇的行进状态 8 #define U 1 9 #define D 2 10 #define L 3 11 #define R 4 12 13 void wall_draw(); //制造围墙 14 int border(int,int); //边界限制 15 int snake_move(); //蛇的移动 16 int snake_tail_delete(); //删除蛇尾 17 int path_log(); //蛇的位移路径记录 18 int food_generate(); //食物生成 19 int food_get(int,int); //获得食物 20 int gotoxy(int,int); //移动光标 21 int rectangle(int,int,int,int); //建立矩形 22 int control(); //控制移动方向 23 24 //全局变量的设定 25 26 int state; //当前蛇的状态 27 int wall_x=1,wall_y=1,wall_x1=97,wall_y1=25; //围墙的坐标 28 int speed =200; //蛇每一次运行的时间间隔 29 int snake_x=6,snake_y=5; //蛇头的坐标 30 int food_x,food_y; //食物的坐标 31 int path_list[30000][2]; //路径列表 32 int path_find_line=0; //路径记录到第几行 33 int point_food=0; //食物得到次数 34 35 int main() 36 { 37 wall_draw(); //绘制边界 38 39 gotoxy(snake_x,snake_y); 40 printf("█"); 41 42 food_generate(); //生成一次食物 43 44 //游戏进程 45 control(); //控制蛇的移动方向 46 47 //游戏结束 48 system("cls"); 49 wall_draw(); 50 } 51 52 53 54 int gotoxy(int x,int y) 55 { 56 COORD coord = {x,y}; 57 SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord); 58 } 59 void wall_draw() 60 { 61 //确定窗口大小 62 system("mode con cols=100 lines=30"); 63 //绘画矩形 64 rectangel(wall_x,wall_y,wall_x1,wall_y1); 65 //打印标题 66 gotoxy(43,0); 67 printf("***Snake***"); 68 //回到光标区 69 gotoxy(0,29); 70 } 71 int rectangel(int x,int y,int x2,int y2) 72 { 73 gotoxy(x,y); 74 printf("┌"); 75 int i; 76 for (i = 0; i < x2-x-1; i++) 77 { 78 printf("─"); 79 } 80 printf("┐"); 81 for (i = 0; i < y2-y-1; i++) 82 { 83 gotoxy(x,y+i+1); 84 printf("│"); 85 gotoxy(x2,y+i+1); 86 printf("│"); 87 } 88 gotoxy(x,y2); 89 printf("└"); 90 for (i = 0; i < x2-x-1; i++) 91 { 92 printf("─"); 93 } 94 printf("┘"); 95 } 96 97 //是否生成在障碍物上 是:1 否:0 border(x,y) 98 int border(int x,int y) 99 { 100 //查询物体是否撞到蛇身 101 int i; 102 for (i = 0; i < path_find_line+1; i++) 103 { 104 if (x==path_list[i][0] && y==path_list[i][1]) 105 { 106 return 1; 107 } 108 109 } 110 111 //查询物体是否撞到墙 112 if (wall_x>=x || wall_x1<=x || wall_y>=y || wall_y1<=y) 113 { 114 return 1; 115 } 116 117 return 0; 118 } 119 120 //蛇的移动路径记录 121 int path_log() 122 { 123 path_list[path_find_line][0]=snake_x; 124 path_list[path_find_line][1]=snake_y; 125 ++path_find_line; 126 } 127 128 //食物的生成 成功:1 失败:0 129 int food_generate() 130 { 131 //初始化随机种子 132 srand((unsigned int)time(NULL)); 133 int x,y; 134 x=food_x; 135 y=food_y; 136 while (1) 137 { 138 //随机食物坐标 139 food_x=rand()%wall_x1+wall_x; 140 food_y=rand()%wall_y1+wall_y; 141 142 //查询食物是否生成位置错误 143 if (food_x%2 ==1) 144 { 145 continue; 146 } 147 148 if (food_x==x && food_y==y) 149 { 150 continue; 151 } 152 if (border(food_x,food_y)==1) 153 { 154 continue; 155 } 156 157 //打印食物 158 gotoxy(food_x,food_y); 159 printf("\a⊕"); 160 gotoxy(0,29); 161 162 return 1; 163 } 164 return 0; 165 } 166 167 //获得食物 food_get(snake_x,snake_y) 168 int food_get(int x,int y) 169 { 170 if (x==food_x && y==food_y) 171 { 172 food_generate(); 173 ++point_food; 174 gotoxy(1,0); 175 printf("Point:%4d",point_food); 176 gotoxy(0,29); 177 } 178 179 } 180 181 //删除蛇尾 182 int snake_tail_delete() 183 { 184 //查询蛇尾位置 185 int x,y; 186 x=path_list[path_find_line-point_food-1][0]; 187 y=path_list[path_find_line-point_food-1][1]; 188 gotoxy(x,y); 189 printf(" "); 190 path_list[path_find_line-point_food-1][0]=0; 191 path_list[path_find_line-point_food-1][1]=0; 192 193 //返回光标区 194 gotoxy(0,29); 195 196 return 0; 197 } 198 199 //监听键盘 成功:1 失败:0 200 int control() 201 { 202 state = D; 203 gotoxy(snake_x,snake_y); 204 printf(" "); 205 while(1) 206 { 207 if(GetAsyncKeyState(VK_UP) && state!=D) 208 { 209 state=U; 210 } 211 else if(GetAsyncKeyState(VK_DOWN) && state!=U) 212 { 213 state=D; 214 } 215 else if(GetAsyncKeyState(VK_LEFT)&& state!=R) 216 { 217 state=L; 218 } 219 else if(GetAsyncKeyState(VK_RIGHT)&& state!=L) 220 { 221 state=R; 222 } 223 else if(GetAsyncKeyState(VK_F1)) 224 { 225 if (speed<=20) 226 { 227 speed=10; 228 } 229 else 230 { 231 speed=speed-10; 232 } 233 234 235 } 236 else if(GetAsyncKeyState(VK_F2)) 237 { 238 speed=speed+10; 239 } 240 if (snake_move()==1) 241 { 242 break; 243 } 244 } 245 } 246 //蛇的移动 247 int snake_move() 248 { 249 snake_tail_delete(); 250 if (state == U) 251 { 252 --snake_y; 253 gotoxy(snake_x,snake_y); 254 printf("█"); 255 gotoxy(10,29); 256 printf("Speed:%4d",speed); 257 } 258 if (state == D) 259 { 260 ++snake_y; 261 gotoxy(snake_x,snake_y); 262 printf("█"); 263 gotoxy(10,29); 264 printf("Speed:%4d",speed); 265 } 266 if (state == L) 267 { 268 --snake_x; 269 --snake_x; 270 gotoxy(snake_x,snake_y); 271 printf("█"); 272 gotoxy(10,29); 273 printf("Speed:%4d",speed); 274 } 275 if (state == R) 276 { 277 ++snake_x; 278 ++snake_x; 279 gotoxy(snake_x,snake_y); 280 printf("█"); 281 gotoxy(10,29); 282 printf("Speed:%4d",speed); 283 } 284 food_get(snake_x,snake_y); 285 286 if (border(snake_x,snake_y)==1) 287 { 288 return 1; 289 } 290 291 292 path_log(); 293 294 Sleep(speed); 295 296 }
如果这个程序有什么问题,或者是有什么建议的话,可以联系我交流.
欢迎讨论,并互相进步!
(第 4次更改)