Hierarchy上に複数のシーンが存在する場合、GameObject
がどこのシーンにあるかを調べる必要があったので方法を調べてみました。
ピンポイントでそのようなことが分かる関数などがあればよかったのですが、ちょこっと調べた限りでは見つからなかったので、Unity既存の関数を組み合わせることで実現しました。
この記事にはUnity5.6.0f3を使用しています。
- [2018/06/25追記] 簡単に取得できました
- シーン直下にあるGameObjectを取得しよう
- GameObjectのルートオブジェクトが誰なのかを調べよう
- 各ルートオブジェクトを比較してみよう。
- DontDestroyOnLoadのオブジェクトの場合はどうなるの
- 所感
[2018/06/25追記] 簡単に取得できました
いろいろとこの記事で試行錯誤をしているのですが、
scene
プロパティをGameObject
が持っているので、それにアクセスすれば簡単にシーン情報が取得できます。
www.urablog.xyz
……そのため、この記事の以下の情報は見なくても大丈夫なのです。
シーン直下にあるGameObjectを取得しよう
各シーンの情報を持つScene
クラスには、GetRootGameObjects()
という関数があります。
この関数を使うことでルートオブジェクトが取得できます。
Scene
クラスはSceneManager
クラス経由で取得できます。
たとえばシーンがこのような状態の場合。
A,B,C,D,E,FのGameObjectが存在し、
Bの子にC。
Cの子にE。
Dの子にF。
この状態で以下のスクリプトのコンポーネントを使って、GetRootGameObjects()
を呼ぶとどのGameObject
が返るか見てみましょう。
using UnityEngine; using UnityEngine.SceneManagement; public class TestComponent : MonoBehaviour { private void Start() { Scene scene = SceneManager.GetSceneByName( "SceneA" ); GameObject[] rootObjects = scene.GetRootGameObjects(); foreach( var obj in rootObjects ) { Debug.LogFormat( "RootObject = {0}", obj.name ); } } } // class TestComponent
RootObject = Object A
RootObject = Object B
RootObject = Object D
シーン直下においてあるGameObject
であるA,B,Dが取得できることが確認できました。
GameObjectのルートオブジェクトが誰なのかを調べよう
GameObject
が持つTransform
のプロパティroot
は、このTransform
の親をたどっていき、ルート直下にあるGameObject
のTransform
を取得することが出来ます。
自分自身がルートオブジェクトの場合は自分自身が返るのでnullになることはないみたいです。
先ほどのシーンにて、各オブジェクトに以下のスクリプトのコンポーネントを付けてどのような値が返るか調べてみましょう。
using UnityEngine; public class TestObject : MonoBehaviour { private void Start() { Transform rootTransform = transform.root; GameObject rootObject = rootTransform.gameObject; Debug.LogFormat( "Object={0}, RootObject={1}", gameObject.name, rootObject.name ); } } // class TestObject
Object=Object A, RootObject=Object A
Object=Object B, RootObject=Object B
Object=Object C, RootObject=Object B
Object=Object D, RootObject=Object D
Object=Object E, RootObject=Object B
Object=Object F, RootObject=Object D
シーン直下においてあるGameObject
が取得できることが確認できました。
各ルートオブジェクトを比較してみよう。
これで各シーン上にあるルートオブジェクトと各GameObject
のルートオブジェクトが取得できました。
あとはこれらを比較してあげることで、このGameObject
がどのシーンにあるものなのかがわかりそうですね。
public string GetSceneContainObject( GameObject i_object ) { GameObject rootObject = i_object.transform.root.gameObject; for( int i = 0; i < SceneManager.sceneCount; ++i ) { Scene scene = SceneManager.GetSceneAt( i ); foreach( var sceneRootObject in scene.GetRootGameObjects() ) { if( sceneRootObject == rootObject ) { return scene.name; } } } return null; }
このような複数のシーンがある状態で、各GameObjectで先ほどの関数を呼んでみましょう。
Object=Object A, Scene=SceneB
Object=Object B, Scene=SceneA
Object=Object C, Scene=SceneA
Object=Object D, Scene=SceneC
Object=Object E, Scene=SceneA
Object=Object F, Scene=SceneC
これで指定したGameObject
がどのシーンにあるかがわかるようになりました。
先ほどの関数を少し変えれば、指定したGameObject
が現在アクティブなシーンにあるかの判定などもできたりします。
public bool IsObjectInActiveScene( GameObject i_object ) { GameObject rootObject = i_object.transform.root.gameObject; Scene scene = SceneManager.GetActiveScene(); foreach( var sceneRootObject in scene.GetRootGameObjects() ) { if( sceneRootObject == rootObject ) { return true; } } return false; }
先ほどのシーンで試すと以下のような結果になります。
Object=Object A, IsinActiveScene=False
Object=Object B, IsinActiveScene=True
Object=Object C, IsinActiveScene=True
Object=Object D, IsinActiveScene=False
Object=Object E, IsinActiveScene=True
Object=Object F, IsinActiveScene=False
DontDestroyOnLoadのオブジェクトの場合はどうなるの
先ほどまで使っていたシーンに新しくオブジェクトGを追加してみましょう。
このオブジェクトGはアクティブなシーンであるSceneA内にありますが、Awake()
にてDontDestroyOnLoad()
を呼んでシーン移動では破棄されない状態にします。
この状態ではどのような結果になるのでしょうか。
先ほどのコンポーネントを使って検証してみます。
Object=Object G, Scene=
Object=Object G, IsinActiveScene=False
どのシーンにも所属していないことがわかりました。
なるほどDontDestroyOnLoad()
を使ったGameObject
は、各シーンと関係が解消されるようですね。
所感
GameObject
がどのGameObject
の子なのか親なのかを判断が必要な場面に比べれば、どのシーンにあるかを気にすることは少ないため、今回の件を利用できる機会は少ないかとは思います。
それでも、いろいろな関数やプロパティを組み合わせることで、何とかなるみたいですね。