#概要
UnityEventはC#のeventよりもほんのちょっとだけ便利に使える。
https://docs.unity3d.com/ScriptReference/Events.UnityEvent.html
利用にあたってのエラーケース等を調べる。
#環境
Unity5.5.2p4
#結果
-
nullは登録しちゃダメ (登録はできるがInvoke時に実行時例外になる)
-
リスナー登録時、重複チェックはしていない。
-
重複して登録しても1回のRemoveListenerで削除される。
-
GetPersistentEventCount()はいつもゼロ。
#テスト内容
ボタンを用意して押したときにUnityEventに登録されたリスナーにイベントを送信する。
##ボタン処理の実装
UnityEngine.UI.Buttonにスクリプトをアタッチした。
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace My
{
public class UnityEventInvoker : MonoBehaviour
{
[SerializeField]
Button _button;
public class OnPressedButton : UnityEvent<UnityEventInvoker> { };
public OnPressedButton onPressedButton = new OnPressedButton();
private void Awake()
{
EventTrigger eventTrigger = _button.gameObject.AddComponent<EventTrigger>();
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerDown;
entry.callback.AddListener(OnPressedButtonTest);
eventTrigger.triggers.Add(entry);
}
void OnPressedButtonTest(BaseEventData eventData)
{
if (!_button.interactable)
return;
Debug.Log("UnityEventInvoker.OnPressedButtonTest() onPressedButton.GetPersistentEventCount:" + onPressedButton.GetPersistentEventCount());
onPressedButton.Invoke(this);
}
public void OnPressedButtonRemove()
{
onPressedButton.RemoveAllListeners();
}
}
}
##正常系のテスト
ボタン押下イベント受信時にログを出力するだけ。
using UnityEngine;
namespace My
{
public class UnityEventReceive : MonoBehaviour
{
[SerializeField]
UnityEventInvoker _invoker;
private void Start()
{
_invoker.onPressedButton.AddListener(OnPressedButton);
}
void OnPressedButton(UnityEventInvoker invoker)
{
Debug.Log("UnityEventReceive.OnPressedButton() " + name + ", invoker:" + invoker.onPressedButton.GetPersistentEventCount());
}
}
}
適当なGameObjectにアタッチした。
UnityEventInvoker.OnPressedButtonTest() onPressedButton.GetPersistentEventCount:0
UnityEventReceive.OnPressedButton() UnityEventReceive, invoker:0
GetPersistentEventCount()は登録されているリスナーの数を返すかと思ったけど0?
##登録したままでGameObjectを削除する
すでに削除済みだからエラーが出るのではないか?
using UnityEngine;
namespace My
{
public class UnityEventKillSelf : MonoBehaviour
{
[SerializeField]
UnityEventInvoker _invoker;
private void Start()
{
_invoker.onPressedButton.AddListener(OnPressedButton);
Destroy(gameObject);
}
void OnPressedButton(UnityEventInvoker invoker)
{
Debug.Log("UnityEventKillSelf.OnPressedButton() " + name + ", invoker:" + invoker.onPressedButton.GetPersistentEventCount());
}
private void OnDestroy()
{
Debug.Log("UnityEventKillSelf.OnDestroy()");
}
}
}
UnityEventKillSelf.OnDestroy()
UnityEventInvoker.OnPressedButtonTest() onPressedButton.GetPersistentEventCount:0
UnityEventReceive.OnPressedButton() UnityEventReceive, invoker:0
特にリスナーを解除しなくてもイベントは通知できた。
そしてエラーはでなかった。
スクリプトと同名のGameObjectを作ったがProfilerにはその存在が確認されない。
またヒエラルキーでもGameObjectが削除されていた。
RemoveListenerの必要ない?
##リスナーとしてnullを登録する
using UnityEngine;
namespace My
{
public class UnityEventNull : MonoBehaviour
{
[SerializeField]
UnityEventInvoker _invoker;
private void Start()
{
_invoker.onPressedButton.AddListener(null);
}
}
}
UnityEventInvoker.OnPressedButtonTest() onPressedButton.GetPersistentEventCount:0
NullReferenceException: Object reference not set to an instance of an object
at UnityEngine.Events.BaseInvokableCall.AllowInvoke (System.Delegate delegate) [0x00002] in C:\buildslave\unity\build\Runtime\Export\UnityEvent.cs:117
at UnityEngine.Events.InvokableCall`1[My.UnityEventInvoker].Invoke (System.Object[] args) [0x00023] in C:\buildslave\unity\build\Runtime\Export\UnityEvent.cs:187
at UnityEngine.Events.InvokableCallList.Invoke (System.Object[] parameters) [0x00056] in C:\buildslave\unity\build\Runtime\Export\UnityEvent.cs:634
at UnityEngine.Events.UnityEventBase.Invoke (System.Object[] parameters) [0x0000e] in C:\buildslave\unity\build\Runtime\Export\UnityEvent.cs:769
at UnityEngine.Events.UnityEvent`1[T0].Invoke (.T0 arg0) [0x00016] in C:\buildslave\unity\build\Runtime\Export\UnityEvent_1.cs:53
at My.UnityEventInvoker.OnPressedButtonTest (UnityEngine.EventSystems.BaseEventData eventData) [0x0003c] in UnityEventInvoker.cs:32
nullは登録できるが、Invokeするとエラーになる。
(それならnullチェックしてくれよ)
##重複して登録する
同じメソッドをリスナーとして2回登録する。
using UnityEngine;
namespace My
{
public class UnityEventDuplicate : MonoBehaviour
{
[SerializeField]
UnityEventInvoker _invoker;
private void Start()
{
_invoker.onPressedButton.AddListener(OnPressedButton);
_invoker.onPressedButton.AddListener(OnPressedButton);
}
void OnPressedButton(UnityEventInvoker invoker)
{
Debug.Log("UnityEventDuplicate.OnPressedButton() " + name + ", invoker:" + invoker.onPressedButton.GetPersistentEventCount());
}
}
}
UnityEventInvoker.OnPressedButtonTest() onPressedButton.GetPersistentEventCount:0
UnityEventDuplicate.OnPressedButton() UnityEventDuplicate, invoker:0
UnityEventDuplicate.OnPressedButton() UnityEventDuplicate, invoker:0
2回イベントが通知される。
重複チェックはしていない。
##重複して登録した後に削除する
同じものを2回登録する。
削除は1回のみ。
using UnityEngine;
namespace My
{
public class UnityEventDuplicateRemove : MonoBehaviour
{
[SerializeField]
UnityEventInvoker _invoker;
private void Start()
{
_invoker.onPressedButton.AddListener(OnPressedButton);
_invoker.onPressedButton.AddListener(OnPressedButton);
_invoker.onPressedButton.RemoveListener(OnPressedButton);
}
void OnPressedButton(UnityEventInvoker invoker)
{
Debug.Log("UnityEventDuplicateRemove.OnPressedButton() " + name + ", invoker:" + invoker.onPressedButton.GetPersistentEventCount());
}
}
}
UnityEventInvoker.OnPressedButtonTest() onPressedButton.GetPersistentEventCount:0
重複して登録しても1回の削除処理ですべて削除される。
##Destroyするときにリスナーを解除する
OnDestroyのときにリスナーを解除する。
using UnityEngine;
namespace My
{
public class UnityEventRemove : MonoBehaviour
{
[SerializeField]
UnityEventInvoker _invoker;
private void Start()
{
_invoker.onPressedButton.AddListener(OnPressedButton);
Destroy(gameObject);
}
void OnPressedButton(UnityEventInvoker invoker)
{
Debug.Log("UnityEventRemove.OnPressedButton() " + name + ", invoker:" + invoker.onPressedButton.GetPersistentEventCount());
}
private void OnDestroy()
{
Debug.Log("UnityEventRemove.OnDestroy()");
_invoker.onPressedButton.RemoveListener(OnPressedButton);
}
}
}
UnityEventRemove.OnDestroy()
UnityEventInvoker.OnPressedButtonTest() onPressedButton.GetPersistentEventCount:0
UnityEventReceive.OnPressedButton() UnityEventReceive, invoker:0
削除しても他のリスナーには正常にイベントは通知できる。
当たり前な動作。
#感想
オープンソースじゃないのでその中身がどうやっているのか分からないのが困るなー。