• 【Unity】Unity开发进阶(六)UnityEvent使用与源码解析



    UnityEvent

    UnityEvent是Unity提供的用于处理事件的类,方便我们自定义事件。为了便于参数传递,Unity默认为我们提供了多个事件类,通过泛型不同实现事件响应参数不同。
    在这里插入图片描述

    如何使用

    对于如何使用UnityEvent,官方文档给出了简单的使用案例,我们以一个泛型的版本为例。

    UnityEvent代表使用此事件时可以添加一种类型,在调用Invoke方法时可以将此类型作为参数传入,事件响应端会接收到这个参数,以获取事件上下文。

    如下图案例,我们自制的MyIntEvent继承UnityEvent,当事件触发调用Invoke方法时将int值5传入,此时Ping方法就会接收到事件响应时传入的5。

    下面为Unity官方文档中提供的代码。
    在这里插入图片描述

    using UnityEngine;
    using UnityEngine.Events;
    
    [System.Serializable]
    public class MyIntEvent : UnityEvent<int>
    {
    }
    
    public class ExampleClass : MonoBehaviour
    {
        public MyIntEvent m_MyEvent;
    
        void Start()
        {
            if (m_MyEvent == null)
                m_MyEvent = new MyIntEvent();
    
            m_MyEvent.AddListener(Ping);
        }
    
        void Update()
        {
            if (Input.anyKeyDown && m_MyEvent != null)
            {
                m_MyEvent.Invoke(5);
            }
        }
    
        void Ping(int i)
        {
            Debug.Log("Ping" + 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

    何时使用

    比如,当我们想要在屏幕上做多个按钮时,每个按钮的触发要有相应的区分,我们可以利用一个for循环来批量添加事件,并在事件触发时传入相应的 i(index索引),并使用同一个事件触发响应器。每个按钮被点击都会触发这个相同的事件响应器,响应器可以接收到参数index,以此来判断是哪个按钮被点击了。

    实现原理

    不带泛型的UnityEvent使用无参数方式调用Invoke方法。

    以下为UnityEvent代码:

    using System.Reflection;
    using UnityEngine.Scripting;
    
    namespace UnityEngine.Events
    {
        //
        // 摘要:
        //     A zero argument persistent callback that can be saved with the Scene.
        public class UnityEvent : UnityEventBase
        {
            //
            // 摘要:
            //     Constructor.
            [RequiredByNativeCode]
            public UnityEvent();
    
            //
            // 摘要:
            //     Add a non persistent listener to the UnityEvent.
            //
            // 参数:
            //   call:
            //     Callback function.
            public void AddListener(UnityAction call);
            //
            // 摘要:
            //     Invoke all registered callbacks (runtime and persistent).
            public void Invoke();
            //
            // 摘要:
            //     Remove a non persistent listener from the UnityEvent.
            //
            // 参数:
            //   call:
            //     Callback function.
            public void RemoveListener(UnityAction call);
            protected override MethodInfo FindMethod_Impl(string name, object targetObj);
        }
    }
    
    • 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

    带泛型的UnityEvent将泛型作为参数传递给Invoke方法。

    以下为UnityEvent代码:

    using System.Reflection;
    using UnityEngine.Scripting;
    
    namespace UnityEngine.Events
    {
        //
        // 摘要:
        //     One argument version of UnityEvent.
        public abstract class UnityEvent<T0> : UnityEventBase
        {
            [RequiredByNativeCode]
            public UnityEvent();
    
            public void AddListener(UnityAction<T0> call);
            public void Invoke(T0 arg0);
            public void RemoveListener(UnityAction<T0> call);
            protected override MethodInfo FindMethod_Impl(string name, object targetObj);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    多个泛型的情况以此类推。

    在AddListener和RemoveListener中都传入了参数UnityAction call,看这个名字就像个回调函数,我们看一下代码:

    以下为UnityAction代码:

    namespace UnityEngine.Events
    {
        //
        // 摘要:
        //     Zero argument delegate used by UnityEvents.
        public delegate void UnityAction();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    果然,UnityAction其实就是个委托方法。

    看过了UnityEvent和UnityAction,那么UnityEvent的基类都做了些什么呢?

    以下为UnityEventBase代码:

    using System;
    using System.Reflection;
    using UnityEngine.Scripting;
    
    namespace UnityEngine.Events
    {
        //
        // 摘要:
        //     Abstract base class for UnityEvents.
        [UsedByNativeCode]
        public abstract class UnityEventBase : ISerializationCallbackReceiver
        {
            protected UnityEventBase();
    
            //
            // 摘要:
            //     Given an object, function name, and a list of argument types; find the method
            //     that matches.
            //
            // 参数:
            //   obj:
            //     Object to search for the method.
            //
            //   functionName:
            //     Function name to search for.
            //
            //   argumentTypes:
            //     Argument types for the function.
            public static MethodInfo GetValidMethodInfo(object obj, string functionName, Type[] argumentTypes);
            //
            // 摘要:
            //     Get the number of registered persistent listeners.
            public int GetPersistentEventCount();
            //
            // 摘要:
            //     Get the target method name of the listener at index index.
            //
            // 参数:
            //   index:
            //     Index of the listener to query.
            public string GetPersistentMethodName(int index);
            //
            // 摘要:
            //     Get the target component of the listener at index index.
            //
            // 参数:
            //   index:
            //     Index of the listener to query.
            public Object GetPersistentTarget(int index);
            //
            // 摘要:
            //     Remove all non-persisent (ie created from script) listeners from the event.
            public void RemoveAllListeners();
            //
            // 摘要:
            //     Modify the execution state of a persistent listener.
            //
            // 参数:
            //   index:
            //     Index of the listener to query.
            //
            //   state:
            //     State to set.
            public void SetPersistentListenerState(int index, UnityEventCallState state);
            public override string ToString();
            protected void AddListener(object targetObj, MethodInfo method);
            protected abstract MethodInfo FindMethod_Impl(string name, object targetObj);
            protected void Invoke(object[] parameters);
            protected void RegisterPersistentListener(int index, object targetObj, MethodInfo method);
            protected void RemoveListener(object targetObj, MethodInfo method);
            protected bool ValidateRegistration(MethodInfo method, object targetObj, PersistentListenerMode mode);
            protected bool ValidateRegistration(MethodInfo method, object targetObj, PersistentListenerMode mode, Type argumentType);
        }
    }
    
    • 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

    以下为ISerializationCallbackReceiver代码:

    using UnityEngine.Scripting;
    
    namespace UnityEngine
    {
        [RequiredByNativeCode]
        public interface ISerializationCallbackReceiver
        {
            //
            // 摘要:
            //     Implement this method to receive a callback after Unity deserializes your object.
            [RequiredByNativeCode]
            void OnAfterDeserialize();
            //
            // 摘要:
            //     Implement this method to receive a callback before Unity serializes your object.
            [RequiredByNativeCode]
            void OnBeforeSerialize();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    从UnityEventBase和ISerializationCallbackReceiver的内容可以看出,其实Unity提供的功能并不复杂,只是提供了一些工具方法和一些抽象方法,其最主要的工作还是帮助开发者完成了委托,以提高代码可读性。

    总结

    UnityEvent其实就是Unity帮我们提前写好的用于处理事件的功能,其作用是减去了自己写委托的过程,且提高了开发的规范性,方便代码整合。


    更多内容请查看总目录【Unity】Unity学习笔记目录整理

  • 相关阅读:
    MyBatis关联中进行(级联)查询的方法分享
    前端node.js入门
    Linux - 实现一个简单的 shell
    乳清白蛋白纳米粒修饰生物素
    开发Qt上位机获取阿里云IOT设备数据(开发上位机对接阿里云IOT平台)
    Proxmox VE 近期有ARM 版本发布么?
    第8章 docker网络管理
    Docker安装MySQL并使用Navicat连接
    UML类图简单认识
    插入排序 python实现
  • 原文地址:https://blog.csdn.net/xiaoyaoACi/article/details/126057911