1. Reputeless

    Posted

    Reputeless
Changes in title
+【C++/OpenSiv3D】SceneManager の State にもっと情報を持たせる
Changes in tags
Changes in body
Source | HTML | Preview
@@ -0,0 +1,235 @@
+OpenSiv3D のシーンマネージャー `SceneManager<State, Data>` のシーンステート `State` には、`String` や `enum class` を使うのが一般的ですが、もう少し情報を持たせたユーザ定義のクラスを使うと、各シーンの実装が少しスマートになるケースがあります。
+
+`State` は `HashTable` のキーとして使われるため、以下の 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` に持たせるサンプルを紹介します。
+
+```C++
+# 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;
+ }
+ }
+}
+```