概述
事件分发器(Event Dispatchers)是一种Actor通信方法,采用此方法时,当一个Actor触发了一个事件后,监听该事件的所有其他Actor都会收到通知。
在这种方法中,负责发送事件的Actor需要创建一个 事件分发器(Event Dispatcher) ,所有监听该事件的Actor都会订阅该事件分发器。此通信方法采用一对多关系,每当当前Actor触发事件分发器后,监听该Actor的其他Actor都会收到通知。 在此快速入门指南中,你将学习如何使用事件分发器创建将在关卡中的两个Actor上触发的 OnBossDied 事件。
设置
-
首先点击 游戏(Games) > 第三人称(Third Person) > 蓝图(Blueprint) 创建新项目,并启用初学者内容包。
(w:600)
创建OnBossDied事件
-
右键点击 内容浏览器(Content Browser) ,然后在 创建基本资产(Create Basic Asset) 分段下点击 蓝图类(Blueprint Class) 。
-
选择 Actor 类作为父类,并将蓝图命名为 BP_BossDied 。
-
在蓝图编辑器中打开 BP_BossDied ,然后在 视口(Viewport) 中点击 添加组件(Add Components) 按钮,搜索并选择 盒体碰撞(Box Collision) 。
-
在选择 盒体(Box) 碰撞之后,将刻度更改为 X = 4 、Y = 4 和 Z = 2 。
-
选中 盒体(Box) 碰撞组件后,转到 细节(Details) 面板的 渲染(Rendering) 部分,取消选中 在游戏中隐藏复选框(Hidden in Game checkbox) 。这样就能在游戏中显示碰撞盒体。
-
右键点击 盒体(Box) 碰撞,然后选择 添加OnComponentBeginOverlap(Add OnComponentBeginOverlap) 。你将会看到节点已经添加到 事件图表(Event Graph) 。
-
在左侧的 我的蓝图(My Blueprint) 面板中,找到 事件分发器(Event Dispatcher) 部分,然后点击 新增(+ Add New) 以添加新事件。将此事件命名为 OnBossDied 。
-
将 OnBossDied 拖动到 事件图表(Event Graph) ,然后选择 调用(Call) 来添加节点。
-
将 On Component Begin Overlap 节点连接到 Call OnBossDied 节点。 编译(Compile) 并 保存(Save) 蓝图。
-
将 BP_BossDied 蓝图拖动到关卡中。
创建交互式门
-
右键点击 内容浏览器(Content Browser) ,然后在 创建基本资产(Create Basic Asset) 分段下点击 蓝图类(Blueprint Class) 。
-
选择 Actor 类作为父类,并将蓝图命名为 BP_BossDoor 。
-
在 视口(Viewport) 中打开 BP_BossDoor ,点击 添加组件(Add Components) 下拉菜单,然后搜索并选择 静态网格体(Static Mesh) 。将组件命名为 Frame 。
-
添加另一个 静态网格体(Static Mesh) 组件,将其命名为 Door 。
-
选择 Frame 组件,在 细节(Details) 面板中,点击 静态网格体(Static Mesh) 下拉菜单,然后搜索并选择 SM_DoorFrame 。
-
针对 Door 组件重复执行以上步骤,并添加 SM_Door 静态网格体。
-
继续选中 Door 组件,将 Y 位置设置为 45,如下所示。你将会看到与框架对齐的门。
-
右键点击 事件图表(Event Graph) ,然后搜索并选择 添加自定义事件(Add Custom Event) 。将事件命名为 OpenDoor 。
-
从 OpenDoor 事件拖出一根引线,然后搜索并选择 添加时间轴(Add Timeline) 。将时间轴命名为 TM_Door 。
-
双击 TM_Door 将其打开。点击 添加浮点曲线(Add Float Curve) 按钮,添加浮点轨道并将其命名为 Alpha 。将长度设置为 1.0 。
-
右键点击图表,然后选择 将键添加到CurveFloat_1(Add key to CurveFloat_1) 以添加新点。将 时间(Time) 和 值(Value) 设置为 0.0 。
-
将 时间(Time) 和 值(Value) 设置为 1.0 ,重复执行以上步骤以添加另一个点。
-
返回 事件图表(Event Graph) ,将 Door 静态网格体组件拖动到 事件图表(Event Graph) 中以创建节点。从 Door 节点拖出一根引线,然后搜索并选择 SetRelativeRotation 。
-
将 更新(Update) 引脚从 TM_Door 连接到 SetRelativeRotation 节点。右键点击 新旋转(New Rotation) 引脚并选择 分割结构体引脚(Split Struct Pin) 。
-
右键点击 事件图表(Event Graph) ,然后搜索并选择 Lerp浮点(Lerp Float) 。将 返回值(Return Value) 连接到 SetRelativeRotation 节点上的 偏转(Yaw) 引脚。将 Alpha 引脚从 TM_Door 连接到 Lerp 节点上的 Alpha 引脚。最后,将 B 的值设置为 90.0 ,如下所示。
-
点击 变量(Variable) 部分旁边的 +按钮(+ button) ,添加新变量。将变量命名为 BossDiedReference 。
-
在 细节(Details) 面板上,点击 变量类型(Variable Type) 下拉菜单,然后搜索并选择 BP_BossDied 的 对象引用(Object Reference) 。勾选 实例可编辑(Instance Editable) 复选框。
-
将 BossDiedReference 拖动到 事件图表(Event Graph) ,然后选择 Get BossDiedRerence 。从节点中拖出一条引线,然后搜索并选择 Bind Event to On Boss Died 。
-
从 Bind Event to On Boss Died 的红色 事件(Event) 引脚拖出一根引线,然后搜索并选择 添加自定义事件(Add Custom Event) 。将事件命名为 BossDied 。
-
从 BossDied 事件节点拖出一根引线,然后搜索并选择 开门(Open Door) 。将 Event Begin Play 连接到 Bind Event to On Boss Died 节点,如下所示。
-
编译(Compile) 并 保存(Save) 蓝图。
测试事件分发器
-
将 BP_BossDoor Actor拖动到关卡中。转到 细节(Details) 面板,点击 Boss引用死亡(Boss Reference Died) 下拉菜单,然后搜索并选择 BP_BossDied 。
-
按 播放(Play) 并走到 BP_BossDied 触发器,模拟Boss在游戏中死亡的情况。
(convert:false)
后续步骤
现在你已了解如何使用事件分发器,接下来可以查看 Actor通信 文档中的其他通信类型。
概述
委托能够以一种类型安全的方式调用 Actor蓝图 中的方法。委托可以动态绑定,使一个Actor能够触发一个事件,让"监听"该Actor的其他Actor监听事件。 在此快速入门指南中,你将学习如何使用委托来创建一个 OnBossDied 事件,并用它在关卡中触发两个Actor蓝图。
如需更多文档,请参见委托。
所需设置
-
首先,新建一个 游戏(Games) > 第三人称(Third Person) > C++ 类型的项目,并勾选初学者内容。
创建Boss Actor和OnBossDied委托
-
在C++类向导中新建名为BossActor的Actor类。
(w:600)
-
导航至你的 BossActor.h。在include下,声明以下Delegate。
DECLARE_DELEGATE(FOnBossDiedDelegate);
-
在类默认值中,声明以下代码
protected: UFUNCTION() void HandleBossDiedEvent(); UPROPERTY(EditInstanceOnly, BlueprintReadWrite) class UBoxComponent* BoxComp; virtual void NotifyActorBeginOverlap(AActor* OtherActor); public: FOnBossDiedDelegate OnBossDied;
-
导航至BossActor.cpp并添加以下类库。
#include "Components/BoxComponent.h"
-
实现以下类定义。
ABossActor::ABossActor() { BoxComp = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComp")); BoxComp->SetBoxExtent(FVector(128, 128, 64)); BoxComp->SetVisibility(true); } void ABossActor::HandleBossDiedEvent() { OnBossDied.ExecuteIfBound(); } void ABossActor::NotifyActorBeginOverlap(AActor* OtherActor) { HandleBossDiedEvent(); }
-
编译 你的代码。
-
在 C++类文件夹(C++ Classes folder) 中,右键点击 BossActor,然后在 C++类操作(C++ Class Actions) 下拉菜单中,选择 基于BossActor创建蓝图类(Create Blueprint class based on BossActor)。将你的蓝图类命名为 BP_BossActor。
-
将你的 BP_BossActor 实例放入关卡。
完成后的代码
BossActor.h
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "BossActor.generated.h"
DECLARE_DELEGATE(FOnBossDiedDelegate);
UCLASS()
class BPCOMMUNICATION_API ABossActor : public AActor
{
GENERATED_BODY()
public:
// 为此Actor的属性设置默认值
ABossActor();
protected:
// 当游戏开始或重生(Spawn)时被调用
virtual void BeginPlay() override;
UFUNCTION()
void HandleBossDiedEvent();
UPROPERTY(EditInstanceOnly, BlueprintReadWrite)
class UBoxComponent* BoxComp;
virtual void NotifyActorBeginOverlap(AActor* OtherActor);
public:
// 每一帧都调用
virtual void Tick(float DeltaTime) override;
FOnBossDiedDelegate OnBossDied;
};
BossActor.cpp
#include "BossActor.h"
#include "Components/BoxComponent.h"
#include "BPCommunicationGameMode.h"
// 设置默认值
ABossActor::ABossActor()
{
// 将此Actor设置为每帧调用Tick()。 如果你不需要此特性,你可以关闭它以提升性能。
PrimaryActorTick.bCanEverTick = true;
BoxComp = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComp"));
BoxComp->SetBoxExtent(FVector(128, 128, 64));
BoxComp->SetVisibility(true);
}
// 当游戏开始或重生(Spawn)时被调用
void ABossActor::BeginPlay()
{
Super::BeginPlay();
}
void ABossActor::HandleBossDiedEvent()
{
OnBossDied.ExecuteIfBound();
}
void ABossActor::NotifyActorBeginOverlap(AActor* OtherActor)
{
HandleBossDiedEvent();
}
// 每一帧都调用
void ABossActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
创建交互式门
-
从 C++类向导(C++ Class Wizard) 中新建名为 DoorActor 的 Actor 类。
-
导航至DoorActor.h文件,并声明以下include:
#include "Components/TimelineComponent.h"
-
然后声明以下类定义。
// 用于保留曲线资产的变量 UPROPERTY(EditInstanceOnly) UCurveFloat* DoorTimelineFloatCurve; protected: void BossDiedEventFunction(); UPROPERTY(EditInstanceOnly,BlueprintReadWrite) class ABossActor* BossActorReference; //表示门的网格体组件 UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UStaticMeshComponent* DoorFrame; UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UStaticMeshComponent* Door; //用于对门网格体进行动画处理的时间轴组件 UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UTimelineComponent* DoorTimelineComp; //用于处理我们的更新轨道事件的浮点轨道签名 FOnTimelineFloat UpdateFunctionFloat; //用于使用时间轴图表更新门相对位置的函数 UFUNCTION() void UpdateTimelineComp(float Output);
-
在 DoorActor.cpp 中,声明以下类库。
#include "BossActor.h"
-
实现以下类定义。
ADoorActor::ADoorActor() { //创建我们的默认组件 DoorFrame = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorFrameMesh")); Door = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh")); DoorTimelineComp = CreateDefaultSubobject<UTimelineComponent>(TEXT("DoorTimelineComp")); //设置绑定 DoorFrame->SetupAttachment(RootComponent); Door->AttachToComponent(DoorFrame, FAttachmentTransformRules::KeepRelativeTransform); Door->SetRelativeLocation(FVector(0, 35, 0)); } // 在游戏开始或重生(Spawn)时调用 void ADoorActor::BeginPlay() { Super::BeginPlay(); //将浮点轨道绑定到UpdateTimelineComp函数的输出 UpdateFunctionFloat.BindDynamic(this, &ADoorActor::UpdateTimelineComp); //如果有浮点曲线,将其图表绑定到我们的更新函数 if (DoorTimelineFloatCurve) { DoorTimelineComp->AddInterpFloat(DoorTimelineFloatCurve, UpdateFunctionFloat); } if (BossActorReference) { BossActorReference->OnBossDied.BindUObject(this, &ADoorActor::BossDiedEventFunction); } } void ADoorActor::BossDiedEventFunction() { DoorTimelineComp->Play(); } void ADoorActor::UpdateTimelineComp(float Output) { // 基于时间轴曲线的输出,创建并设置门的新相对位置 FRotator DoorNewRotation = FRotator(0.0f, Output, 0.f); Door->SetRelativeRotation(DoorNewRotation); }
-
编译你的代码。
-
从 内容浏览器 中选择 添加/导入(Add/Import) > 杂项(Miscellaneous) > 曲线(Curve)。
-
选择 CurveFloat 并将CurveFloat资产命名为 DoorCurveFloat,然后双击你的DoorCurveFloat资产。向你的浮点曲线添加两个键,为一个键赋予时间值(0,0),另一个键赋予时间值(4,90)。
-
按住Shift键并选中这两个键,将它们设置为 自动立方体插值(Auto Cubic interpolation),然后保存曲线。
-
保存你的DoorCurveFloat。
-
在内容浏览器中,导航至你的 C++类文件夹(C++ Classes folder),右键点击你的DoorActor类,然后选择 基于门Actor创建蓝图类(Create Blueprint Class based on Door Actor)。将你的蓝图Actor命名为 Bp_DoorActor。
-
在 BP_DoorActor 的 类默认值(class defaults) 中,找到 组件(Components) 选项卡然后选择 DoorFrame 静态网格体组件(Static Mesh component),导航至 细节(Details) 面板,将静态网格体更改为 SM_DoorFrame。
-
接下来,从组件(Components)选项卡中选择DoorMesh组件。导航至细节面板,将静态网格体更改为 SM_Door。
-
在细节面板中,在门时间轴浮点曲线(Door Timeline Float Curve)下拉菜单中选择DoorCurveFloat。
-
编译并保存蓝图。
已完成代码
DoorActor.h
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/TimelineComponent.h"
#include "DoorActor.generated.h"
UCLASS()
class BPCOMMUNICATION_API ADoorActor : public AActor
{
GENERATED_BODY()
public:
// 为此Actor的属性设置默认值
ADoorActor();
// 用于保留曲线资产的变量
UPROPERTY(EditInstanceOnly)
UCurveFloat* DoorTimelineFloatCurve;
protected:
// 当游戏开始或重生(Spawn)时被调用
virtual void BeginPlay() override;
void BossDiedEventFunction();
UPROPERTY(EditInstanceOnly,BlueprintReadWrite)
class ABossActor* BossActorReference;
//用于表示门资产的网格体组件
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UStaticMeshComponent* DoorFrame;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UStaticMeshComponent* Door;
//用于对门网格体进行动画处理的时间轴组件
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UTimelineComponent* DoorTimelineComp;
//用于处理我们的更新轨道事件的浮点轨道签名
FOnTimelineFloat UpdateFunctionFloat;
//用于使用时间轴图表更新门相对位置的函数
UFUNCTION()
void UpdateTimelineComp(float Output);
public:
// 每一帧都调用
virtual void Tick(float DeltaTime) override;
};
DoorActor.cpp
#include "DoorActor.h"
#include "BossActor.h"
// 设置默认值
ADoorActor::ADoorActor()
{
// 将此Actor设置为每帧调用Tick()。 如果你不需要此特性,你可以关闭它以提升性能。
PrimaryActorTick.bCanEverTick = true;
//创建我们的默认组件
DoorFrame = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorFrameMesh"));
Door = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh"));
DoorTimelineComp = CreateDefaultSubobject<UTimelineComponent>(TEXT("DoorTimelineComp"));
//设置绑定
DoorFrame->SetupAttachment(RootComponent);
Door->AttachToComponent(DoorFrame, FAttachmentTransformRules::KeepRelativeTransform);
Door->SetRelativeLocation(FVector(0, 35, 0));
}
// 当游戏开始或重生(Spawn)时调用
void ADoorActor::BeginPlay()
{
Super::BeginPlay();
//将浮点轨道绑定到UpdateTimelineComp函数的输出
UpdateFunctionFloat.BindDynamic(this, &ADoorActor::UpdateTimelineComp);
//如果有浮点曲线,将其图表绑定到我们的更新函数
if (DoorTimelineFloatCurve)
{
DoorTimelineComp->AddInterpFloat(DoorTimelineFloatCurve, UpdateFunctionFloat);
}
if (DoorTimelineFloatCurve)
{
BossActorReference->OnBossDied.BindUObject(this, &ADoorActor::BossDiedEventFunction);
}
}
void ADoorActor::BossDiedEventFunction()
{
DoorTimelineComp->Play();
}
void ADoorActor::UpdateTimelineComp(float Output)
{
// 基于时间轴曲线的输出创建并设置门的新相对位置
FRotator DoorNewRotation = FRotator(0.0f, Output, 0.f);
Door->SetRelativeRotation(DoorNewRotation);
}
// 每一帧都调用
void ADoorActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
5 - 测试事件分发器
-
将 BP_Door 蓝图拖动到关卡中。转到 细节(Details) 面板,点击 Boss引用死亡(Boss Reference Died) 下拉菜单,然后搜索并选择 BP_BossDied。
-
选择Bp_DoorActor之后,导航至 细节(Details) 面板,点击 Boss Actor引用(Boss Actor Reference) 下拉箭头,然后搜索并选择 BP_BossActpr。
-
点击运行(Play),让 BP_BossActor 触发器模拟Boss在游戏中的死亡。
后续步骤
现在你已了解如何使用委托,接下来请查看 Actor通信 文档中的其他通信类型。