C语言链表实现贪吃蛇:节点无法跟随前一节点问题求助
贪吃蛇节点跟随问题的解决方案
问题根源
你的代码里蛇身移动逻辑完全错误:每次移动头部后,两次遍历蛇身节点直接覆盖位置还额外加偏移,导致所有节点瞬间对齐头部,没有保留前一帧的位置信息,自然无法形成蛇形弯曲效果。
正确的蛇身移动逻辑应该是:
- 先记录头部的旧位置
- 从蛇尾开始,每个节点依次移动到前一个节点的旧位置
- 最后再移动头部到新位置
具体修改步骤
- 重构移动逻辑:删除每个方向分支里重复的蛇身遍历代码,换成从尾部向前更新位置的逻辑
- 移除错误的二次遍历:原代码里每个方向都有两次蛇身遍历,第二次是错误的,必须删掉
- 优化代码冗余:把方向控制和移动逻辑提取成函数,避免重复代码
- 修复边界判断错误:原代码里所有方向的游戏结束判断都用
start->y == 0,明显错误,要对应各自的边界
修正后的完整代码
#include <stdio.h> #include <windows.h> #include <conio.h> #include <stdlib.h> #include <time.h> int point_x = 0, point_y = 0; int dir = 0; // 0:上 1:下 2:左 3:右 struct snake { int x, y; struct snake *next; struct snake *prev; } *start, *tail; void create() { struct snake *ptr = (struct snake *)malloc(sizeof(struct snake)); ptr->next = NULL; ptr->prev = NULL; if (start == NULL) { start = ptr; tail = ptr; } else { tail->next = ptr; ptr->prev = tail; tail = ptr; } } void gotoxy(int x, int y) { COORD CRD; CRD.X = x; CRD.Y = y; SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), CRD); } void generatePoint() { srand(time(0)); point_x = rand() % 120; point_y = rand() % 30; } void drawSnake() { struct snake *temp = start; while (temp != NULL) { gotoxy(temp->x, temp->y); printf("%c", (char)254); temp = temp->next; } } void moveSnake() { // 先记录头部的旧位置 int old_x = start->x; int old_y = start->y; // 移动头部 switch(dir) { case 0: start->y--; break; case 1: start->y++; break; case 2: start->x--; break; case 3: start->x++; break; } // 从第二个节点开始,依次移动到前一个节点的旧位置 struct snake *temp = start->next; while (temp != NULL) { int curr_x = temp->x; int curr_y = temp->y; temp->x = old_x; temp->y = old_y; old_x = curr_x; old_y = curr_y; temp = temp->next; } } int checkCollision() { // 边界碰撞 if (start->x < 0 || start->x >= 120 || start->y < 0 || start->y >= 30) return 1; // 自身碰撞(可选) struct snake *temp = start->next; while (temp != NULL) { if (start->x == temp->x && start->y == temp->y) return 1; temp = temp->next; } return 0; } int main() { // 隐藏光标 HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE); CONSOLE_CURSOR_INFO info; info.dwSize = 100; info.bVisible = FALSE; SetConsoleCursorInfo(consoleHandle, &info); start = NULL; int points = 0; char ch; // 初始化蛇(3节) create(); start->x = 60; start->y = 15; create(); start->next->x = 60; start->next->y = 16; create(); tail->x = 60; tail->y = 17; generatePoint(); while(1) { // 处理方向输入 if (_kbhit()) { ch = _getch(); switch(ch) { case 'w': if (dir != 1) dir = 0; break; // 不能直接反向 case 's': if (dir != 0) dir = 1; break; case 'a': if (dir != 3) dir = 2; break; case 'd': if (dir != 2) dir = 3; break; case 27: exit(0); // ESC退出 } } system("cls"); moveSnake(); // 检查是否吃到食物 if (start->x == point_x && start->y == point_y) { points++; create(); // 新节点放在尾部旧位置 tail->x = tail->prev->x; tail->y = tail->prev->y; generatePoint(); } // 绘制食物 gotoxy(point_x, point_y); printf("\033[32m%c\033[0m", (char)254); // 绘制蛇 drawSnake(); // 绘制分数 gotoxy(110, 0); printf("\033[34mPoints: %d\033[0m", points); // 检查碰撞 if (checkCollision()) { system("cls"); gotoxy(55, 15); printf("\033[31mGame over!\033[0m"); system("pause>nul"); exit(0); } Sleep(90); } return 0; }
关键修改说明
- 移动逻辑:
moveSnake函数里先记录头部旧位置,然后从第二个节点开始,每个节点都移动到前一个的旧位置,这样就能保留蛇身的弯曲形态 - 方向控制:添加了方向变量
dir,避免重复的方向分支代码,同时限制不能直接反向移动(比如向上时不能直接按向下) - 尾部追踪:新增
tail指针,方便快速添加新节点,不用每次遍历找尾部 - 碰撞检测:完善了边界碰撞和自身碰撞的判断,原代码的边界判断完全错误,现在修正为对应上下左右的边界
内容的提问来源于stack exchange,提问作者Sumit Bose
相关产品推荐
相关产品推荐

