Help us understand the problem. What is going on with this article?

【C++/OpenSiv3D】SceneManager の State にもっと情報を持たせる

More than 1 year has passed since last update.

OpenSiv3D のシーンマネージャー SceneManager<State, Data> のシーンステート State には、Stringenum class を使うのが一般的ですが、もう少し情報を持たせたユーザ定義のクラスを使うと、各シーンの実装が少しスマートになるケースがあります。

StateHashTable のキーとして使われるため、以下の 2 つを満たす必要があります。

  • キーが等しいかを調べる比較演算子が定義されていること
  • std::hash テンプレートの特殊化が定義されていること

サンプル

Title, Story, Game, Result の 4 種類のシーンクラスがあり、

Title
↓
Stage-1 Story
↓
Stage-1 Game
↓
Stage-2 Story
↓
Stage-2 Game
↓
Stage-3 Story
↓
Stage-3 Game
↓
Result
↓
(最初に戻る)

と進行するゲームにおいて、Stage 番号を State に持たせるサンプルを紹介します。

# include <Siv3D.hpp> // OpenSiv3D v0.4.2

struct GameData
{

};

// シーンマネージャーの State に使うクラス
struct SceneState
{
    enum class State
    {
        Title,
        Story,
        Game,
        Result,
    };

    State scene = State::Title;

    size_t stageID = 0;

    [[nodiscard]] constexpr bool hasStageID() const noexcept
    {
        return ((scene == State::Story) || (scene == State::Game));
    }

    [[nodiscard]] constexpr bool operator ==(const SceneState& other) const noexcept
    {
        return (scene == other.scene)
            && (hasStageID() ? (stageID == other.stageID) : true);
    }

    [[nodiscard]] constexpr bool operator !=(const SceneState& other) const noexcept
    {
        return !(*this == other);
    }

    // シーンのハッシュ値
    [[nodiscard]] size_t hash() const noexcept
    {
        const size_t sceneID = static_cast<size_t>(scene) * 10000 + stageID;
        return std::hash<size_t>{}(sceneID);
    }

    [[nodiscard]] constexpr static SceneState Title() noexcept
    {
        return SceneState{ State::Title };
    }

    [[nodiscard]] constexpr static SceneState Story(size_t stageID) noexcept
    {
        return SceneState{ State::Story, stageID };
    }

    [[nodiscard]] constexpr static SceneState Game(size_t stageID) noexcept
    {
        return SceneState{ State::Game, stageID };
    }

    [[nodiscard]] constexpr static SceneState Result() noexcept
    {
        return SceneState{ State::Result };
    }
};

namespace std
{
    template <>
    struct hash<SceneState>
    {
        [[nodiscard]] size_t operator ()(const SceneState& value) const noexcept
        {
            return value.hash();
        }
    };
}

using App = SceneManager<SceneState, GameData>;

struct Title : App::Scene
{
    Title(const InitData& init)
        : IScene(init)
    {

    }

    void update() override
    {
        if (MouseL.down())
        {
            changeScene(SceneState::Story(1));
        }
    }

    void draw() const override
    {
        ClearPrint();
        Print << U"Title";
    }
};

struct Story : App::Scene
{
    Story(const InitData& init)
        : IScene(init)
    {

    }

    void update() override
    {
        if (MouseL.down())
        {
            changeScene(SceneState::Game(getState().stageID));
        }
    }

    void draw() const override
    {
        ClearPrint();
        Print << U"Stage-{} Story"_fmt(getState().stageID);
    }
};

struct Game : App::Scene
{
    Game(const InitData& init)
        : IScene(init)
    {

    }

    void update() override
    {
        if (MouseL.down())
        {
            if (getState().stageID == 3)
            {
                changeScene(SceneState::Result());
            }
            else
            {
                changeScene(SceneState::Story(getState().stageID + 1));
            }
        }
    }

    void draw() const override
    {
        ClearPrint();
        Print << U"Stage-{} Game"_fmt(getState().stageID);
    }
};

struct Result : App::Scene
{
    Result(const InitData& init)
        : IScene(init)
    {

    }

    void update() override
    {
        if (MouseL.down())
        {
            changeScene(SceneState::Title());
        }
    }

    void draw() const override
    {
        ClearPrint();
        Print << U"Result";
    }
};

void Main()
{
    Scene::SetBackground(Palette::Seagreen);

    App manager;
    manager.setFadeColor(Palette::White)
        .add<Title>(SceneState::Title())
        .add<Result>(SceneState::Result());
    for (size_t i : Range(1, 3))
    {
        manager.add<Story>(SceneState::Story(i))
            .add<Game>(SceneState::Game(i));
    }

    while (System::Update())
    {
        if (!manager.update())
        {
            break;
        }
    }
}
Reputeless
小さくなってもコードはモダン!▼ 未定義動作なしの名コーダー!▼ 実装はいつも 3 つ!!(GCC/Clang/MSVC)▼ OpenSiv3D, cppmap 作者
https://ryo-suzuki-contact.github.io/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away