Unreal在Detail中绑定事件
首先安利一下我的插件Unreal中的MulticastDelegate
使用DECLARE_DYNAMIC_MULTICAST_DELEGATE系列宏(或者加个sparse)声明,并能够通过UPROPERTY标记进入反射系统,本插件主要针对的就是这类Delegate,这些宏本身不产生代码,而是被反射系统收集后自动生成。
其中DECLARE_DYNAMIC_MULTICAST_DELEGATE产生的Delegate继承于TMulticastScriptDelegate,通过一个TScriptDelegate的数组来存储Delegate,而TScriptDelegate中存有一个WeakObjectPtr以及函数的名字,调用时通过ProcessEvent反射调用事件
而DECLARE_DYNAMIC_MULTICAST_SPARSE_DELEGATE产生的Delegate将事件存储于一个全局的Map中,这类Delegate只存储一个bool值因此更节省空间,但是在添加,删除时性能会低于前者
其中前者并未提供序列化的函数,后者因为存储于全局变量中,所以也并不利于实现序列化,因此,Unreal中的Delegate只能通过书写C++代码或蓝图来做运行期绑定,所以无法简单无法实现类似Unity那样的Inspector面板绑定。
为什么要开发这个功能
最近在撸VR项目的时候,写了一个3DUI,然后计划将它作为一个通用的触发器,用来触发场景中各个Actor的事件,当时主要设想了以下方案。
接口,但是TScriptInterface并无法在Detail上Pick对象,PickActor的话又无法过滤那些没有实现接口的对象,而且当Actor有多个需要用于通知的函数时,就需要通过接口传递一个Enum或者FName,写起来得两边确认,很不舒适。转发器,写一个Actor做事件转发,这样就无需传递Enum或FName,但是这样会产生大量的类,而且多一个Actor很丑直接多态,这样干掉了转发器,直接通过子类绑定事件,但是事件一共有四十多种,也就是要创建四十多张蓝图,很不优雅。反射调用,直接填写目标函数的名字,最优雅的一种方式,但是容易发生发生错误,且不够方便,但是可以通过定制Detail改良。
既然都考虑定制Detail面板了,那我何不实现一个类似UnityEventSystem的绑定器呢。
数据存哪儿
既然Delegate无法序列化,那么我必须找个地方去存储我们编辑的绑定信息,由于绑定的信息必定存在于World中的Actor,所以我们选取了UAssetUsetData作为数据存储。
ULevel作为一个Asset,理所当然的实现了IInterface_AssetUserData
在AssetUserData中存储了一个PerActor的绑定表
如何绑定
我们希望Event在World加载之初就完成绑定,最佳的非侵入方案就是UWorldSubsystem的Initialize,在Initialize中我们收集World中所有的Level并绑定事件
_ApplyLevelBindInfo实现如下
对于当前无法找到绑定对象的绑定项,我们将它存到LostReferenceItems中,等待其它StreamingLevel加载时再行绑定
跨Level绑定
为了实现跨关卡绑定,我们监听了LevelAddedToWorld和LevelRemovedFromWorld,代码如下
_TryApplyItemsLostReference中还处理了发出事件Actor被销毁的情况,由于LostReferenceItems加入了反射系统,所以UE会为我们处理引用
注入Detail
我们希望对于任意Actor都能绑定事件,并且无需额外代码,我一开始的想法是提供一个EventBindComponent,通过添加Component的方式来实现绑定效果,但是这种方式不够优雅,而且选中Actor后还要再选中Component的操作有点烦人,于是我们虚晃一枪,"利用"Unreal的反射系统实现了Detail注入。
首先准备一个本体------一个空的Struct
然后我们在StartupModule为Actor强行加入一个Struct
再注册Property的扩展就大功告成了
至此,我们为Unreal实现了一个简单的EventBinder
页:
[1]