C语言贪吃蛇游戏超详解(包含音效、颜色、封装成应用等)

一、项目概述

贪吃蛇是一款经典的益智游戏,也是学习C语言编程的绝佳实践项目。本项目将详细讲解如何使用C语言从零开始实现一个功能完整的贪吃蛇游戏,包含音效系统、彩色界面、游戏逻辑控制,并最终封装成可执行应用程序。

1.1 项目特色

  • 完整游戏功能:支持蛇的移动、转向、吃食物、碰撞检测等核心功能
  • 彩色界面:使用Windows API实现多彩的游戏界面
  • 音效系统:包含背景音乐和事件音效
  • 可执行封装:最终编译成exe文件,可在任意Windows电脑运行
  • 模块化设计:代码结构清晰,便于理解和扩展

1.2 开发环境

  • 编译器:GCC/MinGW或Visual Studio
  • 操作系统:Windows 10及以上
  • 依赖库:Windows API(系统自带)

二、核心数据结构设计

2.1 坐标点结构体

// 坐标点结构体
typedef struct {
    int x;
    int y;
} Point;

2.2 方向枚举

// 方向枚举
typedef enum {
    UP = 1,
    DOWN,
    LEFT,
    RIGHT
} Direction;

2.3 游戏状态结构体

// 游戏状态结构体
typedef struct {
    Point snake[100];      // 蛇身数组,最大长度100
    int snake_len;         // 当前蛇身长度
    Point food;            // 食物坐标
    Direction dir;         // 当前移动方向
    int score;             // 游戏分数
    int game_over;         // 游戏结束标志
    int speed;             // 游戏速度
} GameState;

三、游戏界面与颜色设置

3.1 控制台颜色控制

方法一:使用system命令(简单但效率低)

#include <stdlib.h>

// 设置控制台颜色(背景色+前景色)
void setColor(int bg, int fg) {
    char cmd[20];
    sprintf(cmd, "color %X%X", bg, fg);
    system(cmd);
}

// 颜色代码表
// 0=黑色 1=蓝色 2=绿色 3=湖蓝色 4=红色 5=紫色 6=黄色 7=白色
// 8=灰色 9=淡蓝色 A=淡绿色 B=淡浅绿色 C=淡红色 D=淡紫色 E=淡黄色 F=亮白色

方法二:使用Windows API(推荐,效率高)

#include <windows.h>

// 设置控制台文本属性
void setConsoleColor(WORD color) {
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, color);
}

// 颜色组合示例
// 红色文字:FOREGROUND_RED | FOREGROUND_INTENSITY
// 绿色文字:FOREGROUND_GREEN | FOREGROUND_INTENSITY
// 黄色文字:FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY
// 白色文字:FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
// 红色背景:BACKGROUND_RED
// 绿色背景:BACKGROUND_GREEN

3.2 光标控制函数

// 设置光标位置
void setCursorPosition(int x, int y) {
    COORD pos;
    pos.X = x;
    pos.Y = y;
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorPosition(hConsole, pos);
}

// 隐藏光标
void hideCursor() {
    CONSOLE_CURSOR_INFO cursorInfo;
    cursorInfo.dwSize = 1;
    cursorInfo.bVisible = FALSE;
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorInfo(hConsole, &cursorInfo);
}

3.3 游戏界面绘制

// 绘制游戏界面
void drawGame(GameState* game) {
    system("cls");  // 清屏
    
    // 绘制顶部信息栏
    setConsoleColor(FOREGROUND_GREEN | FOREGROUND_INTENSITY);
    printf("=== 贪吃蛇大作战 ===\n");
    printf("分数: %d | 长度: %d\n", game->score, game->snake_len);
    printf("使用WASD控制方向,按Q退出游戏\n\n");
    
    // 绘制游戏区域
    for (int y = 0; y < HEIGHT; y++) {
        for (int x = 0; x < WIDTH; x++) {
            // 绘制食物
            if (x == game->food.x && y == game->food.y) {
                setConsoleColor(FOREGROUND_RED | FOREGROUND_INTENSITY);
                printf("◆");
            }
            // 绘制蛇身
            else {
                int is_snake = 0;
                for (int i = 0; i < game->snake_len; i++) {
                    if (game->snake[i].x == x && game->snake[i].y == y) {
                        if (i == 0) {
                            setConsoleColor(FOREGROUND_GREEN | FOREGROUND_INTENSITY);
                            printf("●");  // 蛇头
                        } else {
                            setConsoleColor(FOREGROUND_GREEN);
                            printf("○");  // 蛇身
                        }
                        is_snake = 1;
                        break;
                    }
                }
                // 绘制空地
                if (!is_snake) {
                    setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
                    printf("·");
                }
            }
        }
        printf("\n");
    }
    
    // 恢复默认颜色
    setConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}

