提到 C 语言的实战项目,很多人第一个想到的就是贪吃蛇。它麻雀虽小,五脏俱全,涵盖了 C 语言的基础语法、数据结构、以及控制台交互等核心知识点。本文将带你从零开始,一步一步地用 C 语言实现一个经典的贪吃蛇游戏。
需求分析与设计
在开始编写代码之前,我们需要明确游戏的需求,并进行简单的设计。
- 游戏地图: 使用控制台的字符来模拟游戏地图,例如使用
#表示墙壁,空格表示空地,@表示蛇的身体,*表示食物。 - 蛇: 蛇由多个节点组成,每个节点代表蛇身体的一部分。我们需要记录蛇的位置、长度和移动方向。
- 食物: 食物随机出现在地图的空地上,蛇吃到食物后,身体变长。
- 移动: 蛇根据当前方向不断移动,如果撞到墙壁或自己的身体,游戏结束。
- 控制: 使用键盘上的方向键控制蛇的移动方向。
底层原理剖析:链表与控制台操作
实现贪吃蛇,需要用到一些基本的数据结构和控制台操作。
- 链表: 蛇的身体可以使用链表来表示,每个节点存储蛇身体一部分的坐标。链表的优点是方便插入和删除节点,这对于蛇的移动和生长非常重要。
- 控制台操作: 在 Windows 平台下,可以使用
windows.h头文件中的函数来进行控制台操作,例如清屏、设置光标位置、输出字符等。在 Linux 平台下,可以使用curses库。
考虑到跨平台兼容性,这里我们将使用 windows.h 来演示,后续可以考虑使用条件编译来适配 Linux 环境,类似 Nginx 的跨平台处理。
代码实现:一步一步搭建贪吃蛇
下面我们开始编写代码。首先,定义一些常量和数据结构:
#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <time.h>
#define WIDTH 20 // 地图宽度
#define HEIGHT 15 // 地图高度
// 蛇的结构体
typedef struct SnakeNode {
int x;
int y;
struct SnakeNode* next;
} SnakeNode;
// 全局变量
SnakeNode* snakeHead; // 蛇头
int foodX, foodY; // 食物坐标
int score = 0; // 得分
int gameOver = 0; // 游戏结束标志
// 定义移动方向
enum Direction {
UP, DOWN, LEFT, RIGHT
};
int currentDirection = RIGHT; // 初始方向向右
// 函数声明
void initGame();
void drawMap();
void createFood();
void moveSnake();
void changeDirection(int key);
int checkCollision();
void gameOverScreen();
int main() {
initGame();
while (!gameOver) {
if (_kbhit()) { // 检查是否有按键按下
changeDirection(_getch()); // 获取按键并改变方向
}
moveSnake(); // 移动蛇
drawMap(); // 绘制地图
Sleep(150); // 控制游戏速度
}
gameOverScreen();
return 0;
}
接下来,实现 initGame 函数,初始化游戏:
void initGame() {
// 初始化随机数种子
srand(time(NULL));
// 创建蛇头
snakeHead = (SnakeNode*)malloc(sizeof(SnakeNode));
snakeHead->x = WIDTH / 2;
snakeHead->y = HEIGHT / 2;
snakeHead->next = NULL;
// 创建食物
createFood();
}
然后,实现 drawMap 函数,绘制游戏地图:
void drawMap() {
system("cls"); // 清屏
printf("Score: %d\n", score);
// 绘制上边框
for (int i = 0; i < WIDTH + 2; i++) {
printf("#");
}
printf("\n");
for (int i = 0; i < HEIGHT; i++) {
printf("#"); // 绘制左边框
for (int j = 0; j < WIDTH; j++) {
if (i == foodY && j == foodX) {
printf("*"); // 绘制食物
} else {
SnakeNode* current = snakeHead;
int isSnake = 0;
while (current != NULL) {
if (current->x == j && current->y == i) {
printf("@"); // 绘制蛇身
isSnake = 1;
break;
}
current = current->next;
}
if (!isSnake) {
printf(" "); // 绘制空地
}
}
}
printf("#\n"); // 绘制右边框
}
// 绘制下边框
for (int i = 0; i < WIDTH + 2; i++) {
printf("#");
}
printf("\n");
}
最后实现食物生成和蛇的移动,控制方向和碰撞检测等函数,这部分代码比较多,这里就省略了,读者可以自行完成。完整代码会放在 github 上。
实战避坑经验总结
- 内存管理: 务必注意内存分配和释放,防止内存泄漏。使用
malloc分配的内存,一定要使用free释放。 - 边界条件: 特别是碰撞检测,注意蛇头与自身碰撞、蛇头超出地图边界的情况。
- 游戏速度: 游戏速度的控制可以使用
Sleep函数,但不同的机器性能不同,可能会导致游戏速度不一致。可以考虑使用更精确的计时器。 - 跨平台兼容: 如果需要在 Linux 平台下运行,需要使用
curses库,并使用条件编译来适配不同的平台。这和 Nginx 的跨平台处理类似,都需要考虑到各种底层 API 的差异。
希望通过这个 C 语言实战项目:贪吃蛇,你能对 C 语言的编程能力有进一步的提升。也欢迎大家在评论区留言交流。
冠军资讯
加班到秃头