C语言写一个贪吃蛇游戏

C语言实现贪吃蛇:从零开始,200行代码搞定经典游戏

------适合新手的完整教程,含详细注释和逐步讲解

一、先看效果

plain

复制

==============================

* 贪吃蛇游戏 *

==============================

Score: 0 Length: 3

##############################

@

*

*

$

##############################

WASD移动 P暂停 ESC退出

运行效果:

@ 代表蛇头

* 代表蛇身

$ 代表食物

代表围墙

二、核心思路:把问题变简单

很多新手觉得贪吃蛇难,是因为想一次性搞定所有功能。正确的做法是:拆成小步骤

表格

步骤 任务 难度

1 画出游戏地图(围墙) ⭐

2 让蛇出现在地图上 ⭐⭐

3 让蛇动起来(自动前进) ⭐⭐

4 用键盘控制方向 ⭐⭐

5 吃到食物变长 ⭐⭐⭐

6 判断撞墙/撞自己 ⭐⭐⭐

记住:每次只测试一个功能,没问题了再进行下一步!

三、完整代码(可直接运行)

c

复制

/*

* C语言贪吃蛇完整代码

* 编译环境:Windows (使用conio.h)

* 编译命令:gcc snake.c -o snake.exe

*/

#include <stdio.h>

#include <stdlib.h>

#include <conio.h> // Windows下获取键盘输入

#include <windows.h> // Windows下Sleep函数

// ========== 游戏参数配置 ==========

#define WIDTH 20 // 游戏区域宽度

#define HEIGHT 20 // 游戏区域高度

#define MAX_LEN 100 // 蛇的最大长度

// ========== 方向枚举 ==========

typedef enum {

UP = 0,

DOWN,

LEFT,

RIGHT,

STOP // 初始状态/暂停

} Direction;

// ========== 蛇的结构体 ==========

typedef struct {

int xMAX_LEN; // 每一节身体的x坐标

int yMAX_LEN; // 每一节身体的y坐标

int length; // 当前长度

Direction dir; // 当前移动方向

} Snake;

// ========== 全局变量 ==========

Snake snake; // 蛇

int foodX, foodY; // 食物坐标

int score; // 分数

int gameOver; // 游戏结束标志

int speed = 200; // 移动速度(毫秒,越小越快)

// ========== 函数声明 ==========

void initGame(); // 初始化游戏

void draw(); // 绘制画面

void input(); // 处理输入

void logic(); // 游戏逻辑

void spawnFood(); // 生成食物

void gameOverScreen(); // 游戏结束画面

// ========== 主函数 ==========

int main() {

initGame();

while (!gameOver) {

draw(); // 1. 画出来

input(); // 2. 读键盘

logic(); // 3. 算下一步

Sleep(speed); // 4. 等待(控制速度)

}

gameOverScreen();

return 0;

}

// ========== 初始化游戏 ==========

void initGame() {

// 初始化蛇:从屏幕中间开始,长度3,向右移动

snake.length = 3;

snake.dir = RIGHT;

// 蛇头在中间,身体在左边

for (int i = 0; i < snake.length; i++) {

snake.xi = WIDTH / 2 - i; // x坐标递减(向左排)

snake.yi = HEIGHT / 2; // y坐标相同(横着排)

}

score = 0;

gameOver = 0;

spawnFood(); // 生成第一个食物

}

// ========== 生成食物 ==========

void spawnFood() {

// 随机生成,但不能生成在蛇身上

int valid;

do {

valid = 1;

foodX = rand() % (WIDTH - 2) + 1; // 1 ~ WIDTH-2(避开围墙)

foodY = rand() % (HEIGHT - 2) + 1; // 1 ~ HEIGHT-2

// 检查是否和蛇身重叠

for (int i = 0; i < snake.length; i++) {

if (snake.xi == foodX && snake.yi == foodY) {

valid = 0; // 重叠了,重新生成

break;

}

}

} while (!valid);

}

// ========== 绘制画面 ==========

void draw() {

// 清屏(Windows系统)

system("cls");

// 打印标题和分数

printf("==============================\n");

printf("* 贪吃蛇游戏 *\n");

printf("==============================\n");

printf("Score: %d Length: %d\n\n", score, snake.length);

// 打印游戏区域

for (int y = 0; y < HEIGHT; y++) {

for (int x = 0; x < WIDTH; x++) {

// 1. 打印围墙(边界)

if (x == 0 || x == WIDTH - 1 || y == 0 || y == HEIGHT - 1) {

printf("#");

}

// 2. 打印蛇头

else if (x == snake.x0 && y == snake.y0) {

printf("@");

}

// 3. 打印蛇身

else {

int isBody = 0;

for (int i = 1; i < snake.length; i++) {

if (x == snake.xi && y == snake.yi) {

printf("*");

isBody = 1;

break;

}

}

// 4. 打印食物

if (!isBody) {

if (x == foodX && y == foodY) {

printf("$");

}

// 5. 打印空白

else {

printf(" ");

}

}

}

}

printf("\n");

}

printf("\nWASD移动 P暂停 ESC退出");

}