四、音效系统实现

4.1 Windows音效函数

#include <windows.h>

// 播放简单音效(蜂鸣器)
void playBeep(int frequency, int duration) {
    Beep(frequency, duration);
}

// 播放吃食物音效
void playEatSound() {
    Beep(523, 100);  // C5音,持续100ms
}

// 播放游戏结束音效
void playGameOverSound() {
    Beep(392, 200);  // G4
    Beep(349, 200);  // F4
    Beep(330, 400);  // E4
}

// 播放碰撞音效
void playCollisionSound() {
    Beep(262, 150);  // C4
}

4.2 使用mciSendString播放音频文件

#include <windows.h>
#include <mmsystem.h>
#pragma comment(lib, "winmm.lib")

// 播放背景音乐
void playBackgroundMusic() {
    mciSendString("open background.mp3 alias bgm", NULL, 0, NULL);
    mciSendString("play bgm repeat", NULL, 0, NULL);
}

// 播放音效
void playSoundEffect(const char* filename) {
    char cmd[256];
    sprintf(cmd, "open %s alias sound", filename);
    mciSendString(cmd, NULL, 0, NULL);
    mciSendString("play sound", NULL, 0, NULL);
}

// 停止背景音乐
void stopBackgroundMusic() {
    mciSendString("stop bgm", NULL, 0, NULL);
    mciSendString("close bgm", NULL, 0, NULL);
}

五、游戏核心逻辑实现

5.1 游戏初始化

// 游戏初始化
void initGame(GameState* game) {
    // 初始化蛇身(水平排列3个节点)
    game->snake[0].x = 10; game->snake[0].y = 10;  // 蛇头
    game->snake[1].x = 9;  game->snake[1].y = 10;  // 蛇身
    game->snake[2].x = 8;  game->snake[2].y = 10;  // 蛇尾
    game->snake_len = 3;
    
    // 初始方向向右
    game->dir = RIGHT;
    
    // 初始分数
    game->score = 0;
    
    // 游戏未结束
    game->game_over = 0;
    
    // 游戏速度(毫秒)
    game->speed = 200;
    
    // 生成食物
    generateFood(game);
    
    // 隐藏光标
    hideCursor();
    
    // 设置控制台大小
    system("mode con cols=60 lines=32");
    
    // 设置控制台标题
    system("title 贪吃蛇游戏");
}

5.2 食物生成

// 生成食物
void generateFood(GameState* game) {
    srand(time(NULL));
    
    do {
        game->food.x = rand() % WIDTH;
        game->food.y = rand() % HEIGHT;
    } while (checkFoodOnSnake(game));  // 确保食物不在蛇身上
}

// 检查食物是否在蛇身上
int checkFoodOnSnake(GameState* game) {
    for (int i = 0; i < game->snake_len; i++) {
        if (game->snake[i].x == game->food.x && game->snake[i].y == game->food.y) {
            return 1;
        }
    }
    return 0;
}

5.3 蛇的移动算法(核心)

// 移动蛇
void moveSnake(GameState* game) {
    // 保存蛇尾位置(用于吃到食物时增长)
    Point tail = game->snake[game->snake_len - 1];
    
    // 蛇身向前移动(从尾部向头部)
    for (int i = game->snake_len - 1; i > 0; i--) {
        game->snake[i] = game->snake[i - 1];
    }
    
    // 根据方向移动蛇头
    switch (game->dir) {
        case UP:    game->snake[0].y--; break;
        case DOWN:  game->snake[0].y++; break;
        case LEFT:  game->snake[0].x--; break;
        case RIGHT: game->snake[0].x++; break;
    }
    
    // 检查是否吃到食物
    if (game->snake[0].x == game->food.x && game->snake[0].y == game->food.y) {
        // 吃到食物,蛇身增长
        game->snake[game->snake_len] = tail;
        game->snake_len++;
        
        // 增加分数
        game->score += 10;
        
        // 播放吃食物音效
        playEatSound();
        
        // 生成新食物
        generateFood(game);
    }
}

5.4 碰撞检测

