I am creating timeline in C++ like this (note that I want to generate UCurveFloat
as well and not just reference it from UProperty
), from inside of UActorComponent
:
UCurveFloat* Curve = NewObject<UCurveFloat>(this);
FKeyHandle KeyHandle = Curve->FloatCurve.UpdateOrAddKey(0.f, 0.f);
Curve->FloatCurve.UpdateOrAddKey(1.f, 1.f);
Curve->FloatCurve.SetKeyInterpMode(KeyHandle, ERichCurveInterpMode::RCIM_Cubic, true);
TimelineCallback.BindUFunction(this, FName("RotateUpdate"));
TimelineFinishedCallback.BindUFunction(this, FName("RotateFinish"));
RotateToSeatTimeline.AddInterpFloat(Curve, TimelineCallback);
RotateToSeatTimeline.SetTimelineFinishedFunc(TimelineFinishedCallback);
RotateToSeatTimeline.PlayFromStart();
The problem is, I get only one call of RotateUpdate()
and none of RotateFinish()
.
I read that currently timelines do not require to be updated manually in tick event (it would also render them pointless in first place) - and PlayFromStart()
should be sufficient.
3dRaven
(3dRaven)
October 27, 2022, 6:33pm
2
.h file
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "TimerTest.generated.h"
UCLASS(Blueprintable)
class YOUROWN_API ATimerTest : public AActor
{
GENERATED_BODY()
public:
ATimerTest();
protected:
virtual void BeginPlay() override;
public:
virtual void Tick(float DeltaTime) override;
UFUNCTION()
void RotateUpdate();
UFUNCTION()
void RotateFinish();
UPROPERTY(EditAnywhere,BlueprintReadWrite)
float rotation = 0;
UPROPERTY(editAnywhere, BlueprintReadWrite)
UCurveFloat* Curve;
UPROPERTY(editAnywhere, BlueprintReadWrite)
class UTimelineComponent* RotateToSeatTimeline;
};
.cpp file
#include "TimerTest.h"
#include "TimerManager.h"
#include "Components/TimelineComponent.h"
ATimerTest::ATimerTest()
{
// 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;
Curve = CreateDefaultSubobject<UCurveFloat>(TEXT("Timeline Curve"));
RotateToSeatTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("Seat Timeline"));
}
void ATimerTest::BeginPlay()
{
Super::BeginPlay();
FOnTimelineFloat TimelineCallback;
FOnTimelineEventStatic TimelineFinishedCallback;
FKeyHandle KeyHandle = Curve->FloatCurve.UpdateOrAddKey(0.f, 0.f);
Curve->FloatCurve.UpdateOrAddKey(1.f, 1.f);
Curve->FloatCurve.SetKeyInterpMode(KeyHandle, ERichCurveInterpMode::RCIM_Cubic, true);
TimelineCallback.BindUFunction(this, FName({ TEXT("RotateUpdate") }));
TimelineFinishedCallback.BindUFunction(this, FName({ TEXT("RotateFinish") }));
RotateToSeatTimeline->AddInterpFloat(Curve, TimelineCallback);
RotateToSeatTimeline->SetTimelineFinishedFunc(TimelineFinishedCallback);
RotateToSeatTimeline->PlayFromStart();
}
// Called every frame
void ATimerTest::Tick(float DeltaTime)
{
Super::Tick(DeltaTime);
}
void ATimerTest::RotateUpdate() {
rotation += 0.5f;
if(GEngine)
GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Cyan, "Rotate "+ FString::SanitizeFloat(rotation));
}
void ATimerTest::RotateFinish() {
if (GEngine)
GEngine->AddOnScreenDebugMessage(-1, 1, FColor::Cyan, "RotateFinish");
}
1 Like
Hi. I do exactly this but I need to have it inside my custom component (that inherits from UActorComponent
). But when I move it there, the update is called only once (I guess from inside beginPlay where I craete timeline and run PlayFromStart
).
Do I need to do something more when playing timeline from component? Or is it impossible?
3dRaven
(3dRaven)
October 27, 2022, 6:51pm
4
No shouldn’t be a problem to put it in a component here is an example of a test component I did about a week ago with a timeline.
.h
#pragma once
#include "CoreMinimal.h"
#include "Components/ActorComponent.h"
#include "Components/TimelineComponent.h"
#include "MySceneComponent.generated.h"
UCLASS( ClassGroup=(Custom), meta=(BlueprintSpawnableComponent) , Blueprintable )
class YOUR_API UMySceneComponent : public UActorComponent
{
GENERATED_BODY()
public:
UMySceneComponent();
protected:
// Called when the game starts
virtual void BeginPlay() override;
public:
virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override;
float passedTime;
UPROPERTY(editAnywhere, BlueprintReadWrite)
UCurveFloat* TimelineCurve;
UPROPERTY(editAnywhere, BlueprintReadWrite)
UTimelineComponent* MyTimeline;
FOnTimelineFloat InterpFunction{};
UFUNCTION()
void TimelineUpdate(float val);
};
.cpp
#include "MySceneComponent.h"
#include "TimerManager.h"
#include "Components/TimelineComponent.h"
#include "Curves/CurveFloat.h"
UMySceneComponent::UMySceneComponent()
{
PrimaryComponentTick.bCanEverTick = false;
TimelineCurve = CreateDefaultSubobject<UCurveFloat>(TEXT("Timeline Curve"));
MyTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("Timeline"));
}
void UMySceneComponent::BeginPlay()
{
Super::BeginPlay();
if (TimelineCurve != nullptr) {
InterpFunction.BindUFunction(this, FName{ TEXT("TimelineUpdate") });
MyTimeline->AddInterpFloat(TimelineCurve, InterpFunction, FName{ TEXT("doUpdate") });
}
MyTimeline->SetLooping(true);
MyTimeline->SetPlayRate(1);
MyTimeline->SetTimelineLengthMode(TL_LastKeyFrame);
MyTimeline->PlayFromStart();
}
// Called every frame
void UMySceneComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
// ...
}
void UMySceneComponent::TimelineUpdate(float val) {
if (TimelineCurve != nullptr) {
bool isplay = MyTimeline->IsPlaying();
if (isplay == true) {
if (GEngine) {
GEngine->AddOnScreenDebugMessage(-1, 2, FColor::Green, GetName() + " Progress:" + FString::SanitizeFloat(val));
}
}
}
}
The changes are minimal. You just need to add the finished bind. Just copy over the parameters and should be gold
2 Likes
Thank you, @3dRaven !
I finally spotted the problem by comparison. I was using FTimeline
in place of UTimelineComponent
. Now it all works fine.
Ivan3z
(Ivan3z)
June 2, 2023, 9:02pm
6
It works perfect!!
I can’t find the original post, but I wanted to thank you anyway.
You Rock!!
2 Likes