From 5ae2b3f18c957dd0117d448aeddb34edc7d34b6b Mon Sep 17 00:00:00 2001 From: Victor Giers Date: Sat, 11 Apr 2026 21:43:09 +0200 Subject: [PATCH] Add previous planar displacement to player locomotion options and update motion computation logic --- src/runtime-three/player-locomotion.ts | 37 +++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/src/runtime-three/player-locomotion.ts b/src/runtime-three/player-locomotion.ts index 34799a99..f4cf0ad4 100644 --- a/src/runtime-three/player-locomotion.ts +++ b/src/runtime-three/player-locomotion.ts @@ -76,6 +76,7 @@ export interface StepPlayerLocomotionOptions { standingShape: FirstPersonPlayerShape; verticalVelocity: number; previousLocomotionState?: RuntimeLocomotionState; + previousPlanarDisplacement: Vec3; jumpBufferRemainingMs: number; coyoteTimeRemainingMs: number; jumpHoldRemainingMs: number; @@ -226,6 +227,17 @@ function computePlanarMotion( }; } +function computePlanarSpeedFromDisplacement( + displacement: Vec3, + dt: number +): number { + if (dt <= 0) { + return 0; + } + + return Math.hypot(displacement.x, displacement.z) / dt; +} + function alignPlanarMotionToGround( planarMotion: Vec3, groundNormal: Vec3 | null @@ -397,12 +409,27 @@ export function stepPlayerLocomotion( ? groundedRequestedPlanarSpeed : airborneRequestedPlanarSpeed : groundedRequestedPlanarSpeed; - const planarMotion = computePlanarMotion( + const planarMotionFromInput = computePlanarMotion( options.movementYawRadians, options.input, requestedPlanarSpeed, options.dt ); + const preserveAirborneMomentum = + activeShape.mode !== "none" && + !currentVolumeState.inWater && + (jumpTriggered || !currentlyGrounded) && + planarMotionFromInput.inputMagnitude <= 0; + const planarMotion = preserveAirborneMomentum + ? { + motion: { + x: options.previousPlanarDisplacement.x, + y: 0, + z: options.previousPlanarDisplacement.z + }, + inputMagnitude: 0 + } + : planarMotionFromInput; const groundedPlanarMotion = currentlyGrounded && !jumpTriggered @@ -527,8 +554,12 @@ export function stepPlayerLocomotion( crouched, sprinting, inputMagnitude: planarMotion.inputMagnitude, - requestedPlanarSpeed: - requestedPlanarSpeed * planarMotion.inputMagnitude, + requestedPlanarSpeed: preserveAirborneMomentum + ? computePlanarSpeedFromDisplacement( + options.previousPlanarDisplacement, + options.dt + ) + : requestedPlanarSpeed * planarMotion.inputMagnitude, planarSpeed: actualPlanarSpeed, verticalVelocity, contact: resolveContactState(resolvedMotion, groundProbe, grounded)