Unityではシーンをまたぐときに全てのゲームオブジェクトが破棄されます。
そのため前シーンのデータを利用するには、データを受け渡す必要があります。
シーン間でデータを受け渡す方法
シーン間のデータの受け渡しはいくつか方法があります。
- DontDestroyOnLoadを使う
- SceneManager.sceneLoadedを使う
- 非MonoBehaviourのstaticクラスを使う
- 非MonoBehaviourのシングルトンを使う
- PlayerPrefsを使う
- ScriptableObjectを使う
- ファイルに書き出す
他にもプラグインを用いた方法や、これらを組み合わせた方法があります。
ざっくりとした紹介
それぞれの詳細は別の記事として作成するので、ここでは比較用にざっくりと特徴を説明します。
※詳細記事は随時執筆中
DontDestroyOnLoadを使う
シーンをまたいでも破棄されないゲームオブジェクトを作成することで、データを受け渡します。
長所
- インスペクターが利用できる
- MonoBehaviourが利用できる
短所
- どのシーンでも常に残り続けるため、シーン遷移が多い場合は不要なデータとして残り続ける
- 同じシーンに戻ってきた際の扱いに注意しないと、重複する可能性がある
- GameObject.Findやシングルトンとの併用が必要
SceneManager.sceneLoadedを使う
シーンロード完了時のイベントハンドラを設定することで、データを受け渡します。
長所
- 遷移前のシーンで完結するため簡単
短所
- 遷移前のシーンで完結するため、遷移前のシーンは次のシーンの状態を知っておく必要がある
- 一つの関数の中で完結する必要がある
非MonoBehaviourのstaticクラスを使う
UnityでのプログラミングはMonoBehaviourを継承し、GameObjectにアタッチすることが多いです。
ですが、MonoBehaviourを使わずGameObjectにもアタッチしないコードも利用可能です。
GameObjectに依存していなければ、シーンをまたいでも破棄されることはありません。
しかし、GameObject.Find等でクラスを取得できなくなるので、どうやってそのクラスにアクセスするかが問題です。
その対応の一つが、MonoBehaviourを継承していないstaticクラスを用意するという方法です。
staticクラスは全てのクラスから自由にアクセスすることができます。
長所
- 全シーン共通のデータをクラスでまとめて管理できる
- 変数を書き換えるだけなので簡単
短所
- どのクラスからもアクセスできるので危険
- データが残り続けるので、初期化を忘れバグに繋がりやすい
詳細はこちら tmls.hatenablog.com
非MonoBehaviourのシングルトンクラスを使う
上記staticクラスはインタフェースや継承を使えなかったり、インスタンスを破棄して初期化するといったことはできません。
これらを改善したのが、シングルトンという設計です。
シングルトンを使うと、staticクラス同様に全てのクラスから自由にアクセスできます。
長所
- staticクラスよりも柔軟な使い方が可能(詳しくはデザインパターンで調べてくださいmm)
短所
- 管理が難しく、怠ると複雑なバグに繋がるケースがある
PlayerPrefsを使う
Unityが標準で用意しているデータ保存機能であるPlayerPrefsを使います。
シーン間というより、ゲームを終了しても残るプレイヤーデータになります。
長所
- Unityの標準機能なので簡単
短所
- 渡すデータの型がfloat, int, stringに限られる
- ゲームを終了してもデータが残る
ScriptableObjectを使う
ScriptableObjectは各データをアセットとして書き出す機能です。 シーン間でのデータの受け渡しだけでなく、事前に初期値をインスペクターで設定することができます。
長所
- 初期値の設定にインスペクターが使える
- Findやインスタンスの受け渡しをせず、それぞれのシーンでデータのロードを行うだけで良い
短所
- Serialize可能な型しか渡せない
- 扱いが特殊で難しい
- 初期化されるタイミングを熟知していないと、意図しないタイミングで初期化される
ファイルに書き出す
C#標準のファイル入出力を使うことで、データをファイルに書き出したり読み込むことができます。 基本的にはScriptableObjectに似た方法になります。
長所
- 自由な形式でファイルに書き出せる
短所
- ゲームを終了してもデータが残る
- ファイルの書き出し、読み込み機能を理解する必要がある
- シーン遷移の度ににファイルロードが走る
表
まとめるとこのようになります。
長所はデータが渡せるだけで十分とも言えるので、重要なのはどの短所をとるかでしょうか。
手法 | 長所 | 短所 |
---|---|---|
DontDestroyOnLoad | ・インスペクターが利用できる ・MonoBehaviourが利用できる |
・どのシーンでも残り続ける ・同じシーンに戻ってきた際の扱いに注意 ・GameObject.Findやシングルトンとの併用が必要 |
SceneManager.sceneLoaded | 遷移前のシーンで完結する | ・次のシーンの状態を知っておく必要がある ・一つの関数の中で完結する必要がある |
非MonoBehaviourのstaticクラス | ・全シーン共通のデータをクラスでまとめて管理できる ・実装が簡単 |
・どのクラスからもアクセスできるので危険 ・初期化を忘れバグに繋がりやすい |
非MonoBehaviourのシングルトンクラス | staticクラスよりも柔軟 | 管理が難しい |
PlayerPrefs | Unityの標準機能なので簡単 | ・渡すデータの型がfloat, int, stringに限られる ・ゲームを終了してもデータが残る |
ScriptableObject | ・初期値の設定にインスペクターが使える ・各シーンでデータのロードを行うだけで良い |
・Serialize可能な型しか渡せない ・扱いが特殊で難しい ・意図しないタイミングで初期化されやすい |
ファイルに書き出す | 自由な形式でファイルに書き出せる | ・ゲームを終了してもデータが残る ・ファイルの書き出し、読み込み機能を理解する必要がある ・シーン遷移の度ににファイルロードが走る |
総括
一長一短があり、一概にどれが良いとは言えません。
おすすめとしては、初心者であればstaticクラス、慣れた人であればDontDestroyOnLoadが良いのではないでしょうか。
業務レベルでは単にデータを受け渡すだけでなく、これらを組み合わせてシーン遷移全体を管理するクラスを作ってしまうケースが多いように思います。
それについてはまた別途執筆できれば。