Choose your operating system:
Windows
macOS
Linux
Choose your implementation method:
Blueprints
C++
This tutorial demonstrates the use of a Timeline Blueprint to set up a proximity-based opening door using resources available in the Engine Starter content.
Creating the Door Actor
Begin by creating a New > Games > Blank > Blueprint project with Starter Content enabled named TimelineDoorActor.
Click the Add/Import button to create a new Blueprint Actor class named BP_DoorActor.
Double-click the BP_Door Actor from the Content Browser to open it in the Blueprint Editor and open the Class Defaults.
Next, from the Components tab click the Add Component button and select Static Mesh to add a new Static Mesh Component.
Right-click your static mesh component and select Rename to rename it to DoorFrame.
Next, from the Components tab click the Add Component button and select Static Mesh to add a new Static Mesh Component. (Repeating step 3)
Right-click your static mesh component and select Rename to rename it to Door.
Click Add Component, select Box Collision from the dropdown menu, and rename it to Box.
Next, open the Event Graph, right-clickthe graph, and choose Add Timeline from the Blueprint Context Menu. Name your Timeline DoorTimelineComponent.
At this point, the BP_DoorActor's class defaults should now look as they do in the example below:
Setting Up the Door Static Mesh
Next, you will need to set up the attachment hierarchy of your Components tab.
You will also need to set up the Static Mesh assets that will visually represent your DoorFrame and Door Static Mesh components.
From the BP_DoorActor's Components tab, select the DoorFrame static mesh and drag it over the DefaultSceneRoot component to make it the new root component.
Next, select the DoorFrame static mesh in the Components tab and from the Details panel change the Static Mesh to SM_DoorFrame.
Next, from the Components panel select the DoorMesh component. Navigate to the Details panel and change the Static Mesh to SM_Door.
Then navigate to the Transform category and change the Y Location value to 45.0.
Click Save and Compile to save these changes to the Bp_DoorActor.
Creating a Timeline Float Track
The Timeline Component requires a Timeline Curve to describe its movement animation. Each curve can contain multiple keys that define a time and value. Curves interpolate these keys to calculate the value at any point during the Timeline.
Begin by double-clicking the DoorTimelineComponent in the Event Graph to open the Timeline Editor.
Click Add Float Curve to add a new curve to the track and name the curve DoorRotationZ.
Right-click to add two keys to your float curve track. One with the value (0,0) and another with the value (5,90).
Shift select both your keys, right-click, and from the Key Interpolation dropdown menu select Auto interpolation.
Save your float track.
Update Event Track Logic
You will now need to create the update logic to rotate your Door static mesh.
Navigate to the BP_DoorActor's Components tab, drag the Door static mesh onto the Event Graph
Drag off the Door pin and from the actions context menu, select SetRelativeRotation.
Right-click the Event Graph, from the context menu select Make Rotator.
From the DoorTimelineComponent node, drag the Door Rotation Z float pin and connect it to the Z(Yaw) pin of the Make Rotator node.
From the DoorTimelineComponent node, drag off of the Update pin, and connect it to the input execution pin of the SetRelativeRotation node.
From the Make Rotator node, connect the Return Value pin to the SetRelativeRotation node's New Rotation pin.
Compile and Save.
Creating Binding Box Collision Overlap Events
The Box component requires the ability to react when an Actor enters or leaves its collision bounds.
Navigate to the Components tab of your BP_DoorActor and select the Box component.
From the Details panel, scroll down to the Events category and click the + icon next to the On Component Begin Overlap event.
From the On Component Begin Overlap(Box) node, drag off the execution pin and connect it into the Play pin of the DoorTimelineComponent node.
Navigate to the Components tab of theBP_DoorActor and select the Box component. Then from the Details panel, scroll down to the Events category and click + icon next to the On Component End Overlap event.
From the On Component End Overlap(Box) node, drag off the execution pin and connect it into the Reverse pin of the TimelineComponent node.
Compile and Save.
Placing Your Actor into the Level
From the Content Browser, select your BP_DoorActor and drag it into the viewport.
Press PIE
Using the WASD keys, we can control our spectator Pawn. Upon navigating to the collision bounds of our DoorActor, we can observe the timeline play as the door opens. Upon exiting the bounds, we can observe it play in reverse.
Finished Blueprint
BP_DoorActor
An example C++ Timeline is used to set up a classic proximity-based opening door using resources available from the Engine Starter content.
Creating the Door Actor
Begin by creating a New > Games > Blank > C++ project with Starter Content enabled named TimelineDoorActor.
From the C++ Class Wizard, create a new Actor class named DoorActor. Navigate to your
DoorActor.h
file and declare the following include:#include "Components/TimelineComponent.h"
Next, in your
DoorActor
class definition, implement the following code:protected: //MeshComponents to represent Door assets UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UStaticMeshComponent* DoorFrame; UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UStaticMeshComponent* Door; //TimelineComponent to animate Door meshes UPROPERTY(VisibleAnywhere, BlueprintReadWrite) UTimelineComponent* DoorTimelineComp; //BoxComponent which will be used as our Proximity volume. UPROPERTY(EditAnywhere, BlueprintReadWrite) class UBoxComponent* DoorProxVolume;
Navigate to
DoorActor.cpp
. You will need to include the following class library to utilize your Box Component.#include "Components/BoxComponent.h"
In the constructor of your ADoorActor::ADoorActor declare the following:
//Create our Default Components DoorFrame = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorFrameMesh")); Door = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh")); DoorTimelineComp = CreateDefaultSubobject<UTimelineComponent>(TEXT("DoorTimelineComp")); DoorProxVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("DoorProximityVolume")); //Setup our Attachments DoorFrame->SetupAttachment(RootComponent); Door->AttachToComponent(DoorFrame,FAttachmentTransformRules::KeepRelativeTransform); DoorProxVolume->AttachToComponent(DoorFrame, FAttachmentTransformRules::KeepRelativeTransform);
Note: We keep the door's relative transform as an attachment rule, in order to manipulate it later with a custom method for our Door Actor. For further documentation, see FAttachmentTransformRules.
Hot reload your code in the Editor, by selecting Compile.
Setting Up the Door Static Mesh
You will need to set up the Static Mesh assets that will visually represent your DoorFrame and Door Static Mesh components.
From the Content Browser, navigate to your C++ Classes folder.
Right click your DoorActor class, select Create Blueprint Class based on Door Actor, and name your Blueprint Actor Bp_DoorActor.
From the Components tab select the DoorFrame Static Mesh component.
Navigate to the Details panel and change the Static Mesh to SM_DoorFrame.
Next, from the Components tab, select the DoorMesh component. Navigate to the Details panel and change the static mesh to SM_Door.
Then navigate to the Transform category and change the Y Location value to 45.0.
Click Save to save these changes to the Bp_DoorActor.
Creating UCurveFloat and Timeline Event Tracks
The Timeline Component requires a Timeline Curve. Each curve can contain multiple keys that define a time and value.Curves interpolate these keys to calculate the value at any point during the Timeline.
For this example, we will be using a UCurveFloat.
Navigate to the
ADoorActor
class definition inDoorActor.h
and declare the following variables:public: // Variable to hold the Curve asset UPROPERTY(EditAnywhere) UCurveFloat* DoorTimelineFloatCurve; private: //Float Track Signature to handle our update track event FOnTimelineFloat UpdateFunctionFloat; //Function which updates our Door's relative location with the timeline graph UFUNCTION() void UpdateTimelineComp(float Output);
Next, navigate to
DoorActor.cpp
and implement theUpdateTimelineComp
method:void ADoorActor::UpdateTimelineComp(float Output) { // Create and set our Door's new relative location based on the output from our Timeline Curve FRotator DoorNewRotation = FRotator(0.0f, Output, 0.f); Door->SetRelativeRotation(DoorNewRotation); }
Then, in the BeginPlay method, add the following code:
//Binding our float track to our UpdateTimelineComp Function's output UpdateFunctionFloat.BindDynamic(this, &ADoorActor::UpdateTimelineComp); //If we have a float curve, bind it's graph to our update function if (DoorTimelineFloatCurve) { DoorTimelineComp->AddInterpFloat(DoorTimelineFloatCurve, UpdateFunctionFloat); }
Work-In-Progress Code
DoorActor.h
// Copyright 1998-2021 Epic Games, Inc. All Rights Reserved.
#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:
// Sets default values for this actor's properties
ADoorActor();
/*Variable to hold Curve asset*/
UPROPERTY(EditAnywhere)
UCurveFloat* DoorTimelineFloatCurve;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
//MeshComponents to represents Door assets
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UStaticMeshComponent* DoorFrame;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UStaticMeshComponent* Door;
//TimelineComponent to animate door meshes
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UTimelineComponent* DoorTimelineComp;
//BoxComponent which will be used as our proximity volume.
UPROPERTY(EditAnywhere, BlueprintReadWrite)
class UBoxComponent* DoorProxVolume;
//Float Track Signature to handle our update track event
FOnTimelineFloat UpdateFunctionFloat;
//Function which updates our Door's relative location with the timeline graph
UFUNCTION()
void UpdateTimelineComp(float Output);
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
DoorActor.cpp
//Copyright 1998-2021 Epic Games, Inc. All Rights Reserved.
#include "DoorActor.h"
#include "Components/BoxComponent.h"
// Sets default values
ADoorActor::ADoorActor()
{
// 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;
//Create our Default Components
DoorFrame = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorFrameMesh"));
Door = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh"));
DoorTimelineComp = CreateDefaultSubobject<UTimelineComponent>(TEXT("DoorTimelineComp"));
DoorProxVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("DoorProximityVolume"));
//Setup our Attachments
DoorFrame->SetupAttachment(RootComponent);
Door->AttachToComponent(DoorFrame,FAttachmentTransformRules::KeepRelativeTransform);
DoorProxVolume->AttachToComponent(DoorFrame, FAttachmentTransformRules::KeepRelativeTransform);
}
// Called when the game starts or when spawned
void ADoorActor::BeginPlay()
{
Super::BeginPlay();
//Binding our float track to our UpdateTimelineComp Function's output
UpdateFunctionFloat.BindDynamic(this, &ADoorActor::UpdateTimelineComp);
//If we have a float curve, bind it's graph to our update function
if (DoorTimelineFloatCurve)
{
DoorTimelineComp->AddInterpFloat(DoorTimelineFloatCurve, UpdateFunctionFloat);
}
}
void ADoorActor::UpdateTimelineComp(float Output)
{
//Create and set our Door's new relative location based on the output from our Timeline Curve
FRotator DoorNewRotation = FRotator(0.0f,Output,0.f);
Door->SetRelativeRotation(DoorNewRotation);
}
// Called every frame
void ADoorActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
Creating and Binding Collision Overlap Events
The Box Component requires the ability to react when an Actor enters or leaves its collision bounds.
Navigate to the class definition of your
TimelineDoorActor.h
file and declare the following:// Begin and End Overlap Events for our 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);
Next, navigate to your
DoorActor.cpp
file to implement yourOnOverlapBegin
andOnOverlapEnd
class methods: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(); }
Bind your overlap functions in the BeginPlay method, as follows:
//Binding our Proximity Box Component to our Overlap Functions DoorProxVolume->OnComponentBeginOverlap.AddDynamic(this, &ADoorActor::OnOverlapBegin); DoorProxVolume->OnComponentEndOverlap.AddDynamic(this, &ADoorActor::OnOverlapEnd);
Compile your code.
Creating a Curve Asset in the Unreal Editor
You must create a Curve Asset in the Unreal Editor to assign to your Timeline Actor Blueprint.
From the Content Browser, select Add/Import > Miscellaneous > Curve.
Select CurveFloat and name your CurveFloat asset DoorCurveFloat
Double click your DoorCurveFloat asset to open the Timeline Editor.
Add two keys to your Float Curve and give one key the time-value (0,0), and the other key the time-value of (4,90).
For more information on editing Timeline curves, see Keys and Curves.
Shift click to select both your keys, and set them to Auto Cubic interpolation, then save your Curve.
Open your Bp_DoorActor and select Bp_DoorActor from the Components tab.
From the Details panel, select DoorCurveFloat from the Door Timeline Float Curve dropdown menu.
From the Level Editor, place the Bp_DoorActor into the scene.
Compile and Save, then press PIE.
Using input with WASD, we can control our spectator Pawn. Upon navigating to the collision bounds of our DoorActor, we can observe the timeline play, and upon exiting the bounds, we can observe it play in reverse.
Finished Code
DoorActor.h
// Copyright 1998-2021 Epic Games, Inc. All Rights Reserved.
#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:
// Sets default values for this actor's properties
ADoorActor();
//Variable to hold Curve asset
UPROPERTY(EditAnywhere)
UCurveFloat* DoorTimelineFloatCurve;
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
/*MeshComponents to represents Door assets*/
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UStaticMeshComponent* DoorFrame;
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UStaticMeshComponent* Door;
//TimelineComponent to animate door meshes
UPROPERTY(VisibleAnywhere, BlueprintReadWrite)
UTimelineComponent* DoorTimelineComp;
//BoxComponent which will be used as our proximity volume.
UPROPERTY(EditAnywhere, BlueprintReadWrite)
class UBoxComponent* DoorProxVolume;
//Float Track Signature to handle our update track event
FOnTimelineFloat UpdateFunctionFloat;
//Function which updates our Door's relative location with the timeline graph
UFUNCTION()
void UpdateTimelineComp(float Output);
/*Begin and End Overlap Events for our 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:
// Called every frame
virtual void Tick(float DeltaTime) override;
};
DoorActor.cpp
// Copyright 1998-2021 Epic Games, Inc. All Rights Reserved.
#include "DoorActor.h"
#include "Components/BoxComponent.h"
// Sets default values
ADoorActor::ADoorActor()
{
// 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;
//Create our Default Components
DoorFrame = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorFrameMesh"));
Door = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("DoorMesh"));
DoorTimelineComp = CreateDefaultSubobject<UTimelineComponent>(TEXT("DoorTimelineComp"));
DoorProxVolume = CreateDefaultSubobject<UBoxComponent>(TEXT("DoorProximityVolume"));
//Setup our Attachments
DoorFrame->SetupAttachment(RootComponent);
Door->AttachToComponent(DoorFrame,FAttachmentTransformRules::KeepRelativeTransform);
DoorProxVolume->AttachToComponent(DoorFrame, FAttachmentTransformRules::KeepRelativeTransform);
}
// Called when the game starts or when spawned
void ADoorActor::BeginPlay()
{
Super::BeginPlay();
UpdateFunctionFloat.BindDynamic(this, &ADoorActor::UpdateTimelineComp);
//If we have a float curve, bind it's graph to our update function
if (DoorTimelineFloatCurve)
{
DoorTimelineComp->AddInterpFloat(DoorTimelineFloatCurve, UpdateFunctionFloat);
}
//Binding our Proximity Box Component to our Overlap Functions
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();
}
// Called every frame
void ADoorActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}