【Unity】StartメソッドよりOnTriggerイベントが先に実行されるケース
一般的には、Startメソッドが先に実行されますが、特定のケースにおいてはOnTrigger等のイベントが先に発生(実行される)ケースがありますので注意が必要です
シーン構成
3Dプロジェクトで、Cube(デフォルト)とSpawner(空のゲームオブジェクトを作成して名前を変更)を追加します
Spawnerには、Spawnerという名前のスクリプトがアタッチされています
Cube
当たり判定の相手側になります
Spawner
空のゲームオブジェクトを作成して、名前を"Spawner"に変更します
Spawnerスクリプトをアタッチします
using UnityEngine;
public class Spawner : MonoBehaviour
{
[SerializeField]
GameObject spawnPrefab; // スポーンさせるPrefabの参照を保持する変数
void Start()
{
Instantiate(spawnPrefab); // ゲーム開始時にPrefabをインスタンス化(生成)する
}
}
[SerializeField]
属性を使用することで、Unityエディター上で直接、生成したいプレハブをspawnPrefab
変数に割り当てることができます。Start
メソッドはゲームオブジェクトが有効になったときに一度だけ呼び出され、その中でInstantiate
関数を使ってプレハブを実際にシーンに生成します。
インスペクターのSpawnerPrefabには、次の作成するプレファブをアウトレット接続します
Sphereプレファブ
最初は、シーンに登場していません
SpawnerスクリプトでInstantiateされます
作成手順
- まず、シーン上でSphereを作成します
- 次にコライダーの直径を大きくします(下のキャプチャー参考)
ちょうど、Instantiateで生成された時にCubeを覆うようにします
これで、このプレファブが生成された時に、トリガーが検出されるようにします - LifeCycleMethodスクリプトをアタッチします
どのメソッドが先に実行されるか確認するためのテスト用のスクリプトになります - Projectビューにドラッグ&ドロップしてPrefabを作成します
- 最後にシーンからSphereを削除します
トリガーイベント検出用のスクリプト
using UnityEngine;
public class LifeCycleMethod : MonoBehaviour
{
// Awakeメソッドは、ゲームオブジェクトがロードされた時に一度だけ呼ばれます。
void Awake()
{
Debug.Log("Awake");
}
// Startメソッドは、Awakeメソッドの直後に一度だけ呼ばれますが、
// オブジェクトがアクティブになった最初のフレームの前に呼ばれます。
void Start()
{
Debug.Log("Start");
}
// OnTriggerEnterメソッドは、このゲームオブジェクトがトリガーとして設定されているColliderに
// 他のColliderが入ったときに呼ばれます。
void OnTriggerEnter(Collider other)
{
// 接触したオブジェクトの名前をログに出力します。
Debug.Log(other.name + "と接触");
}
}
実行結果
Awake
Cubeと接触
Start
実際の開発で起こり得ること
Startメソッドには初期化を記述しておけば、他のメソッドで初期化後の値が使えると思いますよね
ケース
期待値として、Life=10とコンソールに表示されることですが、実際はLife=0となりますね
using UnityEngine;
public class LifeCycleMethod : MonoBehaviour
{
int life;
// Startメソッドは、オブジェクトが初めて有効になった時に1回だけ呼ばれます。
// ここでは、オブジェクトのライフ(生命力)を初期化しています。
void Start()
{
life = 10; // ライフを10に設定
}
// OnTriggerEnterメソッドは、このオブジェクトがColliderを持っていて、
// トリガーとして設定されている場合に、他のColliderがこのオブジェクトのトリガー領域に入った時に呼ばれます。
// ここでは、トリガー領域に他のオブジェクトが入った時にライフの値をログに出力しています。
void OnTriggerEnter(Collider other)
{
Debug.Log("Life=" + life); // ライフの現在値をログに出力
}
}
対処
確実な初期化を行うためにStartメソッドではなく、Awakeメソッドに初期化を記述します
using UnityEngine;
public class LifeCycleMethod : MonoBehaviour
{
int life;
// Startメソッドは、オブジェクトが初めて有効になった時に1回だけ呼ばれます。
// ここでは、オブジェクトのライフ(生命力)を初期化しています。
void Awake()
{
life = 10; // ライフを10に設定
}
// OnTriggerEnterメソッドは、このオブジェクトがColliderを持っていて、
// トリガーとして設定されている場合に、他のColliderがこのオブジェクトのトリガー領域に入った時に呼ばれます。
// ここでは、トリガー領域に他のオブジェクトが入った時にライフの値をログに出力しています。
void OnTriggerEnter(Collider other)
{
Debug.Log("Life=" + life); // ライフの現在値をログに出力
}
}
Startメソッドに初期化を記述してもうまくいくケース
- 最初からゲームオブジェクトをシーンに配置しておく
- Instantiateで生成直後には、トリガーが検出されない場合
コライダーが小さければトリガー検出されないですね
解説
Instantiate
関数を使用してゲームオブジェクトを動的に作成した場合、そのゲームオブジェクトにアタッチされているスクリプトも同様にUnityのライフサイクルイベントに従います。Instantiate
で作成されたオブジェクトは、作成された瞬間にアクティブになる前提で話を進めます(Instantiate
されたオブジェクトがデフォルトでアクティブである場合)。この場合、ライフサイクルイベントは以下の順序で発生します:
Awake
: このメソッドは、ゲームオブジェクトがインスタンス化された直後に呼び出されます。まだフレームの更新は行われていません。この段階で、初期化処理を行うことが一般的です。OnEnable
: オブジェクトがアクティブになった場合に呼び出されます。Instantiate
直後にオブジェクトがアクティブであれば、Awake
の直後にこのメソッドが実行されます。Start
: オブジェクトが初めてアクティブになったフレームの、最初のUpdate
メソッドが呼び出される前に一度だけ実行されます。Awake
やOnEnable
と異なり、Start
はそのオブジェクトの最初のアクティブなフレームにのみ呼び出されることに注意してください。Update
,FixedUpdate
,LateUpdate
: これらのメソッドは、オブジェクトがアクティブである限り、各フレーム(Update
,LateUpdate
)または物理更新のたび(FixedUpdate
)に呼び出されます。
Instantiate
で作成されたオブジェクトにアタッチされたスクリプトがOnTriggerEnter
やその他のコリジョンイベントを含む場合、それらのイベントは物理演算の結果に基づいて発生します。つまり、オブジェクトがアクティブになり、物理演算が実行された後に、これらのイベントが発生する可能性があります。これは、Instantiate
によって作成されたゲームオブジェクトが他のオブジェクトと接触している場合に、Start
メソッドよりも先にOnTriggerEnter
が呼び出される状況を生み出すことがあります。
ディスカッション
コメント一覧
まだ、コメントがありません