困った時の自分用メモ

読んだ本を考察してメモったり、自分でいじった物の感想をメモったりする場。週1更新を目指します。

Unityの話~ゲーム開始時に一番最初に実行される処理の実装~

Unityでゲーム開発をしていると、MonoBehaviourのAwakeやStartよりも、先に処理を実行したいと思う事がある。
今回は、その方法や、処理順の検証をしてみた。

サンプルプロジェクト

github.com

〇同一シーン上に、複数MonoBehaviourが存在する場合に、明示的に実行順を指定する方法 Edit -> Project Settings -> Script Execution Order

f:id:mochimoffu:20180420085735p:plain

〇必ず一番初めに処理が走るようにする方法
ここを参考。
tsubakit1.hateblo.jp

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

public class MustFirstStart : MonoBehaviour {

    //// これだと、AwakeとStartの後になってしまう
    //[RuntimeInitializeOnLoadMethod()]
    //static void EntryPoint() {
    // //new GameObject ("sample");
    // Debug.Log("MustFirstStart EntryPoint");
    //}

    // これで、AwakeとStartの前になる
    [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
    static void EntryPoint() {
        //new GameObject ("sample");
        Debug.Log("MustFirstStart EntryPoint");
        GameObject obj = new GameObject("EntryPoint");
        EntryPoint c = obj.AddComponent<EntryPoint>();
        c.Initialize();
    }

    // Use this for initialization
    void Awake () {
        Debug.Log("MustFirstStart Awake");
    }

    // Use this for initialization
    void Start () {
        Debug.Log("MustFirstStart Start");
    }
}

[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
という指定をしてあげればいいようだ。
処理の中でゲームオブジェクトを作成して、EntryPointをAddComponentしている理由は、この中ではコルーチンが使えなさそうだったからだ。

〇コルーチンと処理順

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

public class EntryPoint : MonoBehaviour {

    // Use this for initialization
    public void Initialize () {
        Debug.Log("EntryPoint Initialize");
        StartCoroutine(CoInitialize());
    }

    public IEnumerator CoInitialize () {
        Debug.Log("EntryPoint CoInitialize before");
        // ↑ここまでは、同一フレーム内で実行される

        // ↓ここで処理が返ると、他のMonoBehaviourのStartやAwakeが呼び出されてしまい
        // 初期化の役割を果たせなくなる
        // なので、初期化の役割を果たさせる場合は
        // 1.コルーチンを使わないようにするか
        // 2.有効となるシーンの先々で、ここで行う初期化が完了しているかどうかを待つ
        // 3.この初期化が終わるまで、シーン上に有効なオブジェクトを配置しない。つまり、ここで配置を行うようにする
        // どれかになるのではないかと思われる。
        yield return null;
        Debug.Log("EntryPoint CoInitialize after");
    }

    // Use this for initialization
    void Awake () {
        Debug.Log("EntryPoint Awake");
    }

    // Use this for initialization
    IEnumerator Start () {
        // 今回の実験と関係ないけど、IEnumeratorが戻り値のStartは、デフォで用意されているっぽい
        // 自分ではStartCoroutineやMoveNextは呼び出していないが、自動的に最後の処理まで実行される
        Debug.Log("EntryPoint CoStart before");
        yield return null;
        Debug.Log("EntryPoint CoStart after");
    }
}

上記で指定したメソッド内コルーチンが返ると、他のAwakeなどが順次実行されてしまう為、 それらに初期化されていないと使えないものが存在していた場合に困る為、ハンドリングを考える必要がある。