高性能的贪吃蛇C语言实现(西安微易码科技暑期项目实训课程)
贪吃蛇是一个非常火爆的经典的小游戏,由于其实现起来较为简单,而且对界面的要求程度不高,经常被初学者当做提升自身编程能力的一个例子,而且由于它带有一定的趣味性,对于培养编程爱好者的兴趣和提高自身信心有很大的帮助。但是由于它有游戏的体验以及带给人很大的成就感,使得人们在编写贪吃蛇时,经常会不顾一切的去实现贪吃蛇的功能以便于尽快满足自身的成就感,以至于不愿意踏踏实实的去研究其内部的代码实现以及去思考如何改良自己的算法。本文会对贪吃蛇的不同的模块进行详细的讲解,也会更加注重代码的质量。
一、蛇的初始化问题
要谈一个软件的运行,初始化是必不可少的,初始化是一个 软件运行的开始,也是基础,后续的代码都是在初始化的数据基础上进行的。
要谈贪吃蛇的初始化问题,就要谈到贪吃蛇所需要用到的数据存储结构,以及描述一个贪吃蛇所需要的数据,这里我们给出存储控制贪吃蛇的数据的总结构体:
typedef struct SNAKE {
POINT snake[MAX_LENGTH]; //真正存储蛇的实体空间(用循环数组存储)
int isAlive; //判断蛇是否活着(是否应该继续移动)
int headIndex; //蛇头所在的snake数组中的下标
int tailIndex; //蛇尾所在的snake数组中的下标
int snakeLength; //蛇的长度
int level; //游戏等级(控制速度)
POINT food; //食物所在的点坐标
}SNAKE;
这里的POINT snake[MAX_LENGTH]数组是用来存储蛇的身体的实体空间(蛇的每一节的位置以及方向信息),因此我们给一个POINT结构体来表示蛇的每一节的位置信息以及方向信息(这里的方向信息仅仅用于表示蛇的每一节的输出方式,即头部向右走表示为‘>’,,向左走表示为‘<’,向上‘^’,向下‘V’,身体向左右走表示为‘-’,向上下走为‘|’,具体的移动是由循环数组实现的,具体的实现方案会在后续的讲解中做详细解释)。
因此我们给出POINT结构体:
typedef struct POINT {
int x;
int y;
int direct;
}POINT;
有了数据存储的基础了,我们就可以开始编写我们的初始化代码了(具体的宏定义见程序源代码):
void initSnake(SNAKE *snake) {
int i;
snake->isAlive = TRUE; //初始化蛇的各项控制数据;
snake->headIndex = DEFAULT_LENGTH - 1;
snake->tailIndex = 0;
snake->snakeLength = DEFAULT_LENGTH;
snake->level = DEFAULT_LEVEL;
for(i = 0; i < snake->snakeLength; i++) { //生成一条长度为DEFAULT_LENGTH(默认长度)的蛇,将其存储到循环数组里,并显示它
snake->snake[i].x = i + 1;
snake->snake[i].y = 1;
snake->snake[i].direct = DEFAULT_DIRECT;
DEFAULT_LENGTH - i - 1 == 0 ? showHeadPoint(snake->snake[i]) ://判断当前要存储的位置是否为蛇头,并决定按蛇头方式输出还是按身体方式输出;
showBodyPoint(snake->snake[i]);
}
snake->food = getFood(snake); //生成第一个食物(具体过程后面会做详细的讲解);
getchar();
}
初始化完成了将默认蛇长数量的点按从末尾到头部的顺序依次存储到我们的循环数组里,并在屏幕上显示他们。由于输出头部和输出蛇的身体有区别,而且要根据方向的不同来判读应该输出什么,因此我们给出两个函数用来专门在屏幕上输出蛇的头或者身体。
void showHeadPoint(POINT point) {
gotoxy(point.x, point.y);
printf("%c", HOLE_headShow[point.direct]);
}
void showBodyPoint(POINT point) {
gotoxy(point.x, point.y);
printf("%c", HOLE_bodyShow[(point.direct + 1) % 2]);
}
我们用gotoxy()函数来定位要在哪里输出字符;
这里的输出POINT类型的数据,我们用了一个全局数组来存储每个方向上应该输出什么样的字符,而用方向的值作为这个全局数组的下标来确定在该方向上应该输出什么样的字符。
char HOLE_headShow[4] = {'^', '<', 'V', '>'};
char HOLE_bodyShow[2] = {'-', '|'};
在这里我们规定,对方向变量的赋值,必须保证要用下面所给的一系列的宏进行赋值,从而保证了向上的方向一定为0,用它作为下标对应的headShow[0]一定为‘^’,从而保证了根据当前POINT类型变量中direct成员的值所确定的输出字符的准确性。
#define DIRECT_UP 0
#define DIRECT_LEFT 1
#define DIRECT_DOWN 2
#define DIRECT_RIGHT 3
#define DIRECT_ERROR -1
这