エフェクトを使ったシーン切替

中～大規模のプロジェクトを作成する際に、SceneManagerを使用することが多いと思います。
SceneManagerには、シーン間の遷移の際にデフォルトでフェードイン/アウトしてくれる機能があります。
今回はデフォルト機能を使わず、Effectを使用してシーン間の遷移を別のものにしてみようという記事です。

#サンプルデモ

<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Siv3D Advent Calendar [<a href="https://t.co/wdbEyfAhSN">https://t.co/wdbEyfAhSN</a>]<br>エフェクトを使ったシーン切替のデモムービー <a href="https://t.co/HWr1IF8gJ7">pic.twitter.com/HWr1IF8gJ7</a></p>&mdash; ao1415 (@ao141592) <a href="https://twitter.com/ao141592/status/1072509063181885440?ref_src=twsrc%5Etfw">2018年12月11日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


[ソースコード](https://github.com/ao1415/SceneChange)

#ザックリ解説

エフェクトでの切替は以下の手順で行っています。

1. 各シーンでSceneManager::changeSceneを呼び出す前に、切替用のエフェクトを生成する
2. シーンの更新後に、シーン切替用のエフェクトを更新する

# 詳細な解説

## シーン切替時にフェードイン/アウトしないようにする

エフェクトでの切替を行う際には、デフォルトのフェードイン/アウトを無効化する必要があります。無効化しなかった場合、エフェクトの再生中に画面が暗くなったり明るくなったりしてしまいます。
各シーンのクラスごとに以下のメソッドを追加することで、デフォルトのフェードインとフェードアウトが無効化できます。

```C++
struct Sample : public MyApp::Scene {

	//フェードイン/アウトを上書きしてさせないようにする
	void drawFadeIn(double) const override { draw(); }
	void drawFadeOut(double) const override { draw(); }

};
```

## 切替用のエフェクトを共有する

シーンクラス内でエフェクトを登録し、Mainループ内で更新を行うためにEffectクラスのインスタンスを共有できるようにします。
これはSceneManagerの共有データを使用すると、以下のように簡単に行うことができます。

```C++
//切替エフェクト用のEffectクラスを共有で持つ
struct GameData {
	Effect fadeEffect;
};

using MyApp = SceneManager<String, GameData>;

void Main() {

	//Mainループ内でエフェクトを使用できるように、
	//Mainループ内で共有データを作成する
	auto p = MakeShared<GameData>();
	MyApp manager(p);

}

```

## シーンの更新の後にエフェクトを更新する

エフェクトでシーンの切替を行うために、シーンの描画を終えた後にエフェクトの描画を行う必要があります。

```C++
struct GameData {
	Effect fadeEffect;
};

void Main() {

	auto p = MakeShared<GameData>();
	MyApp manager(p);

	while (System::Update())
	{
		//シーンを動かす
		if (!manager.update())
		{
			break;
		}
		//切替用のエフェクトを動かす
		p->fadeEffect.update();
	}
}
```

## シーンの切替時にエフェクトを追加する

シーン内でchangeSceneを呼び出す前にエフェクトを追加します。
この際にエフェクトとchangeSceneに、明示的に時間を指定することをお勧めします。
理由としては、以下のものがあります。

- エフェクトが初期設定では1秒しか存在できない
- エフェクトとシーン切替は別々に動作させているため、秒数がずれると切替がずれて見える

```C++

struct Sample : public MyApp::Scene {
	void update() override {

		//切替エフェクト用に作成したBoxEffectクラスを追加
		sceneEffect.add<BoxEffect>(2s);
		changeScene(U"Scene01",2s);

	}
};
```

## 切替用のエフェクトを作成する

切替の描画を行うエフェクトクラスを作成します。
自分の好きなように切替を行うエフェクトを作成してください。

作成の際にはコンストラクタで、切替秒数を受け取ることをお勧めします。理由は上記の「シーンの切替時にエフェクトを追加する」と同様です。

デモムービー再掲
<blockquote class="twitter-tweet" data-lang="ja"><p lang="ja" dir="ltr">Siv3D Advent Calendar [<a href="https://t.co/wdbEyfAhSN">https://t.co/wdbEyfAhSN</a>]<br>エフェクトを使ったシーン切替のデモムービー <a href="https://t.co/HWr1IF8gJ7">pic.twitter.com/HWr1IF8gJ7</a></p>&mdash; ao1415 (@ao141592) <a href="https://twitter.com/ao141592/status/1072509063181885440?ref_src=twsrc%5Etfw">2018年12月11日</a></blockquote>
<script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

[ソースコード](https://github.com/ao1415/SceneChange)
こちらにデモムービーのソースコードを公開しています。
1～4のキーを押下することで、4つのシーン切替を行うことができます。

# 制約

残念ながら、完全に自由なシーン切替を行うことができず、以下のような制約があります。

- フェードイン/アウトの時間は必ず2分割される
  - changeSceneに2秒を指定した場合、フェードインに1秒、フェードアウトに1秒の時間が必ずかかる
- フェードインのスクリーンショットを得ることができない
  - Siv3Dの使用上、一度画面を表示させる必要があるため一瞬ちらついてしまう
- デフォルトのフェードイン/アウトが使用できない
  - 無効にしたため、フェードイン/アウトを行う場合は、自分でエフェクトを作成する必要がある 

# 終わり

エフェクトを活用すると、自分の好きなシーン切替ができるようになります！