【UE5 C++课程系列笔记】09——多播委托的基本使用

目录

多播委托------申明委托

一、DECLARE_MULTICAST_DELEGATE

二、DECLARE_DYNAMIC_MULTICAST_DELEGATE

多播委托------绑定委托

一、Add

二、AddStatic

三、AddRaw

四、AddSP

五、AddUObject

六、Remove

七、RemoveAll

多播委托------执行

载荷数据


上一篇:【UE5 C++课程系列笔记】08------单播委托的基本使用-CSDN博客

多播委托拥有大部分与单播委托相同的功能。它们只拥有对对象的弱引用,可以与结构体一起使用,可以四处轻松复制等等。就像常规委托一样,多播委托可以远程加载/保存和触发;但多播委托函数不能使用返回值。它们最适合用来四处轻松传递一组委托。

与单播委托不同的是,多播委托可以绑定多个不同对象的函数(也可以是同一个对象的多个不同函数)。当调用多播委托时,会按照绑定的顺序依次执行所有与之绑定的函数,适合于需要通知多个地方执行相关操作的场景,比如游戏中某个事件发生了,需要多个不同的系统做出响应。

多播委托------申明委托

多播委托中用于申明委托相关宏如下:

一、DECLARE_MULTICAST_DELEGATE

DECLARE_MULTICAST_DELEGATE 用于声明一个多播委托类型。多播委托可以绑定多个函数,当委托被触发时,所有绑定的函数都会被依次调用。

无参数的多播委托声明:

基本语法为DECLARE_MULTICAST_DELEGATE(FDelegateName),其中FDelegateName是自定义的委托名称,用于在后续代码中定义委托实例、绑定函数等操作。

带参数的多播委托声明:

对于带一个参数的多播委托,语法是DECLARE_MULTICAST_DELEGATE_OneParam(FDelegateName, ParamType, ParamName)。这里ParamType是参数的类型,ParamName是参数在委托中的名称,主要用于在蓝图(如果委托用于蓝图交互)或代码文档中清晰地标识参数。例如,声明一个带有一个int类型参数的多播委托:DECLARE_MULTICAST_DELEGATE_OneParam(FMyDelegateWithIntParam, int, MyIntParam);

对于带多个参数的多播委托,如两个参数,语法是DECLARE_MULTICAST_DELEGATE_TwoParams(FDelegateName, Param1Type, Param1Name, Param2Type, Param2Name),依此类推,按照参数的数量和类型依次声明。

假设有一个游戏中的事件系统,当玩家获得一个成就时,需要通知多个 UI 元素进行更新显示,示例代码如下:

cpp 复制代码
// 1. 声明多播委托
DECLARE_MULTICAST_DELEGATE(FOnAchievementUnlocked);

class APlayerController;
class AMyAchievementSystem : public AActor
{
public:
    // 2. 定义多播委托实例
    FOnAchievementUnlocked OnAchievementUnlocked;

    void UnlockAchievement()
    {
        // 3. 触发多播委托
        OnAchievementUnlocked.Broadcast();
    }
};

class UMyAchievementUI : public UUserWidget
{
public:
    void BindToAchievementSystem(AMyAchievementSystem* AchievementSystem)
    {
        if (AchievementSystem)
        {
            // 4. 将函数绑定到多播委托
            AchievementSystem->OnAchievementUnlocked.AddUObject(this, &UMyAchievementUI::OnAchievementUnlockedHandler);
        }
    }

    void OnAchievementUnlockedHandler()
    {
        // 5. 在这里更新UI显示成就已解锁
        UE_LOG(LogTemp, Warning, TEXT("Achievement unlocked! Updating UI..."));
    }
};

二、DECLARE_DYNAMIC_MULTICAST_DELEGATE

DECLARE_DYNAMIC_MULTICAST_DELEGATE是虚幻引擎(UE)中用于声明动态多播委托的宏。动态多播委托主要用于实现事件广播机制,允许在 C++ 代码中定义事件,并且这些事件能够方便地在蓝图(UE 的可视化脚本系统)中进行绑定和响应。这对于实现游戏中的事件系统,特别是需要在蓝图和 C++ 之间进行交互的情况非常有用。

