Utilizing move_toward individually on every axis will cut back each x and z towards 0 on the identical charge till one in all them reaches the vacation spot, leaving the remaining velocity pointing parallel to the remaining axis.
Say we began with velocity.x = 2 and velocity.z = 4, and DEACC = 1, then as we tick via our deceleration step-by-step, our bearing relative to the z axis adjustments:
| step | velocity.x | velocity.z | bearing |
|---|---|---|---|
| 0 | 2 | 4 | 26.6° |
| 1 | 1 | 3 | 18.4° |
| 2 | 0 | 2 | 0° |
| 3 | 0 | 1 | 0° |
| 4 | 0 | 0 | — |
You may see above how the course of the remaining velocity shifts every tick till just one axis nonetheless has a non-zero worth.
What you need to do is apply the deceleration to each parts collectively:
# Isolate horizontal parts (so we do not sluggish a fall)
var vel2d := Vector2(velocity.x, velocity.z)
# Step the entire vector DEACC items towards (0, 0)
vel2d = vel2d.move_toward(Vector3.ZERO, DEACC)
# Unpack again into the unique 3D velocity (preserving y)
velocity.x = vel2d.x
velocity.z = vel2d.y
That is equal to making use of the scalar move_toward technique to the vector’s size:
# Magnitude of horizontal parts = floor velocity
var oldSpeed := sqrt(velocity.x*velocity.x + velocity.z*velocity.z)
# If not shifting, we're carried out (avoids division by zero)
if oldSpeed > 0:
# Decelerate the velocity
var newSpeed := move_toward(oldSpeed, 0, DEACC)
# Apply the identical scale issue to each x and z
var scale := newSpeed / oldSpeed
velocity.x *= scale
velocity.z *= scale
As a result of x and z change in the identical ratio every tick, we protect the course velocity is pointing in, eliminating the curving you had been observing. These options additionally guarantee your deceleration is constant in each course, fairly than decelerating extra sharply when shifting on a forty five° diagonal (if you cut back velocity by DEACC on every axis, or sqrt(2)*DEACC total, about 41.4% sharper than decelerating on a single axis).
The options above protect the linear deceleration utilized in your query. This operates as if the braking drive was coming from a twine hooked up to a weight on a pulley, making use of a continuing pull backward till we come to a cease and we let go of the twine (to keep away from persevering with to speed up backward towards the pulley). Which means there is a jerk proper on the finish as we come to a cease and launch the twine: our acceleration adjustments from the fixed DEACC per tick to nothing in a discontinuous bounce.
Many actual methods will as a substitute exhibit non-linear deceleration, the place the decelerating drive is proportional to the present velocity (as a result of a faster-moving object creates extra friction with the bottom/air, shedding extra power in the identical timespan). You may discover this feels extra pure, as a result of it avoids the sharp jerk on the finish.
A standard approach that is carried out is with a easy “exponential ease-out”:
# Tune inertia to your liking; 0 = prompt cease, 1 = no velocity loss
var inertia := 0.9
velocity.x *= inertia
velocity.z *= inertia
The draw back with that is that it mathematically by no means reaches zero – it simply retains getting nearer. To keep away from an annoying slight drift, you’ll be able to clamp the velocity to zero as soon as the bottom velocity is “sufficiently small”.
The convenience-out method is acceptable for one thing like a puck sliding on a floor or a rolling car, however the linear method could arguably be higher for a personality with legs strolling / operating, or for making the controls really feel tight and predictable – attempt every one and see what feels greatest on your sport.
