之前在学c语言的时候尝试写过但是没写完,正好趁着图形学课程实验摸鱼还一波愿。

总体逻辑

1
2
3
4
5
6
7
8
9
10
11
int main(void) {
initGame(); // 初始化
while (true) {
getKbhit(); // 监听键盘动作
moveSnake(); // 更新蛇身节点坐标
eatFood(); // 判断是否吃到食物
drawSnake(); // 绘制蛇身
isDead(); // 判断是否死亡

}
}

头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#define WIDTH 600
#define HEIGHT 400
#define STRLEGTH 1 // 初始长度
#define MAXLENGTH 1000 // 最大长度
#define SIZE 8 // 节点半径

int score = 0; // 初始分数
int speed = 10; // 移动速度

typedef struct DIR { // 坐标结构体
int x;
int y;
}Dir;

Dir dir; // 方向向量

Dir snake[MAXLENGTH]; // 用一个坐标数组来表示蛇身
int len = STRLEGTH;

struct FOOD
{
int x;
int y;
}food;

监听键盘输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void getKbhit() {
if (_kbhit()) {
_getch(); // 键盘上的方向键由两个ASCII码构成,先调用一次getch过滤掉第一个ASCII码
switch (_getch())
{
case 75:
if (dir.x != 1 || dir.y != 0) {
dir.x = -1;
dir.y = 0;
}
break;
case 77:
if (dir.x != -1 || dir.y != 0) {
dir.x = 1;
dir.y = 0;
}
break;
case 72:
if (dir.x != 0 || dir.y != 1) {
dir.x = 0;
dir.y = -1;
}
break;
case 80:
if (dir.x != 0 || dir.y != -1) {
dir.x = 0;
dir.y = 1;
}
break;
}
}
}

更新节点坐标

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void moveSnake() {
setcolor(BLACK);
rectangle(snake[len - 1].x - SIZE, snake[len - 1].y - SIZE, snake[len - 1].x + SIZE, snake[len - 1].y + SIZE); // 将最后一个正方形变成黑色实现“擦除”

for (int i = len - 1; i > 0; i--) // 后一个节点的坐标更新为前一个节点的坐标
{
snake[i].x = snake[i - 1].x;
snake[i].y = snake[i - 1].y;
}
snake[0].x += dir.x * 2 * SIZE; // 更新头结点坐标
snake[0].y += dir.y * 2 * SIZE;

if (snake[0].x >= WIDTH) // 穿墙
snake[0].x = 0;
else if (snake[0].x <= 0)
snake[0].x = WIDTH;
else if (snake[0].y >= HEIGHT)
snake[0].y = 0;
else if (snake[0].y <= 0)
snake[0].y = HEIGHT;
}

判断是否吃到食物

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void eatFood() {
if (fabs(snake[0].x - food.x) <= 2 * SIZE && fabs(snake[0].y - food.y) <= 2 * SIZE) //判断是否吃到食物
{
score++; // 分数+1
setcolor(BLACK);
circle(food.x, food.y, SIZE);

crtFood(); // 重新生成食物

len ++;
for (int i = len - 1; i > 0; i--) // 所有结点后移
{
snake[i].x = snake[i - 1].x;
snake[i].y = snake[i - 1].y;
}
snake[0].x += dir.x * 2 * SIZE; // 更新头结点坐标
snake[0].y += dir.y * 2 * SIZE;
}
}

绘制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void drawSnake() {
setcolor(BLUE);
for (int i = 0; i < len; i++)
{
rectangle(snake[i].x - SIZE, snake[i].y - SIZE, snake[i].x + SIZE, snake[i].y + SIZE);
}
setcolor(RED);
circle(food.x, food.y, SIZE);
TCHAR s[5];
_stprintf_s(s, _T("%d"), score);
wchar_t chr[] = L"SCORE:";
outtextxy(10, 20, chr);
outtextxy(80, 20, s);
Sleep(speed); // 控制移动速度
}

判断是否死亡

1
2
3
4
5
6
7
8
9
10
11
12
13
void isDead() {
if (len == 1) //只有一节退出
return;
for (int i = 1; i < len; i++)
if (fabs(snake[i].x - snake[0].x) <= SIZE && fabs(snake[i].y - snake[0].y) <= SIZE) //如果咬到自己
{
wchar_t chr[] = L"GAME OVER!";
outtextxy(200, 200, chr);
Sleep(30000);
closegraph();
exit(0); //退出
}
}

界面展示

img