UE4 Delegate(代理)相关源码分析(一)

UE4 Delegate(代理)相关源码分析


前言


  • 这是一篇关于UE4代理的一篇分析文章, 但可能涉及C++代理的实现, 请自行百度, 个人也不是很了解
  • 这篇文章会使对UE4代理相关的认识更深刻, 然并卵.

核心函数


一堆代理的定义
UE4 Delegate(代理)相关源码分析(一)_第1张图片

正文


先说下代理的类型, 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

DECLARE_EVENT( OwningType, EventName )

这个东西比较特殊吧, 反正暂时没有用到过,虽然看着定义用处不小的样子, 笑.

事件与 组播委托 十分相似。虽然任意类均可绑定事件,但只有声明事件的类可以调用事件 的 Broadcast、IsBound 和 Clear 函数。这意味着事件对象可在公共接口中公开,而无需让外部类访问这些敏感度函数。事件使用情况有:在纯抽象类中包含回调、限制外部类调用 Broadcast、IsBound 和 Clear 函数。
https://api.unrealengine.com/CHN/Programming/UnrealArchitecture/Delegates/Events/index.html

按照官方的说法, 应该只有定义事件的类可以调用Broadcast这些函数, 但实际测试, 并不是. 笑
在这里插入图片描述
UE4 Delegate(代理)相关源码分析(一)_第2张图片
可以看到, 这里是将OwningType变成了多播代理的友元类, 但注意, 这里类是Public继承
然后往里面跳一层, Broadcast是个public函数, 友元此处没卵用, 笑
UE4 Delegate(代理)相关源码分析(一)_第3张图片
不确定是否是版本更新导致的, 或者使用方法不对

官方文档中的版本为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
单播和多播

UE4 Delegate(代理)相关源码分析(一)_第4张图片
单播 TBaseDelegate 继承 FDelegateBase
UE4 Delegate(代理)相关源码分析(一)_第5张图片
并通过DelegateAllocator存储一个IDelegateInstance对象, 通过这个对象来实现代理.

这个对象下篇文章在深扒吧. 感觉篇幅有点长了, 字数水够了
UE4 Delegate(代理)相关源码分析(一)_第6张图片

多播TMulticastDelegate 继承 TBaseMulticastDelegate 继承 FMulticastDelegateBase

UE4 Delegate(代理)相关源码分析(一)_第7张图片
UE4 Delegate(代理)相关源码分析(一)_第8张图片
多播的核心大概就是这个数组了, 存储所有用于调用的代理实例
UE4 Delegate(代理)相关源码分析(一)_第9张图片
配合添加和删除函数
UE4 Delegate(代理)相关源码分析(一)_第10张图片
UE4 Delegate(代理)相关源码分析(一)_第11张图片
然后就是调用, 遍历数组并依次调用, 如果代理不可用, 就标志 NeedsCompaction为true并整理整个列表.
UE4 Delegate(代理)相关源码分析(一)_第12张图片
在单播实现的基础上, 多播其实已经没什么内容了

单播下篇文章分析

代理和动态代理

动态代理可以序列化,它们的函数可以按名称找到,而且它们比常规代理慢。

https://docs.unrealengine.com/en-us/Programming/UnrealArchitecture/Delegates/Dynamic

动态代理和代理的继承树是不一致的, 差别很大, 导致动态代理的局限性更大一些
TBaseDynamicDelegate 继承 TScriptDelegate
在这里插入图片描述
同时深扒TScriptDelegate存储的变量, 只有一个UObject指针和函数名称, 并根据ProccessDelegate代理的执行函数, 动态代理的实现严重依赖于UE4 强大的反射系统.

所以, 记住绑定的函数要加上UFUNCTION()宏
UE4 Delegate(代理)相关源码分析(一)_第13张图片
同时AddDynamic等宏, 传入函数指针, 会转换成相应的函数名称
UE4 Delegate(代理)相关源码分析(一)_第14张图片
并可以调用BindFunction来直接进行绑定.
UE4 Delegate(代理)相关源码分析(一)_第15张图片

补充, 如下图, 动态多播可以通过添加BlueprintAssignable暴露给蓝图使用

  • 因为工作中常用到的是多播, 所以个人称号普通代理为C++的代理, 动态代理为蓝图的代理.
    在这里插入图片描述

结语


  • 水了水, 字数有点多了, 可能是DECLARE_EVENT这个不在计划内吧

    DECLARE_EVENT没有用过, 从文档上看过, 一直以为是特殊的多播, 今天深扒了一下, 好像什么用处都没有的样子.
    但UE4 应该不会专门写一个没用的东西, 并花篇幅介绍.
    所以测试了一下, 结果, 好像就是没用…
    可能会下一个4.9版本的旧版本, 再验证一下,

  • 写着写着, 感觉写的越来越随意了, 贴张图, 写一两句话…

    这几个地方的源码看着, 好像都没啥需要说的, 不清楚往里跳一层,
    和其他模块关联性不强, 实现也没用特别多的技巧, 就简简单单的.(或者看不动, 笑)

  • 单播相关的就下周再分析了, 虽然这个好像是处理的最精彩的地方. 但最近有点忙, 再加上懒, 所以就这样了.

你可能感兴趣的:(UE4)