C++ 推箱子游戏 - 实践
2025-12-27 11:40 tlnshuju 阅读(0) 评论(0) 收藏 举报C++ 推箱子V1版
- C++ 推箱子V1版 纯代码
- 1.游戏截图
- 2.游戏逻辑:
- 游戏概述
- ️ 核心设计思想
- 1. **状态驱动设计**
- 2. **数据与表现分离**
- 核心游戏逻辑
- 1. **初始化流程**
- 2. **游戏主循环**
- 3. **移动处理逻辑**
- **玩家移动决策树**:
- **状态转换规则**:
- 4. **碰撞检测系统**
- 5. **胜利条件判定**
- 输入处理设计
- 方向控制映射:
- 架构优势
- 1. **模块化设计**
- 2. **可扩展性**
- 3. **错误处理**
- 性能考虑
- 1. **高效的状态查找**
- 2. **最小化重绘**
- 可能的优化方向
- 3.代码
C++ 推箱子V1版 纯代码
1.游戏截图
游戏开始
游戏运行
游戏胜利
2.游戏逻辑:
游戏概述
推箱子是一款经典的益智游戏,玩家需要控制角色推动箱子到指定目标位置。本实现采用C++控制台方式,展现了清晰的游戏逻辑架构。
️ 核心设计思想
1. 状态驱动设计
// 使用枚举类精确描述每个格子的状态
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. 游戏主循环
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. 碰撞检测系统
// 三层边界检查:
1. 玩家移动边界检查
2. 箱子推动边界检查
3. 目标位置状态合法性检查
5. 胜利条件判定
// 简单而有效:检查场景中是否存在未完成的箱子
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. 高效的状态查找
// 线性搜索玩家位置 - 对小地图足够高效
for (index = 0; index < width * height; index++) {
if (state[index] == OBJ_MAN || state[index] == OBJ_MAN_ON_GOAL) {
break;
}
}
2. 最小化重绘
- 只在状态变化时重绘
- 清屏操作避免画面残留
可能的优化方向
- 关卡管理系统:支持多个关卡切换
- 撤销功能:记录移动历史
- 步数统计:记录玩家表现
- 更复杂的游戏元素:如传送门、炸弹等
这个设计体现了状态机思想在游戏开发中的应用,通过有限的状态组合表达丰富的游戏行为,是经典而有效的游戏架构模式。
3.代码
#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");#elsesystem("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;}
《游戏开发 世嘉新人培训教材》