0x00 相当难过
先说说题外话
正常的 UI 事件是这样的(我的理解中):在 UI 对象上挂载脚本,脚本中处理对应的逻辑,比如:在处理指针按下,要在脚本中处理实现一个 IPointerDown 的接口。这看起来很直接,但是在使用中会让逻辑很分散。这就是让我相当难受的做法。
而 EventTrigger 的做法类似,也是将一个实现了所有接口的脚本绑定在要触发事件的 UI 对象上,但是……他只触发一个特定事件。
我不确定从 UI 在 EventSystem 中的实现是不是通过事件的方式(我以为是,需要学习一下,搞搞清楚),之前我也是并不理解为什么要在事件触发的回调中触发另一个事件。
0x01
处理 UI 事件的 EventTrigger 是什么:
接受从 EventSystem 触发的事件,并为每一个事件调用注册的回调函数。EventTrigger 可以用来指定为每个 EventSystem 事件调用的函数。可以将多个函数注册给单个事件,每当 EventTrigger 收到该事件时,都会按照这些函数的提供顺序调用它们。(这个组件要挂载到 GameObject 上使用,这会导致该对象拦截所有的事件,并且这些时间都不会传播到父对象上。)
- 要挂上
- 要实现
- 要有注册的回调方法
关于 EventTrigger 的原理这点,在c#委托和事件中有所介绍,在这里是通过 EventSystem 触发绑定在对象上脚本的处理函数,在处理函数中,再触发定义的事件,通过事件调用实际上要执行的逻辑代码。
0x02
在官网文档中,有介绍 EventTrigger 的两种使用方式:
- 扩展 EventTrigger,重写( override )需要拦截的事件的函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| using UnityEngine; using UnityEngine.EventSystems;
public class EventTriggerExample : EventTrigger { public override void OnBeginDrag(PointerEventData data) { Debug.Log("OnBeginDrag called."); }
...
public override void OnPointerClick(PointerEventData data) { Debug.Log("OnPointerClick called."); } }
|
其实这里的用法,就和原本自己定义脚本实现接口的方式相同了,将处理的逻辑写在事件的处理函数中。在上面提出的让逻辑在统一的位置处理,可以在这里触发事件。像这样……(传了一个 gist)
1 2 3 4 5 6 7 8 9 10 11 12 13
| public class UIEventListener : EventTrigger { public delegate void UIDelegate(GameObject go); public event UIDelegate onPointerEnter; public override void OnPointerEnter(PointerEventData eventData) { if (null != onPointerEnter) { onPointerEnter(gameObject); } } }
|
同样,这个脚本也要挂在对象上,但这里的逻辑只触发了自定义的事件,而事件就可以调用放在任意位置的逻辑代码了。但是手动挂脚本还是不怎么方便日后的修改与维护,所以添加一个静态方法用来获取特定对象的 UIEventListener 组件:
1 2 3 4 5 6 7 8 9 10 11
| public static UIEventListener GetListener(GameObject go) { UIEventListener listener = go.GetComponent<UIEventListener>();
if (null == listener) { listener = go.AddComponent<UIEventListener>(); }
return listener; }
|
这样这个问题就解决了。
- 也可以指定单个委托:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| using UnityEngine; using UnityEngine.EventSystems;
public class EventTriggerDelegateExample : MonoBehaviour { void Start() { EventTrigger trigger = GetComponent<EventTrigger>(); EventTrigger.Entry entry = new EventTrigger.Entry(); entry.eventID = EventTriggerType.PointerDown; entry.callback.AddListener((data) => { OnPointerDownDelegate((PointerEventData)data); }); trigger.triggers.Add(entry); }
public void OnPointerDownDelegate(PointerEventData data) { Debug.Log("OnPointerDownDelegate called."); } }
|
这段代码,获取到了挂载的对象上的 EventTrigger 组件,然后监听对象的某个事件,执行特定的回调方法。
当然也可以类似的将这段代码与自动添加组件一起封装一下再使用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
private void AddTriggersListener(GameObject obj, EventTriggerType eventID, UnityAction<BaseEventData> action) { EventTrigger trigger = obj.GetComponent<EventTrigger>(); if (trigger == null) { trigger = obj.AddComponent<EventTrigger>(); }
if (trigger.triggers.Count == 0) { trigger.triggers = new List<EventTrigger.Entry>(); } UnityAction<BaseEventData> callback = new UnityAction<BaseEventData>(action); EventTrigger.Entry entry = new EventTrigger.Entry(); entry.eventID = eventID; entry.callback.AddListener(callback); trigger.triggers.Add(entry); }
|
0x03
在 PC 端与移动端处理 UI 事件会有所不同,可以通过实机调试就可以看出不同。比方说在 PC 端,通过鼠标来操作时,指针离开区域和指针放开的事件是可以明确区分的,但是在移动端,由于没有“指针”,在处理这两个事件时其实是同时的,如果在这两个事件之间处理逻辑,就不会符合预期了。
关于前面抛出的问题,依旧占坑,先去改我费尽苦心写下的 bug 了,明明知道 update 执行是随机的,还在触发事件时用 static 对象来拼人品……
(ps 好了,我“辛辛苦苦”改完了我“辛辛苦苦”写的bug……0点15了……
(占坑以后聊 EventSystem
0xff 附录
附录I
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
| using UnityEngine; using UnityEngine.EventSystems;
public class EventTriggerExample : EventTrigger { public override void OnBeginDrag(PointerEventData data) { Debug.Log("OnBeginDrag called."); }
public override void OnCancel(BaseEventData data) { Debug.Log("OnCancel called."); }
public override void OnDeselect(BaseEventData data) { Debug.Log("OnDeselect called."); }
public override void OnDrag(PointerEventData data) { Debug.Log("OnDrag called."); }
public override void OnDrop(PointerEventData data) { Debug.Log("OnDrop called."); }
public override void OnEndDrag(PointerEventData data) { Debug.Log("OnEndDrag called."); }
public override void OnInitializePotentialDrag(PointerEventData data) { Debug.Log("OnInitializePotentialDrag called."); }
public override void OnMove(AxisEventData data) { Debug.Log("OnMove called."); }
public override void OnPointerClick(PointerEventData data) { Debug.Log("OnPointerClick called."); }
public override void OnPointerDown(PointerEventData data) { Debug.Log("OnPointerDown called."); }
public override void OnPointerEnter(PointerEventData data) { Debug.Log("OnPointerEnter called."); }
public override void OnPointerExit(PointerEventData data) { Debug.Log("OnPointerExit called."); }
public override void OnPointerUp(PointerEventData data) { Debug.Log("OnPointerUp called."); }
public override void OnScroll(PointerEventData data) { Debug.Log("OnScroll called."); }
public override void OnSelect(BaseEventData data) { Debug.Log("OnSelect called."); }
public override void OnSubmit(BaseEventData data) { Debug.Log("OnSubmit called."); }
public override void OnUpdateSelected(BaseEventData data) { Debug.Log("OnUpdateSelected called."); } }
|