Enhance runtime target management and input binding resolution

This commit is contained in:
2026-04-27 15:56:49 +02:00
parent 715f819f76
commit 43d39565d7
2 changed files with 108 additions and 9 deletions

View File

@@ -5726,6 +5726,10 @@ export class RuntimeHost {
this.runtimeTargetSwitchInputHeld = false; this.runtimeTargetSwitchInputHeld = false;
} }
private resolveRuntimePlayerInputBindings() {
return createPlayerStartInputBindings(this.runtimeScene?.playerInputBindings);
}
private resolveRuntimeTargetVisibilityClearance(target: { private resolveRuntimeTargetVisibilityClearance(target: {
kind?: string; kind?: string;
entityId?: string; entityId?: string;
@@ -6006,7 +6010,14 @@ export class RuntimeHost {
} }
if (this.activeRuntimeTargetReference !== null) { if (this.activeRuntimeTargetReference !== null) {
this.clearActiveRuntimeTarget(); if (
this.runtimeScene.playerStart?.targetButtonCyclesActiveTarget ??
DEFAULT_PLAYER_START_TARGET_BUTTON_CYCLES_ACTIVE_TARGET
) {
this.cycleActiveRuntimeTargetFromButton();
} else {
this.clearActiveRuntimeTarget();
}
return; return;
} }
@@ -6024,6 +6035,53 @@ export class RuntimeHost {
} }
} }
private cycleActiveRuntimeTargetFromButton() {
if (this.activeRuntimeTargetReference === null) {
return;
}
const cycleCandidates = this.runtimeTargetCandidates.filter((candidate) => {
if (!this.isRuntimeTargetPlayerVisible(candidate)) {
return false;
}
const screenPoint = this.resolveRuntimeTargetScreenPoint(candidate.center);
return (
screenPoint !== null &&
Math.abs(screenPoint.x) <= TARGETING_SCREEN_PROPOSAL_MAX_ABS_X &&
Math.abs(screenPoint.y) <= TARGETING_SCREEN_PROPOSAL_MAX_ABS_Y
);
});
if (cycleCandidates.length === 0) {
return;
}
const activeIndex = cycleCandidates.findIndex(
(candidate) =>
candidate.kind === this.activeRuntimeTargetReference?.kind &&
candidate.entityId === this.activeRuntimeTargetReference?.entityId
);
const nextCandidate =
activeIndex < 0
? (cycleCandidates[0] ?? null)
: cycleCandidates.length > 1
? (cycleCandidates[(activeIndex + 1) % cycleCandidates.length] ??
null)
: null;
if (nextCandidate === null) {
return;
}
this.setActiveRuntimeTargetReference({
kind: nextCandidate.kind,
entityId: nextCandidate.entityId
});
this.proposedRuntimeTarget = nextCandidate;
}
private clearActiveRuntimeTarget() { private clearActiveRuntimeTarget() {
this.setActiveRuntimeTargetReference(null); this.setActiveRuntimeTargetReference(null);
} }
@@ -6060,6 +6118,18 @@ export class RuntimeHost {
}); });
} }
if (
!(
this.runtimeScene?.playerStart?.allowLookInputTargetSwitch ??
DEFAULT_PLAYER_START_ALLOW_LOOK_INPUT_TARGET_SWITCH
)
) {
this.runtimeTargetSwitchInputHeld = false;
return this.createRuntimeTargetLookInputResult({
activeTargetLocked: true
});
}
if ( if (
this.runtimeTargetSwitchInputHeld || this.runtimeTargetSwitchInputHeld ||
inputMagnitude < TARGETING_DIRECTION_SWITCH_INPUT_THRESHOLD inputMagnitude < TARGETING_DIRECTION_SWITCH_INPUT_THRESHOLD
@@ -6353,6 +6423,29 @@ export class RuntimeHost {
this.previousTargetCycleInputActive = targetInputActive; this.previousTargetCycleInputActive = targetInputActive;
} }
private updateClearTargetInputState() {
if (this.runtimeScene === null || !this.sceneReady) {
this.previousClearTargetInputActive = false;
return;
}
const clearTargetInputActive =
resolvePlayerStartClearTargetInput(
this.pressedKeys,
this.resolveRuntimePlayerInputBindings()
) >= 0.5;
if (
this.activeRuntimeTargetReference !== null &&
clearTargetInputActive &&
!this.previousClearTargetInputActive
) {
this.clearActiveRuntimeTarget();
}
this.previousClearTargetInputActive = clearTargetInputActive;
}
private resolveThirdPersonTargetAssist() { private resolveThirdPersonTargetAssist() {
if ( if (
this.runtimeScene === null || this.runtimeScene === null ||
@@ -6682,8 +6775,8 @@ export class RuntimeHost {
return; return;
} }
const interactKeyboardBinding = const playerInputBindings = this.resolveRuntimePlayerInputBindings();
this.runtimeScene.playerInputBindings.keyboard.interact; const interactKeyboardBinding = playerInputBindings.keyboard.interact;
if ( if (
!isPlayerStartMouseBindingCode(interactKeyboardBinding) && !isPlayerStartMouseBindingCode(interactKeyboardBinding) &&
event.code === interactKeyboardBinding event.code === interactKeyboardBinding
@@ -6701,16 +6794,20 @@ export class RuntimeHost {
return; return;
} }
if (event.code === "Escape" && this.activeRuntimeTargetReference !== null) { const clearTargetKeyboardBinding = playerInputBindings.keyboard.clearTarget;
if (
this.activeRuntimeTargetReference !== null &&
!isPlayerStartMouseBindingCode(clearTargetKeyboardBinding) &&
event.code === clearTargetKeyboardBinding
) {
event.preventDefault(); event.preventDefault();
event.stopImmediatePropagation(); event.stopImmediatePropagation();
this.clearActiveRuntimeTarget(); this.clearActiveRuntimeTarget();
this.previousClearTargetInputActive = true;
return; return;
} }
if ( if (event.code === playerInputBindings.keyboard.pauseTime) {
event.code === this.runtimeScene.playerInputBindings.keyboard.pauseTime
) {
event.preventDefault(); event.preventDefault();
this.toggleManualPause(); this.toggleManualPause();
this.previousPauseInputActive = true; this.previousPauseInputActive = true;
@@ -6788,7 +6885,7 @@ export class RuntimeHost {
const interactInputActive = const interactInputActive =
resolvePlayerStartInteractInput( resolvePlayerStartInteractInput(
this.pressedKeys, this.pressedKeys,
this.runtimeScene.playerInputBindings this.resolveRuntimePlayerInputBindings()
) >= 0.5; ) >= 0.5;
if (interactInputActive && !this.previousInteractInputActive) { if (interactInputActive && !this.previousInteractInputActive) {
@@ -6807,7 +6904,7 @@ export class RuntimeHost {
const pauseInputActive = const pauseInputActive =
resolvePlayerStartPauseInput( resolvePlayerStartPauseInput(
this.pressedKeys, this.pressedKeys,
this.runtimeScene.playerInputBindings this.resolveRuntimePlayerInputBindings()
) >= 0.5; ) >= 0.5;
if (pauseInputActive && !this.previousPauseInputActive) { if (pauseInputActive && !this.previousPauseInputActive) {

View File

@@ -576,6 +576,8 @@ describe("validateSceneDocument", () => {
navigationMode: "invalidMode" as unknown as "firstPerson", navigationMode: "invalidMode" as unknown as "firstPerson",
interactionReachMeters: Number.NaN, interactionReachMeters: Number.NaN,
interactionAngleDegrees: Number.NaN, interactionAngleDegrees: Number.NaN,
allowLookInputTargetSwitch: true,
targetButtonCyclesActiveTarget: false,
movementTemplate: { movementTemplate: {
kind: "invalidTemplate", kind: "invalidTemplate",
moveSpeed: 0, moveSpeed: 0,