// 碰撞检测
int checkCollision(GameState* game) {
    Point head = game->snake[0];
    
    // 检查边界碰撞
    if (head.x < 0 || head.x >= WIDTH || 
        head.y < 0 || head.y >= HEIGHT) {
        return 1;
    }
    
    // 检查自身碰撞(从第2节开始检查,避免与头部比较)
    for (int i = 1; i < game->snake_len; i++) {
        if (head.x == game->snake[i].x && head.y == game->snake[i].y) {
            return 1;
        }
    }
    
    return 0;
}

5.5 键盘输入处理

// 处理键盘输入
void handleInput(GameState* game) {
    if (_kbhit()) {  // 检查是否有按键按下
        char key = _getch();
        
        switch (key) {
            case 'w': case 'W':
                if (game->dir != DOWN) game->dir = UP;
                break;
            case 's': case 'S':
                if (game->dir != UP) game->dir = DOWN;
                break;
            case 'a': case 'A':
                if (game->dir != RIGHT) game->dir = LEFT;
                break;
            case 'd': case 'D':
                if (game->dir != LEFT) game->dir = RIGHT;
                break;
            case 'q': case 'Q':
                game->game_over = 1;
                break;
            case ' ':  // 空格键暂停
                while (_getch() != ' ') {}  // 等待再次按下空格
                break;
        }
    }
}

六、游戏主循环

// 游戏主循环
void gameLoop(GameState* game) {
    // 播放背景音乐
    playBackgroundMusic();
    
    while (!game->game_over) {
        // 绘制游戏界面
        drawGame(game);
        
        // 处理键盘输入
        handleInput(game);
        
        // 移动蛇
        moveSnake(game);
        
        // 检查碰撞
        if (checkCollision(game)) {
            game->game_over = 1;
            playGameOverSound();
            break;
        }
        
        // 控制游戏速度
        Sleep(game->speed);
    }
    
    // 游戏结束处理
    gameOver(game);
}

6.1 游戏结束处理

// 游戏结束
void gameOver(GameState* game) {
    system("cls");
    
    setConsoleColor(FOREGROUND_RED | FOREGROUND_INTENSITY);
    printf("============================\n");
    printf("        游戏结束!\n");
    printf("        最终分数: %d\n", game->score);
    printf("        蛇身长度: %d\n", game->snake_len);
    printf("============================\n\n");
    
    setConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    printf("按任意键退出游戏...");
    _getch();
    
    // 停止背景音乐
    stopBackgroundMusic();
}

七、完整代码整合

7.1 头文件(snake.h)

#ifndef SNAKE_H
#define SNAKE_H

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

// 游戏区域大小
#define WIDTH 20
#define HEIGHT 15

// 坐标点结构体
typedef struct {
    int x;
    int y;
} Point;

// 方向枚举
typedef enum {
    UP = 1,
    DOWN,
    LEFT,
    RIGHT
} Direction;

// 游戏状态结构体
typedef struct {
    Point snake[100];      // 蛇身数组
    int snake_len;         // 当前蛇身长度
    Point food;            // 食物坐标
    Direction dir;         // 当前移动方向
    int score;             // 游戏分数
    int game_over;         // 游戏结束标志
    int speed;             // 游戏速度
} GameState;

// 函数声明
void initGame(GameState* game);
void drawGame(GameState* game);
void moveSnake(GameState* game);
int checkCollision(GameState* game);
void generateFood(GameState* game);
void handleInput(GameState* game);
void gameLoop(GameState* game);
void gameOver(GameState* game);
void setConsoleColor(WORD color);
void hideCursor();
void playEatSound();
void playGameOverSound();
void playBackgroundMusic();
void stopBackgroundMusic();

#endif

7.2 主程序文件(main.c)

#include "snake.h"

int main() {
    // 设置随机数种子
    srand(time(NULL));
    
    // 创建游戏状态
    GameState game;
    
    // 初始化游戏
    initGame(&game);
    
    // 开始游戏循环
    gameLoop(&game);
    
    return 0;
}

7.3 游戏逻辑实现(game.c)

#include "snake.h"

// 游戏初始化
void initGame(GameState* game) {
    // 初始化蛇身
    game->snake[0].x = 10; game->snake[0].y = 10;
    game->snake[1].x = 9;  game->snake[1].y = 10;
    game->snake[2].x = 8;  game->snake[2].y = 10;
    game->snake_len = 3;
    
    game->dir = RIGHT;
    game->score = 0;
    game->game_over = 0;
    game->speed = 200;
    
    generateFood(game);
    hideCursor();
    
    system("mode con cols=60 lines=32");
    system("title 贪吃蛇游戏");
}

