多播代理、事件和广播器都是在软件开发中用于实现观察者模式或发布-订阅模式的机制。它们有一些相似之处,但也有一些区别。以下是它们之间的主要区别和联系:
多播代理 (Multicast Delegates):
-
声明方式: 使用
DECLARE_MULTICAST_DELEGATE宏声明多播代理。 -
语法: 多播代理是一种特殊类型的代理,允许多个函数或成员函数(监听者)注册到同一个代理上。
-
添加和移除监听者: 使用
Add方法添加监听者,使用Remove方法移除监听者。 -
触发: 使用
Broadcast方法触发代理,调用所有注册的监听者的处理函数。 -
内置于UE框架: 多播代理是UE框架中的一部分,被广泛用于实现事件系统。
事件 (Events):
-
声明方式: 使用
DECLARE_EVENT宏声明事件。 -
语法: 事件是一种类似于多播代理的机制,允许多个监听者注册到同一个事件上。
-
添加和移除监听者: 使用
Add方法添加监听者,使用Remove方法移除监听者。 -
触发: 使用
Broadcast方法触发事件,调用所有注册的监听者的处理函数。 -
UE框架内建: 事件也是UE框架中的一部分,通常用于在蓝图和C++中实现事件系统。
广播器 (Broadcaster):
-
实现方式: 广播器是一个自定义的机制,你需要自己实现来管理和通知监听者。
-
语法: 你可以创建一个类来表示广播器,其中包含方法用于添加和移除监听者,以及触发事件。
-
添加和移除监听者: 通常通过自定义的方法添加和移除监听者。
-
触发: 通过调用广播器的方法触发事件,通知所有注册的监听者。
-
灵活性: 广播器提供了更大的灵活性,因为你可以完全控制事件的订阅和取消订阅逻辑。
联系和共通点:
-
实现目标: 多播代理、事件和广播器都用于实现观察者模式或发布-订阅模式,让一个对象可以通知其他对象关于特定事件的发生。
-
支持多个监听者: 三者都支持多个监听者,可以动态添加和移除监听者。
-
触发事件: 在事件发生时,它们都能够通知所有注册的监听者。
在Unreal Engine中,多播代理和事件是相对内置的机制,更容易在UE框架中使用。广播器则是一种更自定义的方式,可以根据项目的具体需求来实现。选择使用哪种方式取决于项目的设计和开发需求。
多播代理示例
以下是一个简单的使用多播代理的示例,演示了如何在Unreal Engine中使用多播代理进行事件通知。在这个示例中,我们创建了一个名为FMyMulticastDelegate的多播代理,并在一个类中触发了这个代理,同时有两个不同的类作为监听器注册到了这个代理上。
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyMulticastDelegateExample.generated.h"
DECLARE_MULTICAST_DELEGATE(FMyMulticastDelegate);
UCLASS()
class YOURGAME_API AMyMulticastDelegateExample : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyMulticastDelegateExample();
// Trigger the multicast delegate
void TriggerDelegate();
// Multicast delegate instance
FMyMulticastDelegate MyDelegate;
};
UCLASS()
class YOURGAME_API AListenerClass1 : public AActor
{
GENERATED_BODY()
public:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Function to handle the multicast delegate
UFUNCTION()
void HandleDelegateFunction()
{
UE_LOG(LogTemp, Warning, TEXT("ListenerClass1 received the delegate broadcast!"));
}
};
UCLASS()
class YOURGAME_API AListenerClass2 : public AActor
{
GENERATED_BODY()
public:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Function to handle the multicast delegate
UFUNCTION()
void HandleDelegateFunction()
{
UE_LOG(LogTemp, Warning, TEXT("ListenerClass2 received the delegate broadcast!"));
}
};
在这个示例中,我们有一个 AMyMulticastDelegateExample 类表示一个触发多播代理的对象。该类包含一个名为 MyDelegate 的多播代理实例,并有一个函数 TriggerDelegate() 用于触发代理。同时,我们有两个监听器类 AListenerClass1 和 AListenerClass2,它们分别包含了处理多播代理的函数 HandleDelegateFunction()。
// MyMulticastDelegateExample.cpp
#include "MyMulticastDelegateExample.h"
// Sets default values
AMyMulticastDelegateExample::AMyMulticastDelegateExample()
{
// Set this actor to call Tick() every frame
PrimaryActorTick.bCanEverTick = false;
}
// Trigger the multicast delegate
void AMyMulticastDelegateExample::TriggerDelegate()
{
UE_LOG(LogTemp, Warning, TEXT("MyMulticastDelegateExample triggering the delegate!"));
MyDelegate.Broadcast();
}
// Called when the game starts or when spawned
void AListenerClass1::BeginPlay()
{
Super::BeginPlay();
// Get a reference to the delegate owner
AMyMulticastDelegateExample* DelegateOwner = Cast<AMyMulticastDelegateExample>(GetWorld()->GetFirstPlayerController()->GetPawn());
// Add listener to the delegate
if (DelegateOwner)
{
DelegateOwner->MyDelegate.AddUObject(this, &AListenerClass1::HandleDelegateFunction);
}
}
// Called when the game starts or when spawned
void AListenerClass2::BeginPlay()
{
Super::BeginPlay();
// Get a reference to the delegate owner
AMyMulticastDelegateExample* DelegateOwner = Cast<AMyMulticastDelegateExample>(GetWorld()->GetFirstPlayerController()->GetPawn());
// Add listener to the delegate
if (DelegateOwner)
{
DelegateOwner->MyDelegate.AddUObject(this, &AListenerClass2::HandleDelegateFunction);
}
}
在这里,AMyMulticastDelegateExample 类中的 TriggerDelegate() 函数会触发多播代理的 Broadcast,通知所有注册的监听器。两个监听器类在它们的 BeginPlay() 函数中将自己注册为代理的监听者。当 MyDelegate 被触发时,两个监听器的处理函数将被调用,输出相应的日志信息。
事件示例
以下是一个简单的示例,演示了如何在UE中使用C++声明和触发事件。
// MyEventExample.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyEventExample.generated.h"
DECLARE_EVENT(AMyEventExample, FMyEvent);
UCLASS()
class YOURGAME_API AMyEventExample : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyEventExample();
// Function to trigger the event
void TriggerEvent();
// Event declaration
FMyEvent OnMyEvent;
};
在上面的示例中,我们使用 DECLARE_EVENT 宏声明了一个名为 FMyEvent 的事件。然后,在 AMyEventExample 类中,我们有一个名为 OnMyEvent 的事件实例。
// MyEventExample.cpp
#include "MyEventExample.h"
// Sets default values
AMyEventExample::AMyEventExample()
{
// Set this actor to call Tick() every frame
PrimaryActorTick.bCanEverTick = false;
}
// Function to trigger the event
void AMyEventExample::TriggerEvent()
{
UE_LOG(LogTemp, Warning, TEXT("AMyEventExample triggering the event!"));
OnMyEvent.Broadcast();
}
在上述代码中,AMyEventExample 类包含了一个名为 TriggerEvent() 的函数,该函数触发了事件 OnMyEvent。当事件被触发时,它会调用所有注册的监听者的处理函数。
接下来,我们将创建一个Actor类作为事件的监听者:
// MyEventListener.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyEventListener.generated.h"
UCLASS()
class YOURGAME_API AMyEventListener : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyEventListener();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// Function to handle the event
UFUNCTION()
void HandleEventFunction();
};
// MyEventListener.cpp
#include "MyEventListener.h"
#include "MyEventExample.h" // Include the header of the event owner class
// Sets default values
AMyEventListener::AMyEventListener()
{
// Set this actor to call Tick() every frame
PrimaryActorTick.bCanEverTick = false;
}
// Called when the game starts or when spawned
void AMyEventListener::BeginPlay()
{
Super::BeginPlay();
// Get a reference to the event owner
AMyEventExample* EventOwner = GetWorld()->SpawnActor<AMyEventExample>(AMyEventExample::StaticClass(), FVector::ZeroVector, FRotator::ZeroRotator);
// Add this listener to the event
if (EventOwner)
{
EventOwner->OnMyEvent.AddUObject(this, &AMyEventListener::HandleEventFunction);
}
}
// Function to handle the event
void AMyEventListener::HandleEventFunction()
{
UE_LOG(LogTemp, Warning, TEXT("AMyEventListener received the event notification!"));
}
在这个示例中,我们创建了一个名为 AMyEventListener 的Actor类,它实现了 HandleEventFunction 函数来处理事件。在 BeginPlay 函数中,我们获取了对事件拥有者的引用(在这里是 AMyEventExample 实例),并将监听者自身添加到事件的监听列表中。
这样,当 AMyEventExample 类的 TriggerEvent 函数被调用时,所有注册的监听者(在这里是 AMyEventListener)的处理函数将被触发,输出相应的日志信息。
广播器示例
以下是一个简单的示例,演示了如何在UE中使用C++实现广播器。
// BroadcasterExample.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "BroadcasterExample.generated.h"
class IBroadcastListener
{
public:
virtual void HandleBroadcast() = 0;
};
UCLASS()
class YOURGAME_API ABroadcasterExample : public AActor
{
GENERATED_BODY()
public:
ABroadcasterExample();
// 添加监听者
void AddListener(IBroadcastListener* Listener);
// 移除监听者
void RemoveListener(IBroadcastListener* Listener);
// 触发广播
void BroadcastEvent();
private:
TArray<IBroadcastListener*> Listeners;
};
在这个示例中,我们声明了一个 IBroadcastListener 接口,该接口包含一个纯虚函数 HandleBroadcast,表示监听者需要实现这个函数来处理广播事件。然后,我们创建了一个名为 ABroadcasterExample 的Actor类,它包含了添加、移除监听者和触发广播的方法。
// BroadcasterExample.cpp
#include "BroadcasterExample.h"
ABroadcasterExample::ABroadcasterExample()
{
PrimaryActorTick.bCanEverTick = false;
}
// 添加监听者
void ABroadcasterExample::AddListener(IBroadcastListener* Listener)
{
if (Listener && !Listeners.Contains(Listener))
{
Listeners.Add(Listener);
}
}
// 移除监听者
void ABroadcasterExample::RemoveListener(IBroadcastListener* Listener)
{
Listeners.Remove(Listener);
}
// 触发广播
void ABroadcasterExample::BroadcastEvent()
{
for (IBroadcastListener* Listener : Listeners)
{
if (Listener)
{
Listener->HandleBroadcast();
}
}
}
在实现文件中,我们对添加、移除监听者和触发广播的函数进行了实现。AddListener 和 RemoveListener 函数负责管理监听者列表,而 BroadcastEvent 函数用于遍历所有监听者并调用它们的 HandleBroadcast 函数。
接下来,我们创建一个实现了 IBroadcastListener 接口的Actor类:
// BroadcastListenerExample.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "BroadcasterExample.h"
#include "BroadcastListenerExample.generated.h"
UCLASS()
class YOURGAME_API ABroadcastListenerExample : public AActor, public IBroadcastListener
{
GENERATED_BODY()
public:
ABroadcastListenerExample();
// Called when the game starts or when spawned
virtual void BeginPlay() override;
// 实现IBroadcastListener接口
virtual void HandleBroadcast() override;
};
// BroadcastListenerExample.cpp
#include "BroadcastListenerExample.h"
ABroadcastListenerExample::ABroadcastListenerExample()
{
PrimaryActorTick.bCanEverTick = false;
}
void ABroadcastListenerExample::BeginPlay()
{
Super::BeginPlay();
// 获取广播器的引用
ABroadcasterExample* Broadcaster = GetWorld()->SpawnActor<ABroadcasterExample>(ABroadcasterExample::StaticClass(), FVector::ZeroVector, FRotator::ZeroRotator);
// 添加自己作为监听者
if (Broadcaster)
{
Broadcaster->AddListener(this);
}
}
void ABroadcastListenerExample::HandleBroadcast()
{
UE_LOG(LogTemp, Warning, TEXT("ABroadcastListenerExample received the broadcast!"));
}
在这个示例中,我们创建了一个名为 ABroadcastListenerExample 的Actor类,它实现了 IBroadcastListener 接口的 HandleBroadcast 函数。在 BeginPlay 函数中,我们获取了广播器的引用,并将当前实例添加为监听者。
最后,当广播器的 BroadcastEvent 被调用时,所有注册的监听者都会收到通知,执行相应的处理函数。在这个示例中,ABroadcastListenerExample 将输出日志信息表示它收到了广播事件。
本文详细比较了UnrealEngine中的多播代理、事件和广播器,解释了它们的声明方式、添加/移除监听者、触发事件的机制,并展示了在实际项目中的应用示例。
3296

被折叠的 条评论
为什么被折叠?



