Rotating an actor with a lerp, timeline and curvefloat. Lerp is not working.

I am currently trying to rotate a planet by using a Timeline and a CurveFloat and then lerping the result over time to my actors rotation. I can’t for the life of me figuer out why it will not lerp.

I specify the CurrentActorRotation and I also give the TargetActorRotation before I start the lerp function. My outputs log shows the correct current rotation, the correct target rotation, and the correct alpha values returned from my curvefloat, but nothing happens in the lerp function. The planet just remains.

Code below:

#.h

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/TimeLineComponent.h"
#include "Planet.generated.h"


class UStaticMeshComponent;
class UTimelineComponent;
class UCurveFloat;


UCLASS()
class STARTRANSPORT_API APlanet : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	APlanet();


	

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;
	
	/* Mesh */
	UPROPERTY(EditDefaultsOnly, Category = "Planet")
	TObjectPtr<UStaticMeshComponent> PlanetMesh;

	/* Timeline */
	UPROPERTY(EditAnywhere,BlueprintReadWrite, Category = "Planet")
	TObjectPtr<UTimelineComponent> MainTimeline;

	/* Curve */
	UPROPERTY(EditAnywhere,BlueprintReadWrite, Category = "Planet")
	TObjectPtr<UCurveFloat> PlanetRotationCurve;


	UFUNCTION()
	void RotatePlanet(float Value);

	UFUNCTION()
	void TimelineEnd();



private:	
		
	FRotator PlanetCurrentRotation = FRotator(0.f);
	FRotator PlanetTargetRotation = FRotator(0.f);

	float RotationAmount = 360.0f;
	float LengthOfTimeline = 30.0f;
	
};

#CPP

#include "Actors/Planet/Planet.h"
#include "Components/StaticMeshComponent.h"
#include "Components/TimeLineComponent.h"
#include "Curves/CurveFloat.h"
#include "Kismet/GameplayStatics.h"


// Sets default values
APlanet::APlanet()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bStartWithTickEnabled = true;
	PrimaryActorTick.bCanEverTick = true;
	bNetLoadOnClient = false;
	SetCanBeDamaged(false);

	//Mesh
	PlanetMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PlanetMesh"));
	PlanetMesh->SetEnableGravity(false);
	PlanetMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);	
	
	//Root Component
	SetRootComponent(PlanetMesh);
	
	//Curve to use
	PlanetRotationCurve = CreateDefaultSubobject<UCurveFloat>(TEXT("CurveFloat"));
	
	//Timeline
	MainTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("TimeLineComp"));
}



// Called when the game starts or when spawned
void APlanet::BeginPlay()
{
	Super::BeginPlay();

	//Bind the timelineupdate to the rotateplanet function
	FOnTimelineFloat TimelineMovementValue;
	FOnTimelineEvent TimelineFinishEvent;

	//Bind functions to timeline
	TimelineMovementValue.BindUFunction(this, FName("RotatePlanet"));
	TimelineFinishEvent.BindUFunction(this, FName("TimelineEnd"));

	if (PlanetRotationCurve)
	{		
		//This will allow the curve to feed the data to TimelineMovementValue, which in turn will give RotatePlanet() its value param.
		MainTimeline->AddInterpFloat(PlanetRotationCurve, TimelineMovementValue);

		//This will now allow the timline, when finished, to call the TimlineFinish event of StartRotating()
		MainTimeline->SetTimelineFinishedFunc(TimelineFinishEvent);
	}
		

	//Initialize the Starting Rotation and Ending Rotation
	PlanetCurrentRotation = GetActorRotation();
	PlanetTargetRotation = PlanetCurrentRotation;

	//Add the 360 degrees on the Yaw
	PlanetTargetRotation.Yaw += RotationAmount;
	
	//Start the timline
	MainTimeline->PlayFromStart();

}



void APlanet::RotatePlanet(float Value)
{
	//Create the new rotation
	FRotator NewRotation = FMath::Lerp(PlanetCurrentRotation, PlanetTargetRotation, Value);	
	SetActorRotation(NewRotation);
	
		
	UE_LOG(LogTemp, Warning, TEXT("VALUE: %f "), Value);
	UE_LOG(LogTemp, Warning, TEXT("CurrentRotation %s"), *GetActorRotation().ToString());
	UE_LOG(LogTemp, Warning, TEXT("PlanetTargetRot: %s"),  *PlanetTargetRotation.ToString());
	



}

//Rotate the planet
void APlanet::TimelineEnd()
{
	UE_LOG(LogTemp, Warning, TEXT("TimelineEndCalled"));
}

Link also available for my code on pastebin https://pastebin.com/spJWXaTg

Any help with this would be appreciated.

Hi unleahedcode,

Could you please replace the link with a copy/pasted snapshot instead - that helps future proof the post, and makes it a lot easy for us to debug. Thx.

Sure thing… is the above now better?

1 Like

Perfect - thank you.

Lerp doesn’t work with rotators, to interpolate two rotators, use something like:

FRotator NewRotation = FQuat::Slerp(PlanetCurrentRotation.Quaternion(), PlanetTargetRotation.Quaternion(), Value).Rotator();

