Choose your operating system:
Windows
macOS
Linux
选择实现方法:
Blueprints
C++
课程:蓝图基本概念|蓝图 - 基本概念|https://cdn2.unrealengine.com/Unreal+Engine%2Fonlinelearning-courses%2Fblueprints---essential-concepts%2FBPEssentialsThumbnail_1920x1080-1920x1080-2fcdde875f1d79ade1c295fe4fef53c4b8c5ecde.PNG?resize=1&w=1400|https://cdn2.unrealengine.com/Unreal+Engine%2Fonlinelearning-courses%2Fblueprints---essential-concepts%2FBlueprint--Essential-Concepts-1000x1000-9f372ce55de42ce9596dffd6c931839e44fcc873.png?resize=1&w=300 课程:使用蓝图进行交互式材质交换|使用蓝图进行交互式材质交换|https://cdn2.unrealengine.com/Unreal+Engine%2Fonlinelearning-courses%2Finteractive-material-swaps-using-blueprints%2F406---Hero2-1920x1080-ce26b6199cf620364149d2d934c73ae30e983110.png?resize=1&w=1400|https://cdn2.unrealengine.com/Unreal+Engine%2Fonlinelearning-courses%2Finteractive-material-swaps-using-blueprints%2FInteractive-Material-Swaps-Using-Blueprints-1000x1000-21158a5fe67365e3bf5db49712b52482382f15ac.png?resize=1&w=300 课程:制作蓝图产品配置器|企业级蓝图|https://cdn2.unrealengine.com/Unreal+Engine%2Fonlinelearning-courses%2Fblueprint-for-enterprise%2F301-Blueprints-%281200X675%29-1200x675-d140b8a0b13b05c0619e35e1076b4c82e4a4b855.png?resize=1&w=1400|https://cdn2.unrealengine.com/Unreal+Engine%2Fonlinelearning-courses%2Fblueprint-for-enterprise%2FBlueprint-for-Enterprise-1000x1000-13caa11b8e5c5e91506a8dd63b39d247318c3208.png?resize=1&w=300
本教程演示如何凭借 引擎初学者内容包 中的可用资源,使用 时间轴 蓝图创建基于距离的开门。
创建门Actor
-
首先,点击 新建(New) > 游戏(Games) > 空白(Blank) > 蓝图(Blueprint) 创建名为 TimelineDoorActor 的项目,并启用 初学者内容包(Starter Content) 。
-
点击 添加/导入(Add/Import) 按钮新建蓝图 Actor 类,将其命名为 BP_DoorActor 。
-
从内容浏览器中双击该BP_Door Actor在蓝图编辑器中打开,然后打开 类默认值(Class Defaults) 。
-
下一步,在 组件(Components) 选项卡中点击 添加组件(Add Component) 按钮,选择 静态网格体(Static Mesh) 添加新的 静态网格体组件(Static Mesh Component) 。
-
右键点击你的静态网格体组件,选择 重命名(Rename) ,将其重命名为 DoorFrame 。
-
下一步,在 组件(Components) 选项卡中点击 添加组件(Add Component) 按钮,选择 静态网格体(Static Mesh) 添加新的 静态网格体组件(Static Mesh Component) 。(重复步骤3)
-
右键点击你的静态网格体组件,选择重命名(Rename),将其重命名为 Door 。
-
点击添加组件(Add Component),从下拉菜单中选择 盒体碰撞(Box Collision) ,将其重命名为 Box 。
-
下一步,打开事件图表,右键点击 图表,从 蓝图上下文菜单(Blueprint Context Menu) 中选择 添加时间轴(Add Timeline) 。将你的时间轴命名为 DoorTimelineComponent 。
此时,BP_DoorActor的类默认值应如下例中所示:
设置门静态网格体
接下来,你需要设置 组件(Components) 选项卡的附件层级。
你还需要设置静态网格体资产,直观地表示你的 DoorFrame 和 Door 静态网格体组件。
-
从 BP_DoorActor 的组件(Components)选项卡中,选择 DoorFrame 静态网格体并将其拖动到 DefaultSceneRoot组件 上,使其成为新的 根组件 。
-
接下来,在组件(Components)选项卡中选择 DoorFrame 静态网格体,从 细节面板(Details panel) 中将静态网格体更改为 SM_DoorFrame 。
-
接下来,从组件(Components)面板中选择DoorMesh组件。导航至细节面板,将静态网格体更改为 SM_Door 。
-
然后导航至 变换(Transform) 类别,将 Y位置(Y Location) 值更改为 45.0 。
-
点击 保存(Save) 和 编译(Compile) ,将这些更改保存到Bp_DoorActor。
创建时间轴浮点轨道
时间轴组件需要 时间轴曲线 来描述其移动动画。每个轨道都可以包含多个键,用于定义时间和值。曲线内插这些键,以计算时间轴中任何点的值。
-
首先,在事件图表中双击DoorTimelineComponent,打开时间轴编辑器。
-
点击 添加浮点曲线(Add Float Curve) 向该轨道添加新曲线,将该曲线命名为 DoorRotationZ 。
-
右键点击,向浮点曲线轨道添加两个键。两个键的值分别为(0,0)和(5,90)。
-
按住Shift键并选中这两个键,点击右键,从 键插值(Key Interpolation) 下拉菜单中选择 自动(Auto) 插值。
-
保存你的浮点轨道。
更新事件轨道逻辑
现在你需要创建更新逻辑,以旋转你的门静态网格体。
-
导航至BP_DoorActor的组件(Components)选项卡,将门静态网格体拖动到事件图表上
-
拖移门引脚,从操作上下文菜单中选择 SetRelativeRotation 。
-
右键点击事件图表,从上下文菜单中选择 制作旋转体(Make Rotator) 。
-
在 DoorTimelineComponent 节点上,拖动 Door Rotation Z 浮点引脚,将其连接至 Make Rotator 节点的 Z ( Yaw ) 引脚。
-
在 DoorTimelineComponent 节点上,拖移 更新(Update) 引脚 ,将其连接至 SetRelativeRotation 节点的输入执行引脚。
-
在 Make Rotator 节点上,将 返回值(Return Value) 引脚连接至SetRelativeRotation节点的 新旋转(New Rotation) 引脚。
-
编译 并 保存 。
创建绑定盒体碰撞重叠事件
盒体组件需要拥有在Actor进入或离开碰撞边界时做出反应的能力。
-
导航至BP_DoorActor的组件(Components)选项卡,选择盒体组件。
-
从细节面板中,向下滚动到事件类别,点击 在组件开始重叠时(On Component Begin Overlap) 事件旁的 + 图标。
-
在 On Component Begin Overlap(Box) 节点上,拖移执行引脚,将其连接至DoorTimelineComponent节点的播放(Play)引脚。
-
导航至BP_DoorActor的组件(Components)选项卡,选择盒体组件。然后,在细节面板向下滚动到事件类别,点击 在组件结束重叠时(On Component End Overlap) 事件旁的 + 图标。
-
在 On Component End Overlap(Box) 节点上,拖移执行引脚,将其连接至TimelineComponent节点的 反向(Reverse) 引脚。
-
编译 并 保存 。
将Actor放到关卡中
-
从内容浏览器中,选择你的BP_DoorActor并将其拖动到视口中。
-
按PIE
完成后的蓝图
BP_DoorActor
凭借 引擎初学者内容包 中的可用资源,使用C++ 时间轴 示例创建基于距离的经典开门。
创建门Actor
-
首先,点击 新建(New) > 游戏(Games) > 空白(Blank) > C++ 创建名为 TimelineDoorActor 的项目,并启用 初学者内容包(Starter Content) 。
-
从 C++类向导(C++ Class Wizard) 中新建名为 DoorActor 的 Actor 类。导航至你的
DoorActor.h
文件,并声明以下内容:#include "Components/TimelineComponent.h"
-
接下来,在你的
DoorActor
类定义中实现以下代码:protected: //用于表示门资产的网格体组件 UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UStaticMeshComponent* DoorFrame; UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UStaticMeshComponent* Door; //用于对门网格体进行动画处理的时间轴组件 UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UTimelineComponent* DoorTimelineComp; //将用作我们的距离体积的盒体组件。 UPROPERTY(EditAnywhere, BlueprintReadWrite) class UBoxComponent* DoorProxVolume;
-
导航至
DoorActor.cpp
。需要包括以下类库,方可利用你的盒体组件。#include "Components/BoxComponent.h"
-
在你的 ADoorActor::ADoorActor 的构造函数中声明以下内容:
//创建我们的默认组件 DoorFrame = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorFrameMesh")); Door = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh")); DoorTimelineComp = CreateDefaultSubobject<UTimelineComponent>(TEXT("DoorTimelineComp")); DoorProxVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("DoorProximityVolume")); //设置我们的附件 DoorFrame->SetupAttachment(RootComponent); Door->AttachToComponent(DoorFrame,FAttachmentTransformRules::KeepRelativeTransform); DoorProxVolume->AttachToComponent(DoorFrame, FAttachmentTransformRules::KeepRelativeTransform);
注意 :我们将门的相对变换保留为附件规则,以便稍后使用门Actor的自定义方法来操作它。 详情请参阅 FAttachmentTransformRules 。
-
选择 编译(Compile) ,在编辑器中 热重载 你的代码。
设置门静态网格体
你需要设置静态网格体资产,直观地表示你的DoorFrame和Door静态网格体组件。
-
从内容浏览器中导航至你的 C++类文件夹 。
-
右键点击你的DoorActor类,选择 基于门Actor创建蓝图类(Create Blueprint Class based on Door Actor) ,并将你的蓝图Actor命名为 Bp_DoorActor 。
-
从 组件(Components) 选项卡中选择DoorFrame静态网格体组件。
-
导航至 细节面板 ,将静态网格体更改为 SM_DoorFrame 。
-
接下来,从组件(Components)选项卡中选择DoorMesh组件。导航至细节面板,将静态网格体更改为 SM_Door 。
-
然后导航至 变换(Transform) 类别,将 Y位置(Y Location) 值更改为 45.0 。
-
点击 保存(Save) ,将这些更改保存到Bp_DoorActor。
创建UCurveFloat和时间轴事件轨道
时间轴组件需要 时间轴曲线 。每个轨道都可以包含多个键,用于定义时间和值。曲线内插这些键,以计算时间轴中任何点的值。
此例中,我们将使用 UCurveFloat 。
-
导航至
DoorActor.h
中的ADoorActor
类定义,并声明以下变量:public: // 用于保留曲线资产的变量 UPROPERTY(EditAnywhere) UCurveFloat* DoorTimelineFloatCurve; private: //用于处理我们的更新轨道事件的浮点轨道签名 FOnTimelineFloat UpdateFunctionFloat; //用于使用时间轴图表更新门相对位置的函数 UFUNCTION() void UpdateTimelineComp(float Output);
-
接下来,导航至
DoorActor.cpp
并实现UpdateTimelineComp
方法:void ADoorActor::UpdateTimelineComp(float Output) { // 基于时间轴曲线的输出创建并设置门的新相对位置 FRotator DoorNewRotation = FRotator(0.0f, Output, 0.f); Door->SetRelativeRotation(DoorNewRotation); }
-
然后,在 BeginPlay 方法中,添加以下代码:
//将浮点轨道绑定到UpdateTimelineComp函数的输出 UpdateFunctionFloat.BindDynamic(this, &ADoorActor::UpdateTimelineComp); //如果有浮点曲线,将其图表绑定到我们的更新函数 if (DoorTimelineFloatCurve) { DoorTimelineComp->AddInterpFloat(DoorTimelineFloatCurve, UpdateFunctionFloat); }
半成品代码
DoorActor.h
// 版权所有 1998-2021 Epic Games, Inc。保留所有权利。
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/TimelineComponent.h"
#include "DoorActor.generated.h"
UCLASS()
class TIMELINEDOORACTOR_API ADoorActor : public AActor
{
GENERATED_BODY()
public:
// 为此Actor的属性设置默认值
ADoorActor();
/*用于保留曲线资产的变量*/
UPROPERTY(EditAnywhere)
UCurveFloat* DoorTimelineFloatCurve;
protected:
// 当游戏开始或重生(Spawn)时被调用
virtual void BeginPlay() override;
//用于表示门资产的网格体组件
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UStaticMeshComponent* DoorFrame;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UStaticMeshComponent* Door;
//用于对门网格体进行动画处理的时间轴组件
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UTimelineComponent* DoorTimelineComp;
//将用作我们的距离体积的盒体组件。
UPROPERTY(EditAnywhere, BlueprintReadWrite)
class UBoxComponent* DoorProxVolume;
//用于处理我们的更新轨道事件的浮点轨道签名
FOnTimelineFloat UpdateFunctionFloat;
//用于使用时间轴图表更新门相对位置的函数
UFUNCTION()
void UpdateTimelineComp(float Output);
public:
// 每一帧都被调用
virtual void Tick(float DeltaTime) override;
};
DoorActor.cpp
//版权所有 1998-2021 Epic Games, Inc。保留所有权利。
#include "DoorActor.h"
#include "Components/BoxComponent.h"
// 设置默认值
ADoorActor::ADoorActor()
{
// 将此actor设置为每帧调用Tick()。 如果不需要此特性,可以关闭以提升性能。
PrimaryActorTick.bCanEverTick = true;
//创建我们的默认组件
DoorFrame = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorFrameMesh"));
Door = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh"));
DoorTimelineComp = CreateDefaultSubobject<UTimelineComponent>(TEXT("DoorTimelineComp"));
DoorProxVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("DoorProximityVolume"));
//设置我们的附件
DoorFrame->SetupAttachment(RootComponent);
Door->AttachToComponent(DoorFrame,FAttachmentTransformRules::KeepRelativeTransform);
DoorProxVolume->AttachToComponent(DoorFrame, FAttachmentTransformRules::KeepRelativeTransform);
}
// 当游戏开始或重生(Spawn)时被调用
void ADoorActor::BeginPlay()
{
Super::BeginPlay();
//将浮点轨道绑定到UpdateTimelineComp函数的输出
UpdateFunctionFloat.BindDynamic(this, &ADoorActor::UpdateTimelineComp);
//如果有浮点曲线,将其图表绑定到我们的更新函数
if (DoorTimelineFloatCurve)
{
DoorTimelineComp->AddInterpFloat(DoorTimelineFloatCurve, UpdateFunctionFloat);
}
}
void ADoorActor::UpdateTimelineComp(float Output)
{
//基于时间轴曲线的输出创建并设置门的新相对位置
FRotator DoorNewRotation = FRotator(0.0f,Output,0.f);
Door->SetRelativeRotation(DoorNewRotation);
}
// 每一帧都被调用
void ADoorActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
创建并绑定盒体碰撞重叠事件
盒体组件需要拥有在Actor进入或离开碰撞边界时做出反应的能力。
-
导航至你的
TimelineDoorActor.h
文件的类定义,并声明以下内容:// DoorProxVolume的开始和结束重叠事件 UFUNCTION() void OnOverlapBegin(class UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); UFUNCTION() void OnOverlapEnd(class UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
-
接下来,导航至
DoorActor.cpp
文件,以实现OnOverlapBegin
和OnOverlapEnd
类方法:void ADoorActor::OnOverlapBegin(UPrimitiveComponent * OverlappedComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult) { DoorTimelineComp->Play(); } void ADoorActor::OnOverlapEnd(UPrimitiveComponent * OverlappedComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex) { DoorTimelineComp->Reverse(); }
-
在 BeginPlay 方法中绑定重叠函数,如下所示:
//将我们的距离盒体组件绑定到我们的重叠函数 DoorProxVolume->OnComponentBeginOverlap.AddDynamic(this, &ADoorActor::OnOverlapBegin); DoorProxVolume->OnComponentEndOverlap.AddDynamic(this, &ADoorActor::OnOverlapEnd);
-
编译你的代码。
在虚幻编辑器中创建曲线资产
必须在虚幻编辑器中创建曲线资产,以分配到你的时间轴Actor蓝图。
-
从 内容浏览器 中选择 添加/导入(Add/Import) > 杂项(Miscellaneous) > 曲线(Curve) 。
-
选择 CurveFloat 并将CurveFloat资产命名为 DoorCurveFloat
-
双击你的DoorCurveFloat资产,以打开 时间轴编辑器 。
-
向你的浮点曲线添加两个键,为一个键赋予时间值(0,0),另一个键赋予时间值(4,90)。
-
按住Shift键并选中这两个键,将它们设置为自动立方体插值,然后保存曲线。
-
打开Bp_DoorActor,并从组件(Components)选项卡中选择Bp_DoorActor。
-
在细节面板中,从门时间轴浮点曲线(Door Timeline Float Curve)下拉菜单中选择DoorCurveFloat。
-
从关卡编辑器将Bp_DoorActor放置到场景中。
-
编译并保存,然后按PIE。
已完成代码
DoorActor.h
// 版权所有 1998-2021 Epic Games, Inc。保留所有权利。
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/TimelineComponent.h"
#include "DoorActor.generated.h"
UCLASS()
class TIMELINEDOORACTOR_API ADoorActor : public AActor
{
GENERATED_BODY()
public:
// 为此Actor的属性设置默认值
ADoorActor();
//用于保留曲线资产的变量
UPROPERTY(EditAnywhere)
UCurveFloat* DoorTimelineFloatCurve;
protected:
// 当游戏开始或重生(Spawn)时被调用
virtual void BeginPlay() override;
/*用于表示门资产的网格体组件*/
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UStaticMeshComponent* DoorFrame;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UStaticMeshComponent* Door;
//用于对门网格体进行动画处理的时间轴组件
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UTimelineComponent* DoorTimelineComp;
//将用作我们的距离体积的盒体组件。
UPROPERTY(EditAnywhere, BlueprintReadWrite)
class UBoxComponent* DoorProxVolume;
//用于处理我们的更新轨道事件的浮点轨道签名
FOnTimelineFloat UpdateFunctionFloat;
//用于使用时间轴图表更新门相对位置的函数
UFUNCTION()
void UpdateTimelineComp(float Output);
/*DoorProxVolume的开始和结束重叠事件*/
UFUNCTION()
void OnOverlapBegin(class UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);
UFUNCTION()
void OnOverlapEnd(class UPrimitiveComponent* OverlappedComp, class AActor* OtherActor, class UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
public:
// 每一帧都被调用
virtual void Tick(float DeltaTime) override;
};
DoorActor.cpp
// 版权所有 1998-2021 Epic Games, Inc。保留所有权利。
#include "DoorActor.h"
#include "Components/BoxComponent.h"
// 设置默认值
ADoorActor::ADoorActor()
{
// 将此actor设置为每帧调用Tick()。 如果不需要此特性,可以关闭以提升性能。
PrimaryActorTick.bCanEverTick = true;
//创建我们的默认组件
DoorFrame = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorFrameMesh"));
Door = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh"));
DoorTimelineComp = CreateDefaultSubobject<UTimelineComponent>(TEXT("DoorTimelineComp"));
DoorProxVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("DoorProximityVolume"));
//设置我们的附件
DoorFrame->SetupAttachment(RootComponent);
Door->AttachToComponent(DoorFrame,FAttachmentTransformRules::KeepRelativeTransform);
DoorProxVolume->AttachToComponent(DoorFrame, FAttachmentTransformRules::KeepRelativeTransform);
}
// 当游戏开始或重生(Spawn)时被调用
void ADoorActor::BeginPlay()
{
Super::BeginPlay();
UpdateFunctionFloat.BindDynamic(this, &ADoorActor::UpdateTimelineComp);
//如果有浮点曲线,将其图表绑定到我们的更新函数
if (DoorTimelineFloatCurve)
{
DoorTimelineComp->AddInterpFloat(DoorTimelineFloatCurve, UpdateFunctionFloat);
}
//将我们的距离盒体组件绑定到我们的重叠函数
DoorProxVolume->OnComponentBeginOverlap.AddDynamic(this, &ADoorActor::OnOverlapBegin);
DoorProxVolume->OnComponentEndOverlap.AddDynamic(this, &ADoorActor::OnOverlapEnd);
}
void ADoorActor::UpdateTimelineComp(float Output)
{
FRotator DoorNewRotation = FRotator(0.0f,Output,0.f);
Door->SetRelativeRotation(DoorNewRotation);
}
void ADoorActor::OnOverlapBegin(UPrimitiveComponent * OverlappedComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult & SweepResult)
{
DoorTimelineComp->Play();
}
void ADoorActor::OnOverlapEnd(UPrimitiveComponent * OverlappedComp, AActor * OtherActor, UPrimitiveComponent * OtherComp, int32 OtherBodyIndex)
{
DoorTimelineComp->Reverse();
}
// 每一帧都被调用
void ADoorActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}