Unityのシーン間でデータをやり取りする4つの方法

Unityのシーン間でデータをやり取りする4つの方法について説明しようと思います。

ここで紹介するのは以下の4つの方法です。

  • DontDestroyOnLoad を用いる方法
  • 同時に複数のシーンを用いる方法
  • スクリプタブルオブジェクトを用いる方法
  • sceneLoadedを用いる方法

まずはシーン遷移の準備

Unityでシーンを遷移するためには、まずビルドセッティングにシーンを登録する必要があります。ここに登録したシーンにSceneManager.LoadSceneを用いることにより遷移することができます。

DontDestroyOnLoadを用いてオブジェクトを保持する

public static void DontDestroyOnLoad (Object target);を用いることにより、オブジェクトを破棄せずにシーンを遷移することができます。

下記のプログラムSceneArticle005_001_Beginを遷移前のシーンの何らかのオブジェクトにアタッチしてシーンを遷移することにより内容を保持したまま次のシーンに遷移できます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneArticle005_001_Begin : MonoBehaviour
{
    public string dontDestroyString;
     void Awake()
    {
        Object.DontDestroyOnLoad(this);        
    }

    private void Start()
    {
        dontDestroyString = "最初のシーンDontDestroy";
        SceneManager.LoadScene("SceneArticle005_001_After");
    }
}

上記コードについ簡単に説明します。

  • 4行目 using UnityEngine.SceneManagement; はシーン遷移に必要なクラスや関数を用いるために必要なusingです。具体的には17行目のSceneManagerを用いるために必要となります。
  • 9行目Awake()は、MonoBehaviourを継承したクラスが持つ関数であり、簡単に言えばStartより前に呼ばれる関数です。もう少し踏み込んでいえば、スクリプトが有効化されていない場合でも実行される関数です。
  • 11行目 Object.DontDestroyOnLoad(this); でシーン遷移により、自身のオブジェクトが自動的に削除されないようにしています。
  • 18行目 SceneManager.LoadScene(“SceneArticle005_001_After”); で目的のシーンに遷移します。

次に遷移後のシーンで用いるスクリプトを示します。

下記のプログラムSceneArticle005_001_Afterでは、11行目で、DontDestroyOnLoadに登録したオブジェクトを取り出しています。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SceneArticle005_001_After : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    { 
        Debug.Log("遷移先での呼び出し");
        Debug.Log(GameObject.Find("DontDestroyGameObject").GetComponent<SceneArticle005_001_Begin>().dontDestroyString);
    }    
}


11行目の、GameObject.Find で DontDestroyOnLoad に登録したオブジェクトを探しています。GameObject.Find で探す以外にも、staticな変数に、あらかじめ参照を保持しておく方法もあります。

実行する前に、遷移前のシーンと遷移後のシーンにそれぞれのオブジェクトをアタッチしておきましょう。ここでは、新しく作ったゲームオブジェクトにアタッチしていますが、別にカメラや、ライトにアタッチしても問題ないはずです。また、青丸のチェックマークを外すと、コンポーネントが無効化されますが、Awake関数だけは実行されます。

実行結果

シーン遷移中に音を鳴らしたい場合などには、DontDestroyOnLoad を用いることにより、シーン遷移中にも途切れず音を鳴らすことができます。

同時に複数のシーンを用いる方法

Unityでは、複数のシーンを同時に開くことができます。こちらの方法では、2つのシーンを開き最初のシーンのオブジェクトを後に開くシーンに移動させるという処理を行います。

下記のプログラムSceneArticle005_002_Beginでは、現在のシーンを開いたまま、次のシーンを開きオブジェクトを次のシーンに移動させています。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneArticle005_002_Begin : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        SceneManager.LoadScene("SceneArticle005_002_After", LoadSceneMode.Additive);
        SceneManager.MoveGameObjectToScene(this.gameObject, SceneManager.GetSceneByName( "SceneArticle005_002_After"));
    }
}


ソースコードの説明をいたします。

  • 11行目 SceneManager.LoadScene には第2引数があります。その引数のデフォルトは、LoadSceneMode.Single、です。明示的に、LoadSceneMode.Additiveを指定することにより複数シーンを同時に開くことができます。
  • 12行目 public static void MoveGameObjectToScene(GameObject go, SceneManagement.Scene scene);により、シーン間でオブジェクトを移動させることが可能です。ここで、goが遷移対象オブジェクトであり、sceneが、遷移先のシーンです。

次に遷移後のシーンで用いるスクリプトを示します。

この方法では遷移前のシーンが残ってしまうので、後片付けが必要です。11行目のように用が済んだら片づけてしまいましょう。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;

public class SceneArticle005_002_After : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        SceneManager.UnloadSceneAsync("SceneArticle005_002_Begin");
    }
}

LoadSceneMode.Additiveを用いる方法は、非常に柔軟に扱える良い方法です。例えば、一つのGameManager用のシーンを常に常駐させておいて、ゲーム全体の管理や音を鳴らすなどの使い方が基本です。ただし、この方法を使う場合には、LoadSceneMode.Singleを用いたら、他のシーンが閉じてしまうので、必ず、LoadSceneMode.Additive を使うこと、そして、使用したシーンの後片付けを自分で行うことが求められます。

スクリプタブルオブジェクトを用いる方法

スクリプタブルオブジェクトを用いる場合には、簡単なエディタ拡張が必要となります。まず、エディタ拡張のソースコードを示します。

以下のスクリプトで、stringを一つ保持する、スクリプタブルオブジェクトを扱えるようになります。

