UE 事件分发机制(一) day9

观察者模式原理

  • 观察者模式通常有观察者与被观察者,当被观察者状态发生改变时,它会通知所有的被观察者对象,使他们能够及时做出响应,所以也被称作“发布-订阅模式”。总得来说就是你关注了一个主播,主播的状态改变会通知这些人,关注主播的这些人都会根据主播的通知改变一些状态

观察者模式游戏工程案例(击杀怪物后的逻辑)

  • 观察者模式定义了对象之间的一对多关系,当主题对象改变状态发出通知的时候,它的所有观察者都会收到提示然后作出自己的响应
    UE 事件分发机制(一) day9_第1张图片

事件分发机制定义

  • 通过观察者模式的“订阅-发布”机制使用事件传递数据驱动行为的方法
    UE 事件分发机制(一) day9_第2张图片
  • 优点:
    • 解耦合
    • 可读性强
    • 模块独立化高
    • 可自行测试

虚幻中的内置事件分发机制

纯蓝图广播事件通知

  • 创建一个Pawn蓝图类,开个定时器,让这个Pawn每三秒发送一下事件,添加一个事件调度器,在定时器中进行广播通知
    UE 事件分发机制(一) day9_第3张图片
    UE 事件分发机制(一) day9_第4张图片

纯蓝图订阅事件通知

  • 新建一个Actor,然后派生出三个子类去订阅广播的事件进行打印
    UE 事件分发机制(一) day9_第5张图片
  • 运行结果
    UE 事件分发机制(一) day9_第6张图片

纯蓝图解绑订阅事件通知

  • 可以在发布者中终止所有订阅通知
    UE 事件分发机制(一) day9_第7张图片
  • 运行结果,这样就只会输出一次三色打印数字,因为四秒后就结束所有订阅者的订阅
    UE 事件分发机制(一) day9_第8张图片
  • 也可以订阅者自己解绑
    UE 事件分发机制(一) day9_第9张图片
  • 运行结果,这样后面就不会有这个订阅者的打印了
    UE 事件分发机制(一) day9_第10张图片

纯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);
	}

UE 事件分发机制(一) day9_第11张图片
UE 事件分发机制(一) day9_第12张图片

  • 运行结果
    UE 事件分发机制(一) day9_第13张图片

纯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 函数的使用方式如下:
    GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("This is a debug message"));
    
    这个函数的第一个参数表示消息 ID,用于区分不同的消息。第二个参数表示 消息的显示时间,单位是秒。第三个参数表示消息的颜色,第四个参数表示消息的内容。需要注意的是,AddOnScreenDebugMessage 函数并不是一个标准库函数,而是 Unreal Engine 中的一个全局函数,所以使用前需要先引入相关的头文件:#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_Pawn
	ACppSender_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_Pawn
	ACppSender_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);
}
  • 运行结果
    UE 事件分发机制(一) day9_第14张图片
  • 删除所有订阅者
    UE 事件分发机制(一) day9_第15张图片

蓝图与Cpp混合多播动态委托的广播事件通知

  • 多播委托可以蓝图与Cpp混合使用,新建一个Pawn类来创建广播事件
  • DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam:使用这个宏就可以定义一个可以在蓝图中调用的多播委托,多加了一个DYNAMIC就可以被蓝图去识别到了
  • 创建多播委托代理,要加上反射,参数为BlueprintAssignable这样就可以被蓝图调用
public:
	// Sets default values for this pawn's properties
	ABPAndCpp_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++拉入场景中也可以调用通知
    UE 事件分发机制(一) day9_第16张图片

蓝图与Cpp混合多播动态委托的蓝图多播动态委托订阅

  • 新建一个Actor蓝图,派生三个蓝图,在蓝图中就可以调用多播委托,因为有反射,会出现一个委托
    UE 事件分发机制(一) day9_第17张图片
    UE 事件分发机制(一) day9_第18张图片
  • 运行结果
    UE 事件分发机制(一) day9_第19张图片
  • 蓝图中的解绑和之前纯蓝图解绑事件还是一样的
  • C++中解绑使用RemoveDynamic就行
    UE 事件分发机制(一) day9_第20张图片
  • 运行结果
    UE 事件分发机制(一) day9_第21张图片

你可能感兴趣的:(UE,C呱呱,虚幻,c++,游戏引擎,笔记,学习)