【C++】【ゲーム開発】デザインパターン(State)導入

eye-catch C/C++
eye-catch

Stateパターンを使ってみた

まずこのStateパターンを実装するにあたってQiitaの記事を参考にさせてもらった。

https://qiita.com/imadedede/items/180cf808eb74f5dda730
C++ で State パターンを書いてみたい – Qiita

ほとんどパクってます😅すみません。

まずは全体の構造としてクラス図で解説してくれているぺージがあるので、以下を参照。

https://qiita.com/i-tanaka730/items/49ee4e3daa3aeaf6e0b5
デザインパターン ~State~ – Qiita

私も感覚的に分かっているわけではないので、間違っている箇所もあると思います。
その点はご容赦下さい…(´・ω・`)

// State pattern context interface class.
class IPhaseContext {

public:
    virtual ~IPhaseContext() {}
    virtual bool recordLog(std::string title, std::string body) = 0;
    virtual void setState(interface__::IPhaseState *objects) = 0;

};

まずは状態遷移を行うステートパターンのインターフェースです。

これを使ってゲームシーンとか場面の切り替えを行っていく。

class IPhaseContext;  // This class exists in a separate file and needs to reference each other.

// State pattern's state interface class.
class IPhaseState {

public:
    virtual ~IPhaseState() {}
    virtual void doExecute(IPhaseContext* obj) = 0;
    // doDrawSprites, doDrawBackgrounds, doPlaySoundEffects, doPlayBGMs, getInputInfo, doRecalculateRegister and more...
    // Should be implemented virtual functions related to input, operation, and output.

};

次にステートパターンで切り替える各シーンの基底クラスです。
これもインターフェースです。

C++には interface キーワードがないので(なんかVisual Studioにはあったけど…💧)仮想関数を組み込んだクラスを書きます。

これはC++の記法ですね😀

そして次。

// Behavior management class for switching between multiple scenes.
// Extends MainComponent abstract class.
class PhaseComponent : abstract__::MainComponent, interface__::IPhaseContext {

private:
    interface__::IPhaseState *pars_;

public:
    PhaseComponent(int num = 0);
    ~PhaseComponent();
    bool recordLog(std::string title, std::string body);
    void setState(interface__::IPhaseState *objects);
    bool doTrigger(void);

};

PhaseComponent::PhaseComponent(int num) : MainComponent(num) {
    pars_ = new StandbyState();
}


PhaseComponent::~PhaseComponent() {
    delete pars_;
    pars_ = nullptr;
}


bool PhaseComponent::recordLog(std::string title, std::string body) {
    using namespace forms::trace;
    duplicate::FormalDuplicate::getInstance()->doWriteILogger(title, body);
    return true;
}


void PhaseComponent::setState(interface__::IPhaseState *objects) {
    delete pars_;
    pars_ = objects;
}


bool PhaseComponent::doTrigger(void) {
    if (nullptr != pars_) {
        pars_->doExecute(this);
    }
    return true;
}

名前空間 「interface__」でIPhaseContextクラス、IPhaseStateクラスを使用します。

pars_のメンバ変数ポインタにIPhaseStateインターフェースを継承した具象クラスのインスタンスを突っ込みます。

シーン切り替え時にこのステートを捨てて(delete)、次の具象クラスをインスタンス化(new)します。

        class StandbyState final : public interface__::IPhaseState {

        public:
            StandbyState();
            void doExecute(interface__::IPhaseContext* obj);
                
        };

        StandbyState::StandbyState() {
            SetBackgroundColor(0xFF, 0xFF, 0xFF);
        }

        void StandbyState::doExecute(interface__::IPhaseContext* obj) {
            DrawString(1, 1, "STANDBY STATE CONNECTION NOW...", GetColor(0x80, 0x80, 0x80));
            if (1 == C16Key::getInstance()->getC16PressButtons(GPAD_BUTTON::START_KEY)) {
                MessageBox(GetMainWindowHandle(), "executed successfully of Eris standby process.", "State info", MB_OK);
                obj->recordLog("Process-Log", "executed successfully of Eris standby process.");
                obj->setState(new CreditLogoState());
            }
        }

中身のごちゃごちゃしたのは無視してもらって、
obj->setState(…)の部分でインスタンスを作成していますよね。

これが以下になります。

        void PhaseComponent::setState(interface__::IPhaseState *objects) {
            delete pars_;
            pars_ = objects;
        }

PhaseComponentクラスはIPhaseContextインターフェースを継承しているので、インスタンスの間接参照で、setStateメソッドが呼び出せます。

これでobjectsポインタが持っているインスタンスをpars_メンバに突っ込んでステートの引継ぎが完了します。

仕組みはこういったものです。

これを行う事で、長ったらしいswitch文を完全に削除できます。

コメント

タイトルとURLをコピーしました