跳转至

项目:贪吃蛇游戏

项目介绍

使用 C 语言在 Windows 控制台中实现经典的贪吃蛇游戏,通过键盘控制蛇的移动,吃到食物增长身体,撞墙或撞自身则游戏结束。

核心功能

  • WASD / 方向键控制蛇的移动
  • 食物随机生成
  • 碰撞检测(墙壁 / 自身)
  • 实时计分显示
  • 游戏结束判定与提示

涉及知识点

知识点 用途
二维数组 表示游戏地图
循环 主游戏循环
条件判断 碰撞检测
控制台函数 隐藏光标、清屏、定位光标
随机数 食物位置生成
结构体 表示蛇身节点

完整代码

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <windows.h>
#include <time.h>

// 地图尺寸
#define WIDTH  20
#define HEIGHT 20

// 游戏状态
int game_over = 0;
int score = 0;

// 蛇的坐标结构
typedef struct {
    int x;
    int y;
} Point;

// 蛇(用数组模拟)
Point snake[WIDTH * HEIGHT];
int snake_len = 3;

// 食物坐标
Point food;

// 移动方向
enum Direction { UP, DOWN, LEFT, RIGHT };
enum Direction dir = RIGHT;

// 隐藏控制台光标
void hide_cursor() {
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
    CONSOLE_CURSOR_INFO info;
    info.dwSize = 100;
    info.bVisible = FALSE;
    SetConsoleCursorInfo(handle, &info);
}

// 定位光标位置
void gotoxy(int x, int y) {
    COORD pos = { x * 2, y };
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

// 初始化游戏
void init_game() {
    // 初始蛇:水平三条
    snake[0].x = 5; snake[0].y = HEIGHT / 2;
    snake[1].x = 4; snake[1].y = HEIGHT / 2;
    snake[2].x = 3; snake[2].y = HEIGHT / 2;

    // 随机生成食物
    srand((unsigned)time(NULL));
    food.x = rand() % (WIDTH - 2) + 1;
    food.y = rand() % (HEIGHT - 2) + 1;

    hide_cursor();
}

// 绘制地图和蛇
void draw() {
    system("cls");

    // 绘制上边界
    for (int i = 0; i < WIDTH; i++)
        printf("█");
    printf("\n");

    // 绘制中间区域
    for (int y = 0; y < HEIGHT; y++) {
        printf("█"); // 左边界
        for (int x = 0; x < WIDTH; x++) {
            int is_snake = 0;
            for (int i = 0; i < snake_len; i++) {
                if (snake[i].x == x && snake[i].y == y) {
                    is_snake = 1;
                    break;
                }
            }
            if (is_snake)
                printf("■");
            else if (food.x == x && food.y == y)
                printf("★");
            else
                printf("  ");
        }
        printf("█\n"); // 右边界
    }

    // 绘制下边界
    for (int i = 0; i < WIDTH; i++)
        printf("█");
    printf("\n");

    printf("得分:%d\n", score);
    printf("按 WASD 控制方向,按 Q 退出\n");
}

// 处理键盘输入
void input() {
    if (_kbhit()) {
        switch (_getch()) {
            case 'w': case 'W': if (dir != DOWN)  dir = UP;    break;
            case 's': case 'S': if (dir != UP)    dir = DOWN;  break;
            case 'a': case 'A': if (dir != RIGHT) dir = LEFT;  break;
            case 'd': case 'D': if (dir != LEFT)  dir = RIGHT; break;
            case 'q': case 'Q': game_over = 1;                 break;
        }
    }
}

// 移动蛇
void move_snake() {
    // 计算新蛇头坐标
    Point new_head = snake[0];
    switch (dir) {
        case UP:    new_head.y--; break;
        case DOWN:  new_head.y++; break;
        case LEFT:  new_head.x--; break;
        case RIGHT: new_head.x++; break;
    }

    // 碰撞检测:墙壁
    if (new_head.x < 0 || new_head.x >= WIDTH ||
        new_head.y < 0 || new_head.y >= HEIGHT) {
        game_over = 1;
        return;
    }

    // 碰撞检测:自身
    for (int i = 0; i < snake_len; i++) {
        if (snake[i].x == new_head.x && snake[i].y == new_head.y) {
            game_over = 1;
            return;
        }
    }

    // 判断是否吃到食物
    int ate_food = (new_head.x == food.x && new_head.y == food.y);

    // 移动蛇:从尾部向前移动
    for (int i = snake_len - 1; i > 0; i--)
        snake[i] = snake[i - 1];
    snake[0] = new_head;

    // 如果吃到食物,在尾部增加一节
    if (ate_food) {
        snake[snake_len] = snake[snake_len - 1];  // 尾部复制
        snake_len++;
        score += 10;

        // 生成新食物
        int valid = 0;
        while (!valid) {
            food.x = rand() % (WIDTH - 2) + 1;
            food.y = rand() % (HEIGHT - 2) + 1;
            valid = 1;
            for (int i = 0; i < snake_len; i++) {
                if (snake[i].x == food.x && snake[i].y == food.y) {
                    valid = 0;
                    break;
                }
            }
        }
    }
}

// 主函数
int main() {
    init_game();

    while (!game_over) {
        draw();
        input();
        move_snake();
        Sleep(150); // 控制游戏速度(毫秒)
    }

    system("cls");
    printf("════════════════════════\n");
    printf("  游戏结束!\n");
    printf("  最终得分:%d\n", score);
    printf("════════════════════════\n");
    printf("按任意键退出...\n");
    _getch();

    return 0;
}

核心算法解析

蛇的移动逻辑

蛇的身体用数组 snake[] 存储,snake[0] 为蛇头。每次移动时: 1. 根据方向计算新蛇头坐标 2. 从尾部开始,每个节点向前移动一位(snake[i] = snake[i-1]) 3. 将新蛇头设为 snake[0]

碰撞检测

  • 墙壁碰撞:检查新蛇头坐标是否超出地图边界
  • 自身碰撞:遍历蛇身数组,检查新蛇头是否与身体重合

食物生成

食物不能生成在蛇身上,因此需要循环检查位置是否被蛇占据。

扩展挑战

  1. 增加难度选择:游戏开始前让玩家选择速度(简单/普通/困难)
  2. 增加障碍物:地图中随机生成障碍物
  3. 记录最高分:将最高分保存到文件中,每次游戏开始时读取
  4. 穿墙模式:蛇从一端穿出,从另一端出现
  5. 彩色界面:使用控制台颜色函数美化界面
  6. 暂停功能:按 P 键暂停/继续游戏