using UnityEngine;
[CreateAssetMenu(fileName = "Data", menuName = "ScriptableObjects/Article005_003_Scriptable", order = 1)]
public class Article005_003_Scriptable : ScriptableObject
{
    public string ScriptableString;
}
  • 2行目 CreateAssetMenu では、作成にメニューを追加することを行っています。画像のほうがわかりやすいので、下で追加説明します。
  • 3行目 MonoBehaviour ではなく、ScriptableObjectを継承しています。ScriptableObject継承することにより、新しいスクリプタブルオブジェクトを定義し、エディタ上で作成することができるようになります。

CreateAssetMenu に関して簡単に説明します。

CreateAssetMenuを用いることにより、以下の図のように、エディタのメニューに新しい要素が追加されます。menuNameの値を変更することにより、そのメニューの名前を変更することが可能です。また、orderでメニューの位置を設定することが可能です。

fileNameを設定することにより、新しく作成するスクリプタブルオブジェクトのファイル名の初期値を設定することができます。

ScriptableObjectの値は、プログラムが実行されている限り保持されており、エディタ上で再生後、停止しても保持され続けます。この性質を用いて、一時的なデータの退避場所とすることも可能です。

以下が、スクリプタブルオブジェクトに書き込む最初のシーンのスクリプトです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SceneArticle005_003_Begin : MonoBehaviour
{
    [SerializeField] Article005_003_Scriptable scriptable;
    void Awake()
    {
        scriptable.ScriptableString = "スクリプタブルオブジェクトに最初のシーンで代入";
    }
    // Start is called before the first frame update
    void Start()
    {
        SceneManager.LoadScene("SceneArticle005_003_After");
    }
}
  • 7,10行目のように、 スクリプタブルオブジェクトは、通常のクラスと同じように扱うことが可能です。7行目でスクリプタブルオブジェクトの変数を定義し、10行目で、スクリプタブルオブジェクトのpublicなメンバ変数に書き込んでいます。尚、スクリプタブルオブジェクトを扱う場合には、あらかじめ使用するスクリプタブルオブジェクトをインスペクタ上で指定しておきましょう。

スクリプタブルオブジェクトを取り出してDebug.Logで出力するだけの処理です。あらかじめ、インスペクタ上で、スクリプタブルオブジェクトを設定するのを忘れないようにしましょう。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SceneArticle005_003_After : MonoBehaviour
{
    [SerializeField] Article005_003_Scriptable scriptable;
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log(scriptable.ScriptableString);
    }
}

複数のデータを取り扱う場合ソースコード上に書き込んだり、テキストファイルを読み込んだりいろいろな方法がありますが、スクリプタブルオブジェクトを用いるのも一つの方法です。自分の目的や好みに合った方法を用いましょう。

sceneLoadedを用いる方法

SceneManager には、sceneLoaded というデリゲートが用意されています。このデリゲートは、シーンがロードするタイミングで呼び出されるので、このデリゲートに登録した関数を通してデータをやり取りするという方法があります。

以下のスクリプトでは、SceneManager.sceneLoadedに、sceneLoadedFunctionを登録しています。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class SceneArticle005_004_Begin : MonoBehaviour
{
    string beginString;
    void Awake()
    {
        beginString = "最初のシーンで設定";
    }

    // Start is called before the first frame update
    void Start()
    {
        SceneManager.sceneLoaded += sceneLoadedFunction;
        SceneManager.LoadScene("SceneArticle005_004_After");
    }

    void sceneLoadedFunction(Scene scene, LoadSceneMode loadSceneMode)
    {
         new GameObject("GameObject").AddComponent<SceneArticle005_004_After>().afterString = beginString;
        SceneManager.sceneLoaded -= sceneLoadedFunction;
    }
}
  • 16行目では、 SceneManager.sceneLoadedに、20行目に定義した関数を登録しています。これにより、次にシーンがロードされた場合に、この関数が実行されます。
  • 17行目で、次のシーンをロードしています。シーンがロードされたタイミングで、SceneManager.sceneLoadedも実行されます。
  • 20行目、SceneManager.sceneLoadedに登録可能な関数の型は、 UnityAction<Scene, LoadSceneMode> です。UnityActionは戻り値を持たない型なので、void型の関数で代用可能です。Sceneには、遷移先のシーン、LoadSceneModeには、AdditiveSingleが入ります。
  • 22行目で新しく作成したシーンでGameObjectを作成しています。この時、遷移先のシーン用のコンポーネントをアタッチして、そのコンポーネントに渡したい文字列をそのまま渡しています。今回このスクリプトでGameObjectを作っているので、遷移先のシーンのヒエラルキーにゲームオブジェクトを追加したり、スクリプトをアタッチする必要はありません。シーンを作成しておくだけで大丈夫です。
  • 23行目、次にシーンをロードしたときに再度呼び出されることがないように、一度処理を実行したら、SceneManager.sceneLoadedのデリゲートからこの関数を削除します。

基本的な処理は、遷移前のシーンのスクリプトでやってしまっているので、こちらはDebug.Logで出力するだけです。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class SceneArticle005_004_After : MonoBehaviour
{
    public string afterString ;
    // Start is called before the first frame update
    void Start()
    {
        Debug.Log(afterString);
    }
}

デリゲートは使いこなせるといろいろなことに使えて便利です。

SceneManager には、3つのデリゲート

があります。適切なタイミングで、適切な関数を呼び出せるようになると、できることが広がるでしょう。

終わりに

上記のほかにも、セーブデータを使う方法などもあり、シーン間でデータをやり取りする方法は、いくつかあります。自身の目的に合った方法を用いましょう。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA