【游戏设计模式】状态模式

状态模式

“允许一个对象在其内部状态改变时改变自身的行为。对象看起来好像是在修改自己的类。”

什么是状态模式

状态模式是一种行为设计模式,它允许对象在内部状态发生变化时改变自己的行为。通过将不同状态的行为封装成独立的类,这样在切换状态时可以更灵活地替换当前状态对象,从而实现行为的变化。

为什么要用状态模式

在游戏开发等场景中,状态模式非常适合用来管理角色、敌人或物体的复杂状态变化。通过状态模式,可以避免将大量条件语句(if-elseswitch)集中在一个类中,代码更清晰、易维护,也方便扩展和调试。

状态模式的实现细节——以编写拥有多状态的游戏玩家为例

1. 一个状态接口

首先,为各个状态定义一个通用接口,以规范每个状态的行为。每个具体状态类都实现这个接口。

代码例子
#include <iostream>
#include <memory>

class PlayerState {
    
    
public:
    virtual ~PlayerState() = default;
    virtual void enterState() = 0; // 进入状态行为
    virtual void exitState() = 0;  // 退出状态行为
    virtual void handleInput() = 0; // 处理输入
};

class Player;

2. 为每一个状态定义一个类

每个状态类实现具体的状态行为。在此例子中,我们定义两个状态:IdleState(空闲状态)和 RunState(跑动状态)。

代码例子
class IdleState : public PlayerState {
    
    
public:
    void enterState() override {
    
    
        std::cout << "Entering Idle State." << std::endl;
    }
    void exitState() override {
    
    
        std::cout << "Exiting Idle State." << std::endl;
    }
    void handleInput() override {
    
    
        std::cout << "Idle: Waiting for player input..." << std::endl;
    }
};

class RunState : public PlayerState {
    
    
public:
    void enterState() override {
    
    
        std::cout << "Entering Run State." << std::endl;
    }
    void exitState() override {
    
    
        std::cout << "Exiting Run State." << std::endl;
    }
    void handleInput() override {
    
    
        std::cout << "Run: Handling player input for running..." << std::endl;
    }
};

3. 状态委托

将状态委托给玩家对象,玩家类可以持有当前状态并在状态切换时更改它。

代码例子
class Player {
    
    
private:
    std::unique_ptr<PlayerState> currentState;

public:
    void changeState(std::unique_ptr<PlayerState> newState) {
    
    
        if (currentState) {
    
    
            currentState->exitState();
        }
        currentState = std::move(newState);
        currentState->enterState();
    }
    
    void handleInput() {
    
    
        if (currentState) {
    
    
            currentState->handleInput();
        }
    }
};

4. 状态对象应该放在哪里

有两种放置状态对象的方法:静态对象实例化对象

静态对象

适用于状态类没有数据成员且只需唯一实例的情况。

优点:高效,简单
缺点:无法存储数据成员,只能存在唯一实例

实例化状态

适用于需要存储数据成员或可能有多个实例的情况。状态切换时要注意内存管理,以防碎片化。

5. 进入状态和退出状态的行为

为每个状态定义进入和退出方法,在状态切换时调用。

扫描二维码关注公众号,回复: 17566853 查看本文章
// 示例:
auto idleState = std::make_unique<IdleState>();
auto runState = std::make_unique<RunState>();

Player player;
player.changeState(std::move(idleState)); // 切换到空闲状态
player.handleInput();
player.changeState(std::move(runState));  // 切换到跑动状态
player.handleInput();

有限状态机的优缺点和它的限制

优点:易于管理、可扩展性高,代码更简洁。
缺点:状态较多时,可能产生大量状态类,增加维护成本。
限制:有限状态机适用于状态之间转换有限的情况,若状态间关系复杂则适合使用行为树或规划系统。

状态机扩展

并发状态机

将不同状态并行执行,实现多状态组合效果。适合需要并发操作的复杂逻辑。

代码例子
// 可以在Player类中同时持有多个状态对象,并分别调用它们的handleInput()方法。

层次状态机

通过继承的方式建立父子状态,子状态无法处理的事件可传递至父状态,复用相同行为。

代码例子
// 可以通过继承构建一个父状态类,子状态可复用父类行为
class BaseState : public PlayerState {
    
    
    // 基础行为实现
};
class SpecialState : public BaseState {
    
    
    // 特殊行为实现,继承BaseState的行为
};

下推自动机

用于记录上一个状态,以便完成当前状态后回到先前状态。

代码例子
#include <stack>

class PlayerWithStateStack {
    
    
    std::stack<std::unique_ptr<PlayerState>> stateStack;

public:
    void pushState(std::unique_ptr<PlayerState> newState) {
    
    
        if (!stateStack.empty()) {
    
    
            stateStack.top()->exitState();
        }
        stateStack.push(std::move(newState));
        stateStack.top()->enterState();
    }

    void popState() {
    
    
        if (!stateStack.empty()) {
    
    
            stateStack.top()->exitState();
            stateStack.pop();
        }
        if (!stateStack.empty()) {
    
    
            stateStack.top()->enterState();
        }
    }

    void handleInput() {
    
    
        if (!stateStack.empty()) {
    
    
            stateStack.top()->handleInput();
        }
    }
};

有限状态机的适用情况

  • 游戏角色、NPC、敌人等行为管理:如闲置、攻击、受伤、逃跑等状态。
  • 用户接口交互状态:如按钮状态、加载状态、提交状态等。
  • 物体的生命周期管理:如武器、道具的不同状态管理。

更复杂的游戏AI——行为树和规划系统

对于复杂的AI,有限状态机可能不够用,可以使用行为树(Behavior Tree)或规划系统(Planner)实现更细致的决策逻辑。

总结

状态模式通过将不同状态封装为独立对象,使对象行为随着状态变化而改变,避免了大量条件判断,代码更清晰。它适用于各种具有有限状态的系统,但在状态关系较复杂的情况下,可能需要借助更复杂的AI系统。