基本语法如下:

(1)无参数的动态多播委托声明:

DECLARE_DYNAMIC_MULTICAST_DELEGATE(FDelegateName);

(2)带有一个参数的动态多播委托声明: DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FDelegateName, ParamType, ParamName);

(3)带有多个参数的动态多播委托声明(例如两个参数):DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FDelegateName, Param1Type, Param1Name, Param2Type, Param2Name);

多播委托------绑定委托

绑定多播委托可使用以下7个绑定函数

一、Add

用于将UObject实例的成员函数绑定到多播委托上。它会自动处理UObject的生命周期,当UObject被销毁时,会自动从委托中移除对应的绑定函数,防止出现悬空指针等问题。这是虚幻引擎中处理UObject相关委托绑定的常用方式,有助于确保系统的稳定性和安全性。

在如下示例代码中,通过AddAAnotherClass实例的AnotherFunction成员函数绑定到AExampleClassMyMulticastDelegate多播委托上。

cpp 复制代码
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyExampleClass.generated.h"

DECLARE_MULTICAST_DELEGATE(FMyMulticastDelegate);

UCLASS()
class MYGAME_API AExampleClass : public AActor
{
    GENERATED_BODY()
public:
    FMyMulticastDelegate MyMulticastDelegate;

    void BindFunction()
    {
        AAnotherClass* AnotherObject = NewObject<AAnotherClass>();
        MyMulticastDelegate.Add(AnotherObject, &AAnotherClass::AnotherFunction);
    }
};

二、AddStatic

用于绑定静态函数到多播委托。静态函数是属于类本身的函数,不依赖于类的具体实例,因此在绑定到委托时,不需要考虑对象生命周期相关的问题。这使得它在处理一些通用的、不依赖于特定对象状态的逻辑时非常方便,能够为多个对象共享相同的静态函数响应委托事件。

在如下示例代码中,通过AddStaticMyUtilityClass的静态函数StaticFunction绑定到FMyStaticDelegate多播委托上。

cpp 复制代码
#include "CoreMinimal.h"
#include "MyUtilityClass.generated.h"

DECLARE_MULTICAST_DELEGATE(FMyStaticDelegate);

class MyUtilityClass
{
public:
    static void StaticFunction()
    {
        // 这里是静态函数的具体逻辑
    }
};

void BindStaticFunction()
{
    FMyStaticDelegate StaticDelegate;
    StaticDelegate.AddStatic(&MyUtilityClass::StaticFunction);
}

三、AddRaw

用于绑定普通的C++ 函数(非UObject相关的成员函数或静态函数)或者函数指针到多播委托。与Add()不同,它不会自动处理对象的生命周期等额外机制,开发者需要自己确保所绑定函数的有效性以及相关资源(如函数所属对象的生命周期,如果是类成员函数的话)的正确管理。这种方式提供了更大的灵活性,但也需要开发者更加谨慎地处理可能出现的问题。

在如下示例代码中,通过AddRaw将全局函数GlobalFunction绑定到FMyRawDelegate多播委托上。

cpp 复制代码
#include "CoreMinimal.h"

DECLARE_MULTICAST_DELEGATE(FMyRawDelegate);

void GlobalFunction()
{
    // 这里是全局函数的具体逻辑
}

void BindRawFunction()
{
    FMyRawDelegate MyRawDelegate;
    MyRawDelegate.AddRaw(GlobalFunction);
}

四、AddSP

主要用于绑定共享指针(TSharedPtr)指向的对象的成员函数到多播委托。在虚幻引擎中,共享指针常用于管理对象的生命周期,AddSP()结合共享指针可以确保在对象的引用计数归零时正确处理与委托的绑定关系,避免出现悬空指针等错误情况。这种方式在处理对象的共享使用和生命周期管理方面提供了一种有效的解决方案。

