代码改变世界

C++ 推箱子游戏 - 实践

2025-12-27 11:40  tlnshuju  阅读(0)  评论(0)    收藏  举报

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 + SPACESPACE + MAN
    • MAN + GOALGOAL + MAN_ON_GOAL
    • MAN_ON_GOAL + SPACEGOAL + MAN
    • MAN_ON_GOAL + GOALGOAL + MAN_ON_GOAL
  • 箱子推动

    • MAN + BOX + SPACESPACE + MAN + BOX
    • MAN + BOX + GOALSPACE + MAN + BOX_ON_GOAL
    • MAN + BOX_ON_GOAL + SPACEGOAL + 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. 最小化重绘

  • 只在状态变化时重绘
  • 清屏操作避免画面残留

可能的优化方向

  1. 关卡管理系统:支持多个关卡切换
  2. 撤销功能:记录移动历史
  3. 步数统计:记录玩家表现
  4. 更复杂的游戏元素:如传送门、炸弹等

这个设计体现了状态机思想在游戏开发中的应用,通过有限的状态组合表达丰富的游戏行为,是经典而有效的游戏架构模式。

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");
        #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;
        }

《游戏开发 世嘉新人培训教材》