经典复刻:C语言控制台贪吃蛇游戏代码解析与优化

本文介绍了使用C语言开发的一款贪吃蛇游戏,融入区块链技术,涉及控制台界面操作、用户输入处理、内存管理及游戏逻辑实现。展示了如何用C语言实现游戏画面、逻辑和内存清理等功能。

开发背景

这个贪吃蛇游戏是使用C语言编写的,它采用了传统的控制台界面来展示游戏画面。游戏的目标是控制一条贪吃蛇在游戏区域内移动,吃掉随机生成的食物,并不断增长身体长度,直到撞到边界或自己的身体为止。代码中引入了区块链技术的概念,让贪吃蛇游戏变得更加智能、有趣和具有挑战性。

每个函数的功能介绍

  1. gotoxy(int x, int y)
    • 功能:该函数用于在控制台上设置光标位置。
    • 用途:在绘制游戏界面时,通过该函数来确定输出位置,以便画面的刷新和更新。
void gotoxy(int x, int y) {
    COORD coord;
    coord.X = x;
    coord.Y = y;
    SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), coord);
}
  1. setTextColor(int color)
    • 功能:该函数用于设置控制台中输出文本的颜色。
    • 用途:通过设置文本颜色,使游戏画面更加丰富多彩,增加视觉体验。
void setTextColor(int color) {
    SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), color);
}
  1. clearInputBuffer():
    • 功能:该函数用于清空输入缓冲区。
    • 用途:避免由于输入过多导致缓冲区阻塞,确保程序能够正确获取用户输入。
void clearInputBuffer() {
    while (_kbhit()) {
        _getch();
    }
}
  1. init():
    • 功能:该函数用于初始化游戏的各种参数和数据结构。
    • 用途:在游戏开始前,对游戏进行初始化,包括贪吃蛇的起始位置、初始长度、食物位置、得分等。
void init() {
    gameover = false;
    direction = RIGHT;
    head = (struct Node*)malloc(sizeof(struct Node));
    // 省略部分初始化代码...
    score = 0;
    srand((unsigned int)time(NULL));
    foodX = rand() % WIDTH;
    foodY = rand() % HEIGHT;
    startNextRound = false;
}
  1. releaseSnake():
    • 功能:该函数用于释放贪吃蛇的内存空间。
    • 用途:游戏结束后,释放贪吃蛇所占用的内存,避免内存泄漏。
void releaseSnake() {
    while (head != NULL) {
        struct Node* temp = head;
        head = head->next;
        free(temp);
    }
}
  1. draw():
    • 功能:该函数用于绘制游戏画面,包括贪吃蛇、食物、得分等。
    • 用途:通过输出不同的字符和颜色,绘制贪吃蛇游戏的整体画面。
void draw() {
    // 设置控制台光标位置
    gotoxy(0, 0);

    // 画上边界
    // 省略部分绘制代码...

    // 显示得分和提示信息
    printf("得分: %d\n", score);
    printf("使用 W, A, S, D 控制移动\n");
    printf("按下空格键开始下一局\n");
}
  1. input():
    • 功能:该函数用于获取用户的输入,控制贪吃蛇的移动方向。
    • 用途:通过键盘输入,获取用户控制贪吃蛇的方向,并根据输入进行相应的操作。
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;
        }
    }
}
  1. move():

    • 功能:该函数用于处理贪吃蛇的移动逻辑,包括吃食物、增长长度和判断游戏结束等。
    • 用途:根据当前贪吃蛇的位置和用户的输入,更新贪吃蛇的位置和长度,并检查游戏是否结束。
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;
    }
}

完整代码如下

#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;
}

二 技术总结

  • 在开发这个贪吃蛇游戏时,可能遇到的问题主要包括:
  • 实现游戏逻辑和画面的控制台显示:由于C语言的控制台操作相对简单,可能需要处理游戏画面的刷新和更新,以及用户输入的响应等问题。
  • 内存管理:贪吃蛇游戏涉及到动态内存分配(malloc()函数)和释放(free()函数),正确地进行内存管理是开发过程中需要注意的关键问题。
  • 处理用户输入:获取用户的输入并根据输入控制贪吃蛇的移动方向,需要考虑输入的合法性和及时响应用户操作的问题。
  • 游戏逻辑的实现:贪吃蛇游戏涉及到复杂的逻辑判断,如食物的生成、贪吃蛇的移动、长度的增长和游戏结束条件的判断等,需要仔细设计和测试逻辑。
  • 选择C语言进行开发主要有以下理由:
  • 性能要求:贪吃蛇游戏虽然相对简单,但需要快速响应用户的操作和刷新画面,要求较高的游戏开发性能。C语言是一种高性能的编程语言,适合处理游戏开发中的计算和数据操作。
    操作系统支持:C语言是一种通用的编程语言,在几乎所有主流操作系统上都有良好的支持。这使得开发的游戏能够在不同平台上运行。
  • 控制台编程:C语言相对于其他高级语言更容易实现控制台界面的操作,特别适合控制台游戏的开发。
  • 底层控制:C语言允许开发者直接操作内存和底层硬件,对于一些需要底层控制的游戏来说,是一种合适的选择。
  • 综上所述,C语言作为一种高性能、通用且可底层控制的编程语言,对于开发这个简单的贪吃蛇游戏来说是一个合理的选择。同时,引入区块链技术作为创新点,为传统游戏增添了新的玩法和可能性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不一样的老墨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值