通过 EventTrigger 使用 c# 事件

0x00 相当难过

先说说题外话

正常的 UI 事件是这样的(我的理解中):在 UI 对象上挂载脚本,脚本中处理对应的逻辑,比如:在处理指针按下,要在脚本中处理实现一个 IPointerDown 的接口。这看起来很直接,但是在使用中会让逻辑很分散。这就是让我相当难受的做法。

而 EventTrigger 的做法类似,也是将一个实现了所有接口的脚本绑定在要触发事件的 UI 对象上,但是……他只触发一个特定事件。

我不确定从 UI 在 EventSystem 中的实现是不是通过事件的方式(我以为是,需要学习一下,搞搞清楚),之前我也是并不理解为什么要在事件触发的回调中触发另一个事件。

0x01

处理 UI 事件的 EventTrigger 是什么:

接受从 EventSystem 触发的事件,并为每一个事件调用注册的回调函数。EventTrigger 可以用来指定为每个 EventSystem 事件调用的函数。可以将多个函数注册给单个事件,每当 EventTrigger 收到该事件时,都会按照这些函数的提供顺序调用它们。(这个组件要挂载到 GameObject 上使用,这会导致该对象拦截所有的事件,并且这些事件都不会传播到父对象上。)

  1. 要挂上
  2. 要实现
  3. 要有注册的回调方法

关于 EventTrigger 的原理这点,在c#委托和事件中有所介绍,在这里是通过 EventSystem 触发绑定在对象上脚本的处理函数,在处理函数中,再触发定义的事件,通过事件调用实际上要执行的逻辑代码。

0x02

在官网文档中,有介绍 EventTrigger 的两种使用方式:

  1. 扩展 EventTrigger,重写( override )需要拦截的事件的函数:
using UnityEngine;
using UnityEngine.EventSystems;

public class EventTriggerExample : EventTrigger
{
    public override void OnBeginDrag(PointerEventData data)
    {
        Debug.Log("OnBeginDrag called.");
    }

    ...// 长,代码见附录I

    public override void OnPointerClick(PointerEventData data)
    {
        Debug.Log("OnPointerClick called.");
    }
}

其实这里的用法,就和原本自己定义脚本实现接口的方式相同了,将处理的逻辑写在事件的处理函数中。在上面提出的让逻辑在统一的位置处理,可以在这里触发事件。像这样……(传了一个 gist

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 组件:

public static UIEventListener GetListener(GameObject go)
{
    UIEventListener listener = go.GetComponent<UIEventListener>();

    if (null == listener)
    {// 如果找不到,那就挂一个
        listener = go.AddComponent<UIEventListener>();
    }

    return listener;
}

这样这个问题就解决了。

  1. 也可以指定单个委托:
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); // triggers 是在 EventTrigger 中注册的所有函数
    }

    // PointerEventData 是 EventSystem 定义的数据类型
    public void OnPointerDownDelegate(PointerEventData data)
    {
        Debug.Log("OnPointerDownDelegate called.");
    }
}

这段代码,获取到了挂载的对象上的 EventTrigger 组件,然后监听对象的某个事件,执行特定的回调方法。

当然也可以类似的将这段代码与自动添加组件一起封装一下再使用:

/// <summary>
/// 添加EventTrigger的监听事件
/// </summary>
/// <param name="obj">添加监听的对象</param>
/// <param name="eventID">添加的监听类型</param>
/// <param name="action">触发方法</param>
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

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.");
    }
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值