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

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

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;
        }
    }
}
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
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  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