概要
UI (Image) に IDragHandler を持つコンポーネントが複数アタッチされており、それらの実行順番を制御したかった.
結論
GameObject に IEventSystemHandler を実装したコンポーネントが複数アタッチされている場合、それらの実行順はアタッチした順番(gameObject.GetComponents()で取得される並び)で決定される.
※自分は勘違いしていたが ExecutionOrder は関係ない.
OnDrag の実行タイミング
そもそも OnDrag()等がどのタイミングで実行されるのか把握していなかったため、
以下のログ出力を行うスクリプトで確認.
public class TestComponent : MonoBehaviour, IBeginDragHandler, IDragHandler, IEndDragHandler {
public RectTransform rect;
//
public void Update() => Debug.Log($"{Time.frameCount} Update");
public void FixedUpdate() => Debug.Log($"{Time.frameCount} FixedUpdate");
//
public void OnBeginDrag(PointerEventData eventData) => Debug_.Log($"{Time.frameCount} OnBeginDrag", Colors.Red);
public void OnEndDrag(PointerEventData eventData) => Debug_.Log($"{Time.frameCount} OnEndDrag", Colors.Red);
public void OnDrag(PointerEventData eventData) {
Debug_.Log($"{Time.frameCount} OnDrag", Colors.Orange);
{ // マウス位置に追従させる
Vector2 localPoint;
RectTransformUtility.ScreenPointToLocalPointInRectangle(rect.parent as RectTransform, eventData.position, eventData.pressEventCamera, out localPoint);
rect.localPosition = localPoint;
}
}
}
【結果】
FixedUpdate() > OnBegineDrag() > OnDrag() > Update() の順に実行されている.
(おそらく MonoBehaviour の OnMouseXXX と同じタイミングで実行されている)
複数アタッチした時の実行順
DefaultExecutionOrder 設定による変化
コンポーネント A, B の DefaultExecutionOrder の値を入れ替えても実行順番に変化は無かった.
[DefaultExecutionOrder(-100)]
public class ExecutionOrderTestA{}
[DefaultExecutionOrder(100)]
public class ExecutionOrderTestB{}
コンポーネントのアタッチ順による変化
アタッチされている順番を入れ替えることで OnBeginDrag()/OnDrag()/OnEndDrag()の実行順番も逆になった.
調べてみると UnityEngine.EventSystems.ExecuteEvents が処理する際に、
普通に gameObject.GetComponets()で取得した順に処理しているようだった.
/// <summary>
/// Bubble the specified event on the game object, figuring out which object will actually receive the event.
/// </summary>
public static GameObject GetEventHandler<T>(GameObject root) where T : IEventSystemHandler
{
if (root == null)
return null;
Transform t = root.transform;
while (t != null)
{
if (CanHandleEvent<T>(t.gameObject))
return t.gameObject;
t = t.parent;
}
return null;
}
/// <summary>
/// Whether the specified game object will be able to handle the specified event.
/// </summary>
public static bool CanHandleEvent<T>(GameObject go) where T : IEventSystemHandler{
var internalHandlers = s_HandlerListPool.Get();
GetEventList<T>(go, internalHandlers);
var handlerCount = internalHandlers.Count;
s_HandlerListPool.Release(internalHandlers);
return handlerCount != 0;
}
/// <summary>
/// Get the specified object's event event.
/// </summary>
private static void GetEventList<T>(GameObject go, IList<IEventSystemHandler> results)
where T : IEventSystemHandler{
// Debug.LogWarning("GetEventList<" + typeof(T).Name + ">");
if (results == null)
throw new ArgumentException("Results array is null", "results");
if (go == null || !go.activeInHierarchy)
return;
var components = ListPool<Component>.Get();
go.GetComponents(components);
for (var i = 0; i < components.Count; i++){
if (!ShouldSendToComponent<T>(components[i]))
continue;
// Debug.Log(string.Format("{2} found! On {0}.{1}", go, s_GetComponentsScratch[i].GetType(), typeof(T)));
results.Add(components[i] as IEventSystemHandler);
}
ListPool<Component>.Release(components);
// Debug.LogWarning("end GetEventList<" + typeof(T).Name + ">");
}
【参考】