代理:
- 代理可以帮助我们解决一对一或是一对多的任务分配工作。主要可以帮助我们解决通知问题。我们可以通过代理完成调用某一个对象的一个函数,而不直接持有该对象的任何指针。
- 代理就是为你跑腿送信的,你可以不用关心给送信的目标人具体是谁,只要按照约定好的信件格式进行送信即可
- 更简单理解,想去调用某个函数,但并不是直接去调用,而是通过另一个入口去调用(代理)
分类:
- 单播代理 只能进行通知一个人
- 多播代理 可以进行多人通知
- 动态代理 可以被序列化(这体现在于蓝图进行交互,C++中可以将通知事件进行蓝图广播)
单播代理
单播代理是一种允许你声明一个可以绑定到单个函数的代理类型。你可以使用 DECLARE_DELEGATE
宏来定义一个单播代理。
1. 定义单播代理
单播代理是一种允许你声明一个可以绑定到单个函数的代理类型。你可以使用 DECLARE_DELEGATE
宏来定义一个单播代理。例如
DECLARE_DELEGATE(FMyDelegate);
这里 FMyDelegate
是你定义的单播代理类型的名称。你可以使用此代理来绑定一个函数。
2. 声明代理
在你的类中,你可以声明一个单播代理类型作为类的成员变量。例如:
class AMyClass : public AActor
{
GENERATED_BODY()
public: // 声明一个单播代理
FMyDelegate OnSomethingHappened;
void TriggerEvent();
};
3. 绑定函数
要将函数绑定到代理,你可以使用 BindUObject
或 BindLambda
等方法来实现。绑定的函数必须与代理类型匹配。例如:
void AMyClass::BeginPlay()
{
Super::BeginPlay(); // 绑定函数到代理
OnSomethingHappened.BindUObject(this, &AMyClass::HandleSomethingHappened);
}
void AMyClass::HandleSomethingHappened() { // 处理事件的函数 }
4. 触发代理
在适当的地方触发代理,例如在某个事件发生时:
void AMyClass::TriggerEvent()
{
// 触发代理,调用绑定的函数
OnSomethingHappened.ExecuteIfBound();
}
5. 解绑
如果你需要解绑一个函数,可以使用 Unbind
方法。例如:
OnSomethingHappened.Unbind();
示例总结
// 声明单播代理类型
DECLARE_DELEGATE(FMyDelegate);
// 在类中声明单播代理变量
class AMyClass : public AActor
{
GENERATED_BODY()
public:
// 单播代理变量
FMyDelegate OnSomethingHappened;
void TriggerEvent();
void HandleSomethingHappened();
};
// 绑定函数到代理
void AMyClass::BeginPlay()
{
Super::BeginPlay();
OnSomethingHappened.BindUObject(this, &AMyClass::HandleSomethingHappened);
}
// 触发代理
void AMyClass::TriggerEvent()
{
OnSomethingHappened.ExecuteIfBound();
}
// 处理事件的函数
void AMyClass::HandleSomethingHappened()
{
UE_LOG(LogTemp, Warning, TEXT("Something happened!"));
}
多播代理
-
可以绑定多个回调函数,当其触发时,所有绑定的回调函数都会执行, 实质是维持了一个单播委托的数组
-
没有返回值.
-
支持参数
-
不支持反射以及序列化
1. 定义虚幻多播代理
在虚幻引擎中,多播代理通常用 DECLARE_DYNAMIC_MULTICAST_DELEGATE
宏定义。例如:
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnDamageTaken, float, DamageAmount);
2. 声明和使用
你可以在类中声明多播代理,并在类的 .cpp
文件中使用它。例如:
public:
UPROPERTY(BlueprintAssignable, Category = "Events")
FOnDamageTaken OnDamageTaken;
void TakeDamage(float DamageAmount);
void AYourClass::TakeDamage(float DamageAmount)
{
OnDamageTaken.Broadcast(DamageAmount);
}
3. 绑定和解绑
你可以在蓝图或 C++ 代码中绑定函数到多播代理。
void AMyOtherClass::BindToDelegate(AYourClass* Target)
{
if (Target)
{
Target->OnDamageTaken.AddDynamic(this, &AMyOtherClass::HandleDamage);
}
}
void AMyOtherClass::UnbindFromDelegate(AYourClass* Target)
{
if (Target)
{
Target->OnDamageTaken.RemoveDynamic(this, &AMyOtherClass::HandleDamage);
}
}
// Function to handle delegate call
void AMyOtherClass::HandleDamage(float DamageAmount)
{
// Handle damage
}
4. 蓝图中的使用
在蓝图中,你可以创建多播代理并将其绑定到蓝图中的函数,进行可视化操作。创建一个事件节点,并将它连接到代理上,可以响应事件触发。
动态代理
- 支持反射以及序列化,但其执行速度比常规委托慢。可
- 支持返回值
- 不支持参数
- 动态多播委托可以暴露给蓝图,在蓝图中动态绑定相关的函数,而普通的委托和动态多播委托则不行
1. 定义动态代理
在 C++ 中,你可以使用 DECLARE_DYNAMIC_DELEGATE
或类似宏来定义动态代理。
DECLARE_DYNAMIC_DELEGATE_OneParam(FMyDelegate, int32, Value);
2. 声明和使用
在你的类中声明动态代理,并在需要时调用它。例如:
public:
UPROPERTY()
FMyDelegate OnValueChanged;
void ChangeValue(int32 NewValue);
void AMyClass::ChangeValue(int32 NewValue)
{
if (OnValueChanged.IsBound())
{
OnValueChanged.Execute(NewValue);
}
}
3. 绑定和解绑
你可以在 C++ 代码中绑定函数到动态代理。例如:
void AOtherClass::BindToDelegate(AMyClass* Target)
{
if (Target)
{
Target->OnValueChanged.BindUFunction(this, TEXT("HandleValueChanged"));
}
}
// Function to handle delegate call
void AOtherClass::HandleValueChanged(int32 Value)
{
// Handle value
}
4. 蓝图中的使用
在蓝图中,你可以创建和使用动态代理。通过“绑定事件”节点将蓝图事件或函数绑定到代理上,然后使用“执行”节点触发代理。
在构建动态代理提供蓝图使用时,需要在代理上增加标记宏UPROPERTY(BlueprintAssignable)
附录
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
函数 | 说明 |
---|---|
"Add()" | 将函数委托添加到该多播委托的调用列表中。 |
"AddStatic()" | 添加原始C++指针全局函数委托。 |
"AddRaw()" | 添加原始C++指针委托。原始指针不使用任何类型的引用,因此如果从委托下面删除了对象,则调用此函数可能不安全。调用Execute()时请小心! |
"AddSP()" | 添加基于共享指针的(快速、非线程安全)成员函数委托。共享指针委托保留对对象的弱引用。 |
"AddUObject()" | 添加基于UObject的成员函数委托。UObject委托保留对对象的弱引用。 |
"Remove()" | 从该多播委托的调用列表中删除函数(性能为O(N))。请注意,委托的顺序可能不会被保留! |
"RemoveAll()" | 从该多播委托的调用列表中删除绑定到指定UserObject的所有函数。请注意,委托的顺序可能不会被保留! |
参考资料
【UE4 C++ 基础知识】<8> Delegate 委托 - 砥才人 - 博客园 (cnblogs.com)