Linux C++ 贪吃蛇游戏 -- 方向键控制蛇移动

1. 代码

复制代码
#include <iostream>
#include <ncurses.h>
#include <vector>
#include <cstdlib>
#include <ctime>
#include <thread>

using namespace std;

// 定义方向
enum class Direction { UP, DOWN, LEFT, RIGHT };

class SnakeGame {
public:
    SnakeGame(int width, int height) : width(width), height(height), score(0), gameover(false) {
        srand(time(nullptr));
        initscr(); // 初始化终端
        keypad(stdscr, true); // 启用键盘输入
        noecho(); // 关闭回显
        nodelay(stdscr, true); // 非阻塞模式
        curs_set(0); // 隐藏光标
        getmaxyx(stdscr, max_y, max_x);
        window = newwin(height, width, (max_y - height) / 2, (max_x - width) / 2);
        box(window, 0, 0); // 绘制窗口边框
        refresh();
        wrefresh(window);
        generateFood();
        snake.push_back(make_pair(height / 2, width / 2)); // 初始化贪吃蛇位置
        drawSnake();
    }

    ~SnakeGame() {
        delwin(window);
        endwin();
    }

    void run() {
        // 游戏循环
        while (!gameover) {
            int ch = getch();
            handleInput(ch);
            moveSnake();
            checkCollision();
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
        showGameOver();
    }

private:
    int width, height;
    int score;
    bool gameover;
    int max_x, max_y;
    WINDOW *window;
    vector<pair<int, int>> snake;
    pair<int, int> food;
    Direction direction = Direction::RIGHT;

    void generateFood() {
        do {
            food.first = rand() % (height - 2) + 1;
            food.second = rand() % (width - 2) + 1;
        } while (isSnakeCell(food.first, food.second));
        mvwaddch(window, food.first, food.second, '@');
        refresh();
        wrefresh(window);
    }

    void drawSnake() {
        for (const auto& cell : snake) {
            mvwaddch(window, cell.first, cell.second, '#');
        }
        refresh();
        wrefresh(window);
    }

    void moveSnake() {
        pair<int, int> head = snake.front();
        pair<int, int> newHead = head;

        switch (direction) {
            case Direction::UP:
                newHead.first--;
                break;
            case Direction::DOWN:
                newHead.first++;
                break;
            case Direction::LEFT:
                newHead.second--;
                break;
            case Direction::RIGHT:
                newHead.second++;
                break;
        }

        snake.insert(snake.begin(), newHead);
        mvwaddch(window, newHead.first, newHead.second, '#');
        refresh();
        wrefresh(window);

        if (newHead != food) {
            mvwaddch(window, snake.back().first, snake.back().second, ' ');
            snake.pop_back();
        } else {
            score++;
            generateFood();
        }
    }

    void handleInput(int ch) {
        switch (ch) {
            case KEY_UP:
                if (direction != Direction::DOWN) {
                    direction = Direction::UP;
                }
                break;
            case KEY_DOWN:
                if (direction != Direction::UP) {
                    direction = Direction::DOWN;
                }
                break;
            case KEY_LEFT:
                if (direction != Direction::RIGHT) {
                    direction = Direction::LEFT;
                }
                break;
            case KEY_RIGHT:
                if (direction != Direction::LEFT) {
                    direction = Direction::RIGHT;
                }
                break;
        }
    }

    void checkCollision() {
        pair<int, int> head = snake.front();

        // 检查是否撞墙
        if (head.first == 0 || head.first == height - 1 || head.second == 0 || head.second == width - 1) {
            gameover = true;
            return;
        }

        // 检查是否撞到自己的身体
        for (size_t i = 1; i < snake.size(); i++) {
            if (snake[i] == head) {
                gameover = true;
                return;
            }
        }
    }

    void showGameOver() {
        clear();
        string gameOverText = "Game Over!";
        mvprintw(max_y / 2, (max_x - gameOverText.length()) / 2, gameOverText.c_str());
        string scoreText = "Score: " + to_string(score);
        mvprintw(max_y / 2 + 1, (max_x - scoreText.length()) / 2, scoreText.c_str());
        refresh();
        getch();
    }

    bool isSnakeCell(int row, int col) {
        for (const auto& cell : snake) {
            if (cell.first == row && cell.second == col) {
                return true;
            }
        }
        return false;
    }
};

int main() {
    SnakeGame game(40, 20);
    game.run();
    return 0;
}

2. 编译命令

复制代码
alfred@ubuntu:~/c_learning/d_cplusplus_learning$ g++ -std=c++11 snake.cpp -o snake -lncurses

3. 运行游戏,使用方向键 ↑ ↓ ← → 上下左右控制蛇移动

复制代码
alfred@ubuntu:~/c_learning/d_cplusplus_learning$ ./snake

4. 调整蛇移动的速度

修改延时时间100,单位为ms,数字越小,移动越快,修改完后重新编译,运行

复制代码
std::this_thread::sleep_for(std::chrono::milliseconds(100));
相关推荐
晚风叙码4 分钟前
Linux 进程概念(上):从冯诺依曼到进程调度
linux·运维·计算机外设
邪修king18 分钟前
Re:Linux系统篇(三十八):进程间通信ChapterSystem V 信号量深度解析|同步互斥、内核源码、C 指针多态、实战代码
java·linux·c语言
坤坤子吖19 分钟前
Linux网络编程基础:从 MAC 到 IP,理解网络数据是怎么传输的
linux·网络·笔记·学习
码小猿的CPP工坊26 分钟前
C++跨平台开发之基于wxWidgets开发GUI程序简介-002
开发语言·c++
chenbingjie_c36 分钟前
C++ 继承全解析(从三种继承方式到菱形继承与内存布局)
开发语言·c++
火22537 分钟前
# C++ 微服务即时通讯系统(四):MySQL、Redis 与 Elasticsearch——数据分工与一致性
c++·mysql·微服务
琥珀.9 小时前
mysql中json数组转列表查询
linux·运维·服务器
dawnsky.liu10 小时前
在 RHEL 中使用 goose 实现远程主机管理
linux·运维·ai
xiaoye-duck11 小时前
《C++ 工具库实战》gflags 命令行参数解析库详解
c++·第三方库
狂奔的sherry11 小时前
C++ 宏生成访问函数的代价:访问权限、调试和类型检查
c++·宏·编译·预处理器·类型检查