经典贪吃蛇游戏 - 用 C 语言实现控制台版

在本篇博客中,我们将一起来实现经典的贪吃蛇游戏,使用 C 语言编写,并在控制台中运行。这个小游戏会让你回忆起童年的经典游戏体验。我们将从游戏的初始化开始,逐步实现游戏的各个功能,包括蛇的移动、食物的生成、得分的计算等等。最后,我们将整个游戏逻辑串起来,形成一个完整的贪吃蛇游戏。

初始化游戏

首先,让我们来初始化游戏的一些必要组件。我们会使用 Windows API 来实现控制台窗口大小和颜色的设置,以及键盘输入的获取。以下是初始化函数的代码:

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <conio.h>
#include <windows.h>

#define WIDTH 40
#define HEIGHT 20
#define INITIAL_LENGTH 3

enum Direction {
    UP,
    DOWN,
    LEFT,
    RIGHT
};

struct Node {
    int x;
    int y;
    struct Node* next;
};

// ...(省略部分代码)...

int main() {
    system("mode con cols=50 lines=30"); // 设置控制台窗口大小
    system("title 经典贪吃蛇游戏"); // 设置控制台窗口标题
    init();

    // ...(省略部分代码)...

    releaseSnake();
    return 0;
}

在初始化函数 init() 中,我们初始化了游戏的一些状态,包括蛇的初始位置、食物的位置、得分等。同时,我们使用 srand() 函数来设置随机数种子,以便在每次游戏开始时都能生成不同的食物位置。

绘制游戏界面

游戏界面的绘制是一个重要的部分。我们会使用 gotoxy() 函数来设置光标位置,以及 setTextColor() 函数来设置文本颜色。以下是绘制函数的代码:

c 复制代码
void gotoxy(int x, int y) {
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

void setTextColor(int color) {
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}

// ...(省略部分代码)...

void draw() {
    // 设置控制台光标位置
    gotoxy(0, 0);

    // 画上边界
    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++) {
            // ...(省略部分代码)...
        }
        printf("|\n");
    }

    // 画下边界
    for (int i = 0; i < WIDTH + 2; i++) {
        printf("=");
    }
    printf("\n");

    // 显示得分和提示信息
    printf("得分: %d\n", score);
    printf("使用 W, A, S, D 控制移动\n");
    printf("按下空格键开始下一局\n");
}

在绘制函数中,我们使用了循环来画出游戏界面的各个部分,包括边界、蛇头、食物、蛇身、得分和提示信息。不同的元素使用不同的颜色来区分,以增强游戏的可视化效果。

处理用户输入

用户输入是控制游戏进行的关键。我们使用 _kbhit()_getch() 函数来获取键盘输入,并根据用户的操作来改变蛇的移动方向。以下是处理输入的代码:

c 复制代码
void clearInputBuffer() {
    while (_kbhit()) {
        _getch();
    }
}

// ...(省略部分代码)...

void input() {
    if (_kbhit()) {
        int key = _getch();
        clearInputBuffer(); // 清空输入缓冲区
        switch (key) {
            // ...(省略部分代码)...
        }
    }
}

在处理输入函数中,我们使用了 switch 语句来根据不同的按键进行相应的操作,包括控制蛇的移动方向、结束游戏等。

移动蛇和游戏逻辑

游戏逻辑的核心在于蛇的移动和吃食物。我们使用一个链表来表示蛇的身体,每个节点表示一个身体部位。以下是移动函数和游戏逻辑的代码:

c 复制代码
// ...(省略部分代码)...

void move() {
    // 移动蛇头
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->next = NULL;
    switch (direction) {
        // ...(省略部分代码)...
    }

    // ...(省略部分代码)...

    // 添加新节点到头部
    newNode->next = head;
    head = newNode;

    // 检查是否撞到自己的身体
    struct Node* current = head->next;
    while (current != NULL) {
        if (current->x == head->x && current->y == head->y) {
            gameover = true;
            break;
        }
        current = current->next;
    }
}

// ...(省略部分代码)...

