Each ship has its’ own scene component (singular). The move function is called once which invokes the timeline and ucurvefloat to be constructed on the fly only once per navigation order. Each ship only receives one navigation order at the moment. So no looping. I then just invoke the timelinecompnent to PlayFromStart(); I let the UTimelineComponent deal with the tick and update the actors position with FVector ShipsSplineLocation = SplineComponent->GetLocationAtDistanceAlongSpline(TimeLength, ESplineCoordinateSpace::World); (Timelength is the Timeline Value (delta) * the length of the spline.)
Don’t hesitate to ask any further questions if you need more clarity on my mess of code 
Below is attached my code for the ships navigation computer:
#include "CoreObjects/Spaceships/ShipComponents/ShipNavigationSystem.h"
#include "Components/SplineComponent.h"
#include "Curves/CurveFloat.h"
#include"Components/TimelineComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"
#include "CoreObjects/Spaceships/SpaceshipBase.h"
// Sets default values for this component's properties
UShipNavigationSystem::UShipNavigationSystem()
{
// Set this component to be initialized when the game starts, and to be ticked every frame. You can turn these features
// off to improve performance if you don't need them.
PrimaryComponentTick.bCanEverTick = false;
PrimaryComponentTick.TickInterval = 0.5f;
SplineComponent = CreateDefaultSubobject<USplineComponent>(TEXT("SplineComp"));
SplineComponent->SetVisibility(true, true);
SplineComponent->ToggleVisibility(true);
//TimelineComponent setup
FlightTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("FlightTimeline"));
//Cast to reference the spaceship itself
DASpaceship = Cast<ASpaceshipBase>(this->GetOwner());
if (DASpaceship)
{
ShipMovementSpeed = DASpaceship->GetSpeed();
}
}
// Called when the game starts
void UShipNavigationSystem::BeginPlay()
{
Super::BeginPlay();
}
// Called every frame
void UShipNavigationSystem::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction)
{
Super::TickComponent(DeltaTime, TickType, ThisTickFunction);
GEngine->AddOnScreenDebugMessage(5, 0.2f, FColor::White, FString::Printf(TEXT("DeltaTime: %f"), DeltaTime));
}
//Navigation Call - Start/End points - obstacle check and reroute if needed
void UShipNavigationSystem::NavigateTo(const FVector EndLocation)
{
DrawDebugSphere(GetWorld(), EndLocation, 50.f, 20, FColor::Yellow);
//LineTrace to be short of hitting its destination - otherwise it will calculate around our destination object
FVector LineTraceLength;
LineTraceLength.X = EndLocation.X - 1000.f; //Shorter trace
LineTraceLength.Y = EndLocation.Y;
LineTraceLength.Z = EndLocation.Z;
//RayTrace Start/Finish setup
FVector ShipLocation = GetOwner()->GetActorLocation();
FVector RayTraceStart = FVector(ShipLocation.X + 50.f, ShipLocation.Y, ShipLocation.Z);
ShipLocation.X = ShipLocation.X + 50.f; /* WARNING: ShipLocation is not accurate to the ship (+50) for collision purposes */
//Obstacle Check - Line Trace
FHitResult HitResult;
bool bHit = GetWorld()->LineTraceSingleByChannel(
HitResult,
RayTraceStart,
LineTraceLength,
ECC_Visibility //channel
);
//Obstacle hit
if (bHit)
{
//debug data
const float LineThickness = 0.5f;
const float Time = 2.0f;
//Which way round the obstacle
FVector VectorToObstacle = FVector(0.f);
FVector AvoidancePoint = FVector(0.f);
FVector AvoidanceDirection = FVector(0.f);
//LineTrace debug
UE_LOG(LogTemp, Warning, TEXT("ObjectHit: %s at %s"), *HitResult.GetActor()->GetName(), *HitResult.Location.ToString());
DrawDebugLine(GetWorld(), ShipLocation, LineTraceLength, FColor::Red, false, Time, 0, LineThickness);
//Show impact point debug
DrawDebugSphere(GetWorld(), HitResult.Location, 50.0f, 5, FColor::Red, false, 1.4f, 0, 1.2f);
//vector from ship to obstacle
VectorToObstacle = HitResult.Location - ShipLocation;
//which way to go round (up/down)
if (FMath::Abs(HitResult.ImpactNormal.Z) > 0.5f)
{
AvoidanceDirection = ((HitResult.ImpactNormal.Z > 0.0f) ? FVector::UpVector : -FVector::UpVector);
}
else //which way to go round (left/right)
{
FVector RightVector = FVector::CrossProduct(VectorToObstacle.GetSafeNormal(), FVector::UpVector);
AvoidanceDirection = (FVector::DotProduct(RightVector, VectorToObstacle) > 0) ? RightVector : -RightVector;
}
//ObstacleSize and buffer zone
FVector Origin, Extent;
float ObstacleRadius = 0.0f;
const float AvoidanceBuffer = 300.0f;
float AvoidanceDistance = 0.0f;
//Calculate avoidance point in regard to size of object hit
TSoftObjectPtr<AActor> HitActor;
HitActor = HitResult.GetActor();
HitActor->GetActorBounds(true, Origin, Extent);
ObstacleRadius = Extent.Size();
AvoidanceDistance = ObstacleRadius + AvoidanceBuffer;
AvoidancePoint = HitResult.Location + AvoidanceDirection * AvoidanceDistance;
DrawDebugSphere(GetWorld(), AvoidancePoint, 50.0f, 5, FColor::Magenta, false, 2.5f, 0, 1.2f);
//Build Navigation 3 point
BuildNavigationPath(AvoidancePoint, EndLocation);
}
else //no object in path
{
BuildNavigationPath(FVector(0.f), EndLocation);
}
}
//Build the navigation path (spline)
void UShipNavigationSystem::BuildNavigationPath(const FVector AvoidanceLocation, const FVector ShipDestination)
{
FVector CurrentShipLocation = GetOwner()->GetActorLocation();
//Clear any previous spline points created
SplineComponent->ClearSplinePoints();
SplineComponent->UpdateSpline();
//If no defined middle point - then just A->B and add middle of two points
if (AvoidanceLocation == FVector(0.f))
{
//Halfway between A -> B
FVector MiddlePoint = (CurrentShipLocation + ShipDestination) * 0.5f;
NavigationPoints.Empty();
//Starting Point/MiddlePoint/Destination
NavigationPoints.Add(CurrentShipLocation);
NavigationPoints.Add(MiddlePoint);
NavigationPoints.Add(ShipDestination);
//Create the spline navigation route
for (const FVector& point : NavigationPoints)
{
SplineComponent->AddSplinePoint(point, ESplineCoordinateSpace::World);
}
}
else //Something is in the way... use the avoidancePoint given
{
NavigationPoints.Empty();
NavigationPoints.Add(CurrentShipLocation);
NavigationPoints.Add(AvoidanceLocation);
NavigationPoints.Add(ShipDestination);
for (const FVector& point : NavigationPoints)
{
SplineComponent->AddSplinePoint(point, ESplineCoordinateSpace::World);
}
}
SplineComponent->UpdateSpline();
//Start Moving----------------------------------------------------------------
SetupTravelTimeline();
FlightTimeline->PlayFromStart();
}
//FTimeline for Travel
void UShipNavigationSystem::SetupTravelTimeline()
{
float Distance = SplineComponent->GetSplineLength();
UCurveFloat* ShipMovementCurve = CreateCurveFloat(Distance);
//create and add the curve to the timeline
if (ShipMovementCurve)
{
//Bind timeline update and finished functions
FOnTimelineFloat MoveShipFunction;
MoveShipFunction.BindUFunction(this, FName("OnTimelineUpdate"));
FOnTimelineEvent FinishedFunction;
FinishedFunction.BindUFunction(this, FName("OnTimelineFinished"));
FlightTimeline->SetTimelineLengthMode(ETimelineLengthMode::TL_LastKeyFrame);
FlightTimeline->SetLooping(false);
FlightTimeline->AddInterpFloat(ShipMovementCurve, MoveShipFunction);
FlightTimeline->SetTimelineFinishedFunc(FinishedFunction);
}
else { UE_LOG(LogTemp, Warning, TEXT("NO TIMELINE FOUND")); }
}
//work on a timer - clear nav points and test for collision again - create new routing
void UShipNavigationSystem::NavigationUpdateCheck()
{
SplineComponent->ClearSplinePoints();
NavigateTo(GetDestination());
}
//Timeline update call
void UShipNavigationSystem::OnTimelineUpdate(float TimeValue)
{
float SplineLength = SplineComponent->GetSplineLength();
float TimeLength = TimeValue * SplineLength;
if (SplineComponent)
{
if (TimeValue == 0.5f)
{
//update navigation on a timer.
float TimeToWait = 25.f, Delay = 25.f;
if (!bHasReachedDestination)
{
if (!GetWorld()->GetTimerManager().IsTimerActive(TimerHandler))
{
GetWorld()->GetTimerManager().SetTimer(TimerHandler, this, &UShipNavigationSystem::NavigationUpdateCheck, TimeToWait, true, Delay);
}
}
else
{
GetWorld()->GetTimerManager().ClearTimer(TimerHandler);
UE_LOG(LogTemp, Warning, TEXT("NavigationTimer Cancelled. Destination reached"));
}
}
//Get the location and rotation of the spline with reference to the time of our curve (float Value)
FVector ShipsSplineLocation = SplineComponent->GetLocationAtDistanceAlongSpline(TimeLength, ESplineCoordinateSpace::World);
FRotator ShipsSplineRotation = SplineComponent->GetRotationAtDistanceAlongSpline(TimeLength, ESplineCoordinateSpace::World);
//Move the actor
AActor* Ship = GetOwner();
if (Ship)
{
Ship->SetActorLocation(ShipsSplineLocation);
Ship->SetActorRotation(ShipsSplineRotation);
UE_LOG(LogTemp, Warning, TEXT("ShipLocation_ %s"), *ShipsSplineLocation.ToString());
}
if (TimeValue == 1.0f)
{
FlightTimeline->Stop();
}
}
}
//Timeline finish
void UShipNavigationSystem::OnTimelineFinished()
{
//here we can flag for more tasking or job harvesting once at destination. need to deter what we are doing
UE_LOG(LogTemp, Warning, TEXT("FINISHED SPLINE MOVEMENT"));
bHasReachedDestination = true;
FlightTimeline->Stop();
}
FVector UShipNavigationSystem::GetDestination()
{
return Destination;
}
void UShipNavigationSystem::SetDestination(FVector EndPoint)
{
Destination = EndPoint;
}
//Create Curve Floats for flight
UCurveFloat* UShipNavigationSystem::CreateCurveFloat(const float TravelDistance)
{
//traveldistance add to ship mileage counter
//
// Time = distance / speed
float TimeToTravel = TravelDistance / DASpaceship->GetSpeed();
//Ships Curve Float
UCurveFloat* ShipCurveFloat = NewObject<UCurveFloat>(UCurveFloat::StaticClass());
//Curve Float Keys (in time /in value (length))
FKeyHandle KeyPoint1 = ShipCurveFloat->FloatCurve.AddKey(0.0f, 0.0f); //beginning
FKeyHandle KeyPoint2 = ShipCurveFloat->FloatCurve.AddKey(TimeToTravel / 2.f, 0.5f);
FKeyHandle KeyPoint3 = ShipCurveFloat->FloatCurve.AddKey(TimeToTravel, 1.0f); //ease in
//Add the keypoints to the curve
ShipCurveFloat->FloatCurve.SetKeyTangentMode(KeyPoint1, ERichCurveTangentMode::RCTM_Auto);
ShipCurveFloat->FloatCurve.SetKeyTangentMode(KeyPoint2, ERichCurveTangentMode::RCTM_Auto);
ShipCurveFloat->FloatCurve.SetKeyTangentMode(KeyPoint3, ERichCurveTangentMode::RCTM_Auto);
return ShipCurveFloat;
}