最近写了一个小的项目贪吃蛇,当然这个项目不是完完全全以自己的知识范围所写出来的,先说一下以我的能力来想的话,最大的问题在哪。首先我听到贪吃蛇之后,最大的难点就在整个项目的输出情况,也就是蛇是怎么显示出来的,之前有写过三子棋的经历,之前的整个游戏的界面是通过二维数组来实现的,贪吃蛇同样和三子棋是平面游戏,通过数组实现也不是没有可能,但是有一点是很难解决的,就是你需要不断的让你的蛇来移动甚至增加你蛇的长度。但是这也就意味着当你的蛇每移动一次的时候就需要让你的整个二维数组里的内容变化一下,同样你的墙也应该存在于二维数组中,这样当你每次移动蛇或者吃食物,或者刷新食物的时候,也就是你的界面变化一次你的二维数组都要进行相应的改变,然后进行输出。这样对一个一直在移动的贪吃蛇游戏来说,是很麻烦的,虽然没有验证,但是我认为这种方式会让贪吃蛇看起来十分的卡顿。所以这次我们用了其他的方法来解决这件事情。
第一个函数是光标移动函数,SetConsoleCursorPosition function(),这个函数可以将你的光标移动到你想移动到的位置,这时候再将你蛇,或者食物进行输出就可以了。这个函数是需要传入两个参数,但是参数并不是你想要移动的X、Y位置坐标,这里的参数是我之前所没有接触到的。SetConsoleCursorPosition(hout,coord);这里的函数,第二个参数是一个结构体,这个是由系统所定义好的结构体,结构体中包括了两个一个是x,一个是y,这就是你所想要移动到的坐标,但是hout是什么呢?Hout是我们所说的句柄。因为我现在的知识量还不够特别深入详细的明白句柄的作用。在使用这个函数的时候,只需要将coord中的X和Y两个元素赋值就可以进行操作。
HANDLE hout;
COORD coord;
coord.X=3;
coord.Y=3;
hout=GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(hout,coord);
这里就可以将你的光标移动的3,3,位置然后直接调用输出函数就可以
https://blog.csdn.net/hanani_jia/article/details/80012420
这是我之前在写贪吃蛇的过程中介绍过的一次光标移动函数。
之后就是第二个函数,GetAsyncKeyState(VK_UP),这个函数是用来接收键盘的输入值,这里我们通过键盘的上下左右来操作蛇,直接就是UP、DOWN、RIGHT、LEFT来代表键盘的上下左右,这全都是由系统函数所规定的,我们直接调用就可以。
介绍完这两个函数之后,剩下的知识都是用的我当前所能完成的内容。
首先,需要完成的是游戏的界面,
typedef struct UI
{
int MarginTop;//DOS框上和左边为了美观留了一部分表示留的空间的大小
int MarginLeft;
int GameWidth;//这里表示的是游戏区域的字符个数,因为小方块并不是横竖都占一个字符,方便计算
int GameHeight;
int WindowWidth;
int WindowHeight;//表示整个窗口的宽度和高度
char *SnakeBlock;//存储的是蛇、墙、以及食物的内容虽然都一样但是为了分清楚 用指针不用指定大小方便存储
char *FoodBlock;
char *WallBlock;
int BlockWidth;//每个小方块的宽度,因为是目前已知小方块并不是正方形,还是为了上边GameWidth的计算
}UI;
这是定义的有关界面的结构体结构体中包括的一些变量我都用注释来说明了,当然还是那句话程序并不是唯一的,只要能实现就可以,所以这里的变量大家可以增加可以减少,并没有一定的确定值。
static void _SetPos(int x, int y)
{
COORD position;
position.X = x;
position.Y = y;
HANDLE handle;
handle = GetStdHandle(STD_OUTPUT_HANDLE);
SetConsoleCursorPosition(handle, position);
}
这是贪吃蛇中我所用的光标移动函数,在输出墙的时候就可以使用了,这样能让程序方便很多。
static void _DisplayWall(const UI *pUI)
{
int i = 0;
//墙的上边界
_SetPos(pUI->MarginLeft, pUI->MarginTop);
for (i=0; i < pUI->GameWidth + 2; i++)
{
printf("%s", pUI->WallBlock);
}
//墙的下边界
_SetPos(pUI->MarginLeft, pUI->MarginTop+pUI->GameHeight);
for (i=0; i < pUI->GameWidth + 2; i++)
{
printf("%s", pUI->WallBlock);
}
//墙的左边界
for (i = 0; i < pUI->GameHeight + 1; i++)
{
_SetPos(pUI->MarginLeft, pUI->MarginTop+i);
printf("%s", pUI->WallBlock);
}
//墙的右边界
for (i = 0; i < pUI->GameHeight + 1; i++)
{
_SetPos(pUI->MarginLeft+(pUI->GameWidth+1)*pUI->BlockWidth, pUI->MarginTop + i);
printf("%s", pUI->WallBlock);
}
}
在输出墙的时候我们通过不断的移动坐标然后实现对墙体的输出,当输出上下边界的时候就很容易,移动到指定位置之后通过for循环不断的输出就可以,但是当输出左边界和右边界的时候就需要不断的换行来移动光标进行输出。
static void _DisplayDesc(const UI *pUI)
{
char *message[5] =
{
"蛇不可以撞墙不可以撞自己",
"通过坐标建控制蛇移动",
"ESC 退出游戏 SPACE 暂停游戏",
"版权所有@贾浩男",
"贪吃蛇版本号1.0"
};
int i = 0;
for (i = 0; i < 5; i++)
{
_SetPos(pUI->MarginLeft + (pUI->GameWidth + 2)*pUI->BlockWidth + 3, pUI->GameHeight / 2 - 3 + i);
printf("%s", message[i]);
}
}