C++ 推箱子游戏

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 + 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. 碰撞检测系统

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. 最小化重绘

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

📈 可能的优化方向

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

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

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; 
}

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

相关推荐
JienDa1 小时前
JienDa聊PHP:PHP从入门到精通—PHP开发入门:从环境搭建到第一个程序
开发语言·php
Data_agent1 小时前
1688获得1688公司档案信息API,python请求示例
开发语言·数据库·python
qq_336313931 小时前
java基础-排序算法
java·开发语言·排序算法
路过君_P2 小时前
C++ 算法题解:迷宫寻路
c++·算法·深度优先
止观止2 小时前
告别“祖传C++”:开启你的现代C++之旅
c++·c++11·c++20·编程思想·现代c++
周杰伦fans2 小时前
C#中OrderByDescending 是LINQ扩展方法之一
开发语言·c#·linq
顾安r2 小时前
11.29 脚本游戏 单页面格斗游戏模板
前端·javascript·css·游戏·virtualenv
罗湖老棍子2 小时前
二维vector完全指南1:从定义到增删改查
数据结构·c++·算法·stl