贪吃蛇是一款经典的小游戏。玩家可以使用设置好的键操控蛇吞下食物使身体变长。当蛇头撞到蛇身或者障壁时游戏结束。
1、对整个源代码加注释,对每个函数和变量分析注释,明确标识出二维数组canvas中每个取值的意义游戏操作的键及对应的功能等,初始代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#define High 20 //二维数组的行最大值
#define Width 30 //二维数组的列最大值
int moveDirection; //不同的值表示蛇的不同的移动方向
int food_x = 3, food_y = 5; //食物的初始行坐标,列坐标
int canvas[High][Width] = { 0 }; //定义了一个High行Width列的二维数组用于贪吃蛇游戏的图像建立,初始值均赋值为0
void gotoxy(int x, int y) //声明于<conio.h> 中的一个函数,将光标移到(x,y)的位置,横向为 X 轴,纵向为 Y 轴
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); //结构体指针变量,这个指针赋值变成一个windows输出的句柄
COORD pos; //结构体变量位置,其中包含x,y
pos.X = x; //光标在命令行中即将偏移的水平单位
pos.Y = y; //光标在命令行中即将偏移的竖直单位
SetConsoleCursorPosition(handle, pos); //来源于<windows.h>的头文件,偏移的光标位置
}
void moveSnakeByDirection() //控制蛇的移动
{
int i, j; //i用于行的自增,j用于列的自增
for (i = 1; i < High - 1; i++) //行自增
for (j = 1; j < Width - 1; j++) //列自增
if (canvas[i][j] > 0) //用于寻找蛇的位置
canvas[i][j]++; //蛇的二维数组值自增,即让蛇身前进一步
int oldTail_i, oldTail_j, oldHead_i, oldHead_j; //四个整型变量,旧的身体行位置,旧的身体列位置,旧的头行位置,旧的头列位置
int max = 0; //int型变量max,赋值为0,用于寻找蛇尾
for (i = 1; i < High - 1; i++) //行自增
for (j = 1; j < Width - 1; j++) //列自增
if (canvas[i][j] > 0) //判断二维数组值是否大于0,若是,说明为蛇身,蛇体,执行if语句
{
if (max < canvas[i][j]) //选出二维数组中值最大的位置,即为蛇尾的位置,并确定行,列的位置
{
max = canvas[i][j]; //将max赋值为二维数组中的最大值,即蛇尾的值
oldTail_i = i; //确定旧蛇尾的行标
oldTail_j = j; //确定旧蛇尾的列标
}
if (canvas[i][j] == 2) //判断二维数组值是否为2,确定旧蛇头的位置
{
oldHead_i = i; //确定旧蛇头的行标
oldHead_j = j; //确定旧蛇头的列标
}
}
int newHead_i, newHead_j; //两个int型变量,表示新蛇头位置的行标,新蛇头位置的列标
if (moveDirection == 1) //向上转时,蛇头位置的改变
{
newHead_i = oldHead_i - 1; //新蛇头的行标=旧蛇头行标-1
newHead_j = oldHead_j; //新蛇头列标=旧蛇头列标
}
if (moveDirection == 2) //向下转时,蛇头位置的改变
{
newHead_i = oldHead_i + 1; //新蛇头的行标=旧蛇头行标+1
newHead_j = oldHead_j; //新蛇头列标=旧蛇头列标
}
if (moveDirection == 3) //向左转时,蛇头位置的改变
{
newHead_i = oldHead_i; //新蛇头行标=旧蛇头行标
newHead_j = oldHead_j - 1; //新蛇头的列标=旧蛇头列标-1
}
if (moveDirection == 4) //向右转时,蛇头位置的改变
{
newHead_i = oldHead_i; //新蛇头行标=旧蛇头行标
newHead_j = oldHead_j + 1;//新蛇头的列标=旧蛇头列标+1
}
if (canvas[newHead_i][newHead_j] == -2) //判断二维数组值是否为2,即判断是否吃到食物,若吃到该位置二维数组的值变为0,蛇变长
{
canvas[food_x][food_y] = 0;
}
else //否则,旧尾巴的位置值变为0,蛇长度不变
canvas[oldTail_i][oldTail_j] = 0;
if (canvas[newHead_i][newHead_j] > 0 || canvas[newHead_i][newHead_j] == -1) //判定是否撞到边界或者身体
{
printf("游戏失败!\n"); //打印游戏失败
Sleep(2000); //程序休眠2s
system("pause"); //包含于<stdlib.h>的头文件中,执行暂停批处理文件的处理并显示消息的命令
exit(0); //正常退出
}
else
canvas[newHead_i][newHead_j] = 1; //未撞到边界时和身体时的头的新数组位置赋值为1,蛇正常向新位置移动
}
void startup() //设定游戏开始时的场景
{
int i, j; //i用于行的自增,j用于列的自增
for (i = 0; i < High; i++) //设置边界:垂直的边界,将边界位置的值定为-1
{
canvas[i][0] = -1; //设置左边界,将边界位置值赋为-1
canvas[i][Width - 1] = -1; //设置右边界,将边界位置值赋为-1
}
for (j = 0; j < Width; j++) //设置边界:水平的边界,将边界位置的值定为-1
{
canvas[0][j] = -1; //设置上边界,将边界位置值赋为-1
canvas[High - 1][j] = -1; //设置下边界,将边界位置值赋为-1
}
canvas[High / 2][Width / 2] = 1; //将边界框内的中心位置赋值为1,定为蛇头出现的位置
for (i = 1; i <= 4; i++) //将蛇头左边的四个坐标赋值为i+1,定为蛇身出现的位置
canvas[High / 2][Width / 2 - i] = i + 1;
moveDirection = 4; //将移动方向赋值为4,表示蛇初始移动方向为向右移动
canvas[food_x][food_y] = -2; //将食物出现的位置的二维数组值赋值为-2,定为食物出现的位置
}
void show() //将蛇头蛇身和边界可视化
{
gotoxy(0, 0); //将光标移到左上角(0,0)的位置,横向为 X 轴,纵向为 Y 轴
int i, j; //i用于行的自增,j用于列的自增
for (i = 0; i < High; i++) //行循环
{
for (j = 0; j < Width; j++) //列循环
{
if (canvas[i][j] == 0) //二维数组值为0的位置打印空格
printf(" ");
else if (canvas[i][j] == -1) //二位数组值为-1的位置打印边界
printf("#");
else if (canvas[i][j] == 1) //二位数组值为1的位置打印蛇头
printf("@");
else if (canvas[i][j] > 1) //二维数组值大于一的位置打印蛇身
printf("*");
else if (canvas[i][j] == -2) //二位数组值为-2的位置打印食物
printf("$");
}
printf("\n"); //每行打印一个回车用于换行
}
Sleep(500); //程序暂停休眠500ms
}
void updateWithoutInput() //未输入转向时的自动偏移,沿着蛇头的方向移动
{
moveSnakeByDirection();
}
void updateWithInput() //根据输入的按键进行转向
{
char input; //字符型变量,赋值为读取到的键盘输入值,用于转向的判定
if (_kbhit()) //_kbhit()是<conio.h>中的函数,判定检查当前是否有键盘输入,若有则返回一个非0值,执行if语句;否则返回0,不执行if语句
{
input = _getch(); //将input赋值为_getch()从控制台中获取的字符
if (input == 'a') //若input=a,将蛇偏移方向变量赋值为3,执行偏移蛇函数,即蛇向左转,直行
{
moveDirection = 3;
moveSnakeByDirection();
}
else if (input == 'd') //若input=d,将蛇偏移方向变量赋值为4,执行偏移蛇函数,即蛇向右转,直行
{
moveDirection = 4;
moveSnakeByDirection();
}
else if (input == 'w') //input=w,将蛇偏移方向变量赋值为1,执行偏移蛇函数,即蛇向上转,直行
{
moveDirection = 1;
moveSnakeByDirection();
}
else if (input == 's') //input=s,将蛇偏移方向变量赋值为2,执行偏移蛇函数,即蛇向下转,直行
{
moveDirection = 2;
moveSnakeByDirection();
}
}
}
int main() //主函数
{
startup(); //设定游戏开始时的场景
while (1) //死循环
{
show(); //将蛇头蛇身和边界可视化
updateWithoutInput(); //未输入偏转方向时蛇的移动方向
updateWithInput(); //人为输入偏转方向时的蛇的移动方向
}
return 0; //程序正常退出,返回值为0;
2、修改程序,蛇的表示以姓首字母为头,名的首字母为身,初始长度为5;食物表示:用学号最后一位表示。对应代码区修改如下:
void show()
{
gotoxy(0, 0);
int i, j;
for (i = 0; i < High; i++)
{
for (j = 0; j < Width+12; j++)
{ if (i == 1 && j == Width+2)
printf("Score:%3d", score);
if (i == 2 && j == Width+2)
printf("Min:%5d", min);
if (canvas[i][j] == 0&&j<Width+1)
printf(" ");
else if (canvas[i][j] == -1)
printf("#");
else if (canvas[i][j] == 1)
printf("Z");
else if (canvas[i][j] > 1)
printf("T");
else if (canvas[i][j] == -2)
printf("6");
else if (canvas[i][j] == -3)
printf("m");
else if (canvas[i][j] == -4)
printf("s");
}
printf("\n");
}
Sleep(500);
}
3、初始小蛇移动方向改为向下移动。修改对应代码区,修改moveDirection参数如下:
void startup()
{
int i, j;
for (i = 13; i < 18; i++)
canvas[3][i] = -1;
for (i = 20; i < 25; i++)
canvas[3][i] = -1;
for (i = 4; i < 9; i++)
{
canvas[i][15] = -1;
canvas[i][22] = -1;
}
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;
}
for (j = Width+1; j < Width + 12; j++)
{
canvas[0][j] = -1;
canvas[3][j] = -1;
}
for (i = 1; i < 3; i++)
{
canvas[i][Width+1] = -1;
canvas[i][Width + 11] = -1;
}
canvas[High / 2][Width / 2] = 1;
for (i = 1; i <= 4; i++)
canvas[High / 2-i][Width / 2 ] = i + 1;
moveDirection = 2; //设置初始移动方向
canvas[food_x][food_y] = -2;
canvas[slow_x][slow_y] = -4;
canvas[min_x][min_y] = -3;
}
4.修改程序每次吃完食物,蛇身长度加1,食物随机再一次出现。
修改void moveSnakeByDirection()部分代码,对应代码修改后对应如下:
if (canvas[newHead_i][newHead_j] == -2)
{
srand((unsigned)time(NULL));
canvas[food_x][food_y] = 0;
score++;
food_x = rand() % 18 + 1;
food_y = rand()%38+1;
canvas[food_x][food_y] = -2;
}
else
canvas[oldTail_i][oldTail_j] = 0;
5、增加道具,吃完可以加命。
(1)在void moveSnakeByDirection()部分增加代码,代码如下所示:
if (canvas[newHead_i][newHead_j] == -3)
{
srand((unsigned)time(NULL));
canvas[min_x][min_y] = 0;
min += 1;
min_x = rand() % 18 + 1;
min_y = rand() % 38 + 1;
canvas[min_x][min_y] = -3;
}
(2)在void moveSnakeByDirection()部分修改部分代码,修改代码如下所示:
if (canvas[newHead_i][newHead_j] > 0 || canvas[newHead_i][newHead_j] == -1)
{
if (min >= 1)
{
min--;
for (i = 0; i < High; i++)
for (j = 0; j < Width; j++)
{
if (canvas[i][j] > 0)
canvas[i][j] = 0;
}
canvas[oldHead_i][oldHead_j] = 0;
canvas[High / 2][Width / 2] = 1;
for (i = 1; i <= 4; i++)
canvas[High / 2 - i][Width / 2] = i + 1;
moveDirection = 2;
show();
}
else
{
printf("游戏失败!\n");
Sleep(2000);
system("pause");
exit(0);
}
}
else
canvas[newHead_i][newHead_j] = 1;
6、增加道具,吃完可以减速。
(1)在全局函数部分添加slow变量,初始为0,并设置道具的初始横纵坐标;
int slow = 0;
int slow_x = 10, slow_y = 17;
(2)在void moveSnakeByDirection()部分增加代码,代码如下所示:
if (canvas[newHead_i][newHead_j] == -4)
{
canvas[slow_x][slow_y] = 0;
srand((unsigned)time(NULL));
slow_x = rand() % 18 + 1;
slow_y = rand() % 38 + 1;
canvas[slow_x][slow_y] = -4;
slow+=3;
}
canvas[slow_x][slow_y] = -4; //将事物所出现的坐标赋值
(3)修改main()函数部分,如下图所示
int main()
{
startup();
while (1)
{
show();
if (slow > 0) {
slow--;
Sleep(1000);
}
updateWithoutInput();
updateWithInput();
}
return 0;
}
7.游戏界面美化
修改startup部分的画布,根据需要自行调整。
整个小游戏的完整代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <time.h>
#define High 20
#define Width 40
int newHead_i, newHead_j;
int moveDirection;
int slow = 0;
int slow_x = 10, slow_y = 17;
int food_x = 9, food_y = 10;
int min_x = 7, min_y = 9;
int min = 0,score=0;
int canvas[High][Width+12] = { 0 };
void gotoxy(int x, int y)
{
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos;
pos.X = x;
pos.Y = y;
SetConsoleCursorPosition(handle, pos);
}
void moveSnakeByDirection()
{
void show();
int i, j;
for (i = 1; i < High - 1; i++)
for (j = 1; 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 = 1; i < High - 1; i++)
for (j = 1; 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 (moveDirection == 1)
{
newHead_i = oldHead_i - 1;
newHead_j = oldHead_j;
}
if (moveDirection == 2)
{
newHead_i = oldHead_i + 1;
newHead_j = oldHead_j;
}
if (moveDirection == 3)
{
newHead_i = oldHead_i;
newHead_j = oldHead_j - 1;
}
if (moveDirection == 4)
{
newHead_i = oldHead_i;
newHead_j = oldHead_j + 1;
}
if (canvas[newHead_i][newHead_j] == -2)
{
srand((unsigned)time(NULL));
canvas[food_x][food_y] = 0;
score++;
food_x = rand() % 18 + 1;
food_y = rand()%38+1;
canvas[food_x][food_y] = -2;
}
else
canvas[oldTail_i][oldTail_j] = 0;
if (canvas[newHead_i][newHead_j] == -3)
{
srand((unsigned)time(NULL));
canvas[min_x][min_y] = 0;
min += 1;
min_x = rand() % 18 + 1;
min_y = rand() % 38 + 1;
canvas[min_x][min_y] = -3;
}
if (canvas[newHead_i][newHead_j] == -4)
{
canvas[slow_x][slow_y] = 0;
srand((unsigned)time(NULL));
slow_x = rand() % 18 + 1;
slow_y = rand() % 38 + 1;
canvas[slow_x][slow_y] = -4;
slow+=3;
}
if (canvas[newHead_i][newHead_j] > 0 || canvas[newHead_i][newHead_j] == -1)
{
if (min >= 1)
{
min--;
for (i = 0; i < High; i++)
for (j = 0; j < Width; j++)
{
if (canvas[i][j] > 0)
canvas[i][j] = 0;
}
canvas[oldHead_i][oldHead_j] = 0;
canvas[High / 2][Width / 2] = 1;
for (i = 1; i <= 4; i++)
canvas[High / 2 - i][Width / 2] = i + 1;
moveDirection = 2;
show();
}
else
{
printf("游戏失败!\n");
Sleep(2000);
system("pause");
exit(0);
}
}
else
canvas[newHead_i][newHead_j] = 1;
}
void startup()
{
int i, j;
for (i = 13; i < 18; i++)
canvas[3][i] = -1;
for (i = 20; i < 25; i++)
canvas[3][i] = -1;
for (i = 4; i < 9; i++)
{
canvas[i][15] = -1;
canvas[i][22] = -1;
}
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;
}
for (j = Width+1; j < Width + 12; j++)
{
canvas[0][j] = -1;
canvas[3][j] = -1;
}
for (i = 1; i < 3; i++)
{
canvas[i][Width+1] = -1;
canvas[i][Width + 11] = -1;
}
canvas[High / 2][Width / 2] = 1;
for (i = 1; i <= 4; i++)
canvas[High / 2-i][Width / 2 ] = i + 1;
moveDirection = 2;
canvas[food_x][food_y] = -2;
canvas[slow_x][slow_y] = -4;
canvas[min_x][min_y] = -3;
}
void show()
{
gotoxy(0, 0);
int i, j;
for (i = 0; i < High; i++)
{
for (j = 0; j < Width+12; j++)
{ if (i == 1 && j == Width+2)
printf("Score:%3d", score);
if (i == 2 && j == Width+2)
printf("Min:%5d", min);
if (canvas[i][j] == 0&&j<Width+1)
printf(" ");
else if (canvas[i][j] == -1)
printf("#");
else if (canvas[i][j] == 1)
printf("Z");
else if (canvas[i][j] > 1)
printf("T");
else if (canvas[i][j] == -2)
printf("6");
else if (canvas[i][j] == -3)
printf("m");
else if (canvas[i][j] == -4)
printf("s");
}
printf("\n");
}
Sleep(500);
}
void updateWithoutInput()
{
moveSnakeByDirection();
}
void updateWithInput()
{
char input;
if (_kbhit())
{
input = _getch();
if (input == 'a')
{
moveDirection = 3;
moveSnakeByDirection();
}
else if (input == 'd')
{
moveDirection = 4;
moveSnakeByDirection();
}
else if (input == 'w')
{
moveDirection = 1;
moveSnakeByDirection();
}
else if (input == 's')
{
moveDirection = 2;
moveSnakeByDirection();
}
}
}
int main()
{
startup();
while (1)
{
show();
if (slow > 0) {
slow--;
Sleep(1000);
}
updateWithoutInput();
updateWithInput();
}
return 0;
}