auto-git:

[change] src/runtime-three/first-person-navigation-controller.ts
This commit is contained in:
2026-05-01 16:46:05 +02:00
parent 6d57ead353
commit 9f4471a260

View File

@@ -22,8 +22,10 @@ import {
type RuntimePlayerClimbSurface type RuntimePlayerClimbSurface
} from "./player-climbing"; } from "./player-climbing";
import { import {
resolvePlayerLedgeGrabTarget,
resolvePlayerEdgeAssistTopOut, resolvePlayerEdgeAssistTopOut,
shouldAttemptPlayerEdgeAssist shouldAttemptPlayerEdgeAssist,
type RuntimePlayerLedgeGrabTarget
} from "./player-edge-assist"; } from "./player-edge-assist";
import { import {
createIdleRuntimeLocomotionState, createIdleRuntimeLocomotionState,
@@ -60,6 +62,10 @@ function toEyePosition(feetPosition: Vec3, eyeHeight: number): Vec3 {
}; };
} }
function dotPlanarVec3(left: Vec3, right: Vec3): number {
return left.x * right.x + left.z * right.z;
}
function cloneRuntimePlayerMovement( function cloneRuntimePlayerMovement(
movement: RuntimePlayerMovement movement: RuntimePlayerMovement
): RuntimePlayerMovement { ): RuntimePlayerMovement {
@@ -141,6 +147,7 @@ export class FirstPersonNavigationController implements NavigationController {
private jumpHoldRemainingMs = 0; private jumpHoldRemainingMs = 0;
private climbSurface: RuntimePlayerClimbSurface | null = null; private climbSurface: RuntimePlayerClimbSurface | null = null;
private climbLatchBlocked = false; private climbLatchBlocked = false;
private ledgeGrabTarget: RuntimePlayerLedgeGrabTarget | null = null;
activate(ctx: RuntimeControllerContext): void { activate(ctx: RuntimeControllerContext): void {
this.context = ctx; this.context = ctx;
@@ -247,6 +254,7 @@ export class FirstPersonNavigationController implements NavigationController {
this.jumpHoldRemainingMs = 0; this.jumpHoldRemainingMs = 0;
this.climbSurface = null; this.climbSurface = null;
this.climbLatchBlocked = false; this.climbLatchBlocked = false;
this.ledgeGrabTarget = null;
this.previousTelemetry = null; this.previousTelemetry = null;
ctx.setRuntimeMessage(null); ctx.setRuntimeMessage(null);
ctx.setPlayerControllerTelemetry(null); ctx.setPlayerControllerTelemetry(null);
@@ -291,6 +299,7 @@ export class FirstPersonNavigationController implements NavigationController {
this.jumpHoldRemainingMs = 0; this.jumpHoldRemainingMs = 0;
this.climbSurface = null; this.climbSurface = null;
this.climbLatchBlocked = false; this.climbLatchBlocked = false;
this.ledgeGrabTarget = null;
} }
update(dt: number): void { update(dt: number): void {
@@ -321,6 +330,12 @@ export class FirstPersonNavigationController implements NavigationController {
); );
} }
if (
this.stepLedgeGrab(dt, inputState, playerMovement, this.yawRadians)
) {
return;
}
if ( if (
this.stepClimbing(dt, inputState, playerMovement, this.yawRadians) this.stepClimbing(dt, inputState, playerMovement, this.yawRadians)
) { ) {
@@ -376,7 +391,7 @@ export class FirstPersonNavigationController implements NavigationController {
inputState, inputState,
this.yawRadians this.yawRadians
); );
const edgeAssist = const shouldTryEdgeAssist =
edgeInputDirection.direction !== null && edgeInputDirection.direction !== null &&
shouldAttemptPlayerEdgeAssist({ shouldAttemptPlayerEdgeAssist({
enabled: playerMovement.edgeAssist.enabled, enabled: playerMovement.edgeAssist.enabled,
@@ -387,7 +402,9 @@ export class FirstPersonNavigationController implements NavigationController {
planarSpeed: locomotionStep.locomotionState.planarSpeed, planarSpeed: locomotionStep.locomotionState.planarSpeed,
collisionCount: locomotionStep.locomotionState.contact.collisionCount, collisionCount: locomotionStep.locomotionState.contact.collisionCount,
airborne: locomotionStep.locomotionState.locomotionMode === "airborne" airborne: locomotionStep.locomotionState.locomotionMode === "airborne"
}) });
const edgeAssist =
shouldTryEdgeAssist && edgeInputDirection.direction !== null
? resolvePlayerEdgeAssistTopOut({ ? resolvePlayerEdgeAssistTopOut({
feetPosition: locomotionStep.feetPosition, feetPosition: locomotionStep.feetPosition,
shape: locomotionStep.activeShape, shape: locomotionStep.activeShape,
@@ -409,6 +426,40 @@ export class FirstPersonNavigationController implements NavigationController {
} }
}) })
: null; : null;
if (
edgeAssist === null &&
shouldTryEdgeAssist &&
edgeInputDirection.direction !== null
) {
const ledgeGrabTarget = resolvePlayerLedgeGrabTarget({
feetPosition: locomotionStep.feetPosition,
shape: locomotionStep.activeShape,
direction: edgeInputDirection.direction,
pushToTopHeight: playerMovement.edgeAssist.pushToTopHeight,
canOccupyShape: (feetPosition, shape) =>
this.context?.canOccupyPlayerShape?.(feetPosition, shape) ?? true,
probeGround: (feetPosition, shape, maxDistance) =>
this.context?.probePlayerGround?.(
feetPosition,
shape,
maxDistance
) ?? {
grounded: false,
distance: null,
normal: null,
slopeDegrees: null
}
});
if (ledgeGrabTarget !== null) {
this.enterLedgeGrab(ledgeGrabTarget, edgeInputDirection.inputMagnitude);
this.updateCameraTransform();
this.publishTelemetry();
return;
}
}
const nextFeetPosition = const nextFeetPosition =
edgeAssist === null ? locomotionStep.feetPosition : edgeAssist.feetPosition; edgeAssist === null ? locomotionStep.feetPosition : edgeAssist.feetPosition;
const nextLocomotionState = const nextLocomotionState =
@@ -496,6 +547,7 @@ export class FirstPersonNavigationController implements NavigationController {
this.jumpHoldRemainingMs = 0; this.jumpHoldRemainingMs = 0;
this.climbSurface = null; this.climbSurface = null;
this.climbLatchBlocked = false; this.climbLatchBlocked = false;
this.ledgeGrabTarget = null;
this.inWaterVolume = false; this.inWaterVolume = false;
this.inFogVolume = false; this.inFogVolume = false;
this.updateCameraTransform(); this.updateCameraTransform();