LoginSignup
8
6

More than 3 years have passed since last update.

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

Posted at

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;
        }
    }
}
8
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6