Root motion not following the correct motion

Hello,

I apologise if this is a dumb question but I am losing my mind for the last few day on this. If any of you have any lead on this it would be much appreciated.
Basically I have this animation of climbing up a ledge exported from Blender and when playing it using root motion enabled, it move my character (not teleport) very far away from the location it is suppose to be.

Actual result (root motion enabled):

Expected result (obtained by disabling root motion):

I am also using a custom movement mode here is the part handling root motion:

// Inside CustomPhys
if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
{
    Velocity = (UpdatedComponent->GetComponentLocation() - oldLocation) / deltaTime;
}
else
{
    FTransform rootMotionTransform = RootMotionParams.GetRootMotionTransform();
    FVector newLocation = UpdatedComponent->GetComponentLocation() + 
    rootMotionTransform.GetTranslation();
    MoveUpdatedComponent(newLocation, UpdatedComponent->GetComponentRotation(), true);
}

A few more precision:

  • The parameter of the animation are all the default one expect for the blend out time and enabling root motion
  • I tried importing/exporting this animation many time for the same result
  • I have another animation using root motion which work without any problem and is using the same export/import setting for both of them
  • It is the root bone which is animated and not the hip
  • I am in v5.3.2

Hello,

Just to be on the safe side, can you make the character’s Capsule Component visible at all times? (Ensure “Hidden In Game?” is set to FALSE) I want to see if it’s actually moving with the mesh.

If it isn’t, then your animation is NOT supported by root motion. (This is especially true if you got the animation from Mixamo.)

Thanks for your time.


I apologise it’s a bit hard to see, but the capsule is following the character.

Here’s what it look like in Blender:

I am moving the armature which is the root bone of my skeletal mesh, it’s named “Object Transform” in the graph editor.
I believe is it the correct way to do it ?

Also I forgot to add but I tried to enable “Use Normalized Root Motion Scale” but it make the character go 3 times furthers forward than when it’s disabled.

I need more information.

Can you upload a link to a video showing the entire sequence (the whole scene, including how the character got up there) so I’m not inputting incorrect assumptions of what’s offscreen?

I need to see the capsule from start to finish, I also need to see where the wall is after the character attempts to scale it.

I also need to see the Blueprint of this function (unless you’re using C++; but my C++ is very rusty right now).

With regard to Blender, I am not an animator, so I do not know its ins and outs. I’m assuming you did things correctly on that end, which is why I’m focusing on what’s happening in Unreal Engine.

Also, if you could show what this animation does without the wall, as well as display the Capsule Component’s world position (you can use a print string attached to a tick function to track its world location).

I tried to make a video with what you need, please tell me if you need more input in it since I am not very familiar with this.

I play the animation at the end of the video and I actually never try to play montage outside of climbing so it looks like the root motion work as attended and the issue comme from my code instead ? If this is the case i feel very dumb to screeming at the wrong tree for so long.

I am using C++ which basically shoot a line trace in front of the character and if it hit a surface which can be climb, it pass my movement mode to a custom climbing mode. When climbing it check if the character is at the top or bottom of the wall. If it’s at the top it will stop the climbing move mode and play the anim montage of climbing the ledge.

My custom physic:

void UHoloCharacterMovementComponent::PhysClimbing(float deltaTime, int32 Iterations)
{
	if (deltaTime < MIN_TICK_TIME)
	{
		return;
	}
//Calculate surfaces normals
	ComputeSurfaceInfo();

	if (ShouldStopClimbing() || ClimbDownToFloor())
	{
		// Stop climbing
		wantsToClimb = false;
		SetMovementMode(EMovementMode::MOVE_Falling);
		StartNewPhysics(deltaTime, Iterations);

		return;
	}

//Calculate velocity
	RestorePreAdditiveRootMotionVelocity();

	if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
	{
		constexpr float friction = 0.0f;
		constexpr bool bFluid = false;

		CalcVelocity(deltaTime, friction, false, brakingDecelerationClimbing);
	}

	ApplyRootMotionToVelocity(deltaTime);

	const FVector oldLocation = UpdatedComponent->GetComponentLocation();

	MoveAlongClimbingSurface(deltaTime);

	TryClimbUpLedge();

	if (!HasAnimRootMotion() && !CurrentRootMotion.HasOverrideVelocity())
	{
		Velocity = (UpdatedComponent->GetComponentLocation() - oldLocation) / deltaTime;
	}
	else
	{
		FTransform rootMotionTransform = RootMotionParams.GetRootMotionTransform();

		FVector newLocation = UpdatedComponent->GetComponentLocation() + rootMotionTransform.GetTranslation();

		MoveUpdatedComponent(newLocation, UpdatedComponent->GetComponentRotation(), true);
	}

	SnapToClimbingSurface(deltaTime);
}

