fix: a pb with octree

This commit is contained in:
Tom Boullay
2026-05-11 16:41:11 +02:00
parent 26a9c1c4d4
commit 8088e67625
12 changed files with 51 additions and 14 deletions
@@ -4,6 +4,7 @@ import { RepairPromptVideo } from "@/components/three/gameplay/RepairPromptVideo
import { RepairMissionCase } from "@/components/three/gameplay/RepairMissionCase"; import { RepairMissionCase } from "@/components/three/gameplay/RepairMissionCase";
import { TriggerObject } from "@/components/three/interaction/TriggerObject"; import { TriggerObject } from "@/components/three/interaction/TriggerObject";
import { REPAIR_CASE_ANIMATION_DURATION } from "@/data/gameplay/repairCaseConfig"; import { REPAIR_CASE_ANIMATION_DURATION } from "@/data/gameplay/repairCaseConfig";
import { REPAIR_INTERACTION_RADIUS } from "@/data/gameplay/repairGameConfig";
import type { RepairMissionConfig } from "@/data/gameplay/repairMissions"; import type { RepairMissionConfig } from "@/data/gameplay/repairMissions";
interface RepairCompletionStepProps { interface RepairCompletionStepProps {
@@ -42,7 +43,7 @@ export function RepairCompletionStep({
<RepairObjectModel <RepairObjectModel
label={config.label} label={config.label}
modelPath={config.modelPath} modelPath={config.modelPath}
scale={1} scale={config.modelScale ?? 1}
/> />
{!isClosingCase ? ( {!isClosingCase ? (
@@ -50,6 +51,7 @@ export function RepairCompletionStep({
position={[0, 1.1, 0]} position={[0, 1.1, 0]}
colliders="ball" colliders="ball"
label={`Valider ${config.label}`} label={`Valider ${config.label}`}
radius={REPAIR_INTERACTION_RADIUS}
onTrigger={() => setIsClosingCase(true)} onTrigger={() => setIsClosingCase(true)}
> >
<mesh> <mesh>
+5 -1
View File
@@ -122,7 +122,11 @@ export function RepairGame({
/> />
) : null} ) : null}
{step === "fragmented" ? ( {step === "fragmented" ? (
<ExplodableModel modelPath={config.modelPath} split /> <ExplodableModel
modelPath={config.modelPath}
scale={config.modelScale ?? 1}
split
/>
) : null} ) : null}
{step === "scanning" ? ( {step === "scanning" ? (
<RepairScanSequence <RepairScanSequence
@@ -1,6 +1,7 @@
import { InteractableObject } from "@/components/three/interaction/InteractableObject"; import { InteractableObject } from "@/components/three/interaction/InteractableObject";
import { RepairObjectModel } from "@/components/three/gameplay/RepairObjectModel"; import { RepairObjectModel } from "@/components/three/gameplay/RepairObjectModel";
import { RepairPromptVideo } from "@/components/three/gameplay/RepairPromptVideo"; import { RepairPromptVideo } from "@/components/three/gameplay/RepairPromptVideo";
import { REPAIR_INTERACTION_RADIUS } from "@/data/gameplay/repairGameConfig";
import type { RepairMissionConfig } from "@/data/gameplay/repairMissions"; import type { RepairMissionConfig } from "@/data/gameplay/repairMissions";
import type { Vector3Tuple } from "@/types/three/three"; import type { Vector3Tuple } from "@/types/three/three";
@@ -20,14 +21,15 @@ export function RepairInspectionObject({
kind="trigger" kind="trigger"
label={`Inspecter ${config.label}`} label={`Inspecter ${config.label}`}
position={worldPosition} position={worldPosition}
radius={REPAIR_INTERACTION_RADIUS}
onPress={onInspect} onPress={onInspect}
> >
<RepairObjectModel <RepairObjectModel
label={config.label} label={config.label}
modelPath={config.modelPath} modelPath={config.modelPath}
scale={0.9} scale={config.modelScale ?? 0.9}
/> />
<RepairPromptVideo src={config.interactUiPath} /> <RepairPromptVideo src={config.stageUiPath} />
</InteractableObject> </InteractableObject>
); );
} }
@@ -9,6 +9,7 @@ import {
REPAIR_CASE_FOCUS_SCALE, REPAIR_CASE_FOCUS_SCALE,
REPAIR_CASE_MODEL_PATH, REPAIR_CASE_MODEL_PATH,
} from "@/data/gameplay/repairCaseConfig"; } from "@/data/gameplay/repairCaseConfig";
import { REPAIR_INTERACTION_RADIUS } from "@/data/gameplay/repairGameConfig";
import type { RepairMissionConfig } from "@/data/gameplay/repairMissions"; import type { RepairMissionConfig } from "@/data/gameplay/repairMissions";
import type { Vector3Tuple } from "@/types/three/three"; import type { Vector3Tuple } from "@/types/three/three";
@@ -48,6 +49,7 @@ export function RepairMissionCase({
position={casePosition} position={casePosition}
colliders="ball" colliders="ball"
label={`Ouvrir ${config.label}`} label={`Ouvrir ${config.label}`}
radius={REPAIR_INTERACTION_RADIUS}
onTrigger={onInteract} onTrigger={onInteract}
> >
<RepairCaseModel <RepairCaseModel
@@ -35,6 +35,7 @@ export function RepairReassemblyStep({
<group> <group>
<ExplodableModel <ExplodableModel
modelPath={config.modelPath} modelPath={config.modelPath}
scale={config.modelScale ?? 1}
split={split} split={split}
splitDistance={1.2} splitDistance={1.2}
/> />
@@ -11,6 +11,7 @@ import {
REPAIR_CASE_PLACEHOLDER_SNAP_DURATION, REPAIR_CASE_PLACEHOLDER_SNAP_DURATION,
REPAIR_CASE_PLACEHOLDER_SNAP_RADIUS, REPAIR_CASE_PLACEHOLDER_SNAP_RADIUS,
} from "@/data/gameplay/repairCaseConfig"; } from "@/data/gameplay/repairCaseConfig";
import { REPAIR_INTERACTION_RADIUS } from "@/data/gameplay/repairGameConfig";
import type { import type {
RepairMissionConfig, RepairMissionConfig,
RepairMissionPartConfig, RepairMissionPartConfig,
@@ -299,6 +300,7 @@ function RepairInstallTarget({
position={INSTALL_TARGET_POSITION} position={INSTALL_TARGET_POSITION}
colliders="ball" colliders="ball"
label={label} label={label}
radius={REPAIR_INTERACTION_RADIUS}
onTrigger={() => { onTrigger={() => {
if (!isReadyToInstall) { if (!isReadyToInstall) {
onBlocked(); onBlocked();
@@ -60,6 +60,7 @@ export function RepairScanSequence({
<group> <group>
<ExplodableModel <ExplodableModel
modelPath={config.modelPath} modelPath={config.modelPath}
scale={config.modelScale ?? 1}
split split
onPartsReady={setParts} onPartsReady={setParts}
/> />
@@ -19,6 +19,7 @@ import type { Vector3Tuple } from "@/types/three/three";
interface InteractableObjectBaseProps { interface InteractableObjectBaseProps {
label: string; label: string;
position: Vector3Tuple; position: Vector3Tuple;
radius?: number;
bodyRef?: RefObject<RapierRigidBody | null>; bodyRef?: RefObject<RapierRigidBody | null>;
onPress: () => void; onPress: () => void;
children: React.ReactNode; children: React.ReactNode;
@@ -64,7 +65,15 @@ function createInteractableHandle(
export function InteractableObject( export function InteractableObject(
props: InteractableObjectProps, props: InteractableObjectProps,
): React.JSX.Element { ): React.JSX.Element {
const { kind, label, position, bodyRef, onPress, children } = props; const {
kind,
label,
position,
radius = INTERACTION_RADIUS,
bodyRef,
onPress,
children,
} = props;
const onRelease = props.kind === "grab" ? props.onRelease : null; const onRelease = props.kind === "grab" ? props.onRelease : null;
const camera = useThree((state) => state.camera); const camera = useThree((state) => state.camera);
const groupRef = useRef<THREE.Group>(null); const groupRef = useRef<THREE.Group>(null);
@@ -156,7 +165,7 @@ export function InteractableObject(
camera.getWorldPosition(_cameraPos); camera.getWorldPosition(_cameraPos);
const dist = _cameraPos.distanceTo(_objectPos); const dist = _cameraPos.distanceTo(_objectPos);
const isNearby = dist <= INTERACTION_RADIUS; const isNearby = dist <= radius;
manager.setNearby(handle.current, isNearby); manager.setNearby(handle.current, isNearby);
@@ -169,7 +178,7 @@ export function InteractableObject(
camera.getWorldDirection(_cameraDir); camera.getWorldDirection(_cameraDir);
_raycaster.set(_cameraPos, _cameraDir); _raycaster.set(_cameraPos, _cameraDir);
_raycaster.far = INTERACTION_RADIUS; _raycaster.far = radius;
const hits = group ? _raycaster.intersectObject(group, true) : []; const hits = group ? _raycaster.intersectObject(group, true) : [];
const validHit = hits.find((h) => h.object !== debugSphereRef.current); const validHit = hits.find((h) => h.object !== debugSphereRef.current);
@@ -187,7 +196,7 @@ export function InteractableObject(
<mesh ref={debugSphereRef} visible={false}> <mesh ref={debugSphereRef} visible={false}>
<sphereGeometry <sphereGeometry
args={[ args={[
INTERACTION_RADIUS, radius,
INTERACTION_DEBUG_SPHERE_SEGMENTS, INTERACTION_DEBUG_SPHERE_SEGMENTS,
INTERACTION_DEBUG_SPHERE_SEGMENTS, INTERACTION_DEBUG_SPHERE_SEGMENTS,
]} ]}
@@ -4,6 +4,7 @@ import type { RapierRigidBody } from "@react-three/rapier";
import { InteractableObject } from "@/components/three/interaction/InteractableObject"; import { InteractableObject } from "@/components/three/interaction/InteractableObject";
import { useClonedObject } from "@/hooks/three/useClonedObject"; import { useClonedObject } from "@/hooks/three/useClonedObject";
import { useLoggedGLTF } from "@/hooks/three/useLoggedGLTF"; import { useLoggedGLTF } from "@/hooks/three/useLoggedGLTF";
import { INTERACTION_RADIUS } from "@/data/interaction/interactionConfig";
import { import {
TRIGGER_DEFAULT_COLLIDERS, TRIGGER_DEFAULT_COLLIDERS,
TRIGGER_DEFAULT_LABEL, TRIGGER_DEFAULT_LABEL,
@@ -23,6 +24,7 @@ interface TriggerObjectProps {
children: React.ReactNode; children: React.ReactNode;
colliders?: ColliderShape; colliders?: ColliderShape;
label?: string; label?: string;
radius?: number;
soundPath?: string; soundPath?: string;
soundVolume?: number; soundVolume?: number;
spawnModel?: string; spawnModel?: string;
@@ -53,6 +55,7 @@ export function TriggerObject({
children, children,
colliders = TRIGGER_DEFAULT_COLLIDERS, colliders = TRIGGER_DEFAULT_COLLIDERS,
label = TRIGGER_DEFAULT_LABEL, label = TRIGGER_DEFAULT_LABEL,
radius = INTERACTION_RADIUS,
soundPath, soundPath,
soundVolume = TRIGGER_DEFAULT_SOUND_VOLUME, soundVolume = TRIGGER_DEFAULT_SOUND_VOLUME,
spawnModel, spawnModel,
@@ -74,6 +77,7 @@ export function TriggerObject({
kind="trigger" kind="trigger"
label={label} label={label}
position={position} position={position}
radius={radius}
bodyRef={rbRef} bodyRef={rbRef}
onPress={() => { onPress={() => {
if (soundPath) { if (soundPath) {
+1
View File
@@ -1,4 +1,5 @@
export const REPAIR_FRAGMENTATION_FIST_HOLD_SECONDS = 1; export const REPAIR_FRAGMENTATION_FIST_HOLD_SECONDS = 1;
export const REPAIR_FRAGMENTATION_SEQUENCE_SECONDS = 4; export const REPAIR_FRAGMENTATION_SEQUENCE_SECONDS = 4;
export const REPAIR_INTERACTION_RADIUS = 10;
export const REPAIR_SCAN_PART_SECONDS = 1.2; export const REPAIR_SCAN_PART_SECONDS = 1.2;
export const REPAIR_REASSEMBLY_SECONDS = 1.4; export const REPAIR_REASSEMBLY_SECONDS = 1.4;
+13 -6
View File
@@ -1,5 +1,9 @@
import type { RepairMissionId } from "@/types/gameplay/repairMission"; import type { RepairMissionId } from "@/types/gameplay/repairMission";
import type { Vector3Scale, Vector3Tuple } from "@/types/three/three"; import type {
ModelTransformProps,
Vector3Scale,
Vector3Tuple,
} from "@/types/three/three";
export interface RepairMissionCaseConfig { export interface RepairMissionCaseConfig {
position: Vector3Tuple; position: Vector3Tuple;
@@ -20,6 +24,7 @@ export interface RepairMissionConfig {
label: string; label: string;
description: string; description: string;
modelPath: string; modelPath: string;
modelScale?: ModelTransformProps["scale"];
stageUiPath: string; stageUiPath: string;
interactUiPath: string; interactUiPath: string;
brokenUiPath: string; brokenUiPath: string;
@@ -40,13 +45,14 @@ const DEFAULT_REPAIR_CASE = {
scale: 1.5, scale: 1.5,
} satisfies RepairMissionCaseConfig; } satisfies RepairMissionCaseConfig;
export const REPAIR_MISSIONS = { export const REPAIR_MISSIONS: Record<RepairMissionId, RepairMissionConfig> = {
bike: { bike: {
id: "bike", id: "bike",
label: "E-bike", label: "E-bike",
description: description:
"Repair the damaged cooling module before relaunching the bike", "Repair the damaged cooling module before relaunching the bike",
modelPath: "/models/refroidisseur/model.gltf", modelPath: "/models/ebike/model.gltf",
modelScale: 0.25,
stageUiPath: "/assets/UI/ebike.webm", stageUiPath: "/assets/UI/ebike.webm",
interactUiPath: REPAIR_INTERACT_UI_PATH, interactUiPath: REPAIR_INTERACT_UI_PATH,
brokenUiPath: REPAIR_BROKEN_UI_PATH, brokenUiPath: REPAIR_BROKEN_UI_PATH,
@@ -56,7 +62,8 @@ export const REPAIR_MISSIONS = {
{ {
id: "bike-cooling-core", id: "bike-cooling-core",
label: "Cooling core", label: "Cooling core",
nodeName: "Cylinder", modelPath: "/models/refroidisseur/model.gltf",
nodeName: "refroidisseur",
placeholderName: "placeholder_1", placeholderName: "placeholder_1",
}, },
], ],
@@ -74,7 +81,7 @@ export const REPAIR_MISSIONS = {
{ {
id: "bike-glove-decoy", id: "bike-glove-decoy",
label: "Insulation glove", label: "Insulation glove",
modelPath: "/models/gant/model.gltf", modelPath: "/models/gant_l/model.gltf",
}, },
], ],
}, },
@@ -166,4 +173,4 @@ export const REPAIR_MISSIONS = {
}, },
], ],
}, },
} satisfies Record<RepairMissionId, RepairMissionConfig>; };
@@ -2,6 +2,8 @@ import { useGameStore } from "@/managers/stores/useGameStore";
import type { MissionStep } from "@/types/gameplay/repairMission"; import type { MissionStep } from "@/types/gameplay/repairMission";
export function useRepairMovementLocked(): boolean { export function useRepairMovementLocked(): boolean {
return false;
return useGameStore((state) => { return useGameStore((state) => {
switch (state.mainState) { switch (state.mainState) {
case "bike": case "bike":