Refactor interaction system logic and add angle validation to scene documents
This commit is contained in:
@@ -280,32 +280,6 @@ function isPlayerInsideTriggerVolume(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function raySphereHitDistance(
|
|
||||||
origin: Vec3,
|
|
||||||
direction: Vec3,
|
|
||||||
center: Vec3,
|
|
||||||
radius: number
|
|
||||||
): number | null {
|
|
||||||
const offset = subtractVec3(origin, center);
|
|
||||||
const halfB = dotVec3(offset, direction);
|
|
||||||
const c = dotVec3(offset, offset) - radius * radius;
|
|
||||||
const discriminant = halfB * halfB - c;
|
|
||||||
|
|
||||||
if (discriminant < 0) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const discriminantRoot = Math.sqrt(discriminant);
|
|
||||||
const nearestHit = -halfB - discriminantRoot;
|
|
||||||
|
|
||||||
if (nearestHit >= 0) {
|
|
||||||
return nearestHit;
|
|
||||||
}
|
|
||||||
|
|
||||||
const farHit = -halfB + discriminantRoot;
|
|
||||||
return farHit >= 0 ? 0 : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveTeleportTarget(
|
function resolveTeleportTarget(
|
||||||
runtimeScene: RuntimeSceneDefinition,
|
runtimeScene: RuntimeSceneDefinition,
|
||||||
entityId: string
|
entityId: string
|
||||||
@@ -472,7 +446,6 @@ interface RuntimeInteractionTargetSource {
|
|||||||
acquisitionRange: number;
|
acquisitionRange: number;
|
||||||
horizontalRadius: number;
|
horizontalRadius: number;
|
||||||
bounds?: { min: Vec3; max: Vec3 };
|
bounds?: { min: Vec3; max: Vec3 };
|
||||||
targetRadius?: number;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Vec2 {
|
interface Vec2 {
|
||||||
@@ -554,8 +527,7 @@ function collectRuntimeInteractionTargetSources(
|
|||||||
distance,
|
distance,
|
||||||
range: interactable.radius,
|
range: interactable.radius,
|
||||||
acquisitionRange,
|
acquisitionRange,
|
||||||
horizontalRadius: targetRadius,
|
horizontalRadius: targetRadius
|
||||||
targetRadius
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -753,43 +725,6 @@ export function resolveStableRuntimeTargetProposal(
|
|||||||
return best;
|
return best;
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateBestPrompt(
|
|
||||||
currentBestPrompt: RuntimeInteractionPrompt | null,
|
|
||||||
currentBestHitDistance: number,
|
|
||||||
candidateEntityId: string,
|
|
||||||
candidatePrompt: string,
|
|
||||||
candidateDistance: number,
|
|
||||||
candidateRange: number,
|
|
||||||
candidateHitDistance: number
|
|
||||||
): { prompt: RuntimeInteractionPrompt | null; hitDistance: number } {
|
|
||||||
const nextPrompt: RuntimeInteractionPrompt = {
|
|
||||||
sourceEntityId: candidateEntityId,
|
|
||||||
prompt: candidatePrompt,
|
|
||||||
distance: candidateDistance,
|
|
||||||
range: candidateRange
|
|
||||||
};
|
|
||||||
|
|
||||||
if (
|
|
||||||
candidateHitDistance < currentBestHitDistance ||
|
|
||||||
(candidateHitDistance === currentBestHitDistance &&
|
|
||||||
(currentBestPrompt === null ||
|
|
||||||
candidateDistance < currentBestPrompt.distance ||
|
|
||||||
(candidateDistance === currentBestPrompt.distance &&
|
|
||||||
candidateEntityId.localeCompare(currentBestPrompt.sourceEntityId) <
|
|
||||||
0)))
|
|
||||||
) {
|
|
||||||
return {
|
|
||||||
prompt: nextPrompt,
|
|
||||||
hitDistance: candidateHitDistance
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
prompt: currentBestPrompt,
|
|
||||||
hitDistance: currentBestHitDistance
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
export class RuntimeInteractionSystem {
|
export class RuntimeInteractionSystem {
|
||||||
private readonly occupiedTriggerVolumes = new Set<string>();
|
private readonly occupiedTriggerVolumes = new Set<string>();
|
||||||
|
|
||||||
|
|||||||
@@ -575,6 +575,7 @@ describe("validateSceneDocument", () => {
|
|||||||
yawDegrees: Number.NaN,
|
yawDegrees: Number.NaN,
|
||||||
navigationMode: "invalidMode" as unknown as "firstPerson",
|
navigationMode: "invalidMode" as unknown as "firstPerson",
|
||||||
interactionReachMeters: Number.NaN,
|
interactionReachMeters: Number.NaN,
|
||||||
|
interactionAngleDegrees: Number.NaN,
|
||||||
movementTemplate: {
|
movementTemplate: {
|
||||||
kind: "invalidTemplate",
|
kind: "invalidTemplate",
|
||||||
moveSpeed: 0,
|
moveSpeed: 0,
|
||||||
@@ -649,6 +650,9 @@ describe("validateSceneDocument", () => {
|
|||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
code: "invalid-player-start-interaction-reach"
|
code: "invalid-player-start-interaction-reach"
|
||||||
}),
|
}),
|
||||||
|
expect.objectContaining({
|
||||||
|
code: "invalid-player-start-interaction-angle"
|
||||||
|
}),
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
code: "invalid-player-start-movement-template-kind"
|
code: "invalid-player-start-movement-template-kind"
|
||||||
}),
|
}),
|
||||||
|
|||||||
Reference in New Issue
Block a user