虚幻的代理和委托

代理:

  • 代理可以帮助我们解决一对一或是一对多的任务分配工作。主要可以帮助我们解决通知问题。我们可以通过代理完成调用某一个对象的一个函数,而不直接持有该对象的任何指针。
  • 代理就是为你跑腿送信的,你可以不用关心给送信的目标人具体是谁,只要按照约定好的信件格式进行送信即可
  • 更简单理解,想去调用某个函数,但并不是直接去调用,而是通过另一个入口去调用(代理)

分类:

  • 单播代理 只能进行通知一个人
  • 多播代理 可以进行多人通知
  • 动态代理 可以被序列化(这体现在于蓝图进行交互,C++中可以将通知事件进行蓝图广播)

单播代理

单播代理是一种允许你声明一个可以绑定到单个函数的代理类型。你可以使用 DECLARE_DELEGATE 宏来定义一个单播代理。

1. 定义单播代理

单播代理是一种允许你声明一个可以绑定到单个函数的代理类型。你可以使用 DECLARE_DELEGATE 宏来定义一个单播代理。例如

DECLARE_DELEGATE(FMyDelegate);

 这里 FMyDelegate 是你定义的单播代理类型的名称。你可以使用此代理来绑定一个函数。

2. 声明代理

在你的类中,你可以声明一个单播代理类型作为类的成员变量。例如:

class AMyClass : public AActor 
{ 
GENERATED_BODY() 
public: // 声明一个单播代理 
FMyDelegate OnSomethingHappened; 
void TriggerEvent(); 
};

3. 绑定函数

要将函数绑定到代理,你可以使用 BindUObjectBindLambda 等方法来实现。绑定的函数必须与代理类型匹配。例如:

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)

附录

void Function()

DECLARE_DELEGATE(DelegateName)

void Function(Param1)

DECLARE_DELEGATE_OneParam(DelegateName, Param1Type)

void Function(Param1, Param2)

DECLARE_DELEGATE_TwoParams(DelegateName, Param1Type, Param2Type)

void Function(Param1, Param2, ...)

DECLARE_DELEGATE_<Num>Params(DelegateName, Param1Type, Param2Type, ...)

<RetValType> Function()

DECLARE_DELEGATE_RetVal(RetValType, DelegateName)

<RetValType> Function(Param1)

DECLARE_DELEGATE_RetVal_OneParam(RetValType, DelegateName, Param1Type)

<RetValType> Function(Param1, Param2)

DECLARE_DELEGATE_RetVal_TwoParams(RetValType, DelegateName, Param1Type, Param2Type)

<RetValType> Function(Param1, Param2, ...)

DECLARE_DELEGATE_RetVal_<Num>Params(RetValType, DelegateName, Param1Type, Param2Type, ...)

函数

说明

"Add()"

将函数委托添加到该多播委托的调用列表中。

"AddStatic()"

添加原始C++指针全局函数委托。

"AddRaw()"

添加原始C++指针委托。原始指针不使用任何类型的引用,因此如果从委托下面删除了对象,则调用此函数可能不安全。调用Execute()时请小心!

"AddSP()"

添加基于共享指针的(快速、非线程安全)成员函数委托。共享指针委托保留对对象的弱引用。

"AddUObject()"

添加基于UObject的成员函数委托。UObject委托保留对对象的弱引用。

"Remove()"

从该多播委托的调用列表中删除函数(性能为O(N))。请注意,委托的顺序可能不会被保留!

"RemoveAll()"

从该多播委托的调用列表中删除绑定到指定UserObject的所有函数。请注意,委托的顺序可能不会被保留!

 

参考资料

【UE4 C++ 基础知识】<8> Delegate 委托 - 砥才人 - 博客园 (cnblogs.com)

  UEC++ 代理/委托 - 黎沐不吃香菜 - 博客园 (cnblogs.com)

[UE4 C++入门到进阶] 4.Delegate(委托) - 哔哩哔哩 (bilibili.com)

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
虚幻引擎和Unity都有许多经典的书籍和电子书,这里列举一些比较优秀的书籍供参考: 虚幻引擎书籍推荐: 1. 《虚幻引擎官方文档》:虚幻引擎官方文档是学习虚幻引擎的必备资料,包含了虚幻引擎的各个方面,从入门到高级技巧都有详细的讲解。 2. 《虚幻引擎4游戏开发实战》:本书是一本虚幻引擎4的实战教程,通过实践项目的方式讲解了虚幻引擎4的各个方面,从场景制作到物理模拟,从UI设计到人工智能都有涉及。 3. 《虚幻引擎4游戏设计基础》:本书是一本适合初学者的虚幻引擎4入门教程,从基础概念到实践操作都有详细的讲解,适合快速入门。 Unity书籍推荐: 1. 《Unity官方文档》:Unity官方文档是学习Unity的必备资料,包含了Unity的各个方面,从入门到高级技巧都有详细的讲解。 2. 《Unity 5.x游戏开发实战》:本书是一本Unity 5.x的实战教程,通过实践项目的方式讲解了Unity 5.x的各个方面,从场景制作到物理模拟,从UI设计到人工智能都有涉及。 3. 《Unity 5.x游戏开发从入门到精通》:本书是一本适合初学者的Unity 5.x入门教程,从基础概念到实践操作都有详细的讲解,适合快速入门。 以上是一些较为优秀的虚幻引擎和Unity书籍,供开发者参考。当然,随着游戏引擎的不断更新和发展,也会有越来越多的优秀学习资料问世。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值