1、首先兴建一个坐标系,蛇生成和移动与食物的生成均在此坐标系上移动,使用两个一维数组分别表示X轴和Y轴。以圆形作为蛇头和蛇身,每一次蛇移动的圆形都要在坐标上。此处生成一个640 x 480的窗口大小
int x_coor[64], y_coor[48];
for (int i = 0; i < 64; i++)
x_coor[i] = RADIUS + 2 * i * RADIUS;
for (int i = 0; i < 48; i++)
y_coor[i] = RADIUS + 2 * i * RADIUS;
2、链表表示蛇头和蛇身。蛇的链表结构表示为:
struct Snake {
int x;
int y;
int Length;
Snake* next;
};
初始化一个蛇头,坐标在坐标系中随机选择一个
Snake* snake = NULL;
int direction = RIGHT;
snake = (Snake*)malloc(sizeof(Snake));
int x = rand() % 64;
int y = rand() % 48;
if (!snake) exit(OVERFLOW);
snake->x = x_coor[x];
snake->y = y_coor[y];
snake->next = NULL;
初始化蛇的身体,蛇的身体,初始化蛇身体函数传入蛇头,初始🐍方向向右。🐍身体坐标可根据🐍头坐标依次获得。
void initSnake(Snake* snake) {
int i = 0;
Snake* q = snake;
while (i < O_LENGTH)
{
Snake* p = NULL;
p = (Snake*)malloc(sizeof(Snake));
if (!p)
exit(OVERFLOW);
p->x = (snake->x - (i + 1) * 2 * RADIUS);
p->y = snake->y;
p->next = NULL;
q->next = p;
q = p;
i++;
}
q->next = NULL;
snake = q;
snake->Length = i;
}
画出🐍的头和身体
void drawSnake(Snake* snake)
{
Snake* p = NULL;
p = (Snake*)malloc(sizeof(Snake*));
if (!p)
exit(OVERFLOW);
p = snake;
Color color{};
color.r = rand() % 255;
color.g = rand() % 255;
color.b = rand() % 255;
while (p)
{
setfillcolor(RGB(color.r, color.g, color.b));
solidcircle(p->x, p->y, RADIUS);
p = p->next;
}
free(p);
}
3、初始化食物并且画出食物。食物只包含坐标,X坐标和Y坐标,以及食物坐标的改变。参考🐍头的生成生成食物
struct Food {
int x;
int y;
};
Food* initFood(int x_coor[], int y_coor[])
{
Food* food = NULL;
food = (Food*)malloc(sizeof(Food*));
if (!food)
exit(OVERFLOW);
Color color{};
color.r = rand() % 128 + 128;
color.g = rand() % 128 + 128;
color.b = rand() % 128 + 128;
int x = rand() % 64;
int y = rand() % 48;
food->x = x_coor[x];
food->y = y_coor[y];
setfillcolor(RGB(color.r, color.g, color.b));
solidcircle(food->x, food->y, RADIUS);
return food;
}
void changeFoodxy(Food* food, int x_coor[], int y_coor[])
{
//food = NULL;
if (!food)
exit(OVERFLOW);
Color color{};
color.r = rand() % 128 + 128;
color.g = rand() % 128 + 128;
color.b = rand() % 128 + 128;
int x = rand() % 64;
int y = rand() % 48;
food->x = x_coor[x];
food->y = y_coor[y];
setfillcolor(RGB(color.r, color.g, color.b));
solidcircle(food->x, food->y, RADIUS);
}
4、考虑🐍的移动,🐍移动只需要考虑蛇头的移动,🐍头移动之后,🐍的身体一次跟随头移动,第二节身体坐标即头改变前的坐标,第三节身体移动后坐标即第二节身体移动前坐标,后面一次替换即可。移动完成之后需要对🐍最后一节擦除,即颜色与背景颜色一致即可,并且重新画🐍
void snakeMove_RIGHT(Snake* snake)
{
Snake temp{};
Snake temp2{};
temp.x = snake->x;
temp.y = snake->y;
snake->x = snake->x + 2 * RADIUS;
snake->y = snake->y;
//蛇移动应该是上下左右四个方向,此处先做向右移动
while (snake->next)
{
temp2.x = snake->next->x;
temp2.y = snake->next->y;
snake->next->x = temp.x;
snake->next->y = temp.y;
snake = snake->next;
temp.x = temp2.x;
temp.y = temp2.y;
}
setfillcolor(BLACK);
solidcircle(temp2.x, temp2.y, RADIUS);
}
5、判断🐍吃掉食物。吃掉食物头部坐标与食物圆心坐标相重合
6、身体变长。吃掉食物后身体长度应该加1,用尾插法在尾部加一节。新增一节坐标要考虑四种情况,分别是从上下左右吃掉食物,吃掉食物后需要重新生成
void snakeGrow(Snake* snake, Food* food, int direction,
int x_coor[], int y_coor[])
{
if (eatFood(snake, food))
{
setfillcolor(BLACK);
solidcircle(food->x, food->y, RADIUS);
Snake* q = snake;
snake->Length++;
Snake* p;
p = (Snake*)malloc(sizeof(Snake*));
if (!p)
exit(OVERFLOW);
int i = 0;
while (q->next)
{
q = q->next;
i++;
}
switch (direction)
{
case RIGHT:
p->x = snake->x - RADIUS * i;
p->y = snake->y;
q->next = p;
q = p;
break;
case LEFT:
p->x = snake->x + RADIUS * i;
p->y = snake->y;
p->next = NULL;
q->next = p;
q = p;
break;
case TOP:
p->x = snake->x;
p->y = snake->y - RADIUS * i;
q->next = p;
q = p;
break;
case BOTTOM:
p->x = snake->x;
p->y = snake->y + RADIUS * i;
q->next = p;
q = p;
break;
default:
break;
}
snake = q;
free(p);
changeFoodxy(food, x_coor, y_coor); //重新生成食物
}
7、控制🐍移动。定义一个键盘输入的"D"表示向右移动
void snakeControl(Snake* snake, char &key, int &direction)
{
if (_kbhit())
key = _getch();
switch (key)
{
case 'W':snakeMove_UP(snake);
direction = TOP;
break;
case 'A':snakeMove_LEFT(snake);
direction = LEFT;
break;
case 'S':snakeMove_DOWN(snake);
direction = BOTTOM;
break;
case 'D':snakeMove_RIGHT(snake);
direction = RIGHT;
default:
break;
}
}
8、判定🐍死亡。撞墙或者触碰到身体即表示死亡。
bool snakeDeath(Snake* snake)
{
Snake* p = NULL;
p = (Snake*)malloc(sizeof(Snake*));
if (!p)
exit(OVERFLOW);
Snake temp{};
p = snake;
temp.x = snake->x;
temp.y = snake->y;
p = p->next;
while (p)
{
if (p->x == temp.x && p->y == temp.y)
return true; //触碰自己死亡
p = p->next;
}
free(p);
if ((snake->x + RADIUS) > RIGHT_BOUNDARY || (snake->x - RADIUS) < LEFT_BOUNDARY
|| (snake->y + RADIUS) > BOTTOM_BOUNDARY || (snake->y - RADIUS) < TOP_BOUNDARY)
return true; //撞到墙体死亡
else
return false;
}
总结:最后不断循环即可,读者可以自行完善。基本可以正常运行,但在测试中依然有一丢丢的bug,希望大家可以多多指正。谢谢!!!