这里有官方文档介绍比较详细,我这边简单描述,重在实践。
时间轴(Timeline)可以实现从事件中播放基于时间的动画,这些事件可以沿着时间轴在关键帧处触发。可以使用时间轴来处理简单的非动画任务,例如开门、更改光源或对场景中的Actor执行其他以时间为中心的操纵。
关键组件:UTimelineComponent
包含一系列的 事件(events)、 浮点数(floats)、向量(vectors) 或 颜色(colors) 及其关联的关键帧。这些内容继承自UActorComponents
双击后可以添加浮点曲线等
创建一个继承于Actor的C++类 MyTimelineActor,在这个类里写Timeline相关逻辑。
MyTimelineActor.h
#pragma once
#include "CoreMinimal.h"
#include "Components/TimelineComponent.h"
#include "GameFramework/Actor.h"
#include "MyTimelineActor.generated.h"
UCLASS()
class DEMO_API AMyTimelineActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyTimelineActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 浮点数曲线
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyCurve")
UCurveFloat *MyCurveFloat;
// 时间轴组件
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MySceneComponent")
UTimelineComponent *MyTimeline;
// 声明时间轴回调函数
FOnTimelineFloat TimelineCallback;
// 声明时间轴结束事件 时间轴事件(FOnTimelineEvent)是动态委托,为时间轴组件提供处理事件的能力
FOnTimelineEvent TimelineFinishedCallback;
// 时间轴开始函数
UFUNCTION(BlueprintCallable, Category = "MyTimeline")
void TimelineStart(float Value);
// 时间轴结束函数
UFUNCTION(BlueprintCallable, Category = "MyTimeline")
void TimelineFinished();
};
MyTimelineActor.cpp
#include "MyTimelineActor.h"
// Sets default values
AMyTimelineActor::AMyTimelineActor()
{
// 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;
MyTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("MyTimeline"));
}
// Called when the game starts or when spawned
void AMyTimelineActor::BeginPlay()
{
Super::BeginPlay();
TimelineCallback.BindUFunction(this, FName("TimelineStart"));
TimelineFinishedCallback.BindUFunction(this, FName("TimelineFinished"));
// 绑定时间轴曲线
MyTimeline->AddInterpFloat(MyCurveFloat, TimelineCallback);
//
MyTimeline->SetLooping(false);
MyTimeline->PlayFromStart();
MyTimeline->Play();
MyTimeline->SetTimelineFinishedFunc(TimelineFinishedCallback);
}
// Called every frame
void AMyTimelineActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AMyTimelineActor::TimelineStart(float Value)
{
UE_LOG(LogTemp, Warning, TEXT("TimelineStart"));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("TimelineStart")));
}
void AMyTimelineActor::TimelineFinished()
{
UE_LOG(LogTemp, Warning, TEXT("TimelineFinished"));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("TimelineFinished")));
}
编译之后,创建浮点曲线
这里添加的浮点曲线,相当于蓝图中添加的浮点曲线
右键,添加关键帧
保存之后,创建MyTimelineActor的蓝图类
添加刚才创建的浮点曲线
编译,保存之后,拖到场景中运行,打印日志
拷贝引用 门模型:/Script/Engine.StaticMesh’/Game/StarterContent/Architecture/Wall_400x400.Wall_400x400’
MyTimelineActor.h
#pragma once
#include "CoreMinimal.h"
#include "Components/TimelineComponent.h"
// 引入组件
#include "Components/SceneComponent.h"
#include "Components/StaticMeshComponent.h"
#include "Components/BoxComponent.h"
#include "MyCharacter.h"
#include "GameFramework/Actor.h"
#include "MyTimelineActor.generated.h"
UCLASS()
class DEMO_API AMyTimelineActor : public AActor
{
GENERATED_BODY()
public:
// Sets default values for this actor's properties
AMyTimelineActor();
protected:
// Called when the game starts or when spawned
virtual void BeginPlay() override;
public:
// Called every frame
virtual void Tick(float DeltaTime) override;
// 浮点数曲线
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyCurve")
UCurveFloat *MyCurveFloat;
// 时间轴组件
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyScene")
UTimelineComponent *MyTimeline;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyScene")
USceneComponent *MyScene;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyScene")
UStaticMeshComponent *MyStaticMesh;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "MyScene")
UBoxComponent *MyBox;
//声明绑定碰撞函数
UFUNCTION()
void BeginOverlapFunction(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult);
UFUNCTION()
void EndOverlapFunction(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex);
// 声明时间轴回调函数
FOnTimelineFloat TimelineCallback;
// 声明时间轴结束事件 时间轴事件(FOnTimelineEvent)是动态委托,为时间轴组件提供处理事件的能力
FOnTimelineEvent TimelineFinishedCallback;
// 时间轴开始函数
UFUNCTION(BlueprintCallable, Category = "MyTimeline")
void TimelineStart(float Value);
// 时间轴结束函数
UFUNCTION(BlueprintCallable, Category = "MyTimeline")
void TimelineFinished();
};
MyTimelineActor.h
// Fill out your copyright notice in the Description page of Project Settings.
#include "MyTimelineActor.h"
// Sets default values
AMyTimelineActor::AMyTimelineActor()
{
// 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;
MyTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("MyTimeline"));
MyScene = CreateDefaultSubobject<USceneComponent>(TEXT("MyScene"));
MyStaticMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MyStaticMesh"));
MyBox = CreateDefaultSubobject<UBoxComponent>(TEXT("MyBox"));
RootComponent = MyScene;
MyStaticMesh->SetupAttachment(MyScene);
MyBox->SetupAttachment(MyScene);
MyBox->SetRelativeLocation(FVector(200.f, 0.f, 0.f));
MyBox->SetBoxExtent(FVector(200.f, 80.f, 100.f));
// note: 这段代码会导致UE5崩溃,原因未知 所以静态Mesh我在蓝图手动添加
// // 添加门的模型
// static ConstructorHelpers::FObjectFinder<UStaticMesh> TempStaticMesh(TEXT("/Script/Engine.StaticMesh'/Game/StarterContent/Architecture/Wall_400x400.Wall_400x400'"));
// if (TempStaticMesh.Succeeded())
// {
// MyStaticMesh->SetStaticMesh(TempStaticMesh.Object);
// }
}
// Called when the game starts or when spawned
void AMyTimelineActor::BeginPlay()
{
Super::BeginPlay();
TimelineCallback.BindUFunction(this, FName("TimelineStart"));
TimelineFinishedCallback.BindUFunction(this, FName("TimelineFinished"));
// 绑定时间轴曲线
MyTimeline->AddInterpFloat(MyCurveFloat, TimelineCallback);
//
// MyTimeline->SetLooping(false);
// MyTimeline->PlayFromStart();
// MyTimeline->Play();
MyTimeline->SetTimelineFinishedFunc(TimelineFinishedCallback);
MyBox->OnComponentBeginOverlap.AddDynamic(this, &AMyTimelineActor::BeginOverlapFunction);
MyBox->OnComponentEndOverlap.AddDynamic(this, &AMyTimelineActor::EndOverlapFunction);
}
// Called every frame
void AMyTimelineActor::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void AMyTimelineActor::TimelineStart(float Value)
{
UE_LOG(LogTemp, Warning, TEXT("TimelineStart"));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("TimelineStart")));
// 旋转门 动画
float YawRotation = FMath::Lerp(0.f, 90.f, Value);
MyStaticMesh->SetRelativeRotation(FRotator(0.f, YawRotation, 0.f));
}
void AMyTimelineActor::TimelineFinished()
{
UE_LOG(LogTemp, Warning, TEXT("TimelineFinished"));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("TimelineFinished")));
}
void AMyTimelineActor::BeginOverlapFunction(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult)
{
AMyCharacter *MyCharacter = Cast<AMyCharacter>(OtherActor);
UE_LOG(LogTemp, Warning, TEXT("BeginOverlapFunction"));
GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, FString::Printf(TEXT("BeginOverlapFunction")));
if (MyCharacter)
{
MyTimeline->PlayFromStart();
}
}
void AMyTimelineActor::EndOverlapFunction(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex)
{
AMyCharacter *MyCharacter = Cast<AMyCharacter>(OtherActor);
if (MyCharacter)
{
MyTimeline->ReverseFromEnd();
}
}
编译之后,打开蓝图BP_MyTimelineActor ,调整一下
然后拖到场景中,摆放一下位置
点击运行,人物接触时门自动打开