// 生成食物
void generateFood(GameState* game) {
    do {
        game->food.x = rand() % WIDTH;
        game->food.y = rand() % HEIGHT;
    } while (checkFoodOnSnake(game));
}

// 检查食物是否在蛇身上
int checkFoodOnSnake(GameState* game) {
    for (int i = 0; i < game->snake_len; i++) {
        if (game->snake[i].x == game->food.x && game->snake[i].y == game->food.y) {
            return 1;
        }
    }
    return 0;
}

// 移动蛇
void moveSnake(GameState* game) {
    Point tail = game->snake[game->snake_len - 1];
    
    for (int i = game->snake_len - 1; i > 0; i--) {
        game->snake[i] = game->snake[i - 1];
    }
    
    switch (game->dir) {
        case UP:    game->snake[0].y--; break;
        case DOWN:  game->snake[0].y++; break;
        case LEFT:  game->snake[0].x--; break;
        case RIGHT: game->snake[0].x++; break;
    }
    
    if (game->snake[0].x == game->food.x && game->snake[0].y == game->food.y) {
        game->snake[game->snake_len] = tail;
        game->snake_len++;
        game->score += 10;
        playEatSound();
        generateFood(game);
    }
}

// 碰撞检测
int checkCollision(GameState* game) {
    Point head = game->snake[0];
    
    if (head.x < 0 || head.x >= WIDTH || 
        head.y < 0 || head.y >= HEIGHT) {
        return 1;
    }
    
    for (int i = 1; i < game->snake_len; i++) {
        if (head.x == game->snake[i].x && head.y == game->snake[i].y) {
            return 1;
        }
    }
    
    return 0;
}

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

// 游戏主循环
void gameLoop(GameState* game) {
    playBackgroundMusic();
    
    while (!game->game_over) {
        drawGame(game);
        handleInput(game);
        moveSnake(game);
        
        if (checkCollision(game)) {
            game->game_over = 1;
            playGameOverSound();
            break;
        }
        
        Sleep(game->speed);
    }
    
    gameOver(game);
}

// 游戏结束
void gameOver(GameState* game) {
    system("cls");
    
    setConsoleColor(FOREGROUND_RED | FOREGROUND_INTENSITY);
    printf("============================\n");
    printf("        游戏结束!\n");
    printf("        最终分数: %d\n", game->score);
    printf("        蛇身长度: %d\n", game->snake_len);
    printf("============================\n\n");
    
    setConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
    printf("按任意键退出游戏...");
    _getch();
    
    stopBackgroundMusic();
}

7.4 界面与音效实现(ui.c)

#include "snake.h"

// 设置控制台文本属性
void setConsoleColor(WORD color) {
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hConsole, color);
}

// 隐藏光标
void hideCursor() {
    CONSOLE_CURSOR_INFO cursorInfo;
    cursorInfo.dwSize = 1;
    cursorInfo.bVisible = FALSE;
    HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleCursorInfo(hConsole, &cursorInfo);
}