在如下示例代码中,通过AddSP将共享指针SharedPtr指向的MySharedClass对象的SharedFunction成员函数绑定到FMySPDelegate多播委托上。

cpp 复制代码
#include "CoreMinimal.h"
#include "MySharedClass.generated.h"

DECLARE_MULTICAST_DELEGATE(FMySPDelegate);

UCLASS()
class MYGAME_API MySharedClass
{
    GENERATED_BODY()
public:
    void SharedFunction()
    {
        // 这里是成员函数的具体逻辑
    }
};

void BindSPFunction()
{
    TSharedPtr<MySharedClass> SharedPtr = MakeShared<MySharedClass>();
    FMySPDelegate MySPDelegate;
    MySPDelegate.AddSP(SharedPtr, &MySharedClass::SharedFunction);
}

五、AddUObject

AddUObject()Add()功能类似,用于将UObject实例的成员函数绑定到多播委托。它是一种方便的、针对UObject的委托绑定方式,能够自动处理UObject的生命周期相关问题,保证委托在调用绑定函数时的安全性和有效性。在虚幻引擎的开发中,这种方式常用于在不同的UObject之间建立事件响应机制。

在如下示例代码中,通过AddUObjectAAnotherClass实例的AnotherFunction成员函数绑定到AExampleClassMyMulticastDelegate多播委托上。

cpp 复制代码
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyExampleClass.generated.h"

DECLARE_MULTICAST_DELEGATE(FMyMulticastDelegate);

UCLASS()
class MYGAME_API AExampleClass : public AActor
{
    GENERATED_BODY()
public:
    FMyMulticastDelegate MyMulticastDelegate;

    void BindFunction()
    {
        AAnotherClass* AnotherObject = NewObject<AAnotherClass>();
        MyMulticastDelegate.AddUObject(AnotherObject, &AAnotherClass::AnotherFunction);
    }
};

六、Remove

用于从多播委托中移除特定的绑定函数。当不再需要某个函数对委托事件做出响应时,或者在对象生命周期结束等情况下,需要手动使用Remove()来清理委托中的绑定关系,以避免不必要的函数调用或潜在的错误。

在如下示例代码中,先使用AddRawSomeFunction绑定到MyDelegate,然后使用Remove将其从委托中移除。

cpp 复制代码
#include "CoreMinimal.h"

DECLARE_MULTICAST_DELEGATE(FMyDelegate);

void SomeFunction()
{
    // 函数逻辑
}

void RemoveFunctionBinding()
{
    FMyDelegate MyDelegate;
    MyDelegate.AddRaw(SomeFunction);
    // 移除绑定
    MyDelegate.Remove(SomeFunction);
}

七、RemoveAll

用于移除多播委托中所有已绑定的函数。这在需要重置委托的状态,或者当持有委托的对象即将被销毁等情况下非常有用,可以确保委托处于一个干净的状态,不会在后续操作中意外调用已失效的绑定函数。

在如下代码中,RemoveAll操作会清除FMyAllDelegate多播委托中所有已绑定的函数。

cpp 复制代码
#include "CoreMinimal.h"

DECLARE_MULTICAST_DELEGATE(FMyAllDelegate);

void RemoveAllFunctionBindings()
{
    FMyAllDelegate MyAllDelegate;
    // 假设已经绑定了一些函数,这里省略绑定过程

    // 移除所有绑定函数
    MyAllDelegate.RemoveAll();
}

多播委托------执行

多播委托允许附加多个函数委托,然后通过调用多播委托的"Broadcast()"函数一次性同时执行多个委托函数。多播委托签名不得使用返回值。

在多播委托上调用"Broadcast()"总是安全的,即使是在没有任何绑定时也是如此。唯一需要注意的是,如果您使用委托来初始化输出变量,通常会带来非常不利的后果。

调用"Broadcast()"时绑定函数的执行顺序尚未定义。执行顺序可能与函数的添加顺序不相同。

调用Broadcast()的语法如下:

cpp 复制代码
MyDelegate.Broadcast();

载荷数据

