大家好!今天带大家用 C 语言开发经典的控制台贪吃蛇游戏 🎮。这个项目非常适合刚学完 C 语言基础(数组、循环、函数)的同学练手,既能巩固知识点,又能体验从 0 到 1 做项目的成就感。话不多说,咱们直接开干!
一、项目准备:环境与工具 🛠️
- 编译环境:Dev-C++、VS2022、GCC(任选其一,控制台程序即可)
- 核心头文件:
- stdio.h:控制台输入输出
- conio.h:获取键盘按键(_kbhit ()、_getch () 函数,VS 需加下划线)
- windows.h:控制窗口大小、延迟(Sleep () 函数)
- time.h:生成随机数(用于食物位置)
- 核心思路:用二维数组模拟游戏地图,通过键盘控制蛇的移动方向,检测碰撞(边界、自身、食物),实现得分与游戏结束逻辑。
二、核心功能拆解与代码实现 🚀
1. 定义全局变量:存储游戏状态 📦
首先定义游戏所需的全局变量,比如蛇的坐标、食物位置、得分、方向等,避免频繁传参:’
2. 初始化游戏:开局配置 🔧
游戏启动时需要初始化蛇的初始位置、食物位置、得分等,用init_game()函数实现:
// 初始化游戏
void init_game() {
// 1. 初始化蛇:初始长度2,位置在地图中间偏左
snake.len = 2;
snake.x[0] = WIDTH / 2; // 蛇头x坐标
snake.y[0] = HEIGHT / 2; // 蛇头y坐标
snake.x[1] = WIDTH / 2 - 1; // 蛇身x坐标(在蛇头左边)
snake.y[1] = HEIGHT / 2; // 蛇身y坐标
// 2. 初始化食物(随机位置,不能在蛇身上)
srand((unsigned int)time(NULL)); // 设置随机数种子
food_x = rand() % (WIDTH - 2) + 1; // 食物x:1~WIDTH-2(避开边界)
food_y = rand() % (HEIGHT - 2) + 1; // 食物y:1~HEIGHT-2
// 3. 初始化得分
score = 0;
}
3. 绘制游戏界面:控制台渲染 🎨
用draw_map()函数绘制地图边界、蛇身、食物和得分,每次蛇移动后都要重新绘制:
// 绘制游戏地图
void draw_map() {
// 清屏(避免画面闪烁,控制台常用技巧)
system("cls");
// 1. 绘制上边界(用"■"符号,更清晰)
for (int i = 0; i
printf("■");
}
printf("\n");
// 2. 绘制中间区域(行:HEIGHT-2行,列:WIDTH列)
for (int i = 0; i 2; i++) {
for (int j = 0; j < WIDTH; j++) {
// 绘制左边界
if (j == 0) {
printf("■");
continue;
}
// 绘制右边界
if (j == WIDTH - 1) {
printf("■");
continue;
}
// 绘制蛇头(用"●"区分蛇身)
if (j == snake.x[0] && i == snake.y[0]) {
printf("●");
continue;
}
// 绘制蛇身(用"○")
int is_snake_body = 0;
for (int k = 1; k ++) {
if (j == snake.x[k] && i == snake.y[k]) {
printf("○");
is_snake_body = 1;
break;
}
}
if (is_snake_body) continue;
// 绘制食物(用"★")
if (j == food_x && i == food_y) {
printf("★");
continue;
}
// 空白区域(除了以上元素,都打印空格)
printf(" ");
}
printf("\n");
}
// 3. 绘制下边界
for (int i = 0; i ++) {
printf("■");
}
printf("\n");
// 4. 显示得分和操作提示
printf("当前得分:%d | 操作:W(上) S(下) A(左) D(右) | 退出:ESC\n", score);
}
4. 控制蛇的移动:键盘交互 ⌨️
用control_dir()函数监听键盘按键,修改蛇的移动方向(注意:不能反向移动,比如向右时不能直接向左):
// 控制蛇的移动方向
void control_dir() {
// _kbhit():检测是否有按键按下(非阻塞)
if (_kbhit()) {
// _getch():获取按键(无需按回车,控制台常用)
switch (_getch()) {
case 'w':
case 'W':
if (dir != 2) dir = 1; // 向上:不能从向下反向
break;
case 's':
case 'S':
if (dir != 1) dir = 2; // 向下:不能从向上反向
break;
case 'a':
case 'A':
if (dir != 4) dir = 3; // 向左:不能从向右反向
break;
case 'd':
case 'D':
if (dir != 3) dir = 4; // 向右:不能从向左反向
break;
case 27: // ESC键的ASCII码是27
game_state = 0; // 按下ESC,游戏结束
break;
}
}
}
5. 蛇的移动逻辑:更新坐标 🐾
蛇移动的核心是 “蛇头向前走,蛇身跟着前一个节点走”,如果吃到食物则长度 + 1,否则蛇尾删除:
// 蛇的移动逻辑
void snake_move() {
// 1. 蛇身跟随:从尾到头,每个节点 = 前一个节点的坐标
for (int i = snake.len - 1; i > 0; i--) {
snake.x[i] = snake.x[i - 1];
snake.y[i] = snake.y[i - 1];
}
// 2. 蛇头根据方向移动(x/y坐标变化)
switch (dir) {
case 1: // 上:y坐标-1
snake.y[0]--;
break;
case 2: // 下:y坐标+1
snake.y[0]++;
break;
case 3: // 左:x坐标-1
snake.x[0]--;
break;
case 4: // 右:x坐标+1
snake.x[0]++;
break;
}
// 3. 检测是否吃到食物(蛇头坐标 == 食物坐标)
if (snake.x[0] == food_x && snake.y[0] == food_y) {
score += 10; // 得分+10
snake.len++; // 蛇长度+1
// 重新生成食物(避免在蛇身上)
while (1) {
int is_food_on_snake = 0;
// 随机生成新食物位置
food_x = rand() % (WIDTH - 2) + 1;
food_y = rand() % (HEIGHT - 2) + 1;
// 检查食物是否在蛇身上
for (int i = 0; i ++) {
if (snake.x[i] == food_x && snake.y[i] == food_y) {
is_food_on_snake = 1;
break;
}
}
if (!is_food_on_snake) break; // 不在蛇身上则退出循环
}
}
}
6. 碰撞检测:游戏结束条件 ❌
蛇撞边界或撞自身时,游戏结束,用check_collision()函数实现:
// 碰撞检测(边界、自身)
void check_collision() {
// 1. 撞边界:蛇头超出1~WIDTH-2 或 1~HEIGHT-2
if (snake.x[0] snake.x[0] >= WIDTH - 1 ||
snake.y[0] [0] >= HEIGHT - 1) {
game_state = 0;
printf("\n游戏结束!撞墙了~最终得分:%d\n", score);
}
// 2. 撞自身:蛇头坐标 == 任意蛇身坐标
for (int i = 1; i ++) {
if (snake.x[0] == snake.x[i] && snake.y[0] == snake.y[i]) {
game_state = 0;
printf("\n游戏结束!撞到自己了~最终得分:%d\n", score);
break;
}
}
}
7. 主函数:串联所有模块 🔗
主函数负责调用初始化、绘制、控制、移动、碰撞检测,用循环实现游戏主逻辑:
int main() {
// 1. 初始化游戏
init_game();
// 2. 游戏主循环(game_state=1时运行)
while (game_state) {
draw_map(); // 绘制界面
control_dir(); // 监听键盘控制
snake_move(); // 蛇移动
check_collision(); // 碰撞检测
Sleep(200); // 延迟200ms(控制游戏速度,值越小越快)
}
// 3. 游戏结束,等待按键退出(避免窗口一闪而过)
printf("按任意键退出...\n");
_getch();
return 0;
}
三、运行效果与优化建议 ✨
1. 预期效果
- 控制台显示 20x10 的地图,蛇头(●)、蛇身(○)、食物(★)清晰可见;
- 按 W/S/A/D 控制方向,ESC 退出游戏;
- 吃到食物得分 + 10,蛇变长;
- 撞边界或自身时,显示最终得分并退出。
2. 优化方向(进阶练习)
- 调整游戏速度:得分越高,Sleep () 延迟越小,难度递增;
// 示例:根据得分动态调整速度
int speed = 200 - (score / 10) * 10;
if (speed < 50) speed = 50; // 最低速度限制
Sleep(speed);
- 增加关卡:比如每得 100 分进入下一关,速度加快;
- 自定义地图:用文件读取地图,添加障碍物;
- 记录最高分:将最高分存入文件,下次启动时显示。
四、常见问题解决 ❓
- VS 编译报错 “_kbhit 未定义”:VS 中需用_kbhit()和_getch()(加下划线),Dev-C++ 无需;
- 画面闪烁严重:除了system("cls"),还可以用 Windows API 的控制台缓冲区刷新(更高级);
// 示例:用API刷新缓冲区,减少闪烁
HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
COORD pos = {0, 0};
SetConsoleCursorPosition(hOut, pos); // 光标移到左上角,覆盖旧内容
- 食物生成在蛇身上:代码中已加 “食物不在蛇身上” 的判断,若仍有问题,检查循环逻辑。
话题
#C 语言实战 #贪吃蛇游戏 #控制台程序 #C 语言项目 #编程入门