当前位置:网站首页>【Unity】Unity开发进阶(六)UnityEvent使用与源码解析
【Unity】Unity开发进阶(六)UnityEvent使用与源码解析
2022-08-02 21:33:00 【xiaoyaoACi】
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);
}
}
何时使用
比如,当我们想要在屏幕上做多个按钮时,每个按钮的触发要有相应的区分,我们可以利用一个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);
}
}
带泛型的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);
}
}
多个泛型的情况以此类推。
在AddListener和RemoveListener中都传入了参数UnityAction call,看这个名字就像个回调函数,我们看一下代码:
以下为UnityAction代码:
namespace UnityEngine.Events
{
//
// 摘要:
// Zero argument delegate used by UnityEvents.
public delegate void UnityAction();
}
果然,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);
}
}
以下为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();
}
}
从UnityEventBase和ISerializationCallbackReceiver的内容可以看出,其实Unity提供的功能并不复杂,只是提供了一些工具方法和一些抽象方法,其最主要的工作还是帮助开发者完成了委托,以提高代码可读性。
总结
UnityEvent其实就是Unity帮我们提前写好的用于处理事件的功能,其作用是减去了自己写委托的过程,且提高了开发的规范性,方便代码整合。
更多内容请查看总目录【Unity】Unity学习笔记目录整理
边栏推荐
猜你喜欢
包管理工具npm- node package management相关知识 、检查包更新、NPM包上传、更换镜像、npm ERR! registry error parsing json
总结嵌入式C语言难点(2部分)
kubernetes pod podsecurityPolicies(PSP)
【3D视觉】深度摄像头与3D重建
【使用pyside2遇到的问题】This application failed to start because no Qt platform plugin could be initialized.
面试官居然问我:删库后,除了跑路还能干什么?
LeetCode 2360. 图中的最长环 基环树找环+时间戳
源码构建LAMP环境-2
Jmeter二次开发实现rsa加密
VisualStudio 制作Dynamic Link Library动态链接库文件
随机推荐
How to seize the new trend of NFT, yuan|universe|universe?
【DEBUG】ImportError: Unable to import required dependencies: numpy: DLL load failed: 找不到指定的模块。
增删改查这么多年,最后栽在MySQL的架构设计上!
典型相关分析CCA计算过程
面试了个985毕业的,回答“性能调优”题时表情令我毕生难忘
golang刷leetcode: 卖木头块
Abstract Factory Pattern
双轴晶体中的锥形折射
JS函数防抖&函数节流及其使用场景
YAML文件格式
golang 刷leetcode:将字符串翻转到单调递增
golang刷leetcode:按位与结果大于零的最长组合
若依集成minio实现分布式文件存储
You and I will meet the needs of: how to export the data in a MySQL simple ~!Practical!
kubernetes pod podsecurityPolicies(PSP)
你我都会遇到的需求:如何导出MySQL中的数据~ 简单!实用!
go 序列化与反序列化
【使用pyside2遇到的问题】This application failed to start because no Qt platform plugin could be initialized.
UDP(用户数据报协议)
Ansible installation and configuration