親要素を持つGameObjectをDontDestroyOnLoadさせる

需要があるかどうかは分からないですけど、自分のメモ程度に少しまとめてみます。ただ内容的には至極単純ではあります。

シーンを跨いで使いたいGameObject(例えば、Managerクラスだったり、BGMだったり)があるときはDontDestroyOnLoad() を使って管理しているかと思います。ただ、このDontDestroyOnLoad() はルート階層にあるGameObjectでないと無視されて、下記のワーニングが出ちゃいます。

DontDestroyOnLoad only work for root GameObjects or components on root GameObjects.

補足しておくとルート階層はHierarchy上で親要素を持っていない状態の階層のことになります。

それで、じゃあこの問題をどうやって解決するかなんですが、一番シンプルなのはDontDestroyOnLoad() させたいGameObjectをルート階層に持ってくれば解決です。なんですが、それだとタイトルの趣旨と変わってしまうので違う方法で解決を考えます。

違う方法を書く前に、なぜ自分がその方法が必要だったかというのを少し説明すると、問題はシーンの構成管理に依存していました。人やチームによって様々だと思いますが、自分はシーンの構成をジャンル?別にナンバリングしてそれぞれ必要なオブジェクトやコンポーネントをその下の階層で管理する方法を取っていました。そうしたときに、シーンを跨いで使いたいGameObjectもルート階層の下に入っちゃっていたんですよね。そこから出すのは簡単なんですけど、何となく見栄えがよろしくなく+別々に管理したオブジェクトも有り・・・みたいな感じでした。

さて、話は戻って、親要素を持つ GameObject を DontDestroyOnLoad() させましょう。ここまでちゃんと読んでくれた人は察しが付いてるとは思いますが、要は親要素を失くしてルート階層に持っていけば済む話です。とてもシンプルです。コードも一行で済みました。

public class ChildDontDestroy : MonoBehaviour 
{
    void Start () {
        transform.parent = null;
        DontDestroyOnLoad(this.gameObject);
    }
}

transform.parent に親要素の情報を持っているので、そこを空にしてあげることによってそのGameObject はルート階層に移動します。そうすれば、問題なくDontDestroyOnLoad() できるということになります。