auto-git:

[change] src/runtime-three/runtime-interaction-system.ts
This commit is contained in:
2026-04-25 10:26:46 +02:00
parent 393bdfc0d8
commit 13635d0705

View File

@@ -20,6 +20,11 @@ import type {
const DEFAULT_INTERACTABLE_TARGET_RADIUS = 0.75;
const DEFAULT_NPC_DIALOGUE_TARGET_RADIUS = 1.5;
const TARGETING_INTERACTABLE_REACH_MULTIPLIER = 3.5;
const TARGETING_NPC_REACH_MULTIPLIER = 4.5;
const TARGETING_MIN_INTERACTABLE_REACH = 5.5;
const TARGETING_MIN_NPC_REACH = 7;
const TARGETING_MIN_VIEW_DOT = 0.1;
export interface RuntimeDialogueStartSource {
kind: "interactionLink" | "npc" | "direct";
@@ -125,6 +130,10 @@ function lengthSquaredVec3(vector: Vec3): number {
return dotVec3(vector, vector);
}
function clampUnitInterval(value: number): number {
return Math.max(0, Math.min(1, value));
}
function distanceBetweenVec3(left: Vec3, right: Vec3): number {
return Math.sqrt(lengthSquaredVec3(subtractVec3(left, right)));
}
@@ -462,13 +471,15 @@ interface RuntimeInteractionTargetSource {
center: Vec3;
distance: number;
range: number;
acquisitionRange: number;
bounds?: { min: Vec3; max: Vec3 };
targetRadius?: number;
}
function collectRuntimeInteractionTargetSources(
interactionOrigin: Vec3,
runtimeScene: RuntimeSceneDefinition
runtimeScene: RuntimeSceneDefinition,
options: { useTargetingReach?: boolean } = {}
): RuntimeInteractionTargetSource[] {
const candidates: RuntimeInteractionTargetSource[] = [];
@@ -481,8 +492,14 @@ function collectRuntimeInteractionTargetSources(
}
const distance = distanceBetweenVec3(interactionOrigin, interactable.position);
const acquisitionRange = options.useTargetingReach
? Math.max(
interactable.radius * TARGETING_INTERACTABLE_REACH_MULTIPLIER,
TARGETING_MIN_INTERACTABLE_REACH
)
: interactable.radius;
if (distance > interactable.radius) {
if (distance > acquisitionRange) {
continue;
}
@@ -494,6 +511,7 @@ function collectRuntimeInteractionTargetSources(
center: interactable.position,
distance,
range: interactable.radius,
acquisitionRange,
targetRadius: getInteractableTargetRadius(interactable)
});
}
@@ -511,8 +529,14 @@ function collectRuntimeInteractionTargetSources(
const bounds = getNpcDialogueTargetBounds(npc);
const distance = distanceToAxisAlignedBox(interactionOrigin, bounds);
const acquisitionRange = options.useTargetingReach
? Math.max(
bounds.range * TARGETING_NPC_REACH_MULTIPLIER,
TARGETING_MIN_NPC_REACH
)
: bounds.range;
if (distance > bounds.range) {
if (distance > acquisitionRange) {
continue;
}
@@ -524,6 +548,7 @@ function collectRuntimeInteractionTargetSources(
center: bounds.center,
distance,
range: bounds.range,
acquisitionRange,
bounds
});
}
@@ -549,7 +574,8 @@ export function resolveRuntimeTargetCandidates(options: {
for (const source of collectRuntimeInteractionTargetSources(
options.interactionOrigin,
options.runtimeScene
options.runtimeScene,
{ useTargetingReach: true }
)) {
const toTarget = subtractVec3(source.center, options.cameraPosition);
const cameraDistanceSquared = lengthSquaredVec3(toTarget);
@@ -562,18 +588,21 @@ export function resolveRuntimeTargetCandidates(options: {
const viewDirection = scaleVec3(toTarget, 1 / cameraDistance);
const viewDot = dotVec3(viewDirection, normalizedViewDirection);
if (viewDot <= 0.05) {
if (viewDot <= TARGETING_MIN_VIEW_DOT) {
continue;
}
const interactionDistanceScore =
1 / (1 + source.distance / Math.max(source.range, 0.001));
const acquisitionDistanceScore =
1 - clampUnitInterval(source.distance / Math.max(source.acquisitionRange, 0.001));
const cameraDistanceScore = 1 / (1 + cameraDistance * 0.12);
const stabilityBonus = source.entityId === previousId ? 0.12 : 0;
const score =
viewDot * 2 +
interactionDistanceScore * 0.6 +
cameraDistanceScore * 0.4 +
viewDot * 2.2 +
interactionDistanceScore * 0.45 +
acquisitionDistanceScore * 0.5 +
cameraDistanceScore * 0.25 +
stabilityBonus;
candidates.push({