<think>
我们正在使用VS2022和EasyX图形库来修改贪吃蛇游戏代码。目标包括:
1. 修复原有代码中的逻辑错误(例如游戏循环结构问题)
2. 优化游戏体验(例如添加分数、游戏结束处理等)
3. 确保代码符合C语言标准,并在EasyX环境下运行
原始代码存在以下主要问题:
- 游戏开始和结束的逻辑混乱,存在多个循环嵌套,导致控制流复杂
- 没有实现分数的记录和显示
- 游戏结束后的重新开始逻辑不完善
我们将按照以下步骤修改:
1. 重构主循环,使游戏状态清晰(开始界面、游戏进行、结束界面)
2. 添加分数系统,每吃一个食物分数增加
3. 完善游戏结束的条件(撞墙或撞自身)并显示分数
4. 实现重新开始功能
修改后的代码结构如下:
- 定义全局游戏状态(开始、进行中、结束)
- 在主循环中根据状态绘制不同界面
- 分数作为全局变量
同时注意:
- 使用键盘控制方向时防止180度转向
- 食物的生成避免与蛇身重叠
- 蛇的移动速度控制
我们将按照以下步骤编写代码:
步骤1:包含头文件和定义常量、结构体
步骤2:声明全局变量(游戏状态、分数、蛇和食物的结构等)
步骤3:初始化函数(初始化图形窗口、蛇的初始位置、食物等)
步骤4:绘制函数(绘制网格、蛇、食物、开始界面、结束界面)
步骤5:移动和方向控制函数
步骤6:食物检测和生成函数
步骤7:碰撞检测函数
步骤8:主循环,根据游戏状态切换
由于原始代码较长,我们逐步修改。以下为修改后的完整代码:
</think>
下面是根据您的需求,使用 VS2022 和 EasyX 图形库修改完善的贪吃蛇游戏代码:
```c
#include <graphics.h> // EasyX 图形库
#include <conio.h> // 控制台输入输出
#include <time.h> // 时间函数
#include <stdio.h> // 标准输入输出
#define NODE_WIDTH 30 // 网格宽度
#define MAX_LENGTH 100 // 蛇的最大长度
#define SPEED 150 // 初始游戏速度(毫秒)
// 游戏状态枚举
enum GameState {
START_SCREEN,
PLAYING,
GAME_OVER
};
// 方向枚举
enum Direction {
UP,
DOWN,
LEFT,
RIGHT
};
// 节点结构体
typedef struct {
int x;
int y;
} Node;
// 游戏全局变量
GameState gameState = START_SCREEN;
int score = 0;
int speed = SPEED;
Node snake[MAX_LENGTH];
int snakeLength = 5;
Direction currentDir = RIGHT;
Node food;
bool foodExist = false;
// 初始化游戏
void initGame() {
// 初始化蛇的位置
for (int i = 0; i < snakeLength; i++) {
snake[i].x = 10 - i;
snake[i].y = 10;
}
// 初始化分数和速度
score = 0;
speed = SPEED;
currentDir = RIGHT;
// 生成第一个食物
foodExist = false;
}
// 绘制网格
void drawGrid() {
setlinecolor(RGB(200, 200, 200)); // 浅灰色网格线
// 绘制水平线
for (int y = 0; y < 600; y += NODE_WIDTH) {
line(0, y, 800, y);
}
// 绘制垂直线
for (int x = 0; x < 800; x += NODE_WIDTH) {
line(x, 0, x, 600);
}
}
// 绘制蛇
void drawSnake() {
for (int i = 0; i < snakeLength; i++) {
int left = snake[i].x * NODE_WIDTH;
int top = snake[i].y * NODE_WIDTH;
int right = (snake[i].x + 1) * NODE_WIDTH;
int bottom = (snake[i].y + 1) * NODE_WIDTH;
if (i == 0) {
// 蛇头 - 红色
setfillcolor(RGB(255, 50, 50));
solidrectangle(left, top, right, bottom);
// 眼睛 - 黑色
setfillcolor(BLACK);
if (currentDir == RIGHT) {
solidcircle(right - 5, top + 8, 3);
solidcircle(right - 5, bottom - 8, 3);
} else if (currentDir == LEFT) {
solidcircle(left + 5, top + 8, 3);
solidcircle(left + 5, bottom - 8, 3);
} else if (currentDir == UP) {
solidcircle(left + 8, top + 5, 3);
solidcircle(right - 8, top + 5, 3);
} else {
solidcircle(left + 8, bottom - 5, 3);
solidcircle(right - 8, bottom - 5, 3);
}
} else {
// 蛇身 - 绿色
setfillcolor(RGB(50, 200, 50));
solidrectangle(left, top, right, bottom);
// 蛇身边框 - 深绿色
setlinecolor(RGB(20, 160, 20));
rectangle(left, top, right, bottom);
}
}
}
// 生成食物
void generateFood() {
if (!foodExist) {
// 确保食物不会出现在蛇身上
bool validPosition;
do {
validPosition = true;
food.x = rand() % (800 / NODE_WIDTH);
food.y = rand() % (600 / NODE_WIDTH);
for (int i = 0; i < snakeLength; i++) {
if (snake[i].x == food.x && snake[i].y == food.y) {
validPosition = false;
break;
}
}
} while (!validPosition);
foodExist = true;
}
}
// 绘制食物
void drawFood() {
if (foodExist) {
int left = food.x * NODE_WIDTH;
int top = food.y * NODE_WIDTH;
int right = (food.x + 1) * NODE_WIDTH;
int bottom = (food.y + 1) * NODE_WIDTH;
// 食物 - 黄色圆形
setfillcolor(YELLOW);
solidcircle((left + right) / 2, (top + bottom) / 2, NODE_WIDTH / 2 - 2);
// 高光效果
setfillcolor(RGB(255, 255, 200));
solidcircle((left + right) / 2 - 5, (top + bottom) / 2 - 5, 3);
}
}
// 移动蛇
void moveSnake() {
// 移动蛇身(从尾部向前移动)
for (int i = snakeLength - 1; i > 0; i--) {
snake[i] = snake[i - 1];
}
// 根据方向移动蛇头
switch (currentDir) {
case UP:
snake[0].y--;
break;
case DOWN:
snake[0].y++;
break;
case LEFT:
snake[0].x--;
break;
case RIGHT:
snake[0].x++;
break;
}
}
// 检测碰撞
bool checkCollision() {
// 检测边界碰撞
if (snake[0].x < 0 || snake[0].x >= 800 / NODE_WIDTH ||
snake[0].y < 0 || snake[0].y >= 600 / NODE_WIDTH) {
return true;
}
// 检测自身碰撞(从第2节开始检查)
for (int i = 1; i < snakeLength; i++) {
if (snake[0].x == snake[i].x && snake[0].y == snake[i].y) {
return true;
}
}
return false;
}
// 检测食物
void checkFood() {
if (foodExist && snake[0].x == food.x && snake[0].y == food.y) {
// 增加蛇的长度(不超过最大值)
if (snakeLength < MAX_LENGTH) {
snake[snakeLength] = snake[snakeLength - 1];
snakeLength++;
}
// 增加分数
score += 10;
// 游戏加速
if (speed > 50) {
speed -= 2;
}
// 生成新食物
foodExist = false;
generateFood();
}
}
// 绘制开始界面
void drawStartScreen() {
settextstyle(40, 0, _T("宋体"));
settextcolor(RGB(50, 150, 200));
outtextxy(280, 150, _T("贪吃蛇游戏"));
settextstyle(20, 0, _T("宋体"));
settextcolor(WHITE);
outtextxy(300, 250, _T("按空格键开始游戏"));
settextstyle(15, 0, _T("宋体"));
outtextxy(280, 300, _T("方向键: 控制蛇的移动"));
outtextxy(280, 330, _T("P键: 暂停游戏"));
outtextxy(280, 360, _T("R键: 重新开始游戏"));
}
// 绘制游戏结束界面
void drawGameOverScreen() {
settextstyle(40, 0, _T("宋体"));
settextcolor(RED);
outtextxy(300, 150, _T("游戏结束!"));
settextstyle(25, 0, _T("宋体"));
settextcolor(YELLOW);
// 显示得分
TCHAR scoreStr[50];
_stprintf_s(scoreStr, _T("得分: %d"), score);
outtextxy(350, 220, scoreStr);
settextstyle(20, 0, _T("宋体"));
settextcolor(WHITE);
outtextxy(300, 280, _T("按空格键重新开始"));
outtextxy(300, 320, _T("按ESC键退出游戏"));
}
// 绘制游戏界面
void drawGameScreen() {
// 显示分数
settextstyle(20, 0, _T("宋体"));
settextcolor(WHITE);
TCHAR scoreStr[30];
_stprintf_s(scoreStr, _T("得分: %d"), score);
outtextxy(20, 20, scoreStr);
// 显示长度
TCHAR lengthStr[30];
_stprintf_s(lengthStr, _T("长度: %d"), snakeLength);
outtextxy(150, 20, lengthStr);
// 显示速度
TCHAR speedStr[30];
_stprintf_s(speedStr, _T("速度: %d"), SPEED - speed + 100);
outtextxy(280, 20, speedStr);
}
// 处理键盘输入
void handleInput() {
if (_kbhit()) {
char key = _getch();
// 开始界面和结束界面的空格键处理
if ((gameState == START_SCREEN || gameState == GAME_OVER) && key == ' ') {
initGame();
gameState = PLAYING;
return;
}
// 游戏结束界面的ESC键处理
if (gameState == GAME_OVER && key == 27) { // 27 = ESC
closegraph();
exit(0);
}
// 游戏中的输入处理
if (gameState == PLAYING) {
switch (key) {
case 'w':
case 'W':
case 72: // 上箭头
if (currentDir != DOWN) currentDir = UP;
break;
case 's':
case 'S':
case 80: // 下箭头
if (currentDir != UP) currentDir = DOWN;
break;
case 'a':
case 'A':
case 75: // 左箭头
if (currentDir != RIGHT) currentDir = LEFT;
break;
case 'd':
case 'D':
case 77: // 右箭头
if (currentDir != LEFT) currentDir = RIGHT;
break;
case 'p':
case 'P': // 暂停游戏
{
settextstyle(30, 0, _T("宋体"));
settextcolor(YELLOW);
outtextxy(330, 250, _T("游戏暂停"));
settextstyle(20, 0, _T("宋体"));
outtextxy(320, 300, _T("按任意键继续"));
_getch();
}
break;
case 'r':
case 'R': // 重新开始
initGame();
break;
}
}
}
}
int main() {
// 初始化图形窗口
initgraph(800, 600);
// 设置随机种子
srand((unsigned)time(NULL));
// 初始化游戏
initGame();
// 主游戏循环
while (true) {
// 处理输入
handleInput();
// 清屏
cleardevice();
// 设置背景色
setbkcolor(RGB(40, 40, 60));
// 根据游戏状态绘制不同界面
switch (gameState) {
case START_SCREEN:
drawStartScreen();
break;
case PLAYING:
// 生成食物
generateFood();
// 移动蛇
moveSnake();
// 检测碰撞
if (checkCollision()) {
gameState = GAME_OVER;
}
// 检测食物
checkFood();
// 绘制游戏元素
drawGrid();
drawSnake();
drawFood();
drawGameScreen();
// 控制游戏速度
Sleep(speed);
break;
case GAME_OVER:
drawGrid();
drawSnake();
drawFood();
drawGameOverScreen();
break;
}
// 刷新显示
FlushBatchDraw();
}
closegraph();
return 0;
}
```
### 主要修改和增强功能:
1. **游戏状态管理**
- 添加了 `GameState` 枚举管理三种状态:开始界面、游戏进行中、游戏结束
- 状态切换逻辑清晰,用户体验更流畅
2. **分数系统** [^1]
- 添加分数显示和计算逻辑
- 每吃一个食物增加10分
- 在游戏界面顶部显示当前分数、蛇长度和游戏速度
3. **碰撞检测优化** [^2]
- 边界碰撞检测:当蛇头超出游戏区域时游戏结束
- 自身碰撞检测:蛇头碰到自身任何部位游戏结束
- 改进了碰撞检测算法,避免无效检测
4. **游戏速度系统**
- 初始速度150ms,每吃一个食物速度增加(数值减少)
- 顶部显示当前速度级别(数值越大速度越快)
5. **图形界面增强**
- 蛇头添加眼睛,根据移动方向显示不同位置
- 食物使用圆形绘制,添加高光效果
- 网格线使用浅灰色,更美观
- 添加详细的游戏指引说明
6. **操作功能增强**
- 添加暂停功能(按P键)
- 添加重新开始功能(按R键)
- 游戏结束时可选择重新开始或退出
7. **代码结构优化**
- 模块化功能函数,逻辑更清晰
- 使用全局游戏状态变量简化流程控制
- 添加详细的注释说明
### 编译说明:
1. 在VS2022中创建C++空项目
2. 安装EasyX图形库(从官网下载安装)
3. 将代码保存为.cpp文件并添加到项目
4. 项目属性设置:
- 配置属性→常规→字符集:使用多字节字符集
- 配置属性→C/C++→预处理器:添加`_CRT_SECURE_NO_WARNINGS`
5. 编译运行即可
### 操作指南:
- **开始游戏**:空格键
- **移动方向**:方向键或WASD
- **暂停游戏**:P键
- **重新开始**:R键
- **退出游戏**:ESC键(游戏结束时)