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;
}
private resolveRuntimePlayerInputBindings() {
return createPlayerStartInputBindings(this.runtimeScene?.playerInputBindings);
}
private resolveRuntimeTargetVisibilityClearance(target: {
kind?: string;
entityId?: string;
@@ -6006,7 +6010,14 @@ export class RuntimeHost {
}
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;
}
@@ -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() {
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 (
this.runtimeTargetSwitchInputHeld ||
inputMagnitude < TARGETING_DIRECTION_SWITCH_INPUT_THRESHOLD
@@ -6353,6 +6423,29 @@ export class RuntimeHost {
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() {
if (
this.runtimeScene === null ||
@@ -6682,8 +6775,8 @@ export class RuntimeHost {
return;
}
const interactKeyboardBinding =
this.runtimeScene.playerInputBindings.keyboard.interact;
const playerInputBindings = this.resolveRuntimePlayerInputBindings();
const interactKeyboardBinding = playerInputBindings.keyboard.interact;
if (
!isPlayerStartMouseBindingCode(interactKeyboardBinding) &&
event.code === interactKeyboardBinding
@@ -6701,16 +6794,20 @@ export class RuntimeHost {
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.stopImmediatePropagation();
this.clearActiveRuntimeTarget();
this.previousClearTargetInputActive = true;
return;
}
if (
event.code === this.runtimeScene.playerInputBindings.keyboard.pauseTime
) {
if (event.code === playerInputBindings.keyboard.pauseTime) {
event.preventDefault();
this.toggleManualPause();
this.previousPauseInputActive = true;
@@ -6788,7 +6885,7 @@ export class RuntimeHost {
const interactInputActive =
resolvePlayerStartInteractInput(
this.pressedKeys,
this.runtimeScene.playerInputBindings
this.resolveRuntimePlayerInputBindings()
) >= 0.5;
if (interactInputActive && !this.previousInteractInputActive) {
@@ -6807,7 +6904,7 @@ export class RuntimeHost {
const pauseInputActive =
resolvePlayerStartPauseInput(
this.pressedKeys,
this.runtimeScene.playerInputBindings
this.resolveRuntimePlayerInputBindings()
) >= 0.5;
if (pauseInputActive && !this.previousPauseInputActive) {

View File

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