auto-git:

[change] src/runtime-three/player-climbing.ts
This commit is contained in:
2026-04-30 00:32:46 +02:00
parent 1c5e75b5ec
commit 2b7b1b61f3

View File

@@ -14,6 +14,8 @@ import type {
} from "./runtime-scene-build"; } from "./runtime-scene-build";
export const CLIMB_INPUT_ACTIVE_THRESHOLD = 0.5; export const CLIMB_INPUT_ACTIVE_THRESHOLD = 0.5;
export const CLIMB_AUTO_LATCH_INPUT_THRESHOLD = 0.25;
export const CLIMB_AUTO_LATCH_MIN_INTO_SURFACE_DOT = 0.35;
export const CLIMB_SPEED_METERS_PER_SECOND = 2.4; export const CLIMB_SPEED_METERS_PER_SECOND = 2.4;
export const CLIMB_DETECT_DISTANCE_METERS = 0.85; export const CLIMB_DETECT_DISTANCE_METERS = 0.85;
export const CLIMB_KEEP_DISTANCE_METERS = 1.05; export const CLIMB_KEEP_DISTANCE_METERS = 1.05;
@@ -187,25 +189,97 @@ export function isClimbableWallNormal(normal: Vec3): boolean {
export function shouldEnterClimbing(options: { export function shouldEnterClimbing(options: {
climbInput: number; climbInput: number;
movementIntoSurface: boolean;
surface: RuntimePlayerClimbSurface | null; surface: RuntimePlayerClimbSurface | null;
jumpPressed: boolean; jumpPressed: boolean;
}): boolean { }): boolean {
return ( return (
options.climbInput > CLIMB_INPUT_ACTIVE_THRESHOLD &&
options.surface !== null && options.surface !== null &&
!options.jumpPressed !options.jumpPressed &&
(options.climbInput > CLIMB_INPUT_ACTIVE_THRESHOLD ||
options.movementIntoSurface)
); );
} }
export function shouldExitClimbing(options: { export function shouldExitClimbing(options: {
climbInput: number;
surface: RuntimePlayerClimbSurface | null; surface: RuntimePlayerClimbSurface | null;
jumpPressed: boolean; jumpPressed: boolean;
}): boolean { }): boolean {
return options.surface === null || options.jumpPressed;
}
export function resolveClimbPlanarInputDirection(
input: PlayerStartActionInputState,
movementYawRadians: number
): { direction: Vec3 | null; inputMagnitude: number } {
const inputX = input.moveRight - input.moveLeft;
const inputZ = input.moveForward - input.moveBackward;
const rawMagnitude = Math.hypot(inputX, inputZ);
const inputMagnitude = Math.min(1, rawMagnitude);
if (rawMagnitude <= VECTOR_EPSILON) {
return {
direction: null,
inputMagnitude
};
}
const normalizedInputX = inputX / rawMagnitude;
const normalizedInputZ = inputZ / rawMagnitude;
const forwardX = Math.sin(movementYawRadians);
const forwardZ = Math.cos(movementYawRadians);
const rightX = -Math.cos(movementYawRadians);
const rightZ = Math.sin(movementYawRadians);
const directionX =
forwardX * normalizedInputZ + rightX * normalizedInputX;
const directionZ =
forwardZ * normalizedInputZ + rightZ * normalizedInputX;
const directionMagnitude = Math.hypot(directionX, directionZ);
if (directionMagnitude <= VECTOR_EPSILON) {
return {
direction: null,
inputMagnitude
};
}
return {
direction: {
x: directionX / directionMagnitude,
y: 0,
z: directionZ / directionMagnitude
},
inputMagnitude
};
}
export function isClimbMovementIntoSurface(options: {
input: PlayerStartActionInputState;
movementYawRadians: number;
surface: RuntimePlayerClimbSurface | null;
}): boolean {
if (options.surface === null) {
return false;
}
const inputDirection = resolveClimbPlanarInputDirection(
options.input,
options.movementYawRadians
);
if (
inputDirection.direction === null ||
inputDirection.inputMagnitude < CLIMB_AUTO_LATCH_INPUT_THRESHOLD
) {
return false;
}
const normal = normalizeVec3(options.surface.normal);
return ( return (
options.climbInput <= CLIMB_INPUT_ACTIVE_THRESHOLD || normal !== null &&
options.surface === null || dotVec3(inputDirection.direction, normal) <=
options.jumpPressed -CLIMB_AUTO_LATCH_MIN_INTO_SURFACE_DOT
); );
} }