贪吃蛇游戏的实现:C++ 控制台版

功能概述

  • 控制蛇的移动:使用WASD键控制蛇的移动方向。
  • 随机生成食物:蛇吃到食物后,食物会在游戏区域内随机生成。
  • 显示分数:游戏中会显示当前分数。
  • 游戏结束条件:当蛇碰到自己或走出边界时,游戏结束并显示“游戏失败”消息。
  • 清屏防止闪屏:使用光标控制函数

代码解释

游戏初始化 (Setup 函数)

  • dir = STOP;: 初始化蛇的移动方向为停止状态。
  • snake.clear();: 清空蛇的身体。
  • snake.push_back({ WIDTH / 2, HEIGHT / 2 });: 将蛇的头部放置在游戏区域的中心。
  • score = 0;: 重置分数。
  • food = { rand() % WIDTH, rand() % HEIGHT };: 随机生成食物的位置。
  • gameOver = false;: 游戏状态设置为“未结束”。

绘制游戏界面 (Draw 函数)

  • system("cls");: 清除控制台屏幕。
  • for (int i = 0; i < WIDTH + 2; i++) cout << "#";: 绘制游戏区域的边框。
  • for (int y = 0; y < HEIGHT; y++) { ... }: 绘制游戏区域的每一行。
  • if (x == food.x && y == food.y) cout << "F";: 在食物位置绘制F
  • if (x == 0) cout << "#";: 绘制左边框。
  • if (x == WIDTH - 1) cout << "#";: 绘制右边框。
  • if (gameOver) { ... }: 如果游戏结束,显示游戏结束消息。

处理用户输入 (Input 函数)

  • if (_kbhit()) { char c = _getch(); ... }: 检测并读取用户的键盘输入。
  • case 'a':: 处理向左移动。
  • case 'd':: 处理向右移动。
  • case 'w':: 处理向上移动。
  • case 's':: 处理向下移动。
  • case 'x':: 处理退出游戏。

更新游戏逻辑 (Logic 函数)

  • Point prev = snake[0];: 保存蛇头的当前坐标。
  • snake[0].x += (dir == RIGHT) - (dir == LEFT);: 根据当前方向更新蛇头的x坐标。
  • snake[0].y += (dir == DOWN) - (dir == UP);: 根据当前方向更新蛇头的y坐标。
  • if (snake[0].x >= WIDTH || snake[0].x < 0 || snake[0].y >= HEIGHT || snake[0].y < 0): 检查蛇是否走出边界。
  • if (snake[0].x == food.x && snake[0].y == food.y): 检查蛇是否吃到食物。
  • for (int i = 1; i < snake.size(); i++): 检查蛇是否碰到自己。
  • for (int i = 1; i < snake.size(); i++) { prev2 = snake[i]; snake[i] = prev; prev = prev2; }: 移动蛇的身体。

清屏防止闪屏

原理: 通过控制光标的位置来实现清屏和更新内容,而不是使用 system("cls") 这样的全屏清除方法。

实现:

  • 使用 gotoxy(int x, int y) 函数移动光标到指定位置。
  • 在每次更新屏幕时,先将光标移动到屏幕的起始位置 (0, 0) 清除内容,然后绘制新的内容。

优点: 避免了整个屏幕的清除和重绘,减少了闪烁。

效果图

源代码

编译时在连接器命令行加入以下命令

-std=c++11

 

#include <iostream>
#include <conio.h> // 用于 _getch() 读取键盘输入
#include <windows.h> // 用于 Sleep() 暂停游戏
#include <vector>
#include <ctime>
#include <cstdlib>

using namespace std;

// 定义游戏常量
const int WIDTH = 15;  // 游戏区域的宽度
const int HEIGHT = 15; // 游戏区域的高度
int score = 0;         // 玩家得分
bool gameOver = false; // 游戏是否结束的标志

// 定义方向控制的枚举
enum Direction { STOP = 0, LEFT, RIGHT, UP, DOWN };
Direction dir; // 当前蛇的移动方向

// 表示二维空间中的一个点
struct Point {
    int x, y;
};

// 存储蛇的身体段的向量
vector<Point> snake;
Point food; // 当前食物的位置

// 函数声明
void Setup();
void Draw();
void Input();
void Logic();
void gotoxy(int x, int y); // 函数声明:移动光标到指定位置

