C++模块化项目之贪吃蛇


以下是一个基于C++多文件编程的Windows下贪吃蛇小项目实现。项目使用控制台进行显示,并包含以下功能:

  1. 蛇的移动。
  2. 食物的随机生成。
  3. 撞墙或自撞的判定。

一、项目结构

SnakeGame/
├── SnakeGame.sln           # Visual Studio 工程文件
├── main.cpp                # 主程序入口
├── Snake.h                 # 贪吃蛇类声明
├── Snake.cpp               # 贪吃蛇类实现
├── Game.h                  # 游戏逻辑声明
├── Game.cpp                # 游戏逻辑实现
└── Utils.h                 # 工具函数(如随机数生成)

二、代码实现

1. main.cpp

#include "Game.h"

int main() {
    Game game;
    game.start();
    return 0;
}


2. Snake.h

#pragma once
#ifndef SNAKE_H
#define SNAKE_H

#include <vector>
#include <utility> // for std::pair

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

class Snake {
private:
    std::vector<std::pair<int, int>> body; // 蛇身坐标
    Direction direction;                   // 当前方向

public:
    Snake(int startX, int startY);

    void move();                                   // 移动蛇
    void grow();                                   // 蛇变长
    bool checkCollision(int width, int height) const; // 检测撞墙或自撞
    const std::vector<std::pair<int, int>>& getBody() const;
    void setDirection(Direction newDirection);    // 设置方向
    Direction getDirection() const;               // 获取方向
};

#endif


3. Snake.cpp

#include "Snake.h"

Snake::Snake(int startX, int startY) : direction(RIGHT) {
    body.push_back({ startX, startY }); // 蛇头初始位置
}

void Snake::move() {
    auto head = body.front(); // 当前头部位置
    switch (direction) {
    case UP:    head.second--; break;
    case DOWN:  head.second++; break;
    case LEFT:  head.first--; break;
    case RIGHT: head.first++; break;
    }
    body.insert(body.begin(), head); // 添加新头部
    body.pop_back();                 // 移除尾部
}

void Snake::grow() {
    body.push_back(body.back()); // 尾部增加一节

}

bool Snake::checkCollision(int width, int height) const {
    auto head = body.front();

    // 撞墙检测
    if (head.first < 1 || head.first >= width - 1 || head.second < 1 || head.second >= height - 1)
        return true;

    // 自撞检测(检查蛇头是否与蛇身的其他部分重叠)
    for (size_t i = 1; i < body.size() - 1; ++i) {
        if (body[i] == head) return true;
    }

    return false;
}

const std::vector<std::pair<int, int>>& Snake::getBody() const {
    return body;
}

void Snake::setDirection(Direction newDirection) {
    // 防止蛇反向移动
    if ((direction == UP && newDirection != DOWN) ||
        (direction == DOWN && newDirection != UP) ||
        (direction == LEFT && newDirection != RIGHT) ||
        (direction == RIGHT && newDirection != LEFT)) {
        direction = newDirection;
    }
}

Direction Snake::getDirection() const {
    return direction;
}


4. Game.h

#pragma once
#ifndef GAME_H
#define GAME_H

#include "Snake.h"

class Game {
private:
    Snake snake;
    std::pair<int, int> food;  // 食物位置
    int width, height;         // 游戏区域大小
    bool isRunning;            // 游戏状态

    void generateFood();       // 随机生成食物
    void render();             // 渲染游戏画面
    void handleInput();        // 处理用户输入

public:
    Game();
    void start();              // 启动游戏
};

#endif


5. Game.cpp

#include <iostream>
#include <conio.h>  // 用于 _kbhit 和 _getch
#include <windows.h> // 用于 Sleep
#include "Game.h"
#include "Utils.h"

Game::Game() : snake(5, 5), width(20), height(15), isRunning(true) {
    generateFood();
}

void Game::generateFood() {
    while (true) {
        int x = randInt(1, width - 2);
        int y = randInt(1, height - 2);

        // 检查生成的食物是否与蛇身重叠
        bool isOnSnake = false;
        for (const auto& segment : snake.getBody()) {
            if (segment.first == x && segment.second == y) {
                isOnSnake = true;
                break;
            }
        }

        // 如果食物位置不在蛇身上,生成食物
        if (!isOnSnake) {
            food = { x, y };
            break;
        }
    }
}

void Game::render() {
    system("cls");
    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            if (y == 0 || y == height - 1 || x == 0 || x == width - 1) {
                std::cout << "+"; // 边框
            }
            else if (std::make_pair(x, y) == food) {
                std::cout << "@"; // 食物
            }
            else {
                bool isBody = false;
                for (const auto& segment : snake.getBody()) {
                    if (segment == std::make_pair(x, y)) {
                        std::cout << "#"; // 蛇身
                        isBody = true;
                        break;
                    }
                }
                if (!isBody) std::cout << " ";
            }
        }
        std::cout << "\n";
    }
}

void Game::handleInput() {
    if (_kbhit()) {
        switch (_getch()) {
        case 'w': snake.setDirection(UP); break;
        case 's': snake.setDirection(DOWN); break;
        case 'a': snake.setDirection(LEFT); break;
        case 'd': snake.setDirection(RIGHT); break;
        }
    }
}

void Game::start() {
    while (isRunning) {
        render();
        handleInput();
        snake.move();

        // 检查是否吃到食物
        if (snake.getBody().front() == food) {
            snake.grow();
            generateFood(); // 生成新的食物
        }

        // 检查是否发生碰撞
        if (snake.checkCollision(width, height)) {
            isRunning = false; // 结束游戏
        }

        Sleep(150); // 控制游戏速度
    }

    std::cout << "Game Over!\n";
}


6. Utils.h

#pragma once
#ifndef UTILS_H
#define UTILS_H

#include <cstdlib>
#include <ctime>

inline int randInt(int min, int max) {
    static bool initialized = false;
    if (!initialized) {
        srand(static_cast<unsigned>(time(nullptr)));
        initialized = true;
    }
    return rand() % (max - min + 1) + min;
}

#endif


三、运行效果

  1. 控制台显示蛇在20x20的区域内移动。
  2. 使用 WASD 控制蛇的方向。
  3. 游戏结束时,显示 Game Over!

在这里插入图片描述
项目代码放在github仓库中,需要可以自取。
https://github.com/kerors/SnakeGame

可以在 Visual Studio 中创建一个新的 C++ 工程,将上述文件添加到项目中并运行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

人间酒中仙

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

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

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

打赏作者

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

抵扣说明:

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

余额充值