シーンをまたいでデータを引き継ぎたい場面

Unity でゲームを開発していると、あるシーンで選択した値や結果を別のシーンで使いたい場面が頻繁にあります。たとえば、キャラクター選択シーンで選んだキャラクター ID をバトルシーンに渡したり、リザルト画面で表示するスコアを前のシーンから受け取ったりといったケースです。
SceneManager.LoadScene は新しいシーンを読み込む際に、基本的に前のシーンのオブジェクトを破棄します。そのため、何も工夫せずにシーンを切り替えると手元のデータは失われてしまいます。
本記事では、シーンをまたいでデータを受け渡すための代表的な 4 つの方法と、それぞれの使い分けの指針を解説します。

シーン間でデータを受け渡す 4 つの方法

Unity でシーン間のデータ受け渡しに使われる代表的な手段は、大きく次の 4 つに分類できます。

  1. static 変数で保持する
  2. DontDestroyOnLoad で永続オブジェクトを作る
  3. ScriptableObject にデータを保存する
  4. PlayerPrefs や JSON ファイルに永続化する

それぞれに向き不向きがあるため、データの性質と保持したい期間に応じて使い分けることが重要です。順番に見ていきましょう。

方法 1: static 変数で保持する

最もシンプルなのが、static 変数にデータを入れておく方法です。static 変数はインスタンスに依存せず、ゲームの実行中はずっとメモリに残り続けるため、シーンを切り替えても値が消えません。

public static class GameData
{
    public static int SelectedCharacterId;
    public static int LastScore;
}

// キャラクター選択シーンで保存
GameData.SelectedCharacterId = 3;

// バトルシーンで取得
int id = GameData.SelectedCharacterId;

導入コストが非常に低く、ちょっとした値の受け渡しには最適です。ただし、static 変数はプレイモードを停止するまでリセットされないため、タイトルに戻った際などに明示的な初期化を忘れると、前のプレイデータが残り続けるバグの温床になります。
また、グローバル状態を生み出すためテストが書きづらく、プロジェクト全体に多用するのは避けた方が無難です。

方法 2: DontDestroyOnLoad で永続オブジェクトを作る

DontDestroyOnLoad は、シーン遷移時にゲームオブジェクトを破棄しないようマークする機能です。管理用のシングルトンオブジェクトを用意し、その MonoBehaviour にデータを持たせておけば、どのシーンからでも同じインスタンス経由でアクセスできます。

public class GameManager : MonoBehaviour
{
    public static GameManager Instance { get; private set; }
    public int SelectedCharacterId;

    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }
}

static 変数と違い MonoBehaviour のライフサイクルに乗るため、Update や OnDestroy などの処理も自然に書けるのが利点です。
一方で、同じシーンが複数回ロードされたときに二重生成を防ぐガード処理が必須になる点、Inspector で参照を設定できない他シーンのオブジェクトに対しては FindObjectOfType などで取得する必要がある点には注意してください。

方法 3: ScriptableObject にデータを保存する

ScriptableObject は、シーンやゲームオブジェクトから独立して存在できるデータコンテナです。アセットとしてプロジェクトに保存され、複数のシーンやコンポーネントから共通で参照できます。

[CreateAssetMenu(menuName = "GameData/PlayerStatus")]
public class PlayerStatus : ScriptableObject
{
    public int SelectedCharacterId;
    public int LastScore;
}

各シーンに配置した MonoBehaviour に ScriptableObject を Inspector からアサインしておけば、どのシーンからでも同じアセットを読み書きできます。データの流れが Inspector 上で可視化される点、単体テストが書きやすい点が大きな利点です。
注意点として、ScriptableObject の値はエディタのプレイ中に変更するとアセット自体に書き込まれ、プレイ停止後も変更が残ります。ランタイム専用のデータとして使う場合は、シーンのロード時に初期値へリセットする仕組みを用意するか、エディタ拡張でリセットできるようにしておくと安心です。

方法 4: PlayerPrefs や JSON ファイルに永続化する

アプリを再起動しても値を保持したい場合は、ストレージに書き出しておく必要があります。手軽なのは PlayerPrefs、構造化されたデータを扱いたい場合は JsonUtility と Application.persistentDataPath の組み合わせが定番です。

// PlayerPrefs
PlayerPrefs.SetInt("LastScore", 1200);
PlayerPrefs.Save();
int score = PlayerPrefs.GetInt("LastScore", 0);

// JSON
var status = new PlayerStatusData { SelectedCharacterId = 3, LastScore = 1200 };
string path = Path.Combine(Application.persistentDataPath, "status.json");
File.WriteAllText(path, JsonUtility.ToJson(status));

var loaded = JsonUtility.FromJson<PlayerStatusData>(File.ReadAllText(path));

この方法は、再起動後も保持したいセーブデータや設定値に適しています。シーン遷移のたびに読み書きするとストレージ I/O のコストがかかるため、短時間のデータ受け渡しには向きません。前述の 3 つの方法と組み合わせ、必要なタイミングだけ永続化する構成が現実的です。

4 つの方法の使い分け

それぞれの特徴を踏まえた使い分けの目安は以下のとおりです。

  1. 一時的でちょっとした値の受け渡し → static 変数
  2. シーンをまたいで処理やライフサイクルを維持したい → DontDestroyOnLoad
  3. 複数シーンで参照する設計寄りのデータ → ScriptableObject
  4. アプリ再起動後も残したいセーブデータ → PlayerPrefs / JSON

実際のプロジェクトでは、これらを単独で使うよりも組み合わせて運用するケースがほとんどです。たとえば、ランタイム中の状態は ScriptableObject で管理し、セーブポイントでのみ JSON に書き出すといった構成は定番です。

まとめ

Unity でシーン間のデータを受け渡す方法には、static 変数・DontDestroyOnLoad・ScriptableObject・PlayerPrefs / JSON の 4 つの選択肢があります。データの用途と保持したい期間に応じて適切な方法を選び、必要に応じて組み合わせることで、見通しの良いデータフローを設計できます。
シーン遷移のたびに迷っている方は、本記事で紹介した使い分けを参考に、プロジェクトに最適な方法を選んでみてください。