OnEnableで購読する
CoddyのUnityジャーニー「Unityのゲームアーキテクチャ」セクションの一部。レッスン 13/64。
破棄されたリスナーは、自分でイベントから抜けることはありません。イベントはメソッドを保持し続け、次の Invoke で破棄されたコンポーネントに対してそのメソッドを呼び出します。そのメソッドで最初に transform を使うと MissingReferenceException が発生し、例外が発生すると Invoke が終了するため、その後のリスナーもスキップされます。したがって、すべての += には -= が必要です。Unity には、そのためのメソッドの組み合わせとして OnEnable と OnDisable があります。
public class Guard : MonoBehaviour
{
public Alarm alarm;
void OnEnable()
{
alarm.OnTriggered += RunTo;
}
void OnDisable()
{
alarm.OnTriggered -= RunTo;
}
void RunTo(Vector3 spot)
{
transform.LookAt(spot);
}
}Unity は、SetActive(false) でオブジェクトをオフにしたとき、スクリプトを無効にしたとき、そしてオブジェクトを破棄したときに、OnDestroy の前に OnDisable を呼び出します。オブジェクトが再び有効になるたびに OnEnable が実行されます。この組み合わせなら、ガードは有効な間だけイベントをリッスンします。破棄されたガードはイベントから取り除かれ、カットシーンのためにオフにされたガードは警報を無視し、再びオンになるとまたリッスンします。
ほかにも、うまくいかない組み合わせが2つあります。Start に += を置き、OnDisable に -= を置くと、Start は一度しか実行されないため、オフにしてからオンにしたガードは二度とリッスンしません。OnEnable に += を置いて -= を置かないと、再び有効になるたびに RunTo が追加され、警報1回で2回呼び出されます。登録されていないメソッドを削除しても何も起こらないため、OnDisable の -= の前に確認する必要はありません。
チャレンジ
簡単各村人が有効な間だけ、ベルの音を聞くようにします。
Villager.csで、bell.OnRing += Hop;をStart()から新しいvoid OnEnable()に移動し、Start()を削除します。bell.OnRing -= Hop;を含むvoid OnDisable()を追加します。「確認」では、これをテストするためにCleoをオフにしてからオンにします。
「プレイ」を押して4回ベルを鳴らします。2回目の後にAnaが帰宅し、エラーは表示されません。その後、「確認」を押します。
自分で試してみよう
using UnityEngine;
public class Villager : MonoBehaviour
{
public Bell bell;
public int ringsToLeave = 3;
public int hops;
void Start()
{
bell.OnRing += Hop;
}
void Update()
{
// 跳ねた後、地面に戻る
Vector3 p = transform.position;
transform.position = new Vector3(p.x, Mathf.MoveTowards(p.y, 0.5f, 4f * Time.deltaTime), p.z);
}
void Hop(int ring)
{
hops++;
transform.position += Vector3.up * 0.8f;
if (hops == ringsToLeave)
{
Debug.Log(name + " goes home");
Destroy(gameObject);
}
}
}
Unityのゲームアーキテクチャのすべてのレッスン
自分で練習してみよう: Unityプレイグラウンド