From eb0db21d291f1cc15973ed9388f271ce1e0b30bf Mon Sep 17 00:00:00 2001 From: Tom Boullay Date: Mon, 27 Apr 2026 11:14:43 +0200 Subject: [PATCH] clean --- src/components/3d/InteractableObject.tsx | 40 +++++++++---------- src/data/playerConfig.ts | 8 ++-- src/utils/debug/scene/DebugCameraControls.tsx | 12 +++--- src/world/World.tsx | 12 +++--- src/world/player/PlayerComponent.tsx | 12 +++--- src/world/player/PlayerController.tsx | 27 ++++++++----- vite.config.ts | 4 +- 7 files changed, 61 insertions(+), 54 deletions(-) diff --git a/src/components/3d/InteractableObject.tsx b/src/components/3d/InteractableObject.tsx index 7db8034..4403579 100644 --- a/src/components/3d/InteractableObject.tsx +++ b/src/components/3d/InteractableObject.tsx @@ -14,11 +14,7 @@ import { useDebugFolder } from "@/hooks/debug/useDebugFolder"; import { InteractionManager } from "@/stateManager/InteractionManager"; import { INTERACTION_RADIUS } from "@/data/interactionConfig"; import type { Vector3Tuple } from "@/types/3d"; -import type { - GrabInteractableHandle, - InteractableHandle, - TriggerInteractableHandle, -} from "@/types/interaction"; +import type { InteractableHandle, InteractableKind } from "@/types/interaction"; interface InteractableObjectBaseProps { label: string; @@ -41,6 +37,13 @@ type InteractableObjectProps = | TriggerInteractableObjectProps | GrabInteractableObjectProps; +type MutableInteractableHandle = { + kind: InteractableKind; + label: string; + onPress: () => void; + onRelease?: () => void; +}; + const _cameraPos = new THREE.Vector3(); const _cameraDir = new THREE.Vector3(); const _objectPos = new THREE.Vector3(); @@ -50,6 +53,7 @@ export function InteractableObject( props: InteractableObjectProps, ): React.JSX.Element { const { kind, label, position, bodyRef, onPress, children } = props; + const onRelease = props.kind === "grab" ? props.onRelease : undefined; const camera = useThree((state) => state.camera); const groupRef = useRef(null); const debugSphereRef = useRef(null); @@ -61,27 +65,19 @@ export function InteractableObject( ); useEffect(() => { - if (props.kind === "grab") { - const current = handle.current as GrabInteractableHandle; - current.label = label; - current.onPress = onPress; - current.onRelease = props.onRelease; + const current = handle.current as MutableInteractableHandle; + current.kind = kind; + current.label = label; + current.onPress = onPress; + + if (kind === "grab" && onRelease) { + current.onRelease = onRelease; return; } + delete current.onRelease; return undefined; - }, [label, onPress, props]); - - useEffect(() => { - if (kind === "grab") { - return undefined; - } - - const current = handle.current as TriggerInteractableHandle; - current.label = label; - current.onPress = onPress; - return undefined; - }, [kind, label, onPress]); + }, [kind, label, onPress, onRelease]); const setupInteractionDebugFolder = useCallback((folder: GUI) => { folder diff --git a/src/data/playerConfig.ts b/src/data/playerConfig.ts index 2993701..43e183e 100644 --- a/src/data/playerConfig.ts +++ b/src/data/playerConfig.ts @@ -1,3 +1,5 @@ +import type { Vector3Tuple } from "@/types/3d"; + export const PLAYER_EYE_HEIGHT = 1.75; export const PLAYER_CAPSULE_RADIUS = 0.35; @@ -9,7 +11,5 @@ export const PLAYER_MAX_DELTA = 0.05; export const PLAYER_ACCELERATION_MULTIPLIER = 9; export const PLAYER_XZ_DAMPING_FACTOR = 8; -export const PLAYER_SPAWN_X = 0; -export const PLAYER_SPAWN_Z = 0; -export const PLAYER_SPAWN_Y_GAME = 100; -export const PLAYER_SPAWN_Y_PHYSICS = 3; +export const PLAYER_SPAWN_POSITION_GAME: Vector3Tuple = [0, 100, 0]; +export const PLAYER_SPAWN_POSITION_PHYSICS: Vector3Tuple = [0, 3, 0]; diff --git a/src/utils/debug/scene/DebugCameraControls.tsx b/src/utils/debug/scene/DebugCameraControls.tsx index 04efd92..0e6cd39 100644 --- a/src/utils/debug/scene/DebugCameraControls.tsx +++ b/src/utils/debug/scene/DebugCameraControls.tsx @@ -4,11 +4,13 @@ import { DEBUG_CAMERA_MAX_DISTANCE, DEBUG_CAMERA_MIN_DISTANCE, } from "@/data/debugConfig"; -import { +import { PLAYER_EYE_HEIGHT, PLAYER_SPAWN_POSITION_GAME } from "@/data/playerConfig"; + +const DEBUG_CAMERA_TARGET = [ + PLAYER_SPAWN_POSITION_GAME[0], PLAYER_EYE_HEIGHT, - PLAYER_SPAWN_X, - PLAYER_SPAWN_Z, -} from "@/data/playerConfig"; + PLAYER_SPAWN_POSITION_GAME[2], +] as const; export function DebugCameraControls(): React.JSX.Element { return ( @@ -17,7 +19,7 @@ export function DebugCameraControls(): React.JSX.Element { dampingFactor={DEBUG_CAMERA_DAMPING_FACTOR} minDistance={DEBUG_CAMERA_MIN_DISTANCE} maxDistance={DEBUG_CAMERA_MAX_DISTANCE} - target={[PLAYER_SPAWN_X, PLAYER_EYE_HEIGHT, PLAYER_SPAWN_Z]} + target={DEBUG_CAMERA_TARGET} /> ); } diff --git a/src/world/World.tsx b/src/world/World.tsx index 861a409..aec06e3 100644 --- a/src/world/World.tsx +++ b/src/world/World.tsx @@ -1,8 +1,8 @@ import { useState } from "react"; import type { Octree } from "three/addons/math/Octree.js"; import { - PLAYER_SPAWN_Y_GAME, - PLAYER_SPAWN_Y_PHYSICS, + PLAYER_SPAWN_POSITION_GAME, + PLAYER_SPAWN_POSITION_PHYSICS, } from "@/data/playerConfig"; import { useCameraMode } from "@/hooks/debug/useCameraMode"; import { useSceneMode } from "@/hooks/debug/useSceneMode"; @@ -18,6 +18,10 @@ export function World(): React.JSX.Element { const cameraMode = useCameraMode(); const sceneMode = useSceneMode(); const [octree, setOctree] = useState(null); + const playerSpawnPosition = + sceneMode === "game" + ? PLAYER_SPAWN_POSITION_GAME + : PLAYER_SPAWN_POSITION_PHYSICS; return ( <> @@ -35,9 +39,7 @@ export function World(): React.JSX.Element { {cameraMode !== "debug" ? ( ) : null} diff --git a/src/world/player/PlayerComponent.tsx b/src/world/player/PlayerComponent.tsx index bcf4a22..3b950e0 100644 --- a/src/world/player/PlayerComponent.tsx +++ b/src/world/player/PlayerComponent.tsx @@ -1,29 +1,29 @@ import { useEffect } from "react"; import { useThree } from "@react-three/fiber"; import type { Octree } from "three/addons/math/Octree.js"; -import { PLAYER_SPAWN_X, PLAYER_SPAWN_Z } from "@/data/playerConfig"; +import type { Vector3Tuple } from "@/types/3d"; import { PlayerCamera } from "@/world/player/PlayerCamera"; import { PlayerController } from "@/world/player/PlayerController"; interface PlayerComponentProps { octree: Octree | null; - spawnY: number; + spawnPosition: Vector3Tuple; } export function PlayerComponent({ - spawnY, + spawnPosition, octree, }: PlayerComponentProps): React.JSX.Element { const camera = useThree((state) => state.camera); useEffect(() => { - camera.position.set(PLAYER_SPAWN_X, spawnY, PLAYER_SPAWN_Z); - }, [camera, spawnY]); + camera.position.set(...spawnPosition); + }, [camera, spawnPosition]); return ( <> - + ); } diff --git a/src/world/player/PlayerController.tsx b/src/world/player/PlayerController.tsx index 6059cbc..341c6d9 100644 --- a/src/world/player/PlayerController.tsx +++ b/src/world/player/PlayerController.tsx @@ -20,12 +20,11 @@ import { PLAYER_GRAVITY, PLAYER_JUMP_SPEED, PLAYER_MAX_DELTA, - PLAYER_SPAWN_X, - PLAYER_SPAWN_Z, PLAYER_WALK_SPEED, PLAYER_XZ_DAMPING_FACTOR, } from "@/data/playerConfig"; import { InteractionManager } from "@/stateManager/InteractionManager"; +import type { Vector3Tuple } from "@/types/3d"; type Keys = { forward: boolean; @@ -45,6 +44,7 @@ const DEFAULT_KEYS: Keys = { interface PlayerControllerProps { octree: Octree | null; + spawnPosition: Vector3Tuple; } const _forward = new THREE.Vector3(); @@ -52,8 +52,12 @@ const _right = new THREE.Vector3(); const _wishDir = new THREE.Vector3(); const _up = new THREE.Vector3(0, 1, 0); const _translateVec = new THREE.Vector3(); +const _collisionCorrection = new THREE.Vector3(); -export function PlayerController({ octree }: PlayerControllerProps): null { +export function PlayerController({ + octree, + spawnPosition, +}: PlayerControllerProps): null { const camera = useThree((state) => state.camera); const keys = useRef({ ...DEFAULT_KEYS }); const velocity = useRef(new THREE.Vector3()); @@ -69,14 +73,17 @@ export function PlayerController({ octree }: PlayerControllerProps): null { ); useEffect(() => { - const spawnY = camera.position.y; capsule.current.start.set( - PLAYER_SPAWN_X, - spawnY - PLAYER_EYE_HEIGHT + PLAYER_CAPSULE_RADIUS, - PLAYER_SPAWN_Z, + spawnPosition[0], + spawnPosition[1] - PLAYER_EYE_HEIGHT + PLAYER_CAPSULE_RADIUS, + spawnPosition[2], ); - capsule.current.end.set(PLAYER_SPAWN_X, spawnY, PLAYER_SPAWN_Z); - }, [camera]); + capsule.current.end.set(...spawnPosition); + velocity.current.set(0, 0, 0); + onFloor.current = false; + wantsJump.current = false; + camera.position.copy(capsule.current.end); + }, [camera, spawnPosition]); useEffect(() => { const interaction = InteractionManager.getInstance(); @@ -215,7 +222,7 @@ export function PlayerController({ octree }: PlayerControllerProps): null { } capsule.current.translate( - result.normal.clone().multiplyScalar(result.depth), + _collisionCorrection.copy(result.normal).multiplyScalar(result.depth), ); } } diff --git a/vite.config.ts b/vite.config.ts index ad58148..a8b3919 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,12 +1,12 @@ import { defineConfig } from "vite"; import react from "@vitejs/plugin-react"; -import path from "node:path"; +import { fileURLToPath } from "node:url"; export default defineConfig({ plugins: [react()], resolve: { alias: { - "@": path.resolve(__dirname, "./src"), + "@": fileURLToPath(new URL("./src", import.meta.url)), }, }, });