今回はUnityでシーン移動時のデータやゲームオブジェクトの引き継ぎ方法を考えます。
通常であればシーン移動をする時にゲームオブジェクト等は引き継がれません。
変数の値を次のシーンでも使いたい時はスクリプトのフィールド等をstaticにしておけばシーン移動後もその値を使用する事が出来ます。
実際に試して確認してみます。
シーン間のデータの引継ぎはScriptableObjectを使った方法もあります。
シーン移動時にゲームオブジェクトとデータを引き渡す
まずはタイトル画面からゲーム画面にシーンが移動するとします。
タイトル画面シーンをSceneDataStartScene、ゲーム画面シーンをSceneDataGameSceneとして作成し、UnityメニューのFile→Build Settings…で設定しておきます。
簡単なシーン間のデータ共有
タイトル画面のシーンにGameSystemというゲームオブジェクトを作成し、Add Componentで新しいスクリプトGameStartを設定します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | using UnityEngine; using System.Collections; using UnityEngine.SceneManagement; public class GameStart : MonoBehaviour { public static int gameData1 = 50; // スタートボタンを押したら実行 public void OnClickGameStart() { SceneManager.LoadScene("SceneDataGameScene"); } |
staticでgameData1というint型のフィールドを宣言し、50を入れておきます。
上がサンプルのタイトルシーン(SceneDataStartScene)でスタートボタンを押すとOnClickGameStartメソッドを呼び出し、ゲームシーン(SceneDataGameScene)に移動するようにしておきます。
ゲームシーンに移動するとGameSystemオブジェクトは引き継がれません。
ゲームシーンに配置しているゲームオブジェクト(どれでもOK)にGetStaticDataというスクリプトを作成し取りつけます。
1 2 3 4 5 6 7 8 9 10 11 | using UnityEngine; using System.Collections; public class GetStaticData : MonoBehaviour { void Start () { Debug.Log (GameStart.gameData1); } } |
GameStartのgameData1を表示させようとしています。
実際に実行するとちゃんと50がConsoleに出力されました。
データの引き継ぎが出来る事はわかりましたが、ゲームオブジェクト自身が引き継がれていないのに、データだけ引き継がれているというちょっと不可思議な状態になってしまいます。
これはstaticなフィールドやプロパティ、メソッドはインスタンス化しなくても
クラス名.フィールド
クラス名.メソッド
でアクセス出来るからです。
staticが付いたものは静的メンバーと呼ばれます。
Mathf.RandomやVector3.zero等がそうですね。
その為、シーンをまたいだデータを使いたい時は
1 2 3 4 5 6 7 8 9 10 | using UnityEngine; using System.Collections; public class SampleData : ScriptableObject { public static int sampleData = 50; } |
ScriptableObjectを継承する形でクラスを作成(ゲームオブジェクトに取り付けない為)し、シーン間でずっと使いたいデータをstaticで定義します。
1 2 3 4 | Debug.Log(SampleData.sampleData); SampleData.sampleData = 20; |
のように使用する事が出来ます。
ただ、なんでもかんでもstaticでデータを保持するとどこからでもデータにアクセス出来てしまう為、使い道はよく考えた方がいいかもしれません。
ゲームオブジェクトを次のシーンに残す
主人公をシーンをまたいで残したい、その他オブジェクトも次のシーンで使いたい場合もあると思います。
次のシーンは新しく主人公やその他オブジェクトを配置するという事も出来ますが、かなり面倒です。
そこでシーンをまたいでゲームオブジェクトを残す機能を追加します。
1 2 3 4 5 | void Awake () { DontDestroyOnLoad(gameObject); } |
GameStartスクリプトに上のように記述します。
DontDestroyOnLoadは引数にゲームオブジェクトを指定すれば、そのゲームオブジェクトがシーンを移動する時に破棄されず、次のシーンにも登場するようになります。
上がタイトルシーンに配置されているゲームオブジェクトです。
GameSystemにはGameStartスクリプトを設定し、GameStartではゲームオブジェクトを次のシーンに残す処理が記述されています。
それではタイトルシーンのスタートボタンを押して、ゲームシーンに移動してみます。
↑のように元々カメラとライトしか設置していないシーンですが、タイトルシーンからゲームシーンに移動してもGameSystemオブジェクトが残るようになりました。
ゲームオブジェクトの引き渡しをする事が出来るようになりました。
シーンを移動した時に主人公等はステータス状態もあるのでそのまま引き継げるのは楽になりますね!
ゲームシーンだけで確認する為に
シーンをまたいでゲームオブジェクト、スクリプトを残す事は出来ましたが、
このままだと最初のシーンから始めないとゲームシーンのテストプレイが出来なくなってしまいます。
なぜなら次のシーンを開いてテストプレイしようと思ったら最初のシーンから引き継いで使う事になっているゲームオブジェクトとスクリプトが存在しないからです。
これでは困るのでその対処をしましょう。
ここら辺の処理は
Unityのライブトレーニングを参考にさせて頂きました。
まずはSceneDataGameSceneにSceneDataStartSceneに作ったGameSystemゲームオブジェクトをCtrl+Cでコピーし、貼り付けます。
↑のようにSceneDataGameSceneにもGameStartスクリプトを取り付けたGameSystemゲームオブジェクトがあるのでゲームシーンだけでテストプレイをする事が出来ます。
GameStartスクリプトを変更します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | using UnityEngine; using System.Collections; using UnityEngine.SceneManagement; public class GameStart : MonoBehaviour { public static GameStart singleton; public static int gameData1 = 50; public int gameData2 = 30; private int gameData3 = 10; private int[] gameData4 = new [] {0, 1, 5 }; void Awake() { // スクリプトが設定されていなければゲームオブジェクトを残しつつスクリプトを設定 if (singleton == null) { DontDestroyOnLoad (gameObject); singleton = this; // 既にGameStartスクリプトがあればこのシーンの同じゲームオブジェクトを削除 } else { Destroy (gameObject); } } // スタートボタンを押したら実行 public void OnClickGameStart() { // 静的メンバのデータを書き換え GameStart.gameData1 = 3; // シングルトンインスタンスにデータをセット GameStart.singleton.SetGameData3 (5); // 配列の1番目のデータを50に書き換え GameStart.singleton.SetGameData4(1, 50); SceneManager.LoadScene("SceneDataGameScene"); } public int GetGameData3() { return gameData3; } public void SetGameData3(int data) { this.gameData3 = data; } public int GetGameData4(int index) { return gameData4[index]; } public void SetGameData4(int index, int value) { this.gameData4[index] = value; } } |
GameStartスクリプトにstaticなGameStartスクリプトを設定するsingletonフィールドを作成します。
Awakeメソッドでsingletonが設定されていなければ、このゲームオブジェクトを次のシーンに残し、singletonに自身のスクリプト(GameStart)を設定します。
singletonがすでに設定されている場合はすでにタイトルシーンでGameStartスクリプトがsingletonに設定されているので、ゲームシーンのゲームオブジェクトを削除します。
else以降に行くのはゲームシーンのGameSystemゲームオブジェクトに設定されたGameStartスクリプトのAwakeが呼ばれた時になりますね。
データの引き継ぎが出来るかどうか確認する為、OnClickGameStartメソッドでstaticなデータとprivateなデータにアクセスし値を書き換えます。
値を書き換えたデータはゲームシーンで取り付けたGetStaticDataスクリプトで確認する事にします。
1 2 3 4 5 6 7 8 9 10 11 12 13 | using UnityEngine; using System.Collections; public class GetStaticData : MonoBehaviour { void Start () { Debug.Log (GameStart.gameData1); Debug.Log(GameStart.singleton.GetGameData3()); Debug.Log(GameStart.singleton.GetGameData4(1)); } } |
後は確認してみましょう。
タイトルシーンからゲームシーンへと遷移させるとコンソールには
GameStart.gameData1は3
GameStart.singleton.GetGameData3()は5
GameStart.singleton.GetGameData4(1)は50
と表示され、データの引継ぎが出来ているのを確認出来ます。
ゲームシーンから開き実行するとデータを書き換えていないので
GameStart.gameData1は50
GameStart.singleton.GetGameData3()は10
GameStart.singleton.GetGameData4(1)は1
が表示されます。
ゲームオブジェクトとデータの引き継ぎの両方が出来ている事が確認出来ました。