int main() {
    // ...(省略部分代码)...

    while (!gameover) {
        draw();
        input();
        if (gameover && !startNextRound) {
            // ...(省略部分代码)...
        }
        if (!gameover) {
            move();
            Sleep(100); // 控制游戏速度
        }
       

 if (startNextRound) {
            // ...(省略部分代码)...
        }
    }

    // ...(省略部分代码)...
}

在移动函数中,我们首先根据当前的移动方向计算蛇头的新位置,并创建一个新的节点来表示蛇头。然后,我们检查是否吃到了食物,如果吃到了就增加得分并重新生成食物。如果没有吃到食物,我们删除蛇尾的节点,实现蛇的移动。最后,我们检查蛇是否撞到了自己的身体,如果是则游戏结束。

完整游戏逻辑

main() 函数中,我们将上述的各个功能整合在一起,形成了一个完整的游戏逻辑。游戏会不断地循环进行,直到游戏结束或玩家主动退出。游戏结束时,会显示最终得分,并等待用户按下任意键退出。

总结

通过本篇博客,我们成功地实现了一个简单的控制台版贪吃蛇游戏。从游戏的初始化、绘制界面、处理用户输入,到蛇的移动和游戏逻辑,我们逐步构建了一个完整的游戏框架。这个小游戏不仅可以带我们回忆童年的游戏经历,还可以锻炼我们编程的逻辑思维和综合能力。希望本篇博客对你有所帮助,如果你有任何问题或建议,欢迎在评论区留言!

完整代码

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <conio.h>
#include <windows.h>

#define WIDTH 40
#define HEIGHT 20
#define INITIAL_LENGTH 3

enum Direction {
    UP,
    DOWN,
    LEFT,
    RIGHT
};

struct Node {
    int x;
    int y;
    struct Node* next;
};

enum Direction direction;
struct Node* head;
struct Node* tail;
int foodX, foodY;
int score;
bool gameover;
bool startNextRound; // 用于标记是否开始下一局游戏

void gotoxy(int x, int y) {
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}

void setTextColor(int color) {
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}

void clearInputBuffer() {
    while (_kbhit()) {
        _getch();
    }
}

void init() {
    gameover = false;
    direction = RIGHT;
    head = (struct Node*)malloc(sizeof(struct Node));
    head->x = WIDTH / 2;
    head->y = HEIGHT / 2;
    head->next = NULL;
    tail = head;
    for (int i = 1; i < INITIAL_LENGTH; i++) {
        struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
        newNode->x = head->x - i;
        newNode->y = head->y;
        newNode->next = NULL;
        tail->next = newNode;
        tail = newNode;
    }
    score = 0;
    srand((unsigned int)time(NULL));
    foodX = rand() % WIDTH;
    foodY = rand() % HEIGHT;
    startNextRound = false;
}

void releaseSnake() {
    while (head != NULL) {
        struct Node* temp = head;
        head = head->next;
        free(temp);
    }
}

void draw() {
    // 设置控制台光标位置
    gotoxy(0, 0);

    // 画上边界
    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 == head->y && j == head->x) {
                setTextColor(12); // 颜色为12,红色
                printf("@"); // 蛇头
                setTextColor(15); // 颜色为15,白色
            } else if (i == foodY && j == foodX) {
                setTextColor(14); // 颜色为14,黄色
                printf("@"); // 食物
                setTextColor(15); // 颜色为15,白色
            } else {
                struct Node* current = head->next;
                bool printed = false;
                while (current != NULL) {
                    if (current->x == j && current->y == i) {
                        setTextColor(10); // 颜色为10,绿色
                        printf("#"); // 蛇身
                        setTextColor(15); // 颜色为15,白色
                        printed = true;
                        break;
                    }
                    current = current->next;
                }
                if (!printed) {
                    printf(" ");
                }
            }
        }
        printf("|\n");
    }

    // 画下边界
    for (int i = 0; i < WIDTH + 2; i++) {
        printf("=");
    }
    printf("\n");

    // 显示得分和提示信息
    printf("得分: %d\n", score);
    printf("使用 W, A, S, D 控制移动\n");
    printf("按下空格键开始下一局\n");
}