The code which check if the character can play the climb up ledge montage:

bool UHoloCharacterMovementComponent::TryClimbUpLedge() const
{
	if (ownerHoloCharacter->IsPlayingClimbUpLedgeMontage())
	{
		return false;
	}

	const float upSpeed = FVector::DotProduct(Velocity, UpdatedComponent->GetUpVector());
	const bool isMovingUp = upSpeed >= maxClimbingSpeed / 3;

	if (isMovingUp && HasReachedEdge() && CanMoveToLedgeClimbLocation())
	{
		const FRotator standRotation = FRotator(0, UpdatedComponent->GetComponentRotation().Yaw, 0);
		UpdatedComponent->SetRelativeRotation(standRotation);

		ownerHoloCharacter->PlayClimbUpLedgeMontage();

		return true;
	}

	return false;
}

bool UHoloCharacterMovementComponent::HasReachedEdge() const
{
	const UCapsuleComponent* capsule = CharacterOwner->GetCapsuleComponent();
	const float traceDistance = capsule->GetUnscaledCapsuleRadius() * 2.5f;

	return !EyeHeightTrace(traceDistance);
}

bool UHoloCharacterMovementComponent::IsLocationWalkable(const FVector& checkLocation) const
{
	const FVector checkEnd = checkLocation + (FVector::DownVector * 250.f);

	DrawDebugLine(GetWorld(), checkLocation, checkEnd, FColor::Green, false, 30000, 0, 1);

	FHitResult ledgeHit;
	const bool bHitLedgeGround = GetWorld()->LineTraceSingleByChannel(ledgeHit, checkLocation, checkEnd,
		ECC_WorldStatic, climbColQueryParams);

	return bHitLedgeGround && ledgeHit.Normal.Z >= GetWalkableFloorZ();
}

bool UHoloCharacterMovementComponent::CanMoveToLedgeClimbLocation() const
{
	const FVector verticalOffset = FVector::UpVector * 160.f;
	const FVector horizontalOffset = UpdatedComponent->GetForwardVector() * 120.f;

	const FVector checkLocation = UpdatedComponent->GetComponentLocation() + horizontalOffset + verticalOffset;

	if (!IsLocationWalkable(checkLocation))
	{
		return false;
	}

	FHitResult capsuleHit;
	const FVector capsuleStartCheck = checkLocation - horizontalOffset;
	const UCapsuleComponent* capsule = CharacterOwner->GetCapsuleComponent();

	const bool blocked = GetWorld()->SweepSingleByChannel(capsuleHit, capsuleStartCheck, checkLocation,
		FQuat::Identity, ECC_WorldStatic, capsule->GetCollisionShape(), climbColQueryParams);

	return !blocked;
}

Correct. That’s why I needed you to test all those conditions. It’s definitely your code, not the animation itself.

It has something to do with your physics. I don’t play with physics, so I’m not much help when it comes to adjusting them, however, what I suggest is that you disable any of your custom physics for now, and see if the animation can play normally.

It looks like:

A) You’ve stored a value that has an additive or multiplicative affect on the Capsule’s final position…

And/Or

B) Your character is clipping into the cliff, so Unreal is bouncing you out…

And/Or

C) You have “Can Step On?” set to FALSE in the static mesh settings of the cliff…

To fix B and C (if they apply), you can disable collision for them, temporarily to rule these out as possible agitators.

I actually understand your code, but I don’t know what all those functions do.

And the right answer was A, I was telling my character to apply the velocity of the root motion while moving the character to the location of the root motion at the same time so they both got added together.

Yeah… I feel very stupid right now but anyway thanks for your time.