// ========== 处理键盘输入 ==========

void input() {

// _kbhit() 检查是否有按键按下(不阻塞)

if (_kbhit()) {

// _getch() 读取按键(不显示在屏幕上)

char key = _getch();

// 防止180度掉头(不能直接反向)

switch (key) {

case 'w':

case 'W':

if (snake.dir != DOWN) snake.dir = UP;

break;

case 's':

case 'S':

if (snake.dir != UP) snake.dir = DOWN;

break;

case 'a':

case 'A':

if (snake.dir != RIGHT) snake.dir = LEFT;

break;

case 'd':

case 'D':

if (snake.dir != LEFT) snake.dir = RIGHT;

break;

case 'p':

case 'P':

// 暂停功能

printf("\n\n游戏暂停,按任意键继续...");

_getch();

break;

case 27: // ESC键

gameOver = 1;

break;

}

}

}

// ========== 游戏核心逻辑 ==========

void logic() {

// 1. 计算新蛇头的位置

int newX = snake.x0;

int newY = snake.y0;

switch (snake.dir) {

case UP: newY--; break;

case DOWN: newY++; break;

case LEFT: newX--; break;

case RIGHT: newX++; break;

default: return; // STOP状态不动

}

// 2. 检查是否撞墙

if (newX <= 0 || newX >= WIDTH - 1 || newY <= 0 || newY >= HEIGHT - 1) {

gameOver = 1;

return;

}

// 3. 检查是否撞到自己(从第4节开始检查,前3节不可能撞到)

for (int i = 3; i < snake.length; i++) {

if (newX == snake.xi && newY == snake.yi) {

gameOver = 1;

return;

}

}

// 4. 检查是否吃到食物

int ateFood = (newX == foodX && newY == foodY);

// 5. 移动蛇身(从尾巴开始,每一节移到前一节的位置)

// 如果没吃到食物,尾巴位置会变成空白(蛇移动了)

// 如果吃到食物,尾巴保留(蛇变长了)

if (!ateFood) {

// 没吃到:正常移动,尾巴消失

for (int i = snake.length - 1; i > 0; i--) {

snake.xi = snake.xi-1;

snake.yi = snake.yi-1;

}

} else {

// 吃到了:长度+1,新尾巴先不赋值(后面统一处理)

snake.length++;

for (int i = snake.length - 1; i > 0; i--) {

snake.xi = snake.xi-1;

snake.yi = snake.yi-1;

}

score += 10;

spawnFood(); // 生成新食物

// 加速(可选)

if (speed > 50) speed -= 5;

}

// 6. 移动蛇头

snake.x0 = newX;

snake.y0 = newY;

}

// ========== 游戏结束画面 ==========

void gameOverScreen() {

system("cls");

printf("\n\n");

printf(" ============================\n");

printf(" G A M E O V E R \n");

printf(" ============================\n");

printf("\n");

printf(" 最终得分: %d\n", score);

printf(" 蛇长度: %d\n", snake.length);

printf("\n");

printf(" 按任意键退出...\n");

printf(" ============================\n");

_getch();

}

四、代码详解:重点难点逐个击破

4.1 蛇身怎么表示?------数组的巧妙用法

c

复制

int xMAX_LEN; // 存每一节的x坐标

int yMAX_LEN; // 存每一节的y坐标

关键理解:

x0, y0 永远是蛇头

x1, y1 是第一节身体

xlength-1, ylength-1 是尾巴

移动原理(没吃到食物时):

plain

复制

移动前: H123T H=头, T=尾, 数字=身体

移动后: H123 T消失,新H出现

代码实现:从尾巴开始,每个点移到前一个点的位置

for (i = 尾巴; i > 0; i--) {

xi = xi-1; // 第i节变成第i-1节的位置

yi = yi-1;

}

// 最后单独处理蛇头

x0 = 新位置;

4.2 吃到食物怎么变长?

秘诀:先不删尾巴!

c

复制

if (吃到食物) {

length++; // 长度+1

// 移动时,尾巴位置没人覆盖,自然就变长了!

}

4.3 为什么不能180度掉头?

c

复制

case 'w':

if (snake.dir != DOWN) snake.dir = UP; // 正在向下时,不能向上

