Linear Friction: Decelerating Player Momentum Without Altering Direction
In high-precision character controllers, the difference between a "floaty" feel and a "snappy" one often lies in how the engine handles the absence of input. A common pitfall for beginner developers is applying a generic friction force that inadvertently shifts the player's heading as they slow down, especially when floating-point errors or unintended lateral forces are at play. To achieve a professional feel, you must implement a Direction-Preserving Deceleration. This technique ensures that the velocity vector's magnitude decreases toward zero while its normalized direction remains strictly constant, providing the player with a predictable and satisfying sense of weight and control.
Table of Content
- Purpose: The Importance of Predictable Friction
- The Logic: Normalizing the Velocity Vector
- Step-by-Step: Implementing Smooth Deceleration
- Use Case: Ice Physics and Space Drifting
- Best Results: Frame-Rate Independence
- FAQ
- Disclaimer
Purpose
Controlled deceleration is vital for several core gameplay reasons:
- Game Feel (Juice): It prevents the player from stopping instantly (which feels robotic) or sliding forever (which feels uncontrollable).
- Predictability: By preserving the direction, players can accurately "aim" their slide toward a platform or through a gap.
- Technical Stability: It eliminates "jitter" that occurs when low-velocity vectors are subjected to multi-directional friction forces.
The Logic: Normalizing the Velocity Vector
To slow a player down without turning them, we treat velocity as a Vector consisting of a Direction and a Magnitude (speed).
The mathematical approach is to calculate the current speed, subtract a friction value from that speed, and then re-apply that new speed to the original, normalized direction. This bypasses the traditional velocity += friction_vector approach, which can cause "drifting" if the friction vector isn't perfectly anti-parallel to the movement.
Step-by-Step: Implementing Smooth Deceleration
1. Capture the Current Velocity
Identify the player's current velocity vector. In engines like Unity or Godot, this is typically a Vector3 or Vector2.
2. Store the Direction and Speed
Isolate the heading and the current scalar speed.
Vector3 currentDir = velocity.normalized;
float currentSpeed = velocity.magnitude;
3. Subtract Friction from Speed
Apply your deceleration constant. It is crucial to ensure the speed does not dip below zero, which would cause the player to suddenly move backward.
float deceleration = frictionValue deltaTime;
currentSpeed -= deceleration;
if (currentSpeed < 0) currentSpeed = 0;
4. Reconstruct the Velocity
Multiply the original direction by the new, lower speed.
velocity = currentDir currentSpeed;
Use Case: Ice Physics and Space Drifting
In a racing game featuring an "Ice World," the player needs to feel a lack of traction.
- The Challenge: If the player lets go of the gas, they should slide in a straight line, slowing down gradually.
- The Action: The developer applies a very low
frictionValueusing the direction-preserving method. - The Result: The car slides perfectly straight along its last known heading. If the developer had used a standard "Drag" coefficient that ignores direction, the car might have swerved slightly due to the physics engine's internal calculations for wheel friction and gravity.
Best Results
| Method | Preserves Direction? | Best For |
|---|---|---|
| Linear Interpolation (Lerp) | Yes | Smooth, "organic" stopping feel. |
| Direct Magnitude Subtraction | Yes | Predictable, constant deceleration (Braking). |
| Built-in Rigidbody Drag | Partially | General physics objects, not precise characters. |
| MoveTowards (Vector) | Yes | UI elements or cinematic character movement. |
FAQ
Should I use MoveTowards or Lerp?
Use MoveTowards if you want a constant, linear deceleration (the player slows down by 5 units every second). Use Lerp (Linear Interpolation) if you want the deceleration to be "snappier" at first and slower as they reach zero, creating a more natural ease-out effect.
Why does my player jitter when they almost stop?
This is often due to the velocity never truly reaching absolute zero. Always implement a "Sleep Threshold." If currentSpeed < 0.01, manually set the velocity to Vector3.zero to let the physics engine rest.
Does this work in 3D with gravity?
You should usually apply this logic to the X and Z axes only. Leave the Y-axis (gravity) to the standard physics engine, or you may find your player decelerating their fall mid-air in a way that feels like they are floating in syrup.
Disclaimer
The code examples provided are conceptual. Different engines handle coordinate systems (Right-handed vs. Left-handed) differently. Always multiply your friction and deceleration values by DeltaTime to ensure the behavior is the same at 60 FPS as it is at 144 FPS. March 2026.
Tags: Game_Physics, Player_Movement, Vector_Math, Character_Controller