提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
在游戏开发中,设计模式被广泛用于解决代码复用性、可维护性、扩展性和性能优化等问题。以下是游戏开发中常见的几种设计模式及其典型应用场景和作用:
一、单例模式(Singleton)
作用:确保一个类只有一个实例,并提供全局访问点。
-
应用场景:
-
全局管理器(如 GameManager、AudioManager、SaveManager)。
共享资源池(如配置表、日志系统)。
注意:需谨慎使用,过度依赖单例会提高代码耦合度。
class GameManager {
private:
static GameManager* instance;
GameManager() {} // 私有构造函数
public:
// 删除拷贝构造函数和赋值操作符
GameManager(const GameManager&) = delete;
void operator=(const GameManager&) = delete;
//确保全局只有一个实例
static GameManager* getInstance() {
if (!instance) {
instance = new GameManager();
}
return instance;
}
void loadLevel(int level) {
// 加载关卡逻辑
}
};
// 静态成员初始化
GameManager* GameManager::instance = nullptr;
// 使用示例:
GameManager::getInstance()->loadLevel(1);
二、观察者模式(Observer)
作用:定义对象间的一对多依赖关系,当一个对象状态变化时,自动通知所有依赖它的对象。
-
应用场景:
-
事件系统(如成就系统、UI 更新、技能触发)。
游戏内广播(如玩家死亡、任务完成、资源变动)。
优势:解耦事件发布者和订阅者,支持动态添加/移除监听。
#include <vector>
#include <functional>
// 事件发布者
class Player {
private:
int health;
std::vector<std::function<void(int)>> healthListeners;
public:
void addHealthListener(const std::function<void(int)>& listener) {
healthListeners.push_back(listener);
}
void setHealth(int value) {
health = value;
for (auto& listener : healthListeners) {
listener(health); // 通知所有监听者
}
}
};
// 事件订阅者(UI)
class HealthUI {
public:
void updateHealthDisplay(int health) {
std::cout << "Health: " << health << std::endl;
}
};
// 使用示例:
Player player;
HealthUI ui;
// 绑定监听(使用 lambda 或 std::bind)
player.addHealthListener([&ui](int health) { ui.updateHealthDisplay(health); });
player.setHealth(80); // 触发 UI 更新
三、状态模式(State)
作用:它的核心作用是通过将对象的状态逻辑封装为独立的类,实现状态切换与行为变化的解耦。
场景:角色或对象的行为可能随状态变化(如待机、移动、攻击、死亡),传统方法会依赖大量 if-else 或 switch 分支判断,导致代码臃肿。
#include <iostream>
// 状态接口
class IPlayerState {
public:
virtual void enter(class Player* player) = 0;
virtual void update(Player* player) = 0;
virtual ~IPlayerState() = default;
};
// 具体状态:待机
class IdleState : public IPlayerState {
public:
void enter(Player* player) override {
std::cout << "进入待机状态" << std::endl;
}
void update(Player* player) override;
};
// 具体状态:移动
class MoveState : public IPlayerState {
public:
void enter(Player* player) override {
std::cout << "进入移动状态" << std::endl;
}
void update(Player* player) override;
};
// 玩家类(前向声明解决循环依赖)
class Player {
private:
IPlayerState* currentState = nullptr;
public:
void changeState(IPlayerState* newState) {
delete currentState; // 释放旧状态
currentState = newState;
currentState->enter(this);
}
void update() {
if (currentState) currentState->update(this);
}
// 示例方法:移动逻辑
void move() {
std::cout << "玩家移动中..." << std::endl;
}
};
// IdleState 的 update 实现
void IdleState::update(Player* player) {
// 假设检测到输入(伪代码)
bool isKeyWPressed = true; // 示例值
if (isKeyWPressed) {
player->changeState(new MoveState());
}
}
// MoveState 的 update 实现
void MoveState::update(Player* player) {
player->move();
}
// 使用示例:
Player player;
player.changeState(new IdleState());
player.update(); // 根据输入切换状态
四、对象池模式(Object Pool)
作用:预先创建并复用对象,避免频繁创建/销毁对象的性能开销。
-
应用场景:
-
高频生成/销毁的对象(如子弹、粒子特效、NPC)。
内存敏感场景(移动端游戏)。
优势:减少 GC(垃圾回收)压力,优化性能。
#include <queue>
#include <memory>
class Bullet {
public:
void reset() { /* 重置子弹状态 */ }
void fire() { /* 发射逻辑 */ }
};
class BulletPool {
private:
std::queue<std::unique_ptr<Bullet>> pool;
public:
BulletPool(int initialSize) {
for (int i = 0; i < initialSize; ++i) {
pool.push(std::make_unique<Bullet>());
}
}
std::unique_ptr<Bullet> acquire() {
if (pool.empty()) {
return std::make_unique<Bullet>(); // 池空时新建
} else {
auto bullet = std::move(pool.front());
pool.pop();
bullet->reset();
return bullet;
}
}
void release(std::unique_ptr<Bullet> bullet) {
pool.push(std::move(bullet));
}
};
// 使用示例:
BulletPool pool(10);
auto bullet = pool.acquire();
bullet->fire();
pool.release(std::move(bullet)); // 回收子弹
注意
-
关键差异说明:
-
内存管理:C++ 需要手动管理内存(或使用智能指针如 unique_ptr/shared_ptr)。
接口实现:C++ 通过抽象基类 (class IXXX) 定义接口。
事件系统:C++ 使用 std::function 和 Lambda 实现类似 C# 的委托功能。
线程安全:单例模式的简单实现未考虑线程安全,实际项目中需根据需求添加锁机制。
总结
以上列举为常用的设计模式。设计模式是解决特定问题的工具,而非万能钥匙。
-
在游戏开发中需结合具体需求选择模式:
-
性能敏感:优先使用对象池、享元模式。
状态管理:状态模式、组件模式。
事件驱动:观察者模式、命令模式。
代码扩展性:策略模式、组合模式。
避免过度设计,保持代码简洁性和可维护性始终是关键!