Choose your operating system:
Windows
macOS
Linux
选择实现方法:
Blueprints
C++
概述
事件分发器(Event Dispatchers)是一种Actor通信方法,采用此方法时,当一个Actor触发了一个事件后,监听该事件的所有其他Actor都会收到通知。
在这种方法中,负责发送事件的Actor需要创建一个 事件分发器(Event Dispatcher) ,所有监听该事件的Actor都会订阅该事件分发器。此通信方法采用一对多关系,每当当前Actor触发事件分发器后,监听该Actor的其他Actor都会收到通知。
目标
在此快速入门指南中,你将学习如何使用事件分发器来创建一个 OnBossDied 事件,以便在关卡中的两个Actor上触发事件。
任务
-
创建 BossDied Actor,此Actor将创建事件分发器。
-
创建一个门Actor,将它绑定给 OnBossDied 事件。
-
修改 Blueprint_Effect_Explosion 蓝图,以便绑定到 OnBossDied 事件。
1 - 准备工作
-
在菜单的 新项目类别(New Project Categories) 部分中,选择 游戏(Games) ,然后点击 下一步(Next) 。
-
选择 第三人称(Third Person) 模板,然后点击 下一步(Next) 。
-
选择 蓝图(Blueprint) 和 含初学者内容包(With Starter Content) 选项,然后点击 创建项目(Create Project) 。
阶段成果
你已经创建了一个新的第三人称项目,现在可以开始实现事件分发器。
2 - 创建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 蓝图拖动到关卡中。
阶段成果
在此小节中,你创建了 BP_BossDied Actor来模拟Boss死亡时的效果。在此Actor中,你创建了 OnBossDied 事件分发器,用于在事件触发时通知其他Actor。
3 - 创建交互式门
-
右键点击 内容浏览器(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) 蓝图。
阶段成果
在此小节中,你创建了一个交互式的门Actor,并将它绑定到 BP_BossDied Actor中的 On Boss Died 事件分发器。此绑定发生在开始运行(Begin Play)中,但只会在 BP_BossDied 触发事件时用到。
4 - 创建交互式爆炸
-
转到 初学者内容包(Starter Content) > 蓝图(Blueprints) 并选择 Blueprint_Effect_Explosion 。将其拖动到gameplay文件夹,并选择 在此处复制(Copy Here) 。
-
在蓝图编辑器中打开 Blueprint_Effect_Explosion 。在 视口(Viewport) 中,选择 P_Explosion 组件,然后在 细节(Details) 面板上转到 激活(Activation) 部分,并禁用 自动激活(Auto Activate) 。
-
针对 Explosion Audio 组件重复执行以上步骤。
-
点击 变量(Variable) 部分旁边的 +按钮(+ button) ,添加新变量。将变量命名为 BossDiedReference 。
-
在 细节(Details) 面板上,点击 变量类型(Variable Type) 下拉菜单,然后搜索并选择 BP_BossDied 的 对象引用(Object Reference) 。勾选 实例可编辑(Instance Editable) 复选框。
-
在 事件图表(Event Graph) 中右键点击,然后搜索并选择 事件开始播放(Event Begin Play) 。
-
将 BossDiedReference 拖动到 事件图表(Event Graph) ,然后选择 Get BossDiedReference 。从节点中拖出一条引线,然后搜索并选择 Assign On Boss Died 。将自定义事件命名为 BossDied 。
-
将 Event Begin Play 连接到 Bind Event to Boss Died 节点。
[REGION:note} 在为事件分发器选择分配(Assign)选项时,会创建一个绑定以及自定义事件。 [/REGION}
-
将 P_Explosion 和 Explosion Audio 组件拖动到 事件图表(Event Graph) 。从 P_Explosion 拖出一根引线,然后搜索并选择 Activate 。将 Explosion Sound 连接到 Activate 节点的 目标(Target) 引脚。
-
将 BossDied 事件节点连接到 Activate 节点。
-
编译(Compile) 并 保存(Save) 蓝图。
阶段成果
在此小节中,你修改了 Blueprint_Effect_Explosion ,让它在执行 OnBossDied 事件时激活。
5 - 测试事件分发器
-
将 BP_BossDoor Actor拖动到关卡中。转到 细节(Details) 面板,点击 Boss引用死亡(Boss Reference Died) 下拉菜单,然后搜索并选择 BP_BossDied 。
-
将 Blueprint_Effect_Explosion 的多个实例拖动到关卡中。转到 细节(Details) 面板,点击 Boss引用死亡(Boss Reference Died) 下拉菜单,然后搜索并选择 BP_BossDied 。
-
点击 运行(Play) ,让 BP_BossDied 触发器模拟Boss在游戏中的死亡。
阶段成果
在此小节中,你在关卡中测试了 BP_BossDoor 和 Blueprint_Effect_Explosion Actor。你还测试了当 BP_BossDied Actor触发 OnBossDied 事件时,每个Actor都会响应该事件。
在本指南中,你学习了如何使用事件分发器让当前Actor与多个目标Actor通信。你还学习了如何创建事件分发器以及如何将Actor绑定到事件,以触发特定的功能。
后续步骤
现在你已了解如何使用事件分发器,接下来可以查看 Actor通信 文档中的其他通信类型。
概述
委托能够以一种类型安全的方式调用 Actor蓝图 中的方法。委托可以动态绑定,使一个Actor能够触发一个事件,让"监听"该Actor的其他Actor监听事件。
如需更多文档,请参见 委托 。
目标
在此快速入门指南中,你将学习如何使用委托来创建一个 OnBossDied 事件,并用它在关卡中触发两个Actor蓝图。
任务
-
创建一个 Boss Actor,让它包含OnBossDied委托。
-
创建一个包含时间轴组件的Actor,它将绑定 OnBossDied 事件。
1 - 准备工作
-
在菜单的 新项目类别(New Project Categories) 部分中,选择 游戏(Games) ,然后点击 下一步(Next) 。
-
选择 第三人称(Third Person) 模板,然后点击 下一步(Next) 。
-
选择 C++ 和 含初学者内容包(With Starter Content) 选项,然后点击 创建项目(Create Project) 。
阶段成果
你已经创建了一个新的C++第三人称项目,现在可以学习委托。
2 - 创建Boss Actor和OnBossDied委托
-
从 C++类向导 中新建名为BossActor的Actor类。
-
导航至你的 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);
}
阶段成果
在此小节中,你创建了一个 BossActor 类,其中包含盒体组件,以及 OnBossDied 事件的委托。该委托会在事件触发时通知其他Actor类。
3 - 创建交互式门
-
从 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);
}
阶段成果
在此小节中,你创建了一个可交互的 DoorActor ,并将它与 BossActor 中的 OnBossDied 事件分发器相互绑定。绑定发生在开始运行(Begin Play)这个函数中,并且只有碰撞到BossActor的 盒体组件(Box Component) 时才会触发此事件。
5 - 测试事件分发器
-
将 BP_Door 蓝图拖动到关卡中。转到 细节(Details) 面板,点击 Boss引用死亡(Boss Reference Died) 下拉菜单,然后搜索并选择 BP_BossDied 。
-
选择Bp_DoorActor之后,导航至 细节(Details) 面板,点击 Boss Actor引用(Boss Actor Reference) 下拉箭头,然后搜索并选择 BP_BossActpr 。
-
点击运行(Play),让 BP_BossActor 触发器模拟Boss在游戏中的死亡。
阶段成果
在本小节中,你在关卡中测试了 BP_DoorActor 。当 BP_BossActor的盒体组件 与另一个Actor重叠以触发委托时,Actor响应 OnBossDied 事件。
在本指南中,你学习了如何使用委托在多个Actor类蓝图之间进行通信。
后续步骤
现在你已了解如何使用委托,接下来请查看 Actor通信 文档中的其他通信类型。