先说下代理的类型, Engine\Source\Runtime\Core\Public\Delegates\DelegateCombinations.h(即代理宏定义的文件)看起来宏很多, 但实际上只有几种, 其他的是有更多的传入参数
DECLARE_DELEGATE( DelegateName ), 定义一个无参代理
DECLARE_MULTICAST_DELEGATE( DelegateName ) 定义一个无参多播
DECLARE_EVENT( OwningType, EventName ) 定义一个无参事件
DECLARE_DYNAMIC_DELEGATE( DelegateName ) 定义一个动态代理
DECLARE_DYNAMIC_MULTICAST_DELEGATE( DelegateName ) 定义一个动态多播代理
DECLARE_DELEGATE_RetVal( ReturnValueType, DelegateName ) 定义一个代理, 带返回参数
DECLARE_DYNAMIC_DELEGATE_RetVal 定义一个动态代理, 带返回参数
基础内容参考官方文档, 懒得解释
https://docs.unrealengine.com/en-US/Programming/UnrealArchitecture/Delegates
这个东西比较特殊吧, 反正暂时没有用到过,虽然看着定义用处不小的样子, 笑.
事件与 组播委托 十分相似。虽然任意类均可绑定事件,但只有声明事件的类可以调用事件 的 Broadcast、IsBound 和 Clear 函数。这意味着事件对象可在公共接口中公开,而无需让外部类访问这些敏感度函数。事件使用情况有:在纯抽象类中包含回调、限制外部类调用 Broadcast、IsBound 和 Clear 函数。
https://api.unrealengine.com/CHN/Programming/UnrealArchitecture/Delegates/Events/index.html
按照官方的说法, 应该只有定义事件的类可以调用Broadcast这些函数, 但实际测试, 并不是. 笑
可以看到, 这里是将OwningType变成了多播代理的友元类, 但注意, 这里类是Public继承
然后往里面跳一层, Broadcast是个public函数, 友元此处没卵用, 笑
不确定是否是版本更新导致的, 或者使用方法不对
官方文档中的版本为4.9,
网上并没有找到相关的使用方法教程
附测试代码,版本4.21, 代码简单, 就不做解释了.
UCLASS()
class GUAO_CPLUSPLUSCODE_API AGUAO_CPlusPlusCodeGameModeBase : public AGameModeBase
{
GENERATED_BODY()
public:
virtual void BeginPlay() override;
DECLARE_EVENT(AGUAO_CPlusPlusCodeGameModeBase, FTestDelegate);
FTestDelegate TestDelegate;
FTestDelegate& GetTestDelegate() { return TestDelegate; }
void CallDelegate();
};
void AGUAO_CPlusPlusCodeGameModeBase::BeginPlay()
{
Super::BeginPlay();
TestDelegate.AddLambda([this]() {
UE_LOG(LogTemp, Warning, TEXT("Delegate broad cast"));
});
}
void AGUAO_CPlusPlusCodeGameModeBase::CallDelegate()
{
UE_LOG(LogTemp, Warning, TEXT("This is Game mode"));
TestDelegate.Broadcast();
}
void AMyActor::BeginPlay()
{
Super::BeginPlay();
FTimerHandle TimerHandle;
GetWorld()->GetTimerManager().SetTimer(TimerHandle, [this]() {
if (AGUAO_CPlusPlusCodeGameModeBase* CPlusPlusCodeGameMode = Cast(GetWorld()->GetAuthGameMode()))
{
UE_LOG(LogTemp, Warning, TEXT("This is actor"));
CPlusPlusCodeGameMode->TestDelegate.Broadcast();
UE_LOG(LogTemp, Warning, TEXT("This is actor 2"));
AGUAO_CPlusPlusCodeGameModeBase::FTestDelegate& Delegate = CPlusPlusCodeGameMode->GetTestDelegate();
Delegate.Broadcast();
CPlusPlusCodeGameMode->CallDelegate();
}
}, 5.f, false);
}
执行结果
LogTemp: Warning: This is actor
LogTemp: Warning: Delegate broad cast
LogTemp: Warning: This is actor 2
LogTemp: Warning: Delegate broad cast
LogTemp: Warning: This is Game mode
LogTemp: Warning: Delegate broad cast
单播 TBaseDelegate 继承 FDelegateBase
并通过DelegateAllocator存储一个IDelegateInstance对象, 通过这个对象来实现代理.
多播TMulticastDelegate 继承 TBaseMulticastDelegate 继承 FMulticastDelegateBase
多播的核心大概就是这个数组了, 存储所有用于调用的代理实例
配合添加和删除函数
然后就是调用, 遍历数组并依次调用, 如果代理不可用, 就标志 NeedsCompaction为true并整理整个列表.
在单播实现的基础上, 多播其实已经没什么内容了
单播下篇文章分析
动态代理可以序列化,它们的函数可以按名称找到,而且它们比常规代理慢。
https://docs.unrealengine.com/en-us/Programming/UnrealArchitecture/Delegates/Dynamic
动态代理和代理的继承树是不一致的, 差别很大, 导致动态代理的局限性更大一些
TBaseDynamicDelegate 继承 TScriptDelegate
同时深扒TScriptDelegate存储的变量, 只有一个UObject指针和函数名称, 并根据ProccessDelegate代理的执行函数, 动态代理的实现严重依赖于UE4 强大的反射系统.
所以, 记住绑定的函数要加上UFUNCTION()宏
同时AddDynamic等宏, 传入函数指针, 会转换成相应的函数名称
并可以调用BindFunction来直接进行绑定.
补充, 如下图, 动态多播可以通过添加BlueprintAssignable暴露给蓝图使用
DECLARE_EVENT没有用过, 从文档上看过, 一直以为是特殊的多播, 今天深扒了一下, 好像什么用处都没有的样子.
但UE4 应该不会专门写一个没用的东西, 并花篇幅介绍.
所以测试了一下, 结果, 好像就是没用…
可能会下一个4.9版本的旧版本, 再验证一下,
这几个地方的源码看着, 好像都没啥需要说的, 不清楚往里跳一层,
和其他模块关联性不强, 实现也没用特别多的技巧, 就简简单单的.(或者看不动, 笑)