Godot Version
4.4 stable
Question
So, I’m currently working on my player’s movement and overall game feel. I got acceleration working nicely, but deceleration is a bit trickier.
I use a CharacterBody3D
node as the root of the player in order to avoid conflicts with non-deterministic physics over the network (client and server desync). It works well.
However, all physics must be implemented with code according to the documentation. I’m fine with this, however, I am not proficient in physics logic and especially math. So I’m a little stumped on how I can implement deceleration.
Basic Concept:
(All the code is below this basic explanation)
The movement action is split into two functions:
start_moving
andstop_moving
It’s done like this in order to control the exact reaction when the player presses or lets go of the movement input. (I’m also used to this implementation from Unreal 4/5)
When the player presses a movement input, start_moving
will trigger. This will add a force to the player’s velocity based on a Current_Movement_Speed
variable, which is then added to every physics tick via the MOVEMENT_ACCELERATION_RATE
.
This works wonders. The player feels really nice to control now.
For deceleration however, I want it to work a bit differently.
When the player has no movement input buttons pressed, stop_moving
will trigger. What I want to happen is the following:
- The player’s current velocity is slowed down every physics tick by the
MOVEMENT_DECELERATION_RATE
constant(s). - If the player is on the ground, the deceleration rate is quick.
- If the player is in the air, there is barely any deceleration.
This makes it so the player doesn’t come to a instant halt when the movement inputs are released mid-air and allows the player to carry momentum via jumping.
However, I do not know how to implement deceleration. When the movement inputs are released, the velocity added from start_moving
instantly vanishes.
I could store the current velocity, but I’m not 100% sure where to put that in my logic.
(By the way, the input code works and the logic that determines if the player is airborne works)
Here’s all the code below.
Movement Variables and Constants:
# Movement
var Current_Movement_Speed : float = 0.0
const MAX_MOVEMENT_SPEED : float = 50.0
const MIN_MOVEMENT_SPEED : float = 0.0
const MOVEMENT_ACCELERATION_RATE : float = 5.0
const MOVEMENT_DECELERATION_RATE_GROUNDED : float = 2.5
const MOVEMENT_DECELERATION_RATE_AIRBORNE : float = 0.001
start_moving
func start_moving(input:Vector2) -> void:
# This trigger when the player starts moving
# Works by having a small base speed,-
# -then increasing that speed via acceleration
# Set moving to true
Is_Moving = true
# Get the movement direction and normalize it
var Movement_Direction:Vector3 = Global_Rotation_Pivot.basis \
* Vector3(input.x, 0, input.y).normalized()
# Movement acceleration (Advances every physics tick)
if Current_Movement_Speed < MAX_MOVEMENT_SPEED:
Current_Movement_Speed = Current_Movement_Speed + MOVEMENT_ACCELERATION_RATE
# Movement force
velocity.x = Movement_Direction.x * Current_Movement_Speed
velocity.z = Movement_Direction.z * Current_Movement_Speed
stop_moving
func stop_moving() -> void:
# This triggers when the player stops moving.
# Set is_moving to false
Is_Moving = false
# Movement Deceleration (DOES NOT WORK)
if Current_Movement_Speed > MIN_MOVEMENT_SPEED:
if Is_Airborne == false:
Current_Movement_Speed = Current_Movement_Speed - MOVEMENT_DECELERATION_RATE_GROUNDED
elif Is_Airborne == true:
Current_Movement_Speed = Current_Movement_Speed - MOVEMENT_DECELERATION_RATE_AIRBORNE
What I think is happening is the Current_Movement_Speed
variable isn’t the cause of the player’s velocity. The variable just controls how much velocity is gained. But I can’t find a way to store it currently.
I would appreciate any help I could get on this and maybe some more info about Godot’s 3D physics as well.