C++ 推箱子V1版
- [C++ 推箱子V1版 纯代码](#C++ 推箱子V1版 纯代码)
- 1.游戏截图
- 2.游戏逻辑:
-
- [🎯 游戏概述](#🎯 游戏概述)
- [🏗️ 核心设计思想](#🏗️ 核心设计思想)
-
- [1. **状态驱动设计**](#1. 状态驱动设计)
- [2. **数据与表现分离**](#2. 数据与表现分离)
- [🔄 核心游戏逻辑](#🔄 核心游戏逻辑)
-
- [1. **初始化流程**](#1. 初始化流程)
- [2. **游戏主循环**](#2. 游戏主循环)
- [3. **移动处理逻辑**](#3. 移动处理逻辑)
- [4. **碰撞检测系统**](#4. 碰撞检测系统)
- [5. **胜利条件判定**](#5. 胜利条件判定)
- [🎮 输入处理设计](#🎮 输入处理设计)
- [💡 架构优势](#💡 架构优势)
-
- [1. **模块化设计**](#1. 模块化设计)
- [2. **可扩展性**](#2. 可扩展性)
- [3. **错误处理**](#3. 错误处理)
- [🚀 性能考虑](#🚀 性能考虑)
-
- [1. **高效的状态查找**](#1. 高效的状态查找)
- [2. **最小化重绘**](#2. 最小化重绘)
- [📈 可能的优化方向](#📈 可能的优化方向)
- 3.代码
C++ 推箱子V1版 纯代码
1.游戏截图
游戏开始

游戏运行

游戏胜利

2.游戏逻辑:
🎯 游戏概述
推箱子是一款经典的益智游戏,玩家需要控制角色推动箱子到指定目标位置。本实现采用C++控制台方式,展现了清晰的游戏逻辑架构。
🏗️ 核心设计思想
1. 状态驱动设计
cpp
// 使用枚举类精确描述每个格子的状态
enum class Object {
OBJ_WALL, // 不可移动的障碍物
OBJ_SPACE, // 可通行的空地
OBJ_GOAL, // 目标位置(空)
OBJ_BOX, // 箱子(未在目标上)
OBJ_MAN, // 玩家角色
OBJ_BOX_ON_GOAL, // 箱子在目标上(已完成)
OBJ_MAN_ON_GOAL, // 玩家在目标上
};
设计理念:每个格子有且仅有一种状态,通过状态组合表达复杂游戏情形。
2. 数据与表现分离
- 数据层 :
Object数组存储游戏状态 - 表现层 :
Draw()函数负责渲染显示 - 优势:逻辑处理与界面显示完全解耦
🔄 核心游戏逻辑
1. 初始化流程
读取字符串地图 → 解析字符 → 转换为状态枚举 → 构建游戏世界
特点:使用字符串字面量定义关卡,便于修改和扩展。
2. 游戏主循环
cpp
while (游戏未通关) {
清屏并绘制当前状态
检查通关条件
获取玩家输入
根据输入更新游戏状态
}
3. 移动处理逻辑
玩家移动决策树:
检查目标位置状态:
├── 空地/目标点 → 直接移动
├── 箱子/箱子在目标上 → 检查箱子后方:
│ ├── 空地/目标点 → 推动箱子并移动
│ └── 墙/其他箱子 → 阻止移动
└── 墙 → 阻止移动
状态转换规则:
-
玩家移动:
MAN+SPACE→SPACE+MANMAN+GOAL→GOAL+MAN_ON_GOALMAN_ON_GOAL+SPACE→GOAL+MANMAN_ON_GOAL+GOAL→GOAL+MAN_ON_GOAL
-
箱子推动:
MAN+BOX+SPACE→SPACE+MAN+BOXMAN+BOX+GOAL→SPACE+MAN+BOX_ON_GOALMAN+BOX_ON_GOAL+SPACE→GOAL+MAN_ON_GOAL+BOX
4. 碰撞检测系统
cpp
// 三层边界检查:
1. 玩家移动边界检查
2. 箱子推动边界检查
3. 目标位置状态合法性检查
5. 胜利条件判定
cpp
// 简单而有效:检查场景中是否存在未完成的箱子
bool CheckClear() {
for (每个格子) {
if (找到 OBJ_BOX) return false; // 还有箱子没推到目标
}
return true; // 所有箱子都在目标上
}
🎮 输入处理设计
方向控制映射:
W→ 上移动 (dy = -1)S→ 下移动 (dy = +1)A→ 左移动 (dx = -1)D→ 右移动 (dx = +1)
设计考虑:符合玩家直觉的方向控制,便于操作。
💡 架构优势
1. 模块化设计
Initialize(): 游戏初始化Draw(): 界面渲染Update(): 游戏逻辑更新CheckClear(): 胜利条件检查
2. 可扩展性
- 易于添加新关卡(修改字符串地图)
- 状态枚举便于添加新游戏元素
- 清晰的接口便于功能扩展
3. 错误处理
- 边界检查防止越界
- 无效输入处理
- 非法移动阻止
🚀 性能考虑
1. 高效的状态查找
cpp
// 线性搜索玩家位置 - 对小地图足够高效
for (index = 0; index < width * height; index++) {
if (state[index] == OBJ_MAN || state[index] == OBJ_MAN_ON_GOAL) {
break;
}
}
2. 最小化重绘
- 只在状态变化时重绘
- 清屏操作避免画面残留
📈 可能的优化方向
- 关卡管理系统:支持多个关卡切换
- 撤销功能:记录移动历史
- 步数统计:记录玩家表现
- 更复杂的游戏元素:如传送门、炸弹等
这个设计体现了状态机思想在游戏开发中的应用,通过有限的状态组合表达丰富的游戏行为,是经典而有效的游戏架构模式。
3.代码
cpp
#include <iostream>
#include <string>
// 推箱子游戏 Caron Daltroff做出修改
// 场景宽 高
const int gStageWidth = 8;
const int gStageHight = 5;
// 场景初始化地图
const char gStageData[] = "\
########\n\
# .. p #\n\
# oo #\n\
# #\n\
########";
// 枚举元素的状态
enum class Object
{
OBJ_WALL, // 墙
OBJ_SPACE, // 空地
OBJ_GOAL, // 目标
OBJ_BOX, // 箱子
OBJ_MAN, // 玩家
OBJ_BOX_ON_GOAL, // 箱子在目标上
OBJ_MAN_ON_GOAL, // 玩家在目标上
OBJ_INVALID // 无效
};
void Initialize(Object* state, int width, int height, const char* stageData);
void Draw(const Object* state, int width, int height);
void Update(Object* state, const char input, int width, int height);
bool CheckClear(Object* state, int width, int height);
// 工具类函数,清理屏幕
void ClearScreen();
// 初始化场景
void Initialize(Object* state, int width, int height, const char* stageData)
{
const char* data = stageData;
int x = 0;
int y = 0;
while(*data != '\0')
{
Object temp;
switch (*data)
{
case '#': temp = Object::OBJ_WALL; break;
case ' ': temp = Object::OBJ_SPACE; break;
case '.': temp = Object::OBJ_GOAL; break;
case 'o': temp = Object::OBJ_BOX; break;
case 'p': temp = Object::OBJ_MAN; break;
case 'O': temp = Object::OBJ_BOX_ON_GOAL; break;
case 'P': temp = Object::OBJ_MAN_ON_GOAL; break;
case '\n': // 下一行
x = 0;
y++;
temp = Object::OBJ_INVALID;
break;
default: temp = Object::OBJ_INVALID;
}
// 初始化下一个字符
++data;
// 写入数据
if (temp != Object::OBJ_INVALID)
{
state[y * width + x] = temp;
++x;
}
}
}
// 绘制每一个元素
void Draw(const Object* state, int width, int height)
{
const char font[] = {'#','.',' ','o','O','p','P'};
Object temp = Object::OBJ_INVALID;
for(int y = 0; y < height; ++y)
{
for(int x = 0; x < width; ++x)
{
// 取出来,绘制
temp = state[y * width + x];
switch (temp)
{
case Object::OBJ_WALL: std::cout << "#"; break;
case Object::OBJ_SPACE: std::cout << " "; break;
case Object::OBJ_GOAL: std::cout << "."; break;
case Object::OBJ_BOX: std::cout << "o"; break;
case Object::OBJ_MAN: std::cout << "p"; break;
case Object::OBJ_BOX_ON_GOAL: std::cout << "O"; break;
case Object::OBJ_MAN_ON_GOAL: std::cout << "P"; break;
}
}
std::cout << std::endl;
}
}
// 更新每一次移动
void Update(Object* state, char input, int width, int height)
{
// 移动的变化
int dx = 0;
int dy = 0;
// 获取用户输入
switch (input)
{
case 'a': dx = -1; break;
case 'd': dx = 1; break;
case 'w': dy = -1; break;
case 's': dy = 1; break;
case 'e':exit(0);
}
// 查询玩家位置
int index = -1;
for (index = 0; index < width * height; index++)
{
if (state[index] == Object::OBJ_MAN || state[index] == Object::OBJ_MAN_ON_GOAL)
{
break;
}
}
// 计算当前位置坐标
int posX = index % width;
int posY = index / width;
// 移动后的坐标
int targetPosX = posX + dx;
int targetPosY = posY + dy;
// 计算移动后的坐标是否合理, 即边界判断
if (targetPosX < 0 || targetPosY < 0 || targetPosX >= width || targetPosY >= height)
{
return;
}
// 正式移动 玩家移动和箱子移动
// 玩家当前的位置
int playerPosition = posY * width + posX;
int targetPosition = targetPosY * width + targetPosX;
// 排列组合
// 箱子玩家都可以移动
// MAN/MAN_ON_GOAL BOX/BOX_ON_GOAL WALL 不能
// MAN/MAN_ON_GOAL BOX/BOX_ON_GOAL SPACE 能
// MAN/MAN_ON_GOAL BOX/BOX_ON_GOAL GOAL 能
// MAN/MAN_ON_GOAL BOX/BOX_ON_GOAL BOX_ON_GOAL 不能
// MAN/MAN_ON_GOAL BOX/BOX_ON_GOAL BOX 不能
// 只有玩家可以移动
// MAN/MAN_ON_GOAL SPACE WALL/BOX/BOX_ON_GOAL 能
// MAN/MAN_ON_GOAL GOAL WALL/BOX/BOX_ON_GOAL 能
// MAN/MAN_ON_GOAL BOX WALL/BOX/BOX_ON_GOAL 不能
// MAN/MAN_ON_GOAL WALL WALL/BOX/BOX_ON_GOAL 不能
// MAN/MAN_ON_GOAL BOX_ON_GOAL WALL/BOX/BOX_ON_GOAL 不能
// 人移动,但是箱子不移动
if (state[targetPosition] == Object::OBJ_SPACE || (state[targetPosition] == Object::OBJ_GOAL))
{
// 移动到目标位置的时候,玩家状态改变
state[targetPosition] = (state[targetPosition] == Object::OBJ_GOAL)? Object::OBJ_MAN_ON_GOAL: Object::OBJ_MAN;
// 更改移动前玩家所在的状态
state[playerPosition] = (state[playerPosition] == Object::OBJ_MAN_ON_GOAL)? Object::OBJ_GOAL: Object::OBJ_SPACE;
} // 玩家和箱子都可以移动
else if (state[targetPosition] == Object::OBJ_BOX || state[targetPosition] == Object::OBJ_BOX_ON_GOAL)
{
// 下下个位置范围检查
int tx2 = targetPosX + dx;
int ty2 = targetPosY + dy;
if (tx2 < 0 || ty2 < 0 || tx2 >= width || ty2 >= height)
{
return;
}
// 检查再下一个位置的状态: Wall 不可移动box,space 可以移动box, goal 可移动box并改变状态
int target2Position = (targetPosY + dy) * width + (targetPosX + dx);
if (state[target2Position] == Object::OBJ_SPACE || state[target2Position] == Object::OBJ_GOAL)
{
// 移动box到下一个位置
state[target2Position] = state[target2Position] == Object::OBJ_GOAL ? Object::OBJ_BOX_ON_GOAL : Object::OBJ_BOX;
// 移动玩家
state[targetPosition] = state[targetPosition] == Object::OBJ_BOX_ON_GOAL ? Object::OBJ_MAN_ON_GOAL : Object::OBJ_MAN;
// 修改玩家上一次的位置
state[playerPosition] = state[playerPosition] == Object::OBJ_MAN_ON_GOAL ? Object::OBJ_GOAL : Object::OBJ_SPACE;
}
}
}
// 判断是否游戏结束
bool CheckClear(Object* state, int width, int height)
{
// 如何判断,没有BOX的就行了
for (int index = 0; index < width * height; ++index)
{
if (state[index] == Object::OBJ_BOX)
{
return false;
}
}
return true;
}
// 清理屏幕
void ClearScreen()
{
#if defined(_WIN32)
system("cls");
#else
system("clear");
#endif
}
int main()
{
// 初始化场景
Object* state = new Object[gStageWidth * gStageHight];
Initialize(state, gStageWidth, gStageHight, gStageData);
char input;
while(true)
{
// 绘制
Draw(state, gStageWidth, gStageHight);
// 游戏结束判断
if (CheckClear(state, gStageWidth, gStageHight))
{
// 游戏结束
break;
}
// 没有通关,继续接收玩家移动操作
std::cout << "press the key 'w' 'a' 's' 'd' to control the pawn to move." << std::endl;
std::cin >> input;
// 更新移动后的结果
Update(state, input, gStageWidth, gStageHight);
ClearScreen();
}
// 通关
std::cout << "Congratulation! You Won." << std::endl;
std::cout << "press E to exit game" << std::endl;
std::cin >> input;
if (input == 'e')
{
delete[] state;
state = nullptr;
exit(0);
}
return 0;
}