news 2026/5/11 19:28:26

【C 语言实战】从零实现控制台贪吃蛇游戏,附完整代码与思路解析 [特殊字符]

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【C 语言实战】从零实现控制台贪吃蛇游戏,附完整代码与思路解析 [特殊字符]

大家好!今天带大家用 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. 优化方向(进阶练习)

  1. 调整游戏速度:得分越高,Sleep () 延迟越小,难度递增;

// 示例:根据得分动态调整速度

int speed = 200 - (score / 10) * 10;

if (speed < 50) speed = 50; // 最低速度限制

Sleep(speed);

  1. 增加关卡:比如每得 100 分进入下一关,速度加快;
  1. 自定义地图:用文件读取地图,添加障碍物;
  1. 记录最高分:将最高分存入文件,下次启动时显示。

四、常见问题解决 ❓

  1. VS 编译报错 “_kbhit 未定义”:VS 中需用_kbhit()和_getch()(加下划线),Dev-C++ 无需;
  1. 画面闪烁严重:除了system("cls"),还可以用 Windows API 的控制台缓冲区刷新(更高级);

// 示例:用API刷新缓冲区,减少闪烁

HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);

COORD pos = {0, 0};

SetConsoleCursorPosition(hOut, pos); // 光标移到左上角,覆盖旧内容

  1. 食物生成在蛇身上:代码中已加 “食物不在蛇身上” 的判断,若仍有问题,检查循环逻辑。

话题

#C 语言实战 #贪吃蛇游戏 #控制台程序 #C 语言项目 #编程入门

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/11 16:55:56

29、深入探索fwknop:功能、应用与安全保障

深入探索fwknop:功能、应用与安全保障 1. fwknop与iptables规则 fwknop在操作规则时,不会与iptables策略中的现有规则冲突。在30秒计时器到期前,可在fwknop服务器上执行以下命令查看授予对SSHD访问权限的iptables规则: [root@spaserver ~]# fwknopd --fw-list [+] List…

作者头像 李华
网站建设 2026/5/11 18:21:25

行业专家如何选择远控软件?2026年十大品牌排名场景适配全指南

在远程协作深入各行各业的今天&#xff0c;选择远程控制软件已不再是简单的工具对比&#xff0c;而成为一场关乎工作效率、数据安全与长期成本的战略决策。不同行业、不同场景对远控软件的需求差异显著——设计师要求色彩精准&#xff0c;程序员需要低延迟响应&#xff0c;企业…

作者头像 李华
网站建设 2026/5/11 0:45:56

光伏板太阳能充电MATLAB仿真之旅

光伏板太阳能充电MATLAB仿真&#xff0c;光伏电池输出12v. 经过boost电路升压输出48v&#xff0c;boost采用双闭环控制&#xff0c;输出电流9A。 有文档。最近在研究光伏板太阳能充电相关内容&#xff0c;和大家分享下基于MATLAB的仿真实现过程。这次我们要实现的是光伏电池输…

作者头像 李华
网站建设 2026/5/6 19:37:41

Java面试三连击:原理拆解+实战避坑

很多刚学 Java 的小伙伴在面试时都会遇到一个尴尬的情况&#xff1a;题目明明看着很眼熟&#xff0c;答案也能背个大概&#xff0c;但面试官稍微追问一句“为什么”&#xff0c;就立刻哑口无言。今天我们精选了 3 道 Java 面试中出镜率最高的“老生常谈”&#xff0c;不讲枯燥的…

作者头像 李华