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
に持たせるサンプルを紹介します。
# 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;
}
}
}