MoveComponentTo does not behave the same when called from BP vs C++ (Video)

[UE 5.3.2] The first half of the video is UKismetSystemLibrary::MoveComponentTo being called by an actor in its blueprints during the BeginPlay function. The second half of the video is that same function being called in a C++ ActorComponent. What gives with the foot behavior?

I initialize my latent data in BeginPlay() in c++ like so:

MovementHandle.CallbackTarget = this;
MovementHandle.ExecutionFunction = "OnMoveComplete"; // NOTE! This function must be declared with a UFUNCTION() macro
MovementHandle.Linkage = 0;
MovementHandle.UUID = 123;

This is my function that calls MoveComponentTo. Although, I’ve also tried calling it outside a timer, and it doesn’t change anything.

void UMovingPlatform::AnimateToPosition(int Index)
{
	PositionIndex = Index;
	// Schedule the function to be executed after the delay
	GetWorld()->GetTimerManager().SetTimer(TimerHandle, [&]()
	{
		// Calculate duration
		LastRecordedDuration = Duration;
		if (bIsMovementSpeedBased)
		{
			float d = FVector::Dist(RuntimePositionTargets[PositionIndex].Location, TargetComponent->GetRelativeLocation());
			if (d > 0.1f) LastRecordedDuration = d/Duration; // Duration is being treated as Rate in this instance
		}
		// Begin moving
		MovementStartTime = GetWorld()->GetTimeSeconds();
		UKismetSystemLibrary::MoveComponentTo(TargetComponent, RuntimePositionTargets[PositionIndex].Location, RuntimePositionTargets[PositionIndex].Rotation, true, true, LastRecordedDuration, true, EMoveComponentAction::Move, MovementHandle);
	}, WaitTime, false);
}

Update!

It appears the issue is less about BP vs C++ but rather running the code in an ActorComponent vs an Actor.

I created a BP AC and achieved the character jitter issues as shown in the video. So the only time it works is when the code is ran as a part of an actor. I wonder if there’s a way around this, because I really want to use an AC.

Alright! Thanks to ToyB-Chan on Discord, I understand exactly what’s going on. There are some nuances worth going over, and it’s all thanks to ToyB-Chan rather than my own research.

  1. Every actor ticks before their components tick. This is true as long as their tick groups match. Otherwise, the tick group decides which one ticks first. (All actors default to PrePhysics, the earliest it can be)

  2. Blueprints process their latent actions on their tick. Everything else gets processed between PostPhysics and PostUpdateWork.

Hopefully from that alone, you can start to see what’s going on. So by default, when you create a latent action and bind the CallbackTarget to the ActorComponent, it will be executed between PostPhysics and PostUpdateWork. Keeping in mind that a character’s actor’s tick and CMC tick will likely be PrePhysics, the character and its CMC will update before our ActorComponent’s latent action. That’s what causes the jitter.

In order to achieve the seamless character riding, the latent action needs to be bound to the Actor NOT the ActorComponent. This is to ensure our latent action updates before our character and its CMC.

In a blueprint ActorComponent, you are more or less out of luck. You can’t control directly how the latent action is bound in BP.

In a C++ ActorComponent, you can control the binding.

MovementHandle.CallbackTarget = GetOwner();

And as simple as that, it’s seamless! The downside is that you lose a callback to a function in the ActorComponent. However, you can get around this by creating a new timer and running it parallel to your move latent action. See the original post at the top for a code example of a simple timer.

1 Like