观察者模式原理
- 观察者模式通常有观察者与被观察者,当被观察者状态发生改变时,它会通知所有的被观察者对象,使他们能够及时做出响应,所以也被称作“发布-订阅模式”。总得来说就是你关注了一个主播,主播的状态改变会通知这些人,关注主播的这些人都会根据主播的通知改变一些状态
观察者模式游戏工程案例(击杀怪物后的逻辑)
- 观察者模式定义了对象之间的一对多关系,当主题对象改变状态发出通知的时候,它的所有观察者都会收到提示然后作出自己的响应
事件分发机制定义
- 通过观察者模式的“订阅-发布”机制使用事件传递数据与驱动行为的方法
- 优点:
- 解耦合
- 可读性强
- 模块独立化高
- 可自行测试
虚幻中的内置事件分发机制
纯蓝图广播事件通知
- 创建一个Pawn蓝图类,开个定时器,让这个Pawn每三秒发送一下事件,添加一个事件调度器,在定时器中进行广播通知
纯蓝图订阅事件通知
- 新建一个Actor,然后派生出三个子类去订阅广播的事件进行打印
- 运行结果
纯蓝图解绑订阅事件通知
- 可以在发布者中终止所有订阅通知
- 运行结果,这样就只会输出一次三色打印数字,因为四秒后就结束所有订阅者的订阅
- 也可以订阅者自己解绑
- 运行结果,这样后面就不会有这个订阅者的打印了
纯Cpp之多播委托
- 创建一个新Mode用来使用Cpp,创建一个C++的Pawn类,默认硬编码静态网格与材质
- 硬编码网格与材质
StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));RootComponent = StaticMesh;static ConstructorHelpers::FObjectFinder<UStaticMesh> StaticMeshAsset(TEXT("StaticMesh'/Engine/BasicShapes/Sphere.Sphere'"));static ConstructorHelpers::FObjectFinder<UMaterialInterface> MaterialAsset(TEXT("Material'/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial'"));if (StaticMeshAsset.Succeeded() && MaterialAsset.Succeeded()){StaticMesh->SetStaticMesh(StaticMeshAsset.Object);StaticMesh->SetMaterial(0, MaterialAsset.Object);}
- 运行结果
纯Cpp之多播委托的广播事件通知
- DECLARE_MULTICAST_DELEGATE_OneParam:用于声明一个多播委托。多播委托是一种特殊的委托,它可以同时注册多个监听器,当委托被调用时,所有的监听器都会收到通知并执行相关的方法。
DECLARE_MULTICAST_DELEGATE_OneParam(FMyDelegate, float);
:声明了一个名为 FMyDelegate 的多播委托,它可以接受一个浮点数作为参数。OneParam
表示这个多播委托有一个参数,可以传入一个参数给监听器。OnParam
这里可以写0-9,表示传入的参数个数,例如TwoParam
,就表示传两个参数
- 注意切换Mode的
Default Pawn Class
小心莫名报错 - 声明一个参数的多播委托对象
//声明一个名为 FCpp_Broadcast 的多播委托,它可以接受一个整数作为参数。DECLARE_MULTICAST_DELEGATE_OneParam(FCpp_Broadcast,int)FCpp_Broadcast Cpp_Broadcast;
- 编写多播委托广播
// Called when the game starts or when spawned
void ACppSender_Pawn::BeginPlay()
{Super::BeginPlay();FTimerHandle TimerHandle;auto Lambda = [this](FCpp_Broadcast* CB){CB->Broadcast(FMath::RandRange(0, 100));};//开启定时器,每3秒发一次广播GetWorld()->GetTimerManager().SetTimer(TimerHandle, FTimerDelegate::CreateLambda(Lambda, &Cpp_Broadcast), 3.f, true);
}
- 运行结果,相当与上面的蓝图创建广播事件通知
纯Cpp之多播委托的订阅事件通知
- 新建一个Actor基类然后派生出三个子类进行订阅事件
- Actor基类就只需要硬编码静态网格与材质
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Properties")class UStaticMeshComponent* StaticMesh;
//--------------------------------------------------------------------------
ACppReceiveActor::ACppReceiveActor()
{// Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.PrimaryActorTick.bCanEverTick = true;StaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));RootComponent = StaticMesh;ConstructorHelpers::FObjectFinder<UStaticMesh> StaticMeshAsset(TEXT("StaticMesh'/Engine/BasicShapes/Cube.Cube'"));ConstructorHelpers::FObjectFinder<UMaterialInterface> MaterialAsset(TEXT("Material'/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial'"));if (StaticMeshAsset.Succeeded() && MaterialAsset.Succeeded()){StaticMesh->SetStaticMesh(StaticMeshAsset.Object);StaticMesh->SetMaterial(0, MaterialAsset.Object);}}
- Actor的三个子类去接受广播进行屏幕打印数字,声明一个函数用来打印数字
CppReceiveActor_R.h
// Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h"
#include "UE_Cpp/CppReceiveActor.h"
#include "CppReceiveActor_R.generated.h"/*** */
UCLASS()
class DISTRIBUTE_API ACppReceiveActor_R : public ACppReceiveActor
{GENERATED_BODY()
public:void OnReceive(int Param);
protected:virtual void BeginPlay() override;
};
CppReceiveActor_R.cpp
- AddOnScreenDebugMessage 是虚幻中的一种功能,它可以在屏幕上显示一条调试消息。这对于开发人员在调试过程中非常有用,可以帮助他们快速了解当前程序的状态和进度。
具体来说,AddOnScreenDebugMessage 函数的使用方式如下:
这个函数的第一个参数表示消息 ID,用于区分不同的消息。第二个参数表示 消息的显示时间,单位是秒。第三个参数表示消息的颜色,第四个参数表示消息的内容。需要注意的是,AddOnScreenDebugMessage 函数并不是一个标准库函数,而是 Unreal Engine 中的一个全局函数,所以使用前需要先引入相关的头文件:GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("This is a debug message"));
#include "Engine/Engine.h"
。
// Fill out your copyright notice in the Description page of Project Settings.#include "CppReceiveActor_R.h"
#include "Kismet/GameplayStatics.h"
#include "CppSender_Pawn.h"
#include "Engine/Engine.h"void ACppReceiveActor_R::BeginPlay()
{Super::BeginPlay();//获取到CppSender_PawnACppSender_Pawn* PlayerPawn = Cast<ACppSender_Pawn>(UGameplayStatics::GetPlayerPawn(this, 0));//订阅事件通知PlayerPawn->Cpp_Broadcast.AddUObject(this, &ACppReceiveActor_R::OnReceive);
}void ACppReceiveActor_R::OnReceive(int Param)
{//打印到屏幕GEngine->AddOnScreenDebugMessage(INDEX_NONE, 10.f, FColor::Red, FString::Printf(TEXT("%i"), Param));
}
纯Cpp之多播委托的解绑订阅事件通知
- Cpp中订阅事件通知,会返回一个FDelegate句柄,我们接受这个句柄然后调用Remove删除即可,也可以使用RemoveAll也是一样的效果,但是RemoveAll使用方法不一样
void ACppReceiveActor_R::BeginPlay()
{Super::BeginPlay();//获取到CppSender_PawnACppSender_Pawn* PlayerPawn = Cast<ACppSender_Pawn>(UGameplayStatics::GetPlayerPawn(this, 0));//订阅事件通知FDelegateHandle DelegateHandle = PlayerPawn->Cpp_Broadcast.AddUObject(this, &ACppReceiveActor_R::OnReceive);FTimerHandle TimerHandle;auto Lambda = [=](){PlayerPawn->Cpp_Broadcast.Remove(DelegateHandle);//删除所有订阅者,一般在发布者使用//PlayerPawn->Cpp_Broadcast.Clear();};GetWorld()->GetTimerManager().SetTimer(TimerHandle, FTimerDelegate::CreateLambda(Lambda), 4.f, false);
}
- 运行结果
- 删除所有订阅者
蓝图与Cpp混合多播动态委托的广播事件通知
- 多播委托可以蓝图与Cpp混合使用,新建一个Pawn类来创建广播事件
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam
:使用这个宏就可以定义一个可以在蓝图中调用的多播委托,多加了一个DYNAMIC
就可以被蓝图去识别到了- 创建多播委托代理,要加上反射,参数为
BlueprintAssignable
这样就可以被蓝图调用
public:// Sets default values for this pawn's propertiesABPAndCpp_Sender();
//注意这里要加分号,上个没有加DYNAMIC的不用加分号DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FBPAndCpp_Broadcast, int, Param);UPROPERTY(BlueprintAssignable)FBPAndCpp_Broadcast BPAndCpp_Broadcast;UPROPERTY(VisibleAnywhere,BlueprintReadOnly,Category="Properties")class UStaticMeshComponent* StaticMesh;
void ABPAndCpp_Sender::BeginPlay()
{Super::BeginPlay();FTimerHandle TimerHandle;auto Lambda = [](FBPAndCpp_Broadcast* BPAC){BPAC->Broadcast(FMath::RandRange(0, 100));};GetWorld()->GetTimerManager().SetTimer(TimerHandle, FTimerDelegate::CreateLambda(Lambda,&BPAndCpp_Broadcast), 3.f, true);}
蓝图与Cpp混合多播动态委托的订阅事件通知
- 创建一个基类Actor派生出三个子类进行订阅事件
- 注意这里的接收函数需要添加反射,反射可以无参数
- 注意这里的绑定使用的是
AddDynamic
而不是AddUObject
void ABPAndCpp_Receive_R::BeginPlay()
{Super::BeginPlay();ABPAndCpp_Sender* MainPlayer = Cast<ABPAndCpp_Sender>(UGameplayStatics::GetPlayerPawn(this, 0));//绑定订阅事件通知MainPlayer->BPAndCpp_Broadcast.AddDynamic(this, &ABPAndCpp_Receive_R::OnReceived);
}void ABPAndCpp_Receive_R::OnReceived(int Param)
{GEngine->AddOnScreenDebugMessage(INDEX_NONE, 10.f, FColor::Red, FString::Printf(TEXT("%i"), Param));
}
- 运行结果,此时C++拉入场景中也可以调用通知
蓝图与Cpp混合多播动态委托的蓝图多播动态委托订阅
- 新建一个Actor蓝图,派生三个蓝图,在蓝图中就可以调用多播委托,因为有反射,会出现一个委托
- 运行结果
- 蓝图中的解绑和之前纯蓝图解绑事件还是一样的
- C++中解绑使用
RemoveDynamic
就行
- 运行结果