侧边栏壁纸
博主头像
LittleAO的学习小站 博主等级

在知识的沙漠寻找绿洲

  • 累计撰写 125 篇文章
  • 累计创建 27 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

设计模式之 状态(State)模式

LittleAO
2024-10-04 / 0 评论 / 0 点赞 / 15 阅读 / 0 字
温馨提示:
本文最后更新于2024-10-04,若内容或图片失效,请留言反馈。 部分素材来自网络,若不小心影响到您的利益,请联系我们删除。

前言

在上Unity课的时候,老师介绍了一款可视化编程插件——PlayMaker。即使不需要技术人员,通过这个插件,游戏创作者也能创建出合适的游戏。这个插件内置了一个强大的状态机系统:用户可以定义不同的状态,从而实现对用户物体的操控。

https://assetstore.unity.com/packages/tools/visual-scripting/playmaker-368?locale=zh-CN

这就引发了一个问题:状态这个东西怎么在代码中编写?了解完状态模式后,你会发现,这个模式就值65美元。

需求

我们要设计一个游戏NPC,这个NPC有以下状态:休息,走路,战斗。用一个简单的流程图来表示:

具体怎样触发状态转移这里不做阐述,可以利用回调函数,时间触发等一系列规则。这里我们只设计转移状态的接口。

状态模式

在在面向对象编程中,我们把许多东西看成是类,然后再去实现。同样的,状态也可以是类,而我们操作的对象,本例中是游戏NPC,也是类。利用继承、多态就能实现很多功能,状态模式的UML图如下:

该设计模式包含三个要点:

  • 抽象类State:表示了其状态和行为;

  • ConcreteState:继承自State,实现接口;

  • Context:上下文,有表示状态的成员。

具体设计

首先设计我们的抽象类State,我们假设在每个状态下,NPC都会说话,因此需要设计一个talk接口。

class State {
public:
    virtual void talk() = 0;
    virtual ~State() = default;
};

我们设计的三个状态继承自该接口。由于状态没有必要构造多个对象,我们可以用单例模式来写:

class WalkState : public State {
public:
    static WalkState& getInstance()
    {
        static WalkState singleton;
        return singleton;
    }
    void talk() override {
        std::cout << "I am walking." << std::endl;
    }
};

class RestState : public State {
public:
    static RestState& getInstance()
    {
        static RestState singleton;
        return singleton;
    }
    void talk() override {
        std::cout << "zzz...." << std::endl;
    }
};

class FightState : public State {
public:
    static FightState& getInstance()
    {
        static FightState singleton;
        return singleton;
    }
    void talk() override {
        std::cout << "I am fighting." << std::endl;
    }
};

再来实现我们的NPC类:

class NPC {
public:
    void setState(State& state) {
        nowState = &state;
    }

    const State* getState() const {
        return nowState;
    }

    void talk() {
        if (nowState) {
            nowState->talk();
        }
    }
private:
    State* nowState = nullptr;
};

尝试在main函数中调用:

int main()
{
    NPC somebody;

    somebody.setState(WalkState::getInstance());
    somebody.talk();

    somebody.setState(RestState::getInstance());
    somebody.talk();

    somebody.setState(FightState::getInstance());
    somebody.talk();
}

输出结果如下:

I am walking.
zzz....
I am fighting.

成功实现了我们的需求。

优点

假设以后我们要为这个NPC添加新状态,只需要编写新状态即可,不需要更改NPC类的内部实现。

0

评论区