fix: archi
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
import { useEffect, useRef } from "react";
|
||||
import { useCallback, useEffect, useRef } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import type { RapierRigidBody } from "@react-three/rapier";
|
||||
import * as THREE from "three";
|
||||
import type GUI from "lil-gui";
|
||||
import type { RefObject } from "react";
|
||||
import {
|
||||
INTERACTION_DEBUG_SPHERE_COLOR,
|
||||
@@ -13,54 +14,83 @@ 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 { InteractableHandle, InteractableKind } from "@/types/interaction";
|
||||
import type {
|
||||
GrabInteractableHandle,
|
||||
InteractableHandle,
|
||||
TriggerInteractableHandle,
|
||||
} from "@/types/interaction";
|
||||
|
||||
interface InteractableObjectProps {
|
||||
kind: InteractableKind;
|
||||
interface InteractableObjectBaseProps {
|
||||
label: string;
|
||||
position: Vector3Tuple;
|
||||
bodyRef?: RefObject<RapierRigidBody | null>;
|
||||
onPress: () => void;
|
||||
onRelease?: () => void;
|
||||
children: React.ReactNode;
|
||||
}
|
||||
|
||||
interface TriggerInteractableObjectProps extends InteractableObjectBaseProps {
|
||||
kind: "trigger";
|
||||
}
|
||||
|
||||
interface GrabInteractableObjectProps extends InteractableObjectBaseProps {
|
||||
kind: "grab";
|
||||
onRelease: () => void;
|
||||
}
|
||||
|
||||
type InteractableObjectProps =
|
||||
| TriggerInteractableObjectProps
|
||||
| GrabInteractableObjectProps;
|
||||
|
||||
const _cameraPos = new THREE.Vector3();
|
||||
const _cameraDir = new THREE.Vector3();
|
||||
const _objectPos = new THREE.Vector3();
|
||||
const _raycaster = new THREE.Raycaster();
|
||||
|
||||
export function InteractableObject({
|
||||
kind,
|
||||
label,
|
||||
position,
|
||||
bodyRef,
|
||||
onPress,
|
||||
onRelease = () => {},
|
||||
children,
|
||||
}: InteractableObjectProps): React.JSX.Element {
|
||||
export function InteractableObject(
|
||||
props: InteractableObjectProps,
|
||||
): React.JSX.Element {
|
||||
const { kind, label, position, bodyRef, onPress, children } = props;
|
||||
const camera = useThree((state) => state.camera);
|
||||
const groupRef = useRef<THREE.Group>(null);
|
||||
const debugSphereRef = useRef<THREE.Mesh>(null);
|
||||
|
||||
const handle = useRef<InteractableHandle>({
|
||||
kind,
|
||||
label,
|
||||
onPress,
|
||||
onRelease,
|
||||
});
|
||||
const handle = useRef<InteractableHandle>(
|
||||
props.kind === "grab"
|
||||
? { kind: props.kind, label, onPress, onRelease: props.onRelease }
|
||||
: { kind: props.kind, label, onPress },
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
handle.current.onPress = onPress;
|
||||
handle.current.onRelease = onRelease;
|
||||
});
|
||||
if (props.kind === "grab") {
|
||||
const current = handle.current as GrabInteractableHandle;
|
||||
current.label = label;
|
||||
current.onPress = onPress;
|
||||
current.onRelease = props.onRelease;
|
||||
return;
|
||||
}
|
||||
|
||||
useDebugFolder("Interaction", (folder) => {
|
||||
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]);
|
||||
|
||||
const setupInteractionDebugFolder = useCallback((folder: GUI) => {
|
||||
folder
|
||||
.add({ radius: INTERACTION_RADIUS }, "radius")
|
||||
.name("Interaction radius")
|
||||
.disable();
|
||||
});
|
||||
}, []);
|
||||
|
||||
useDebugFolder("Interaction", setupInteractionDebugFolder);
|
||||
|
||||
useFrame(() => {
|
||||
const group = groupRef.current;
|
||||
|
||||
@@ -35,7 +35,7 @@ export class AudioManager {
|
||||
|
||||
logger.error("AudioManager", "Failed to play sound", {
|
||||
path,
|
||||
error,
|
||||
error: AudioManager._toLogValue(error),
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -66,11 +66,20 @@ export class AudioManager {
|
||||
return pooledAudio;
|
||||
}
|
||||
|
||||
return existingPool[0]!;
|
||||
const recycledAudio = existingPool[0];
|
||||
if (recycledAudio) return recycledAudio;
|
||||
}
|
||||
|
||||
const initialAudio = new Audio(path);
|
||||
this._audioPools.set(path, [initialAudio]);
|
||||
return initialAudio;
|
||||
}
|
||||
|
||||
private static _toLogValue(error: unknown): Error | DOMException | string {
|
||||
if (error instanceof Error || error instanceof DOMException) {
|
||||
return error;
|
||||
}
|
||||
|
||||
return String(error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
GrabInteractableHandle,
|
||||
InteractableHandle,
|
||||
InteractionSnapshot,
|
||||
} from "@/types/interaction";
|
||||
@@ -8,7 +9,7 @@ export class InteractionManager {
|
||||
|
||||
private _focused: InteractableHandle | null = null;
|
||||
private _holding = false;
|
||||
private _holdingHandle: InteractableHandle | null = null;
|
||||
private _holdingHandle: GrabInteractableHandle | null = null;
|
||||
private _snapshot: InteractionSnapshot = {
|
||||
focused: null,
|
||||
holding: false,
|
||||
@@ -40,8 +41,14 @@ export class InteractionManager {
|
||||
pressInteract(): void {
|
||||
if (!this._focused) return;
|
||||
|
||||
this._holding = this._focused.kind === "grab";
|
||||
if (this._holding) this._holdingHandle = this._focused;
|
||||
if (this._focused.kind === "grab") {
|
||||
this._holding = true;
|
||||
this._holdingHandle = this._focused;
|
||||
} else {
|
||||
this._holding = false;
|
||||
this._holdingHandle = null;
|
||||
}
|
||||
|
||||
this._focused.onPress();
|
||||
this._emit();
|
||||
}
|
||||
|
||||
@@ -1,12 +1,22 @@
|
||||
export type InteractableKind = "grab" | "trigger";
|
||||
|
||||
export interface InteractableHandle {
|
||||
kind: InteractableKind;
|
||||
export interface TriggerInteractableHandle {
|
||||
kind: "trigger";
|
||||
label: string;
|
||||
onPress: () => void;
|
||||
}
|
||||
|
||||
export interface GrabInteractableHandle {
|
||||
kind: "grab";
|
||||
label: string;
|
||||
onPress: () => void;
|
||||
onRelease: () => void;
|
||||
}
|
||||
|
||||
export type InteractableHandle =
|
||||
| TriggerInteractableHandle
|
||||
| GrabInteractableHandle;
|
||||
|
||||
export interface InteractionSnapshot {
|
||||
focused: InteractableHandle | null;
|
||||
holding: boolean;
|
||||
|
||||
+12
-1
@@ -1,6 +1,17 @@
|
||||
export type LogLevel = "debug" | "info" | "warn" | "error";
|
||||
|
||||
export type LogContext = Record<string, unknown>;
|
||||
export type LogValue =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| null
|
||||
| undefined
|
||||
| Error
|
||||
| DOMException
|
||||
| { [key: string]: LogValue }
|
||||
| LogValue[];
|
||||
|
||||
export type LogContext = Readonly<Record<string, LogValue>>;
|
||||
|
||||
export interface LogEntry {
|
||||
timestamp: string;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import GUI from "lil-gui";
|
||||
import type { CameraMode, SceneMode } from "@/types/debug";
|
||||
import { isDebugEnabled } from "@/utils/debug/isDebugEnabled";
|
||||
|
||||
export class Debug {
|
||||
private static instance: Debug | null = null;
|
||||
@@ -28,7 +29,7 @@ export class Debug {
|
||||
}
|
||||
|
||||
private constructor() {
|
||||
this.active = new URLSearchParams(window.location.search).has("debug");
|
||||
this.active = isDebugEnabled();
|
||||
this.gui = this.active ? new GUI({ title: "La-Fabrik Debug" }) : null;
|
||||
|
||||
if (this.gui) {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
export function isDebugEnabled(): boolean {
|
||||
if (typeof window === "undefined") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return new URLSearchParams(window.location.search).has("debug");
|
||||
}
|
||||
+2
-3
@@ -4,6 +4,7 @@ import type {
|
||||
LogLevel,
|
||||
LoggerConfig,
|
||||
} from "@/types/logger";
|
||||
import { isDebugEnabled } from "@/utils/debug/isDebugEnabled";
|
||||
|
||||
const LEVEL_PRIORITY: Record<LogLevel, number> = {
|
||||
debug: 10,
|
||||
@@ -102,9 +103,7 @@ function resolveMinLevel(): LogLevel {
|
||||
return "info";
|
||||
}
|
||||
|
||||
const debugEnabled = new URLSearchParams(window.location.search).has("debug");
|
||||
|
||||
return debugEnabled ? "debug" : "info";
|
||||
return isDebugEnabled() ? "debug" : "info";
|
||||
}
|
||||
|
||||
export const logger = new Logger({
|
||||
|
||||
+3
-4
@@ -1,4 +1,4 @@
|
||||
import { useState, useCallback } from "react";
|
||||
import { useState } from "react";
|
||||
import type { Octree } from "three/addons/math/Octree.js";
|
||||
import {
|
||||
PLAYER_SPAWN_Y_GAME,
|
||||
@@ -18,7 +18,6 @@ export function World(): React.JSX.Element {
|
||||
const cameraMode = useCameraMode();
|
||||
const sceneMode = useSceneMode();
|
||||
const [octree, setOctree] = useState<Octree | null>(null);
|
||||
const onOctreeReady = useCallback((o: Octree) => setOctree(o), []);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -28,9 +27,9 @@ export function World(): React.JSX.Element {
|
||||
{cameraMode === "debug" ? <DebugCameraControls /> : null}
|
||||
|
||||
{sceneMode === "game" ? (
|
||||
<Map onOctreeReady={onOctreeReady} />
|
||||
<Map onOctreeReady={setOctree} />
|
||||
) : (
|
||||
<TestScene onOctreeReady={onOctreeReady} />
|
||||
<TestScene onOctreeReady={setOctree} />
|
||||
)}
|
||||
|
||||
{cameraMode !== "debug" ? (
|
||||
|
||||
@@ -6,13 +6,13 @@ import { PlayerCamera } from "@/world/player/PlayerCamera";
|
||||
import { PlayerController } from "@/world/player/PlayerController";
|
||||
|
||||
interface PlayerComponentProps {
|
||||
octree?: Octree | null;
|
||||
octree: Octree | null;
|
||||
spawnY: number;
|
||||
}
|
||||
|
||||
export function PlayerComponent({
|
||||
octree = null,
|
||||
spawnY,
|
||||
octree,
|
||||
}: PlayerComponentProps): React.JSX.Element {
|
||||
const camera = useThree((state) => state.camera);
|
||||
|
||||
|
||||
@@ -123,11 +123,6 @@ export function PlayerController({ octree }: PlayerControllerProps): null {
|
||||
case MOVE_RIGHT_KEY:
|
||||
keys.current.right = false;
|
||||
break;
|
||||
case INTERACT_KEY:
|
||||
if (interaction.getState().focused?.kind === "trigger") {
|
||||
interaction.releaseInteract();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user