// 绘制游戏界面
void drawGame(GameState* game) {
    system("cls");
    
    // 顶部信息栏
    setConsoleColor(FOREGROUND_GREEN | FOREGROUND_INTENSITY);
    printf("=== 贪吃蛇大作战 ===\n");
    printf("分数: %d | 长度: %d\n", game->score, game->snake_len);
    printf("使用WASD控制方向,按Q退出游戏\n\n");
    
    // 游戏区域
    for (int y = 0; y < HEIGHT; y++) {
        for (int x = 0; x < WIDTH; x++) {
            if (x == game->food.x && y == game->food.y) {
                setConsoleColor(FOREGROUND_RED | FOREGROUND_INTENSITY);
                printf("◆");
            } else {
                int is_snake = 0;
                for (int i = 0; i < game->snake_len; i++) {
                    if (game->snake[i].x == x && game->snake[i].y == y) {
                        if (i == 0) {
                            setConsoleColor(FOREGROUND_GREEN | FOREGROUND_INTENSITY);
                            printf("●");
                        } else {
                            setConsoleColor(FOREGROUND_GREEN);
                            printf("○");
                        }
                        is_snake = 1;
                        break;
                    }
                }
                if (!is_snake) {
                    setConsoleColor(FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
                    printf("·");
                }
            }
        }
        printf("\n");
    }
    
    setConsoleColor(FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
}

// 播放吃食物音效
void playEatSound() {
    Beep(523, 100);
}

// 播放游戏结束音效
void playGameOverSound() {
    Beep(392, 200);
    Beep(349, 200);
    Beep(330, 400);
}

// 播放背景音乐
void playBackgroundMusic() {
    // 使用mciSendString播放MP3文件
    // 需要准备background.mp3文件
    mciSendString("open background.mp3 alias bgm", NULL, 0, NULL);
    mciSendString("play bgm repeat", NULL, 0, NULL);
}

// 停止背景音乐
void stopBackgroundMusic() {
    mciSendString("stop bgm", NULL, 0, NULL);
    mciSendString("close bgm", NULL, 0, NULL);
}

八、编译与封装

8.1 使用GCC编译

# 编译所有源文件
gcc main.c game.c ui.c -o snake.exe -lwinmm

# 参数说明:
# -o snake.exe:指定输出文件名
# -lwinmm:链接Windows多媒体库(用于音效)

8.2 使用Visual Studio编译

  1. 创建新的C++控制台项目
  2. 将上述4个文件添加到项目中
  3. 在项目属性中,添加链接库winmm.lib
  4. 编译运行

8.3 制作安装包

使用Inno Setup等工具创建安装程序:

  1. 下载并安装Inno Setup
  2. 创建新的脚本文件
  3. 添加snake.exe和background.mp3到安装包
  4. 编译生成setup.exe

九、功能扩展建议

9.1 难度分级

// 根据分数调整游戏速度
void adjustDifficulty(GameState* game) {
    if (game->score >= 100 && game->score < 200) {
        game->speed = 150;  // 中等难度
    } else if (game->score >= 200) {
        game->speed = 100;  // 困难难度
    }
}

9.2 高分记录

// 保存最高分到文件
void saveHighScore(int score) {
    FILE* file = fopen("highscore.txt", "w");
    if (file) {
        fprintf(file, "%d", score);
        fclose(file);
    }
}

// 读取最高分
int loadHighScore() {
    FILE* file = fopen("highscore.txt", "r");
    int highscore = 0;
    if (file) {
        fscanf(file, "%d", &highscore);
        fclose(file);
    }
    return highscore;
}

9.3 游戏暂停功能

// 在handleInput函数中添加暂停功能
case 'p': case 'P':
    setConsoleColor(FOREGROUND_YELLOW);
    printf("\n游戏已暂停,按P继续...");
    while (_getch() != 'p' && _getch() != 'P') {}
    system("cls");
    break;

十、常见问题与解决方案

10.1 编译错误

问题:undefined reference to __imp_Beep等链接错误

解决:添加链接选项-lwinmm

问题:无法找到conio.h

解决:使用Windows系统,或使用MinGW编译器

10.2 运行问题

问题:控制台闪烁严重

解决:使用双缓冲技术,或减少system(“cls”)调用频率

问题:音效无法播放

解决:确保系统支持Beep函数,或使用mciSendString播放MP3文件

10.3 性能优化

  • 减少system(“cls”)调用,使用局部刷新
  • 使用双缓冲技术避免闪烁
  • 优化碰撞检测算法

十一、总结

通过本项目的学习,您已经掌握了:

  1. C语言游戏开发的基本框架
  2. Windows API控制台编程技巧
  3. 音效系统的实现方法
  4. 游戏逻辑的核心算法
  5. 项目编译与封装技术

贪吃蛇游戏虽然简单,但涵盖了游戏开发的核心要素:状态管理、输入处理、碰撞检测、界面渲染等。希望这个项目能为您进一步学习游戏开发打下坚实的基础。

注意:在实际开发中,建议使用更专业的游戏引擎(如SDL、SFML等)进行复杂游戏开发,控制台游戏更适合学习和练习基础算法。

版权声明:本文为JienDa博主的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
若内容若侵犯到您的权益,请发送邮件至:platform_service@jienda.com我们将第一时间处理!
所有资源仅限于参考和学习,版权归JienDa作者所有,更多请访问JienDa首页。

给TA赞助
共{{data.count}}人
人已赞助
前端

深入理解HTML的全局属性:id、class、data-*等

2025-12-15 16:28:33

前端

HTML+CSS基础:CSS2常用文本属性详解

2025-12-19 17:22:32

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索