Ok, thank you for a fast reply. I didn’t know lerps don’t work with rotations. I am trying with FQuat::Slerp but unreal is complaining that "no suitable user-defined conversion from ‘UE::Math::TQuat’ to ‘FRotator’ exists.

I included Math/Quat.h to my header as recommended in the unreal docs…

Something I am missing? and again, thanks kindly for helping me

I just updated my previous reply with the corrected code - sorry about that.

1 Like

The code is good but unfortunately the result is the same. Slerp is not rotating the planet either…eeeek.

void APlanet::RotatePlanet(float Value)
{
	//Create the new rotation
	NewRotation = FQuat::Slerp(PlanetCurrentRotation.Quaternion(), PlanetTargetRotation.Quaternion(), Value).Rotator();
	
	SetActorRotation(NewRotation);
	
		
	UE_LOG(LogTemp, Warning, TEXT("VALUE: %f "), Value);
	UE_LOG(LogTemp, Warning, TEXT("CurrentRotation %s"), *GetActorRotation().ToString());
	UE_LOG(LogTemp, Warning, TEXT("PlanetTargetRot: %s"),  *PlanetTargetRotation.ToString());
}

any ideas?.. I do see the Value increasing but the slerp just doesnt rotate the planet… mesh is moveable.

output: LogTemp: Warning: VALUE: 0.602341
LogTemp: Warning: CurrentRotation P=0.000000 Y=-10.000000 R=0.000000
LogTemp: Warning: PlanetTargetRot: P=0.000000 Y=350.000000 R=0.000000
LogTemp: Warning: VALUE: 0.602637
LogTemp: Warning: CurrentRotation P=0.000000 Y=-10.000000 R=0.000000
LogTemp: Warning: PlanetTargetRot: P=0.000000 Y=350.000000 R=0.000000
LogTemp: Warning: VALUE: 0.602921
LogTemp: Warning: CurrentRotation P=0.000000 Y=-10.000000 R=0.000000
LogTemp: Warning: PlanetTargetRot: P=0.000000 Y=350.000000 R=0.000000

Interesting - the Slerp doesn’t like negative numbers - if you test with the initial angle of 0 rather than -10 it works, -10 and it breaks.

You’ll need to test the Yaw and add 360 to it if it’s less than 0 (but if you’re after a certain direction, you’ll need to manipulate the dest as well…

lol… just did it… thought would try… unfortunately with no luck.

LogTemp: Warning: CurrentRotation P=0.000000 Y=0.000000 R=0.000000
LogTemp: Warning: PlanetTargetRot: P=0.000000 Y=360.000000 R=0.000000
LogTemp: Warning: VALUE: 0.582703 
LogTemp: Warning: CurrentRotation P=0.000000 Y=0.000000 R=0.000000
LogTemp: Warning: PlanetTargetRot: P=0.000000 Y=360.000000 R=0.000000

Seeing as your only rotating on one axis, maybe it would just be easier to lerp just the yaw? I’ll look more into it when I have more time if you haven’t solved it, just a little busy atm… For orbiting planets, I think I’d use an “increment variable” - the amount to increment each axis per frame, then add that, rather than interpolate

1 Like

Holy crap… I just got it to work with the lerp. It is moving exceptionally slowly though compared to the ucurve setup. By making it 0 → 359 it rotates.

(begin play)-
PlanetTargetRotation = FRotator(PlanetCurrentRotation.Pitch, RotationAmount, PlanetCurrentRotation.Roll);

(RotatePlanet())-
NewRotation = FMath::Lerp(PlanetCurrentRotation, PlanetTargetRotation , Value);

What is very strange to me now is the Current Rotation is going up but is matched with the value of the ucurve output.

so confused…

LogTemp: Warning: VALUE: 0.971398 
LogTemp: Warning: CurrentRotation P=0.000000 Y=-0.971398 R=0.000000
LogTemp: Warning: PlanetTargetRot: P=0.000000 Y=359.000000 R=0.000000
LogTemp: Warning: VALUE: 0.982509 
LogTemp: Warning: CurrentRotation P=0.000000 Y=-0.982509 R=0.000000
LogTemp: Warning: PlanetTargetRot: P=0.000000 Y=359.000000 R=0.000000
LogTemp: Warning: VALUE: 0.993646 
LogTemp: Warning: CurrentRotation P=0.000000 Y=-0.993646 R=0.000000
LogTemp: Warning: PlanetTargetRot: P=0.000000 Y=359.000000 R=0.000000
LogTemp: Warning: VALUE: 1.000000 
LogTemp: Warning: CurrentRotation P=0.000000 Y=-1.000000 R=0.000000
LogTemp: Warning: PlanetTargetRot: P=0.000000 Y=359.000000 R=0.000000
LogTemp: Warning: TimelineEndCalled

That looks like the Y is the VALUE - I’m really not sure what’s up with that! :smile:

lol… Im at a loss… time for some sleep and shed some more tears tomorrow. Thank you for the help tonight… if you have any wild ideas later… feel free to add to the post. Thank you again for the time.

1 Like

Well I guess I just don’t understand Timelines combined with UCurvefloat.

I was under the impression that I needed the UCurvefloat to be 0 to 1 as the lerp calculates the transition (alpha) by 0 to 1. I managed to get it turning first by fixing the discrepancy of degrees from 0->360 and making it 0->359. It would turn ridiculously slowly and not complete a single rotation before reaching the end of the lerp.

I ended up creating a new UCurvefloat and graphed it in the editor with the values 360 on the x AND y. It now spins a complete rotation and at a decent speed. Not sure what to make of it all. I have completed what I wanted but have more questions than answers.

Why does the curvefloat need such weird numbers? I thought the Alpha on the lerp is from 0 to 1. then the curvefloat fed it those numbers over the time specified?

Completely lost although working… anyone can explain I would be ever so grateful.

Completed code below:

Header file

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Components/TimeLineComponent.h"
#include "Planet.generated.h"


class UStaticMeshComponent;
class UTimelineComponent;
class UCurveFloat;


UCLASS()
class STARTRANSPORT_API APlanet : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	APlanet();


	

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

	
	/* Mesh */
	UPROPERTY(EditDefaultsOnly, Category = "Planet")
	TObjectPtr<UStaticMeshComponent> PlanetMesh;

	/* Timeline */
	UPROPERTY(EditAnywhere,BlueprintReadWrite, Category = "Planet")
	TObjectPtr<UTimelineComponent> MainTimeline;

	/* Curve */
	UPROPERTY(EditAnywhere,BlueprintReadWrite, Category = "Planet")
	TObjectPtr<UCurveFloat> PlanetRotationCurve;


	UFUNCTION()
	void RotatePlanet(float Value);

	UFUNCTION()
	void TimelineEnd();



private:	
		
	FRotator PlanetCurrentRotation = FRotator(0.f);
	FRotator PlanetTargetRotation; 
	FRotator NewRotation = FRotator(0.f);

	float RotationAmount = 359.0f;
	
};