否则会出现这种情况:

plain

复制

@\*\* 蛇向右走

↓ 按左键(180度掉头)

\*@\* 蛇头撞到自己的身体,游戏结束!

4.4 食物生成算法

c

复制

do {

foodX = rand() % (WIDTH - 2) + 1; // 1到WIDTH-2

} while (食物在蛇身上);

取模运算解释:

rand() % 18 → 0~17

  • 1 → 1~18(避开0和19的围墙)

五、常见问题 FAQ

Q1:Linux/Mac 怎么编译?

Linux/Mac 没有 conio.h 和 windows.h,需要替换:

c

复制

// 替换清屏

#include <unistd.h>

void clearScreen() {

printf("\033[2J\033[H"); // ANSI清屏码

// 或 system("clear");

}

// 替换Sleep

#include <unistd.h>

usleep(speed * 1000); // 微秒

// 替换_kbhit和_getch

#include <termios.h>

#include <fcntl.h>

// 需要额外实现非阻塞输入,建议用ncurses库

推荐: Windows下用Dev-C++或VS Code+MinGW最简单。

Q2:怎么让蛇移动更流畅?

当前方案:Sleep(200) 是卡顿的,因为200ms才刷新一次。

进阶方案:多线程或精确计时

c

复制

// 非阻塞输入 + 定时刷新(伪代码)

while(1) {

if (时间到了200ms) {

moveSnake(); // 自动移动

draw();

}

if (有按键) {

changeDirection(); // 改变方向

}

}

Q3:怎么添加更多功能?

表格

功能 实现思路

关卡系统 不同速度、不同地图

障碍物 在地图上固定几个 #

最高分记录 读写文件 fopen("score.txt", "r")

双人模式 两个Snake结构体,两套控制键

六、学习建议:如何吃透这段代码

第一步:抄

把代码完整敲一遍(不要复制粘贴)

确保能编译运行

第二步:改

改 WIDTH 和 HEIGHT,看效果

改 speed 初始值,调整难度

把 @ 改成其他符号

第三步:加

添加暂停功能(已完成)

添加障碍物

添加生命值系统

第四步:创

把围墙改成 #,身体改成 o,更美观

添加颜色(Windows API SetConsoleTextAttribute)

用 EasyX 或 SDL 做图形界面版

七、完整项目结构

plain

复制

snake/

├── snake.c // 主代码(本文代码)

├── README.md // 项目说明

└── screenshot.png // 运行截图

GitHub 提交信息建议:

plain

复制

feat: 基础移动功能

feat: 添加食物和吃食物逻辑

feat: 添加撞墙检测

fix: 修复180度掉头bug

docs: 添加代码注释

八、总结

表格

知识点 在本项目中的体现

数组 蛇身坐标存储

结构体 Snake封装

枚举 Direction方向

循环 遍历蛇身、绘制地图

函数 模块化设计

指针 (进阶)可优化为链表实现

200行代码,涵盖了C语言的核心语法。

贪吃蛇是练手经典项目,因为它:

✅ 逻辑清晰(输入→处理→输出)

✅ 实时交互(键盘控制)

✅ 视觉效果(即时反馈)

✅ 可扩展性强( endless optimization )

相关推荐
是阿建吖!6 小时前
【Linux】信号
android·linux·c语言·c++
提子拌饭1337 小时前
逛三园游戏——基于鸿蒙PC Electron框架实现
前端·javascript·游戏·华为·electron·鸿蒙
三品吉他手会点灯7 小时前
C语言学习笔记 - 43.运算符与表达式 - 运算符1 - 运算符的分类和简单介绍
c语言·笔记·学习·算法
foundbug9997 小时前
STM32 睡眠模式测试程序
stm32·单片机·嵌入式硬件
开开心心就好8 小时前
支持多显示器的Windows高效分屏工具
运维·python·科技·游戏·计算机外设·ocr·powerpoint
wxmtwfx9 小时前
littlefs 源码分析
单片机·littlefs·嵌入式文件系统
wuminyu9 小时前
Java锁机制之轻量级锁判断与尝试逻辑源码剖析
java·linux·c语言·jvm·c++
熊猫钓鱼>_>10 小时前
腾讯云 COS × WorkBuddy X skill:实现我的游戏项目资源管理自动化“龙虾”
游戏·自动化·腾讯云·agent·cos·skill·workbuddy
天涯铭11 小时前
深入浅出:单片机I/O口串联电阻选型
单片机·嵌入式硬件·io口串联电阻
国科安芯12 小时前
ASP7A84AS——航天级低噪声高PSRR线性稳压器
网络·单片机·嵌入式硬件·架构·安全性测试