C++模块化项目之贪吃蛇
以下是一个基于C++多文件编程的Windows下贪吃蛇小项目实现。项目使用控制台进行显示,并包含以下功能:
- 蛇的移动。
- 食物的随机生成。
- 撞墙或自撞的判定。
一、项目结构
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
三、运行效果
- 控制台显示蛇在20x20的区域内移动。
- 使用
WASD
控制蛇的方向。 - 游戏结束时,显示
Game Over!
。
项目代码放在github仓库中,需要可以自取。
https://github.com/kerors/SnakeGame
可以在 Visual Studio 中创建一个新的 C++ 工程,将上述文件添加到项目中并运行。