CPP file

#include "Actors/Planet/Planet.h"
#include "Components/StaticMeshComponent.h"
#include "Components/TimeLineComponent.h"
#include "Curves/CurveFloat.h"
#include "Math/Quat.h"


// Sets default values
APlanet::APlanet()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bStartWithTickEnabled = false;
	PrimaryActorTick.bCanEverTick = false;
	bNetLoadOnClient = false;
	SetCanBeDamaged(false);

	//Mesh
	PlanetMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("PlanetMesh"));
	PlanetMesh->SetEnableGravity(false);
	PlanetMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision);	
	
	//Root Component
	SetRootComponent(PlanetMesh);
	
	//Curve to use
	PlanetRotationCurve = CreateDefaultSubobject<UCurveFloat>(TEXT("CurveFloat"));
	
	//Timeline
	MainTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("TimeLineComp"));
}



// Called when the game starts or when spawned
void APlanet::BeginPlay()
{
	Super::BeginPlay();

	//Bind the timelineupdate to the rotateplanet function
	FOnTimelineFloat TimelineMovementValue;
	FOnTimelineEvent TimelineFinishEvent;

	//Bind functions to timeline
	TimelineMovementValue.BindUFunction(this, FName("RotatePlanet"));
	TimelineFinishEvent.BindUFunction(this, FName("TimelineEnd"));

	if (PlanetRotationCurve)
	{		
		//This will allow the curve to feed the data to TimelineMovementValue, which in turn will give RotatePlanet() its value param.
		MainTimeline->AddInterpFloat(PlanetRotationCurve, TimelineMovementValue);
		MainTimeline->SetLooping(true);

		//This will now allow the timline, when finished, to call the TimlineFinish event of StartRotating()
		MainTimeline->SetTimelineFinishedFunc(TimelineFinishEvent);
	}
		

	//Initialize the Starting Rotation and Ending Rotation
	PlanetCurrentRotation = GetActorRotation();
	PlanetTargetRotation = FRotator(0.f, 359.0f, 0.0f);

	//Add the 360 degrees on the Yaw
	//PlanetTargetRotation.Yaw += RotationAmount;
	
	//Start the timline
	MainTimeline->PlayFromStart();

}



void APlanet::RotatePlanet(float Value)
{
	//Create the new rotation
	//NewRotation = FQuat::Slerp(PlanetCurrentRotation.Quaternion(), PlanetTargetRotation.Quaternion(), Value).Rotator();
		
	NewRotation = FMath::Lerp(PlanetCurrentRotation, PlanetTargetRotation, Value);
	SetActorRotation(NewRotation);
		
	UE_LOG(LogTemp, Warning, TEXT("VALUE: %f "), Value);
	UE_LOG(LogTemp, Warning, TEXT("CurrentRotation %s"), *GetActorRotation().ToString());
	UE_LOG(LogTemp, Warning, TEXT("PlanetTargetRot: %s"),  *PlanetTargetRotation.ToString());
}

//Rotate the planet
void APlanet::TimelineEnd()
{
	UE_LOG(LogTemp, Warning, TEXT("TimelineEndCalled"));
}

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.