void input() {
    if (_kbhit()) {
        int key = _getch();
        clearInputBuffer(); // 清空输入缓冲区
        switch (key) {
        case 'w':
            direction = UP;
            break;
        case 's':
            direction = DOWN;
            break;
        case 'a':
            direction = LEFT;
            break;
        case 'd':
            direction = RIGHT;
            break;
        case 'x':
            gameover = true;
            break;
        case ' ':
            if (gameover) {
                startNextRound = true;
            }
            break;
        default:
            break;
        }
    }
}

void move() {
    // 移动蛇头
    struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
    newNode->next = NULL;
    switch (direction) {
    case UP:
        newNode->x = head->x;
        newNode->y = (head->y - 1 + HEIGHT) % HEIGHT; // 从另一面出现
        break;
    case DOWN:
        newNode->x = head->x;
        newNode->y = (head->y + 1) % HEIGHT; // 从另一面出现
        break;
    case LEFT:
        newNode->x = (head->x - 1 + WIDTH) % WIDTH; // 从另一面出现
        newNode->y = head->y;
        break;
    case RIGHT:
        newNode->x = (head->x + 1) % WIDTH; // 从另一面出现
        newNode->y = head->y;
        break;
    default:
        break;
    }

    // 检查是否吃到食物
    if (newNode->x == foodX && newNode->y == foodY) {
        score++;
        foodX = rand() % WIDTH;
        foodY = rand() % HEIGHT;
    }
    else {
        // 删除蛇的尾部节点
        struct Node* temp = head;
        while (temp->next->next != NULL) {
            temp = temp->next;
        }
        free(temp->next);
        temp->next = NULL;
    }

    // 添加新节点到头部
    newNode->next = head;
    head = newNode;

    // 检查是否撞到自己的身体
    struct Node* current = head->next;
    while (current != NULL) {
        if (current->x == head->x && current->y == head->y) {
            gameover = true;
            break;
        }
        current = current->next;
    }
}

int main() {
    system("mode con cols=50 lines=30"); // 设置控制台窗口大小
    system("title 经典贪吃蛇游戏"); // 设置控制台窗口标题
    init();

    while (!gameover) {
        draw();
        input();
        if (gameover && !startNextRound) {
            setTextColor(12); // 颜色为12,红色
            gotoxy(WIDTH / 2 - 4, HEIGHT / 2);
            printf("游戏结束");
            setTextColor(15); // 颜色为15,白色
            gotoxy(WIDTH / 2 - 6, HEIGHT / 2 + 1);
            printf("最终得分:%d", score);
        }
        if (!gameover) {
            move();
            Sleep(100); // 控制游戏速度
        }
        if (startNextRound) {
            releaseSnake();
            init();
            gameover = false;
            startNextRound = false;
        }
    }

    setTextColor(15); // 颜色为15,白色
    gotoxy(WIDTH / 2 - 4, HEIGHT / 2 + 2);
    printf("按下任意键退出");
    _getch();

    releaseSnake();
    return 0;
}
相关推荐
Lizhihao_2 分钟前
JAVA-队列
java·开发语言
远望清一色20 分钟前
基于MATLAB边缘检测博文
开发语言·算法·matlab
何曾参静谧28 分钟前
「Py」Python基础篇 之 Python都可以做哪些自动化?
开发语言·python·自动化
Prejudices32 分钟前
C++如何调用Python脚本
开发语言·c++·python
单音GG35 分钟前
推荐一个基于协程的C++(lua)游戏服务器
服务器·c++·游戏·lua
我狠狠地刷刷刷刷刷1 小时前
中文分词模拟器
开发语言·python·算法
wyh要好好学习1 小时前
C# WPF 记录DataGrid的表头顺序,下次打开界面时应用到表格中
开发语言·c#·wpf
AitTech1 小时前
C#实现:电脑系统信息的全面获取与监控
开发语言·c#
qing_0406031 小时前
C++——多态
开发语言·c++·多态
孙同学_1 小时前
【C++】—掌握STL vector 类:“Vector简介:动态数组的高效应用”
开发语言·c++