OnEnableで購読する
CoddyのUnityジャーニー「Unityにおけるゲームアーキテクチャ」セクションの一部。レッスン 11/50。
破棄されたリスナーは、それだけではイベントから抜けません。イベントは引き続きそのメソッドを保持し、次の 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);
}
}オブジェクトが SetActive(false) でオフにされたとき、スクリプトが無効にされたとき、またオブジェクトが破棄されたとき、Unity は OnDestroy の前に OnDisable を呼び出します。再び有効になるたびに OnEnable が実行されます。この組み合わせにより、ガードは有効な間だけイベントをリッスンします。破棄されたガードはイベントから取り除かれ、カットシーンのためにオフにされたガードはアラームを無視し、再び有効にするとまたリッスンします。
ほかにも、うまくいかない組み合わせが2つあります。Start で += を使い、OnDisable で -= を使うと、Start は一度しか実行されないため、オフにしてから再びオンにしたガードは二度とリッスンしません。OnEnable で += を使い、-= を使わないと、再び有効になるたびに RunTo が追加され、1回のアラームで2回呼び出されます。登録されていないメソッドを削除しても何も起こらないため、OnDisable の -= で確認を行う必要はありません。
チャレンジ
簡単スペースキーを押すたびに村の鐘が鳴り(Bell.cs は準備済み)、そのイベント OnRing が村人に通知します。村人は鐘が鳴るたびに跳ね、ringsToLeave 回鳴ると家に帰ります。Hop が村人を破棄します。Ana は2回鳴ると家に帰ります。3回目に鳴らすと、Console に MissingReferenceException が表示されます。
村人がアクティブな間だけ鐘を監視するように、Villager.cs を修正してください。
- 村人が有効になるたびに、鐘のイベントに
Hopを登録し、村人が無効化または破棄されるときに登録を解除してください。HopとUpdateは変更しないでください。 SetActive(false)で無効化された村人は跳ねません。再び有効にすると、鐘が鳴るたびに1回跳ねます。
Play を押し、鐘を4回鳴らして Console を確認し、それから Check を押してください。
自分で試してみよう
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);
}
}
}
このレッスンには短いクイズがあります。レッスンを始めて解答し、進捗を記録しましょう。