C实现的贪吃蛇是一个用于理解面向过程思想的很好实例,我们将继续沿用起初的框架完成该实例。
基本框架
为了避免把所有代码都放进main函数而使得代码看起来臃肿,我们将通过以下基本框架来实现游戏内容。
//全局变量定义
int main(){
startup(); //游戏初始化
while(1) //游戏循环体
{
show(); //显示界面
updateWithoutInput(); //与输入无关的更新
updateWithInput(); //与输入有关的更新
}
return 0;
}
画个蛇
我们仍然使用二维数组 canvas[High][Width] 来存储画布上的元素,并定义:
- 元素值为 0 时输出空格
- 元素值为 -1 时输出边界#
- 元素值为 1 时输出蛇头@
- 元素值大于 1 时输出蛇身
可以在 startup() 函数中初始化蛇,令蛇头位于中间位置,向左依次生成蛇身,元素值分别为2,3,4,5。
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
//定义画布宽高,根据个人调整
#define High 26
#define Width 32
//全局变量
int canvas[High][Width] = [0]; //画布元素数组
void gotoxy(int x, int y) //移动光标便于清屏重画
{
HANDLE handle = GetStdHandle(STD_UOTPUT_HANDLE);
CROOD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle, pos);
}
void startup() //初始化
{
int i,j;
//初始化边框
for(i = 0; i < High; i ++)
{
canvas[i][0] = -1;
canvas[i][Width - 1] = -1;
}
for(j = 0; j < Width; j ++)
{
canvas[0][j] = -1;
canvas[High - 1][j] = -1;
}
//初始化蛇头
canvas[High / 2][Width / 2] = 1;
//生成蛇身
for(i = 1; i <= 4; i ++)
canvas[High / 2][Width / 2 - i] = i + 1;
}
void show() //画面显示
{
gotoxy(0, 0);
int i,j;
for(i=0; i<High; i++)
{
for(j=0; j<Width; j++)
{
if(canvas[i][j] == 0)
printf(" ");
else if(canvas[i][j] == -1)
printf("#"); //输出边界
else if(canvas[i][j] == 1)
printf("@"); //输出蛇头
else if(canvas[i][j] > 1)
printf("O"); //输出蛇身
}
printf("\n"); //每完成一行后换行
}
sleep(20); //休眠,其实可以删了这行
}
void updateWithoutInput() //与输入无关的更新
{}
void updateWithInput() //与输入有关的更新
{}
int main()
{
startup();
while(1)
{
show();
updateWithoutInput();
updateWithInput();
}
return 0;
让蛇爬
对于蛇移动的实现可以这么想:
- 沿着前进方向把各元素加 1 (包括蛇头前面的一格),这样蛇头变成 2 (蛇身),前面一格变成 1 (蛇头);
- 此时蛇会多出一格,需要删去蛇尾;
- 经过第一步发现,蛇尾刚好是最大的数,所以删去最大值(变为 0 )即可。
同样,我们可以定义 Directio 用于存储蛇移动方向,使用 1,2,3,4 分别表示移动方向为上、下、左、右。
实现蛇的移动
思路:
- 扫描全部元素
- 将正值元素加 1
- 将最大元素(蛇尾)变为 0
- 将元素 2 (原蛇头) 对应方向上元素变为 1 (新蛇头)
void moving()
{
int i,j;
for(i = 0; i < High - 1; i ++)
for(j = 0; j < Width - 1; j ++)
if(canvas[i][j] > 0)
canvas[i][j] ++;
int oldTail_i, oldTail_j, oldHead_i, oldHead_j;
int max = 0;
for(i = 0; i < High - 1; i ++)
for(j = 0; j < Width - 1; j ++)
if(canvas[i][j] > 0)
{
if(max < canvas[i][j])
{
max = canvas[i][j];
oldTail_i = i;
oldTail_j = j;
}
if(canvas[i][j] == 2)
{
oldHead_i = i;
oldHead_j = j;
}
}
canvas[oldTail_i][oldTail_j] = 0;
if(Direction == 1)
canvas[oldHead_i - 1][oldHead_j] = 1;
if(Direction == 2)
canvas[oldHead_i + 1][oldHead_j] = 1;
if(Direction == 3)
canvas[oldHead_i][oldHead_j - 1] = 1;
if(Direction == 4)
canvas[oldHead_i][oldHead_j + 1] = 1;
}
玩家控制移动
这一步的实现就会比较简单,只需要通过判断输入改变 Direction 的值即可。
void updateWithInput()
{
char input;
if(kbhit())
{
input = getch();
if(input == 'a')
{
Direction = 3;
moving();
}
if(input == 's')
{
Direction = 2;
moving();
}
if(input == 'w')
{
Direction = 1;
moving();
}
if(input == 'd')
{
Direction = 4;
moving();
}
}
}
判定失败
游戏失败有两种情况,撞墙或撞自己。
只需在 moving() 函数中加入如下代码:
...
if(Direction == 1)
newHead_i, newHead_j = oldHead_i - 1, oldHead_j;
if(Direction == 2)
newHead_i, newHead_j = oldHead_i + 1, oldHead_j;
if(Direction == 3)
newHead_i, newHead_j = oldHead_i, oldHead_j - 1;
if(Direction == 4)
newHead_i, newHead_j = oldHead_i, oldHead_j + 1;
if(canvas[newHead_i][newHead_j] > 0 || canvas[newHead_i][newHead_j] == -1)
{
printf("游戏失败!\n");
exit(0);
}
else
canvas[newHead_i][newHead_j] = 1;
给蛇吃饭
令画布中元素值为 -2 时输出食物,当蛇头碰到食物时,只需不删蛇尾即可。
所以直接在删除蛇尾时加个判断就行了。
小结
完整代码如下,写了好一会了有可能出现和前面不太一致的地方,仅供参考。
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
//定义画布宽高,根据个人调整
#define High 26
#define Width 32
//全局变量
int canvas[High][Width] = [0]; //画布元素数组
int foodx, foody;
int Direction = 4;
void gotoxy(int x, int y) //移动光标便于清屏重画
{
HANDLE handle = GetStdHandle(STD_UOTPUT_HANDLE);
CROOD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle, pos);
}
void moving()
{
int i,j;
for(i = 0; i < High - 1; i ++)
for(j = 0; j < Width - 1; j ++)
if(canvas[i][j] > 0)
canvas[i][j] ++;
int oldTail_i, oldTail_j, oldHead_i, oldHead_j;
int max = 0;
for(i = 0; i < High - 1; i ++)
for(j = 0; j < Width - 1; j ++)
if(canvas[i][j] > 0)
{
if(max < canvas[i][j])
{
max = canvas[i][j];
oldTail_i = i;
oldTail_j = j;
}
if(canvas[i][j] == 2)
{
oldHead_i = i;
oldHead_j = j;
}
}
if(Direction == 1)
canvas[oldHead_i - 1][oldHead_j] = 1;
if(Direction == 2)
canvas[oldHead_i + 1][oldHead_j] = 1;
if(Direction == 3)
canvas[oldHead_i][oldHead_j - 1] = 1;
if(Direction == 4)
canvas[oldHead_i][oldHead_j + 1] = 1;
if(Direction == 1)
newHead_i, newHead_j = oldHead_i - 1, oldHead_j;
if(Direction == 2)
newHead_i, newHead_j = oldHead_i + 1, oldHead_j;
if(Direction == 3)
newHead_i, newHead_j = oldHead_i, oldHead_j - 1;
if(Direction == 4)
newHead_i, newHead_j = oldHead_i, oldHead_j + 1;
//判断吃东西
if(canvas[newHead_i][newHead_j] == -2)
{
canvas[foodx][foody] = 0;
//食物刷新
foodx = rand()%(High - 5) + 2;
foody = rand()%(Width - 5) + 2;
canvas[foodx][foody] = -2;
}
else
canvas[oldTail_i][oldTail_j] = 0;
//判断失败
if(canvas[newHead_i][newHead_j] > 0 || canvas[newHead_i][newHead_j] == -1)
{
printf("游戏失败!\n");
exit(0);
}
else
canvas[newHead_i][newHead_j] = 1;
}
void startup() //初始化
{
int i,j;
//初始化边框
for(i = 0; i < High; i ++)
{
canvas[i][0] = -1;
canvas[i][Width - 1] = -1;
}
for(j = 0; j < Width; j ++)
{
canvas[0][j] = -1;
canvas[High - 1][j] = -1;
}
//初始化蛇头
canvas[High / 2][Width / 2] = 1;
//生成蛇身
for(i = 1; i <= 4; i ++)
canvas[High / 2][Width / 2 - i] = i + 1;
//生成食物
foodx = rand()%(High - 5) + 2;
foody = rand()%(Width - 5) + 2;
canvas[foodx][foody] = -2;
}
void show() //画面显示
{
gotoxy(0, 0);
int i,j;
for(i=0; i<High; i++)
{
for(j=0; j<Width; j++)
{
if(canvas[i][j] == 0)
printf(" ");
else if(canvas[i][j] == -1)
printf("#"); //输出边界
else if(canvas[i][j] == 1)
printf("@"); //输出蛇头
else if(canvas[i][j] > 1)
printf("O"); //输出蛇身
else if(canvas[i][j] == -2)
printf("F"); //输出食物
}
printf("\n"); //每完成一行后换行
}
sleep(20); //休眠,其实可以删了这行
}
void updateWithoutInput() //与输入无关的更新
{
moving() //注意,这里写上后输入控制会多走一次![在这里插入图片描述](https://img-blog.csdnimg.cn/ea6444f1d09f4c17b9fce7a902c6a75e.png?x-oss-process=image/watermark,type_d3F5LXplbmhlaQ,shadow_50,text_Q1NETiBA5peg6ZmQ55qE6I-c6bif,size_16,color_FFFFFF,t_70,g_se,x_16#pic_center)
}
void updateWithInput() //与输入有关的更新
{
char input;
if(kbhit())
{
input = getch();
if(input == 'a')
{
Direction = 3;
moving();
}
if(input == 's')
{
Direction = 2;
moving();
}
if(input == 'w')
{
Direction = 1;
moving();
}
if(input == 'd')
{
Direction = 4;
moving();
}
}
}
int main()
{
startup();
while(1)
{
show();
updateWithoutInput();
updateWithInput();
}
return 0;