Unity UI Button 事件优先级调整技术方案

Unity UI Button 事件优先级调整技术方案

在 Unity 项目开发过程中,针对 UI Button 的事件执行顺序控制是一个常见需求。本文详细阐述两种将新添加事件置于第一个执行位置的方法,旨在为开发者提供全面且专业的技术参考。

一、基于反射机制的事件插入方法

(一)核心函数:InsertClickListenerAtStart

以下函数实现了将指定的 UnityAction 插入到 ButtononClick 事件列表头部,确保其在其他已有事件之前执行。

csharp 复制代码
private void InsertClickListenerAtStart(Button button, UnityAction action)
{
    // 1. 事件去重处理
    var onClick = button.onClick;
    onClick.RemoveListener(action);  // 确保不会重复添加

    // 2. 反射获取 m_Calls 字段
    var field = typeof(UnityEventBase).GetField("m_Calls", BindingFlags.NonPublic | BindingFlags.Instance);
    if (field!= null)
    {
        var invokableCallList = field.GetValue(onClick);
        if (invokableCallList!= null)
        {
            // 3. 获取 m_RuntimeCalls 字段
            var runtimeCallsField = invokableCallList.GetType().GetField("m_RuntimeCalls", BindingFlags.NonPublic | BindingFlags.Instance);
            if (runtimeCallsField!= null)
            {
                // 4. 创建新的 InvokableCall
                var invokableCallType = typeof(UnityEvent).Assembly.GetType("UnityEngine.Events.InvokableCall");
                if (invokableCallType!= null)
                {
                    var constructor = invokableCallType.GetConstructor(new[] { typeof(UnityAction) });
                    if (constructor!= null)
                    {
                        var newCall = constructor.Invoke(new object[] { action });

                        // 5. 获取 Insert 方法并插入新事件
                        var runtimeCalls = runtimeCallsField.GetValue(invokableCallList);
                        var insertMethod = runtimeCalls.GetType().GetMethod("Insert");
                        if (insertMethod!= null)
                        {
                            insertMethod.Invoke(runtimeCalls, new object[] { 0, newCall });

                            // 6. 设置 m_NeedsUpdate 为 true
                            var needsUpdateField = invokableCallList.GetType().GetField("m_NeedsUpdate", BindingFlags.NonPublic | BindingFlags.Instance);
                            if (needsUpdateField!= null)
                            {
                                needsUpdateField.SetValue(invokableCallList, true);
                            }
                        }
                    }
                }
            }
        }
    }
}

此方法通过反射深入到 UnityEventBaseButton 的内部实现机制,精准地操作事件调用列表。其核心步骤包括:

  • 首先,对目标事件进行去重操作,避免同一事件多次添加导致的逻辑混乱。
  • 接着,利用反射获取 m_Calls 字段,该字段存储了事件的可调用列表信息。在确保获取成功后,进一步获取 m_RuntimeCalls 字段,这是实际存储运行时事件调用信息的关键结构。
  • 随后,根据 UnityEvent 程序集动态获取 InvokableCall 类型,并创建一个新的实例,该实例包装了我们要插入的 action
  • 最后,获取 m_RuntimeCalls 列表的 Insert 方法,将新创建的 InvokableCall 实例插入到列表头部(索引为 0),并设置 m_NeedsUpdate 标志为 true,以确保事件系统在后续处理中能够正确更新状态。

二、基于脚本代理的事件暂存与插入方法

(一)核心脚本:OneTimeButtonEventInsertor

通过创建 OneTimeButtonEventInsertor 脚本,实现了一种更为直观的事件顺序控制方式。

csharp 复制代码
public class OneTimeButtonEventInsertor : MonoBehaviour, IPointerClickHandler
{
    private Button button;

    // 自定义事件,用于在 EventTrigger 之前调用
    public UnityEvent BeforeOnClick = new UnityEvent();
    public Button.ButtonClickedEvent OnClick;

    private void Awake()
    {
        button = GetComponent<Button>();
        OnClick = button.onClick;
        button.onClick = new Button.ButtonClickedEvent();
    }

    public void OnPointerClick(PointerEventData eventData)
    {
        // 先执行 BeforeOnClick 事件
        BeforeOnClick?.Invoke();
        button.onClick = OnClick;
        button.onClick.Invoke();
        Destroy(this);
    }
}

该脚本的工作原理如下:

  • Awake 方法中,获取目标 Button 组件,并备份其原始的 onClick 事件到 OnClick 变量。同时,将 ButtononClick 事件替换为一个新的空事件,以便后续插入自定义事件。
  • 当用户点击按钮时,OnPointerClick 方法被触发。首先,执行自定义的 BeforeOnClick 事件,这是我们希望优先执行的事件。
  • 接着,将 ButtononClick 事件恢复为原始备份的事件,并立即触发该事件,从而保证了自定义事件在原始事件之前执行。
  • 最后,销毁 OneTimeButtonEventInsertor 脚本实例,避免对后续事件处理产生不必要的干扰。

综上所述,这两种方法分别从底层反射和高层脚本代理的角度,为 Unity UI Button 事件优先级控制提供了有效的解决方案。开发者可根据项目的具体需求、性能要求以及代码维护性等因素,灵活选择合适的方法来实现按钮事件的精准排序。

相关推荐
Alger_Hamlet14 小时前
Photoshop 2025 Mac中文 Ps图像编辑软件
macos·ui·photoshop
牙膏上的小苏打233314 小时前
Unity Surround开关后导致获取主显示器分辨率错误
unity·主屏幕
Unity大海16 小时前
诠视科技Unity SDK开发环境配置、项目设置、apk打包。
科技·unity·游戏引擎
浅陌sss1 天前
Unity中 粒子系统使用整理(一)
unity·游戏引擎
维度攻城狮1 天前
实现在Unity3D中仿真汽车,而且还能使用ros2控制
python·unity·docker·汽车·ros2·rviz2
为你写首诗ge1 天前
【Unity网络编程知识】FTP学习
网络·unity
神码编程1 天前
【Unity】 HTFramework框架(六十四)SaveDataRuntime运行时保存组件参数、预制体
unity·编辑器·游戏引擎
菲fay1 天前
Unity 单例模式写法
unity·单例模式
火一线1 天前
【Framework-Client系列】UIGenerate介绍
游戏·unity
ZKY_241 天前
【工具】Json在线解析工具
unity·json