Unityでゲーム制作する際のシーン管理を支援するフレームワークです。 本フレームワークでは単一シーン構造を採用しており、以下の3点を重視しています。
- シーン間のデータの受け渡しをスムーズにしたい
- シーンを開く、閉じる際に処理を挟み込めるようにしたい
- シーンのトップモジュールを設定して親子関係を構築できるようにしたい
- UniTaskを導入する
PackageManagerのAdd package from git URL...に以下を追加する。
https://github.com/sabanishi/ScreenSystem.git?path=/Packages/com.sabanishi.screensystem
-
一般的なUnity開発において、画面毎にSceneを作成し、Scene自体を切り替えることで画面遷移を実現することが多いと思います。
- この方法は仕組みがシンプルな一方で、Sceneを読み込むので画面遷移処理が重い、前のシーンの情報(スコアなど)を次のシーンに受け渡しづらい、シーン初期化時などにイベントを差し込みにくいといった欠点があります。
- また、Sceneの中にSceneを配置できないため、画面の階層構造に対応しづらいです。
-
画面の生成、破棄を管理するために、Screen、ScreenTransitionerという概念を導入します。
- Screenは画面を構成するためのプレハブ(またはヒエラルキー上に配置したGameObject)です。上の例だとHomeScreen.prefabやMainGameScreen.prefabが該当します。
- ScreenTransitionerは生成したScreenを管理するための機構です。
-
前節で導入した概念により画面を管理する仕組みが実現できました。
-
しかし、ScreenTransitonerが1つだと、Screenを入れ子にすることができないため、以下のようにScreen内での画面切り替えに対応できません。
-
そのため、下記のようにScreenに別のScreenTransitionerインスタンスを持たせ、親Screenが子Screenを管理できるようにします。
- 前節までの仕組みにより、Screenオブジェクトを管理する仕組みが実装できました。この節では、Screen間を移動する仕組みを考えます。
- Screenの移動はScreenTransitionerのメソッドを使って行います。
- 移動用メソッドには3つの種類があります。
- Jump: 遷移前のScreenを破棄して遷移します。後述するBackを行う必要がない時に使用します。
- Move: 遷移前のScreenを残したまま遷移します。
- Back: Move()メソッドを実行したScreenに戻ります。
-
前節で確認した処理により、画面遷移を行えるようになりました。では次に、「画面遷移中に処理を挟み込む」方法を考えます。
-
Screenには、画面遷移中に決まった順序で呼ばれるメソッド群が用意されています。
- Screenを継承した具象クラスでこれらをオーバーライドすることで、画面遷移中にメソッドを実行することができます。
- 以下、このメソッド群をTriggerメソッドと呼びます。
-
以下のようなTriggerメソッドが用意されています。
//Screenが生成された直後に呼ばれる UniTask Initialize(); //Screenを破棄する直前に呼ばれる UniTask Dispose(); //Screenを開く時に呼ばれる(暗転解除前) UniTask Activate(); //Screenを閉じる時に呼ばれる(暗転後) UniTask Deactivate(); //暗転解除前、bridgeAction(後述)後に呼ばれる UniTask PostBridgeFunc(); //暗転解除後に呼ばれる UniTask Open(); //暗転前に呼ばれる UniTask Close();
-
実際にオーバーライドする時は、Triggerメソッドを直接オーバーライドするのではなく、これらのメソッドが呼び出しているメソッドの方をオーバーライドします。
//こっちはオーバーライドしない public async UniTask Initialize(){ ... await InitializeInternal(); ... } //こっちをオーバーライドする protected virtual UniTask InitializeInternal(){}
-
Screen間でデータを受け渡すためには、Jump、Move、Backを実行する時に、「遷移先のScreenを引数にとる関数」をメソッドの引数に渡します。
var childTransitioner = new ScreenTransitioner(); //遷移先のScreenを用意 var to = GameObject.Instantiate<CharaDetailScreen>(); //遷移先のScreenに渡すデータを用意 var charaData = new CharaData(); //遷移APIを実行する childTransitioner.Move<CharaDetailScreen>(to, bridgeAction: screen => { //遷移先のScreenにデータを渡す //screenの型は「CharaDetailScreen」(Move<T>の「T」と同じ型) screen.ReceiveCharaData(charaData); }).Forget();
-
画面遷移を行う際に、暗転などのアニメーションを挟み込みたい時があります。
-
本システムでは、ITransitionAnimationというインターフェースを継承したクラスを使用してアニメーションを再生します。
public interface ITransitionAnimation { public UniTask Play(CancellationToken token); }
-
また、簡単な暗転アニメーションを行えるDefaultFadeAnimationというクラスがシングルトンで提供されています。
-
アニメーションを実行する方法は以下のように、遷移メソッドの引数にITransitionAnimationを追加します。
var childTransitioner = new ScreenTransitioner(); var to = GameObject.Instantiate<CharaDetailScreen>(); //遷移時に再生するアニメーションを用意(今回はデフォルトの暗転を使用) var closeAnimation = DefaultFadeAnimation.Instance.CloseAnimation; var openAnimation = DefaultFadeAnimation.Instance.OpenAnimation; //遷移APIを実行する childTransitioner.Move<CharaEditScreen>(to, closeAnimation:closeAnimation, openAnimation:openAnimation).Forget();
-
Jump、Move、Backの内部では、それぞれ以下の順番でTriggerメソッドが実行されます。
- 遷移前のScreenがfromで、遷移後のScreenがtoです。
- bridgeActionは5.2で導入した関数、closeAnimation・openAnimationは5.3で導入したITransitionAnimationです。
-
Jump
from.Close(); closeAnimation.Play(); from.Deactivate(); from.gameobject.SetActive(false); from.Dispose(); to.Initialize(); to.gameobject.SetActive(true); to.Activate(); bridgeAction(to); to.PostBridgeFunc(); openAnimation.Play(); to.Open(); GameObject.Destroy(from.gameobject);
-
Move
from.Close(); closeAnimation.Play(); from.Deactivate(); from.gameobject.SetActive(false); to.Initialize(); to.gameobject.SetActive(true); to.Activate(); bridgeAction(to); to.PostBridgeFunc(); openAnimation.Play(); to.Open();
-
Back
from.Close(); closeAnimation.Play(); from.Deactivate(); from.gameobject.SetActive(false); from.Dispose(); GameObject.Destroy(from.gameobject); //ここで戻るべきScreenを探す to.gameobject.SetActive(true); to.Activate(); to.PostBridgeFunc(); openAnimation.Play(); to.Open();
classDiagram
ConcreteScreen --|> Screen
Screen ..|> IScreen
ScreenTransitioner o-- IScreen
ScreenTransitioner --o Screen
ScreenTransitioner --> ITransitionAnimation
class IScreen{
<<interface>>
Initialize(ScreenTransitioner) UniTask
Dispose() UniTask
Activate() UniTask
Deactivate() UniTask
Open() UniTask
Close() UniTask
PostBridgeDunc() UniTask
GetGameObject() GameObject
}
class Screen{
<<abstract>>
#Screen Parent
+ScreenTransitioner ParentTransitioner
#ScreenTransitioner ChildTransitioner
#ScreenTransitioner DialogTransitioner
#GetInitializeToken() CancellationToken
#GetActiveToken() CancellationToken
#InitializeInternal(CancellationToken) UniTask
#ActivateInternal(CancellationToken) UniTask
#...() 以下省略
}
class ITransitionAnimation{
<<interface>>
+ Play(CancellationToken) UniTask
}
class ConcreteScreen{
<<具体的なScreenクラス>>
}
class ScreenTransitioner{
- List~IScreen~ _screenList
- Transform _parentTransform
+ Screen Parent
+ScreenTransitioner(Screen,Transform)
+DisposeAsync()
+Jump()
+Move()
+Back()
}
MIT Lincense