在如下代码中,当调用 MultiDelegateActor实例的函数CallSimpleMulti()时,会执行多播委托MulityDelegate,由于MulityDelegate绑定了AMultiExecuteActor实例的函数Work(),因此在CallSimpleMulti()被调用后,Work()函数也会被调用。其中,当MulityDelegate绑定委托函数的时候,将参数Health传递到委托函数。

头文件:

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MultiDelegateActor.generated.h"

DECLARE_MULTICAST_DELEGATE_OneParam(MulityDelegate, FString);

UCLASS()
class STUDY_API AMultiExecuteActor : public AActor
{
	GENERATED_BODY()

protected:
	virtual void BeginPlay() override;
public:
	AMultiExecuteActor();
	void Work(FString InFString, int32 InHealth);
	FGuid MyActorID;
};

UCLASS()
class STUDY_API AMultiDelegateActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AMultiDelegateActor();

	UFUNCTION(BlueprintCallable)
	void CallSimpleMulti();  //用于测试委托执行的函数

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	MulityDelegate MulityDelegate;
};

源文件:

cpp 复制代码
// Fill out your copyright notice in the Description page of Project Settings.


#include "Delegate/MultiDelegateActor.h"
#include "Kismet/GameplayStatics.h"

AMultiExecuteActor::AMultiExecuteActor()
{
	RootComponent = CreateDefaultSubobject<USceneComponent>(TEXT("LocationRoot"));
	MyActorID = FGuid::NewGuid();
}

void AMultiExecuteActor::BeginPlay()
{
	AActor* ActorPtr = UGameplayStatics::GetActorOfClass(this, AMultiDelegateActor::StaticClass());
	if (AMultiDelegateActor* MultiDelegateActorPtr = Cast<AMultiDelegateActor>(ActorPtr))
	{
		int32 Health = 100;
		MultiDelegateActorPtr->MulityDelegate.AddUObject(this, &AMultiExecuteActor::Work, Health);  //绑定委托函数并进行数据载荷
	}
}

void AMultiExecuteActor::Work(FString InFString, int32 InHealth)
{
	UE_LOG(LogTemp, Warning, TEXT("ID:[%s],InStr:[%s],Health:[%d]"), *MyActorID.ToString(), *InFString, InHealth);
}

// Sets default values
AMultiDelegateActor::AMultiDelegateActor()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = false;

}

void AMultiDelegateActor::CallSimpleMulti()
{
	MulityDelegate.Broadcast(TEXT("This is simple Multi"));
}

// Called when the game starts or when spawned
void AMultiDelegateActor::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void AMultiDelegateActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

在UEEditor中将MultiDelegateActor和MultiExecuteActor加入场景中,将MultiExecuteActor复制3份

在关卡蓝图中设置函数CallSimpleMulti()的触发方式。

运行后可以看到当调用CallSimpleMulti()后,执行了所有MulityDelegate绑定的委托函数

相关推荐
Ting丶丶44 分钟前
Electron入门笔记
javascript·笔记·electron
奕天者2 小时前
C++学习笔记(十七)——类之封装
c++·笔记·学习
你的微笑像拥抱4 小时前
HTTP 强 Etag 和 弱 Etag
笔记
zhuidushi44064 小时前
文献笔记 - A Survey on Aerial Swarm Robotics
笔记
LuckyLay10 小时前
Golang学习笔记_49——解释器模式
笔记·学习·设计模式·golang·解释器模式
weixin_5025398511 小时前
rust学习笔记13-18. 四数之和
笔记·学习·rust
郭涤生14 小时前
在线程间共享数据_第三章_《C++并发编程》笔记
c++·笔记·算法
执念斩长河15 小时前
Go泛型学习笔记
笔记·学习·golang
一条晒干的咸魚16 小时前
【C#学习笔记02】基本元素与数据类型
开发语言·笔记·学习·c#
没学上了17 小时前
Visual stdio2022 opencv cude pytroch与yolov8/可视化工具的环境搭建,不搞VIP,我也要当雷锋
人工智能·笔记·python·opencv·yolo·计算机视觉·yolov8