Enhance climbing mechanics with detailed input and surface detection

This commit is contained in:
2026-04-30 00:33:13 +02:00
parent 2b7b1b61f3
commit c6091cb31c

View File

@@ -15,6 +15,8 @@ import {
CLIMB_INPUT_ACTIVE_THRESHOLD, CLIMB_INPUT_ACTIVE_THRESHOLD,
CLIMB_SPEED_METERS_PER_SECOND, CLIMB_SPEED_METERS_PER_SECOND,
computeClimbPlaneMovement, computeClimbPlaneMovement,
isClimbMovementIntoSurface,
resolveClimbPlanarInputDirection,
shouldEnterClimbing, shouldEnterClimbing,
shouldExitClimbing, shouldExitClimbing,
type RuntimePlayerClimbSurface type RuntimePlayerClimbSurface
@@ -425,7 +427,10 @@ export class FirstPersonNavigationController implements NavigationController {
this.publishTelemetry(); this.publishTelemetry();
} }
private resolveClimbProbeDirection(movementYawRadians: number): Vec3 { private resolveClimbProbeDirection(
movementYawRadians: number,
inputState: ReturnType<typeof resolvePlayerStartActionInputs>
): Vec3 {
if (this.climbSurface !== null) { if (this.climbSurface !== null) {
return { return {
x: -this.climbSurface.normal.x, x: -this.climbSurface.normal.x,
@@ -434,6 +439,15 @@ export class FirstPersonNavigationController implements NavigationController {
}; };
} }
const inputDirection = resolveClimbPlanarInputDirection(
inputState,
movementYawRadians
);
if (inputDirection.direction !== null) {
return inputDirection.direction;
}
return { return {
x: Math.sin(movementYawRadians), x: Math.sin(movementYawRadians),
y: 0, y: 0,
@@ -491,22 +505,27 @@ export class FirstPersonNavigationController implements NavigationController {
const climbPressed = inputState.climb > CLIMB_INPUT_ACTIVE_THRESHOLD; const climbPressed = inputState.climb > CLIMB_INPUT_ACTIVE_THRESHOLD;
const jumpPressed = inputState.jump > CLIMB_INPUT_ACTIVE_THRESHOLD; const jumpPressed = inputState.jump > CLIMB_INPUT_ACTIVE_THRESHOLD;
if (!climbPressed) {
this.climbLatchBlocked = false;
}
const climbSurface = const climbSurface =
this.context.resolvePlayerClimbSurface?.( this.context.resolvePlayerClimbSurface?.(
this.feetPosition, this.feetPosition,
this.resolveClimbProbeDirection(movementYawRadians), this.resolveClimbProbeDirection(movementYawRadians, inputState),
this.standingPlayerShape, this.standingPlayerShape,
this.climbSurface this.climbSurface
) ?? null; ) ?? null;
const movementIntoSurface = isClimbMovementIntoSurface({
input: inputState,
movementYawRadians,
surface: climbSurface
});
const climbIntentActive = climbPressed || movementIntoSurface;
if (!climbIntentActive) {
this.climbLatchBlocked = false;
}
if ( if (
this.climbSurface !== null && this.climbSurface !== null &&
shouldExitClimbing({ shouldExitClimbing({
climbInput: inputState.climb,
surface: climbSurface, surface: climbSurface,
jumpPressed jumpPressed
}) })
@@ -572,10 +591,6 @@ export class FirstPersonNavigationController implements NavigationController {
return true; return true;
} }
if (climbSurface === null && climbPressed) {
this.climbLatchBlocked = true;
}
return false; return false;
} }
@@ -584,6 +599,7 @@ export class FirstPersonNavigationController implements NavigationController {
(this.climbLatchBlocked || (this.climbLatchBlocked ||
!shouldEnterClimbing({ !shouldEnterClimbing({
climbInput: inputState.climb, climbInput: inputState.climb,
movementIntoSurface,
surface: climbSurface, surface: climbSurface,
jumpPressed jumpPressed
})) }))