Question about floatingPawnMovement component

Just a quick question about the velocity update in the floating pawn movement component…

In the ApplyControlInputToVelocity() function, there is a bit where it modifies the velocity BEFORE doing the actual velocity update properly. specifically this bit…


if (AnalogInputModifier > 0.f && !bExceedingMaxSpeed)
{
    // Apply change in velocity direction
    if (Velocity.SizeSquared() > 0.f)
        {
             Velocity -= (Velocity - ControlAcceleration * Velocity.Size()) * FMath::Min(DeltaTime * 8.f, 1.f);
         }
}

I’m trying to picture in my head what is happening to the vector here.So the ControlAcceleration (which is the control input vector as a local variable) is multiplied against the magnitude of the velocity then deltatime*8 or 1 (the min of the two) then this is subtracted from the velocity. Finally that whole expression is subtracted again from the velocity.

So it is ovbiously some kind of minor adjustment. Just don’t understand why especially as we are doing the main update at the end of the whole function. I get that this bit happens OR the deceleration happens. Also what is the magic number of deltaTime * 8?

I guess this might be a question for Zak?

Also wouldn’t the ControlAcceleration have to be normalized if we are multiplying against the vel.Size?

Good questions, that is a complicated line of code :slight_smile: Hopefully I can clarify:

Basically the code in question applies some extra force to change the direction in line with acceleration, but never increases the magnitude.

First off, ControlAcceleration is clamped to a max magnitude of 1.0 right above, so ControlAcceleration * Velocity.Size() will never exceed the Velocity magnitude.



const FVector ControlAcceleration = GetPendingInputVector().GetClampedToMaxSize(1.f);


The hardcoded “8.f” is the “ability to change”, with higher values increasing the rate of change. It really should be a variable and not hard-coded, I will change that. Clamping to a max of 1.0 just keeps us from exceeding the initial magnitude.

It might be easier to parse if written like so (which is equivalent, but has more operations):



float dt = FMath::Min(DeltaTime * 8.f, 1.f); // never above 1
Velocity = Velocity - (Velocity * dt) + (ControlAcceleration * Velocity.Size() * dt);


If you draw out some simple vectors using this form, it might be easier to understand. You take the Velocity vector, pull back a bit (potentially all the way to a velocity of zero). From there you add a vector in the direction of acceleration by the same amount you pulled back.

Consider the simplest case, which is that ControlAcceleration and Velocity point in the same direction. In that case Velocity - ControlAcceleration * Velocity.Size() equals 0, and Velocity doesn’t change at all. In the case that ControlAcceleration is exactly opposite the current direction, the change is scaled by time and the constant factor, but never allowed to exceed the current magnitude. In the case that you are moving forward and strafe right or left, you get a lateral change equal to the final term above. If “dt” was really high, you just set your velocity in the exact direction of acceleration, with current velocity magnitude.

Again it’s essential to note that we never increase the magnitude using this approach.

Without this code (essentially changing that 8.f to zero), you just change velocity with the later code that applies acceleration to velocity. If you try that you’ll feel like you are drifting a lot, and changes in direction are slow, more like you are floating in space and applying a thruster. However if you want it like that, then you’d set this turning force to zero (which will be easier once I expose it as a variable).

For reference this is also almost identical to the code in UCharacterMovementComponent::CalcVelocity():



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


We use the Friction setting there as an indicator of your ability to change direction.

Perfect timing , thank you both !

Thanks so much zak that does totally make sense. I probably would’ve figured it out when I get round to making a basic movement component and see it drifting around. The ‘turning force’ is something I can visualize now. Actually for my purposes I’m going to be implementing a character that can turn round and go in the opposite direction with the same (or slightly modified) magnitude. So its nice to know where/how/why we would want to inject some update before the actual acceleration update.

My goal is do do a SimpleCharacterController. I will mimic the basic flow of the charactermovementcomponent (startNewPhysics etc with the MovementMode) and be designed for games that don’t require all the network checking stuff. Mainly for educational purposes.

Cheers
Dan