Mover Velocity Calculation Doesn't Match the CMC Version

We’ve been evaluating Mover 2.0 for a new project. When we did a side-by-side comparison of our character moving using the CMC walking move mode versus the Mover walking move mode, there was a big discrepancy in the movement speed, especially when the stick defection on the controller was small. I dug into the velocity calculations and noticed a small difference between the Mover and CMC versions at the point where friction is applied to the velocity.

In the CMC version, the reduction in velocity is calculated using the acceleration direction:

// UCharacterMovementComponent::CalcVelocity
 
// Friction affects our ability to change direction. This is only done for input acceleration, not path following.
const FVector AccelDir = Acceleration.GetSafeNormal();
const float VelSize = Velocity.Size();
Velocity = Velocity - (Velocity - AccelDir * VelSize) * FMath::Min(DeltaTime * Friction, 1.f);

UMovementUtils::ComputeVelocity

In the Mover version, it seems like it’s trying to do the same thing, but it uses GetClampedToMaxSize instead of GetSafeNormal which results in different behavior:

// UMovementUtils::ComputeVelocity
 
const FVector ControlAcceleration = InParams.MoveDirectionIntent.GetClampedToMaxSize(1.f);
const float AnalogInputModifier = (ControlAcceleration.SizeSquared() > 0.f ? ControlAcceleration.Size() : 0.f);
DesiredSpeed = InParams.MaxSpeed * AnalogInputModifier;
 
if (Velocity.SizeSquared() > 0.f)
{
	if (!InParams.MoveDirectionIntent.IsNearlyZero() && AnalogInputModifier > 0.f )
	{
		const FVector VelocityAlongInput = Velocity.ProjectOnTo(InParams.MoveDirectionIntent);
		const bool bExceedingMaxSpeedAlongInput = IsExceedingMaxSpeed(VelocityAlongInput, DesiredSpeed);
 
		if (!bExceedingMaxSpeedAlongInput)
		{
			// Apply change in velocity direction
			// Change direction faster than only using acceleration, but never increase velocity magnitude.
			const float TimeScale = FMath::Clamp(InParams.DeltaSeconds * InParams.TurningBoost, 0.f, 1.f);
			Velocity = Velocity + (ControlAcceleration * Velocity.Size() - Velocity) * FMath::Min(TimeScale * InParams.Friction, 1.f);
		}
	}
...

If the control acceleration magnitude is less than one, then the Mover result won’t match CMC. For our project we have tried to make Mover match the CMC version as much as possible, so we’ve put in a fix. The velocity calculation uses the control acceleration normal vector:

// UMovementUtils::ComputeVelocity
 
Velocity = Velocity + (ControlAcceleration.GetSafeNormal() * Velocity.Size() - Velocity) * FMath::Min(TimeScale * InParams.Friction, 1.f);

I don’t know if this change is on purpose, but I’m posting this here in case this is an oversight.

Thanks for bringing this up! I doubt the intention was to behave differently than CMC, though there might be a reason.

I’ll put in a ticket to evaluate the consequences of changing the math, and if it looks clear then we’ll include it in a future UE release.

Any update on this Pyro?