Add functions for water locomotion checks and update stepPlayerLocomotion logic

This commit is contained in:
2026-04-11 22:21:43 +02:00
parent 7ed32f6fee
commit d25c96bd8c

View File

@@ -239,6 +239,12 @@ function computePlanarSpeedFromDisplacement(
return Math.hypot(displacement.x, displacement.z) / dt;
}
function isWaterLocomotionMode(
locomotionMode: RuntimeLocomotionMode | null | undefined
): boolean {
return locomotionMode === "swimming" || locomotionMode === "diving";
}
function isShallowWater(options: {
inWater: boolean;
waterSurfaceHeight: number | null;
@@ -262,6 +268,28 @@ function isShallowWater(options: {
return waterDepth <= options.shape.eyeHeight + SWIM_HEAD_CLEARANCE;
}
function shouldPreserveAirborneWaterCrossing(options: {
inWater: boolean;
waterSurfaceHeight: number | null;
feetPosition: Vec3;
shape: FirstPersonPlayerShape;
verticalVelocity: number;
previousLocomotionState?: RuntimeLocomotionState;
}): boolean {
if (
!options.inWater ||
options.waterSurfaceHeight === null ||
options.verticalVelocity <= VERTICAL_ASCENT_EPSILON ||
isWaterLocomotionMode(options.previousLocomotionState?.locomotionMode)
) {
return false;
}
const headHeight = options.feetPosition.y + options.shape.eyeHeight;
return headHeight >= options.waterSurfaceHeight;
}
function alignPlanarMotionToGround(
planarMotion: Vec3,
groundNormal: Vec3 | null
@@ -363,7 +391,15 @@ export function stepPlayerLocomotion(
const currentSwimmableWater =
currentVolumeState.inWater &&
currentVolumeState.waterSurfaceHeight !== null &&
!currentShallowWater;
!currentShallowWater &&
!shouldPreserveAirborneWaterCrossing({
inWater: currentVolumeState.inWater,
waterSurfaceHeight: currentVolumeState.waterSurfaceHeight,
feetPosition: options.feetPosition,
shape: activeShape,
verticalVelocity: options.verticalVelocity,
previousLocomotionState: options.previousLocomotionState
});
// The probe can still see nearby floor on the frame after takeoff. While a
// jump is still carrying positive upward velocity, don't let that probe pull
// the controller back into grounded/stick-to-ground logic.
@@ -579,7 +615,15 @@ export function stepPlayerLocomotion(
const nextSwimmableWater =
nextVolumeState.inWater &&
nextVolumeState.waterSurfaceHeight !== null &&
!nextShallowWater;
!nextShallowWater &&
!shouldPreserveAirborneWaterCrossing({
inWater: nextVolumeState.inWater,
waterSurfaceHeight: nextVolumeState.waterSurfaceHeight,
feetPosition: resolvedMotion.feetPosition,
shape: activeShape,
verticalVelocity,
previousLocomotionState: options.previousLocomotionState
});
const nextWaterSurfaceHeight =
nextVolumeState.waterSurfaceHeight ??
resolvedMotion.feetPosition.y + activeShape.eyeHeight;