最近也是结束了C语言学习,总想的搞个东西,然后想到自己以前想搞一个贪吃蛇,那就说搞就搞,就写了这么一个代码,后面我会附上我的代码,以及在代码中遇到的问题会进行一些表述,这个代码有两个大的瑕疵是在程序运行时,蛇在移动的过程中整体游戏界面的展现会忽闪忽闪的,还有一个是蛇的转向处理的不够自然,例如在蛇的长度为三时,假如当前蛇的状态是一条直线,那么在让它转向时,它应该是蛇头向目标位置移动一格,然后蛇身一次取代前面蛇身的位置。他的问题就出在这,在蛇转向过程中(长度为三)他并不会出现这样的情况,而是直接就蛇身整体转向而后又是直接是一条直线。看起来不是那么连贯。但是总体来说我目前没有遇到bug,希望有读者可以解决上面两个问题或者看出bug的话,可以指正我。代码如下:
首先是,头文件的引用以及#define定义的常量,还有大致函数的声明:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#include <string.h>
#include <Windows.h>
#define IN_S_LEN 2//蛇的初始长度
#define FACE_SZ 30//界面的大小
#define TIME_REFRESH 500
typedef struct Snake//蛇的信息
{
int** body_coor;
int len;
}Snake;
//初始设置蛇的身体各个点的坐标(二维数组存储)
void snake_init(Snake* ps);
//打印游戏界面
void game_interface(char face [][FACE_SZ - 2], int sz);
//在游戏界面显示蛇身
void snake_body(Snake* snake, char face[][FACE_SZ - 2]);
//设置蛇要吃的东西在界面上的生成
void set_food(char face[][FACE_SZ - 2]);
//游戏实现
void game_play(Snake* snake, char face[][FACE_SZ - 2]);
然后是整体游戏框架逻辑,其实也是之前制作游戏的框架做了一些改变。
#include "game.h"
void menu()
{
printf("*****************************\n");
printf("**** 1. play 0. exit ****\n");
printf("**** 2.rules of the game ****\n");
printf("*****************************\n");
}
void game_rules()
{
printf(" 1).这个游戏设置的很粗略,玩家需要使用键盘上的‘w’‘a’‘s’‘d’\n");
printf("来操作蛇的移动,没有胜利,只有无尽的吃下去,直到蛇到达边界,或者与\n");
printf("自己的身体重合后游戏失败\n");
printf(" 2).如果在游戏过程中想暂停游戏,按下空格即可\n");
printf(" 要退出当前界面,请按数字0并回车确认\n");
}
void game()
{
Snake snake = { 0 };
char inter_face[FACE_SZ - 2][FACE_SZ - 2] = { 0 };//游戏界面展示数组
memset(inter_face, ' ', sizeof(char) * ((FACE_SZ - 2) * (FACE_SZ - 2)));
snake_init(&snake);
//game_interface(inter_face, FACE_SZ);
snake_body(&snake, inter_face);
game_play(&snake, inter_face);
}
int main()
{
srand((unsigned)time(NULL));
int input1 = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input1);
system("cls");
switch (input1)
{
case 1:
game();
break;
case 2:
{
int input2 = 0;
game_rules();
while(1)
{
printf("请选择:>");
scanf("%d", &input2);
if (input2 == 0)
{
system("cls");
break;
}
else
printf("输入无效\n");
}
break;
}
case 0:
printf("退出游戏\n");
break;
default:
printf("输入错误,请重新输入\n");
break;
}
} while (input1);
return 0;
}
最后是大致函数的实现,以及游戏内部具体的实现和它附随的各种函数
#include "game.h"
int boom_x = 0;//设置全局变量,蛇食物的位置便于检测
int boom_y = 0;
void snake_init(Snake* ps)
{
int** tmp = (int**)malloc(sizeof(int*) * IN_S_LEN);
if (tmp == NULL)
{
perror("tmp_malloc");
return;
}
int i = 0;
for (i = 0; i < IN_S_LEN; i++)
{
tmp[i] = (int*)malloc(sizeof(int) * 2);
if (tmp[i] == NULL)
{
perror("tmp[i]_malloc");
return;
}
}
ps->body_coor = tmp;
ps->len = IN_S_LEN;
}
void game_interface(char face [][FACE_SZ - 2], int sz)
{
int i = 0;
for (i = 0; i < sz; i++)
{
if (i == 0 || i == sz - 1)
{
printf(" ");
int j = 0;
for (j = 0; j < sz - 2; j++)
{
if(j != sz - 3)
{
printf("--");
}
else
{
printf("-");
}
}
printf(" ");
}
else
{
printf("|");
int j = 0;
for (j = 0; j < sz - 2; j++)
{
if(j != sz - 3)
{
printf("%c ", face[i - 1][j]);
}
else
{
printf("%c", face[i - 1][j]);
}
}
printf("|");
}
printf("\n");
}
}
void snake_body(Snake* snake, char face[][FACE_SZ - 2])
{
snake->body_coor[0][0] = (FACE_SZ - 2) / 2;
snake->body_coor[0][1] = (FACE_SZ - 2) / 2;
snake->body_coor[1][0] = (FACE_SZ - 2) / 2;
snake->body_coor[1][1] = (FACE_SZ - 2) / 2 - 1;
face[snake->body_coor[0][0]][snake->body_coor[0][1]] = '#';
face[snake->body_coor[1][0]][snake->body_coor[1][1]] = '*';
}
void set_food(char face[][FACE_SZ - 2])
{
while (1)
{
int x = rand() % (FACE_SZ - 2);
int y = rand() % (FACE_SZ - 2);
if (face[x][y] == ' ')
{
face[x][y] = '1';
boom_x = x;
boom_y = y;
break;
}
}
}
void menu3()
{
printf("*****************************\n");
printf("**** 1. Continue the game****\n");
printf("**** 0. Exit the game ****\n");
printf("*****************************\n");
}
int game_pause()
{
int input = 0;
do
{
menu3();
printf("请选择:>");
scanf("%d", &input);
switch (input)
{
case 1:
return 1;
break;
case 0:
return 0;
break;
default:
printf("输入有误请重新输入\n");
break;
}
} while (input);
}
int snake_move(Snake* snake, char face[][FACE_SZ - 2], char move)
{
int x = 0;
int y = 0;
int i = 0;
int move_x = 0;
int move_y = 0;
if (move == 'w' || move == 'W')
{
move_x = -1;
move_y = 0;
}
else if (move == 'a' || move == 'A')
{
move_x = 0;
move_y = -1;
}
else if (move == 's' || move == 'S')
{
move_x = 1;
move_y = 0;
}
else if (move == 'd' || move == 'D')
{
move_x = 0;
move_y = 1;
}
//else if(move == ' ')
//{
// //游戏暂停函数
// if (game_pause())
// {
// return 1;
// }
// {
// exit(0);
// }
//}
//判定蛇的身体不重合
for (i = 1; i < snake->len; i++)
{
if (snake->body_coor[0][0] == snake->body_coor[i][0] && snake->body_coor[0][1] == snake->body_coor[i][1])
{
printf("游戏失败\n");
free(snake->body_coor);
snake->body_coor = NULL;
exit(0);
}
}
//判定撞墙
if ((((snake->body_coor[0][0] + move_x < FACE_SZ - 2) && (snake->body_coor[0][0] + move_x >= 0)) &&
((snake->body_coor[0][1] + move_y >= 0) && (snake->body_coor[0][1] + move_y < FACE_SZ - 2))) == 0)
{
free(snake->body_coor);
snake->body_coor = NULL;
printf("游戏失败\n");
exit(0);
}
if(snake->body_coor[0][0] + move_x != snake->body_coor[1][0] || snake->body_coor[0][1] + move_y != snake->body_coor[1][1] && move != ' ')
{
for (i = 0; i < snake->len; i++)
{
if (i != snake->len - 1)
{
if (i != 0)
{
int x1 = snake->body_coor[i][0];
int y1 = snake->body_coor[i][1];
snake->body_coor[i][0] = x;
snake->body_coor[i][1] = y;
x = x1;
y = y1;
}
else
{
x = snake->body_coor[i][0];
y = snake->body_coor[i][1];
snake->body_coor[i][0] = snake->body_coor[i][0] + move_x;
snake->body_coor[i][1] = snake->body_coor[i][1] + move_y;
}
}
else
{
int x1 = snake->body_coor[i][0];
int y1 = snake->body_coor[i][1];
snake->body_coor[i][0] = x;
snake->body_coor[i][1] = y;
face[x1][y1] = ' ';
}
}
for (i = 0; i < snake->len; i++)
{
if (i != 0)
{
face[snake->body_coor[i][0]][snake->body_coor[i][1]] = '*';
}
else
{
face[snake->body_coor[i][0]][snake->body_coor[i][1]] = '#';
}
}
return 1;
}
else
{
return 0;
}
}
char legal_move(Snake* snake, char move)
{
int move_x = 0;
int move_y = 0;
if (move == 'w' || move == 'W')
{
move_x = -1;
move_y = 0;
}
else if (move == 'a' || move == 'A')
{
move_x = 0;
move_y = -1;
}
else if (move == 's' || move == 'S')
{
move_x = 1;
move_y = 0;
}
else if (move == 'd' || move == 'D')
{
move_x = 0;
move_y = 1;
}
if (snake->body_coor[0][0] + move_x == snake->body_coor[1][0] && snake->body_coor[0][1] + move_y == snake->body_coor[1][1])
return 'q';
else
{
return move;
}
}
void sanke_longer(Snake* snake, char face[][FACE_SZ - 2])//蛇吃到食物后身体变长的实现
{
if (face[boom_x][boom_y] != '1')
{
int** tmp = (int**)realloc(snake->body_coor, (sizeof(int*) * (snake->len + 1)));
if (tmp == NULL)
{
perror("longer_realloc");
return;
}
tmp[snake->len] = (int*)malloc(sizeof(int) * 2);
if (tmp[snake->len] == NULL)
{
perror("longer_malloc");
return;
}
snake->body_coor = tmp;
tmp = NULL;
if (snake->body_coor[snake->len - 2][0] - snake->body_coor[snake->len - 1][0] == 0)
{
if (snake->body_coor[snake->len - 2][1] - snake->body_coor[snake->len - 1][1] == -1)
{
snake->body_coor[snake->len][0] = snake->body_coor[snake->len - 1][0];
snake->body_coor[snake->len][1] = snake->body_coor[snake->len - 1][1] + 1;
face[snake->body_coor[snake->len][0]][snake->body_coor[snake->len][1]] = '*';
}
else
{
snake->body_coor[snake->len][0] = snake->body_coor[snake->len - 1][0];
snake->body_coor[snake->len][1] = snake->body_coor[snake->len - 1][1] - 1;
face[snake->body_coor[snake->len][0]][snake->body_coor[snake->len][1]] = '*';
}
}
else if(snake->body_coor[snake->len - 2][0] - snake->body_coor[snake->len - 1][0] == -1)
{
snake->body_coor[snake->len][0] = snake->body_coor[snake->len - 1][0] + 1;
snake->body_coor[snake->len][1] = snake->body_coor[snake->len - 1][1];
face[snake->body_coor[snake->len][0]][snake->body_coor[snake->len][1]] = '*';
}
else
{
snake->body_coor[snake->len][0] = snake->body_coor[snake->len - 1][0] - 1;
snake->body_coor[snake->len][1] = snake->body_coor[snake->len - 1][1];
face[snake->body_coor[snake->len][0]][snake->body_coor[snake->len][1]] = '*';
}
snake->len++;
set_food(face);
}
}
void game_play(Snake* snake, char face[][FACE_SZ - 2])
{
int ret = 0;
char decide = 0;
set_food(face);
game_interface(face, FACE_SZ);
char move = 'w';
while (1)
{
printf("输入合法方向键以启动游戏\n");
move = _getch();
decide = move;
ret = move;
if (move != 'a')
break;
else
{
printf("方向不合法\n");
}
}
while (1)
{
if (_kbhit() == 0)
{
if (ret != 0)
{
ret = snake_move(snake, face, move);
sanke_longer(snake, face);
decide = move;
system("cls");
game_interface(face, FACE_SZ);
Sleep(TIME_REFRESH);//时间控制刷新
}
}
else
{
move = _getch();
fflush(stdin);
if(move !=decide)
{
ret = snake_move(snake, face, move);
//system("cls");
//sanke_longer(snake, face);
//game_interface(face, FACE_SZ);
if (move == ' ')//游戏暂停
{
if (game_pause() == 0)
{
free(snake->body_coor);
snake->body_coor = NULL;
exit(0);
}
//ret = snake_move(snake, face, move);
while (1)
{
printf("输入合法方向键以启动游戏\n");
move = _getch();
if (legal_move(snake, move) == 'q')
{
printf("输入违法\n");
}
else
{
break;
}
}
//ret = snake_move(snake, face, move);
//sanke_longer(snake, face);
//system("cls");
//game_interface(face, FACE_SZ);
//Sleep(TIME_REFRESH);//时间控制刷新
}
if (ret == 0)
{
move = decide;
ret = 1;
}
else
{
ret = snake_move(snake, face, move);
sanke_longer(snake, face);
fflush(stdin);
system("cls");
game_interface(face, FACE_SZ);
Sleep(TIME_REFRESH);//时间控制刷新
}
}
}
}
}
然后我接下来会说明我在写的过程中遇到的问题以及是怎么处理的。
首先是蛇信息的存储,在我刚开始的时候是想用两个量,一个是二维数组,和一个int变量来记录蛇身的位置记录和整体长度,后来发现参数调用实在麻烦,后又改成结构体的形式,确实简化了一部分代码量。
其次是食物位置的设置,用srand函数接受time时间戳来生成随机数,设置食物的时候只需要判断当前位置是不是空格就对了。
然后就是具体游戏实现,在实现的过程中会用到两个函数,一个是getch,另一个是kbhit,这两个函数都是windows系统内置的,在Linux上是没有的,引用的头文件是conio.h。getch的特点是从标准输入流中接接收一个字符并且不需要回车来确认,kbhit的特点是,当标准输入缓存区没有东西时,他会返回整形数字0,有的话会返回非零数。利用这两个函数的特性我们可以实现蛇的即时移动以及在不输入下一个状态时,保持原有状态。在这里遇到的问题是,玩家在玩的过程中多次输入会造成缓存区有大量无用数据需要处理,这个问题我使用了一个变量decide以及条件语句来解决,当它与move变量相等时,那么它会重复当前的状态,直到有改变操作时它会做出改变,并记录上一个改变值,以维护下一条问题中移动合法的问题,并用fflush函数来清除玩家成功录入一个操作后将后续无用操作刷新掉。
还有一个问题是合法移动,我用了snake_move来实现它,它会处理蛇跑到边界游戏失败,以及蛇头吃到自己的身体游戏失败,后续在进行合法的移动,当然在这个游戏里蛇回头跑是不现实的,所以我在后续移动中用条件语句避开了这一点,并以返回0的方式处理它,之后用decide记录的上一个操作改变值赋给move,使玩家输入的无效操作在游戏中不会响应。
最后一点是蛇在吃到食物时身体变长的操作,这里需要识别的问题就是它增长的那个节点在身体的末尾处加上一个‘*’,这个‘*’的增加只需要判断最后两个‘*’的位置关系就可以使用realloc函数来添加一个新坐标来实现就可以了。
这就是我在写的过程中遇到的问题,如果有问题,或者我的代码有什么可以优化的地方,望指正。