// 初始化游戏状态的函数
void Setup() {
    dir = STOP; // 设置蛇的初始移动方向为停止
    snake.clear(); // 清空蛇的身体
    snake.push_back({ WIDTH / 2, HEIGHT / 2 }); // 将蛇的头部放置在游戏区域的中心
    score = 0; // 重置分数
    food = { rand() % WIDTH, rand() % HEIGHT }; // 随机生成食物的位置
    gameOver = false; // 游戏状态设置为“未结束”
}

// 移动光标到指定位置
void gotoxy(int x, int y) {
    HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE); // 获取标准输出设备的句柄
    COORD pos = { static_cast<SHORT>(x), static_cast<SHORT>(y) }; // 设置光标位置
    SetConsoleCursorPosition(handle, pos); // 移动光标
}

// 绘制游戏界面的函数
void Draw() {
    gotoxy(0, 0); // 移动光标到控制台的左上角

    // 绘制游戏区域的上边框
    for (int i = 0; i < WIDTH + 2; i++)
        cout << "#";
    cout << endl;

    // 绘制游戏区域的每一行
    for (int y = 0; y < HEIGHT; y++) {
        for (int x = 0; x < WIDTH; x++) {
            if (x == 0)
                cout << "#"; // 绘制左边框

            if (x == food.x && y == food.y)
                cout << "F"; // 绘制食物
            else {
                bool print = false;
                // 检查当前坐标是否被蛇的身体占据
                for (auto s : snake) {
                    if (s.x == x && s.y == y) {
                        cout << "O"; // 绘制蛇的身体段
                        print = true;
                    }
                }
                if (!print)
                    cout << " "; // 空白区域
            }

            if (x == WIDTH - 1)
                cout << "#"; // 绘制右边框
        }
        cout << endl;
    }

    // 绘制游戏区域的下边框
    for (int i = 0; i < WIDTH + 2; i++)
        cout << "#";
    cout << endl;

    // 显示当前得分
    cout << "Score: " << score << endl;

    // 如果游戏结束,显示“游戏结束”消息
    if (gameOver) {
        cout << "Game Over! Press any key to exit..." << endl;
    }
}

// 处理用户输入的函数
void Input() {
    if (_kbhit()) { // 检测是否有键盘输入
        char c = _getch(); // 获取按下的键
        switch (c) {
        case 'a': // 向左移动
            if (dir != RIGHT) // 确保蛇不会反向移动
                dir = LEFT;
            break;
        case 'd': // 向右移动
            if (dir != LEFT)
                dir = RIGHT;
            break;
        case 'w': // 向上移动
            if (dir != DOWN)
                dir = UP;
            break;
        case 's': // 向下移动
            if (dir != UP)
                dir = DOWN;
            break;
        case 'x': // 退出游戏
            gameOver = true;
            break;
        }
    }
}

// 更新游戏逻辑的函数
void Logic() {
    if (gameOver) return; // 如果游戏结束,跳过逻辑更新

    // 更新蛇头的位置
    Point prev = snake[0];
    Point prev2;
    snake[0].x += (dir == RIGHT) - (dir == LEFT); // 根据方向更新蛇头的x坐标
    snake[0].y += (dir == DOWN) - (dir == UP); // 根据方向更新蛇头的y坐标

    // 检查蛇是否超出边界
    if (snake[0].x >= WIDTH || snake[0].x < 0 || snake[0].y >= HEIGHT || snake[0].y < 0) {
        gameOver = true; // 游戏结束
    }

    // 检查蛇是否碰到自己
    for (int i = 1; i < snake.size(); i++) {
        if (snake[i].x == snake[0].x && snake[i].y == snake[0].y) {
            gameOver = true; // 游戏结束
        }
    }

    // 检查蛇是否吃到食物
    if (snake[0].x == food.x && snake[0].y == food.y) {
        score++; // 增加分数
        food = { rand() % WIDTH, rand() % HEIGHT }; // 生成新的食物位置
        snake.push_back(prev); // 将新的一节添加到蛇的身体
    }

    // 移动蛇的身体
    for (int i = 1; i < snake.size(); i++) {
        prev2 = snake[i];
        snake[i] = prev;
        prev = prev2;
    }
}

// 主函数
int main() {
    srand(static_cast<unsigned>(time(0))); // 用当前时间作为随机数生成的种子
    Setup(); // 初始化游戏

    while (!gameOver) { // 游戏循环
        Draw(); // 绘制游戏界面
        Input(); // 处理用户输入
        Logic(); // 更新游戏逻辑
        Sleep(100); // 暂停100毫秒
    }

    Draw(); // 绘制结束界面显示“游戏结束”
    _getch(); // 等待用户输入后退出程序
    return 0;
}

  • 11
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值