Unity シーン間でデータを受け渡す方法まとめ

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が良いのではないでしょうか。

業務レベルでは単にデータを受け渡すだけでなく、これらを組み合わせてシーン遷移全体を管理するクラスを作ってしまうケースが多いように思います。
それについてはまた別途執筆できれば。