refactor: clean map gameplay architecture
This commit is contained in:
@@ -6,10 +6,10 @@ export function useRepairMovementLocked(): boolean {
|
||||
switch (state.mainState) {
|
||||
case "ebike":
|
||||
return isRepairMovementLocked(state.ebike.currentStep);
|
||||
case "pylone":
|
||||
return isRepairMovementLocked(state.pylone.currentStep);
|
||||
case "ferme":
|
||||
return isRepairMovementLocked(state.ferme.currentStep);
|
||||
case "pylon":
|
||||
return isRepairMovementLocked(state.pylon.currentStep);
|
||||
case "farm":
|
||||
return isRepairMovementLocked(state.farm.currentStep);
|
||||
case "intro":
|
||||
case "outro":
|
||||
return false;
|
||||
@@ -23,6 +23,7 @@ function isRepairMovementLocked(step: MissionStep): boolean {
|
||||
step === "fragmented" ||
|
||||
step === "scanning" ||
|
||||
step === "repairing" ||
|
||||
step === "reassembling"
|
||||
step === "reassembling" ||
|
||||
step === "done"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -5,8 +5,8 @@ import {
|
||||
HAND_TRACKING_JPEG_QUALITY,
|
||||
HAND_TRACKING_RESPONSE_TIMEOUT_MS,
|
||||
HAND_TRACKING_TARGET_FPS,
|
||||
getHandTrackingWsUrl,
|
||||
} from "@/data/handTrackingConfig";
|
||||
import { getHandTrackingWsUrl } from "@/utils/handTracking/handTrackingEndpoint";
|
||||
import {
|
||||
INITIAL_HAND_TRACKING_SNAPSHOT,
|
||||
getCameraStreamWithTimeout,
|
||||
|
||||
@@ -1,15 +1,6 @@
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { useMemo } from "react";
|
||||
import * as THREE from "three";
|
||||
import { disposeObject3D } from "@/utils/three/dispose";
|
||||
|
||||
export function useClonedObject<T extends THREE.Object3D>(object: T): T {
|
||||
const clone = useMemo(() => object.clone(true) as T, [object]);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
disposeObject3D(clone);
|
||||
};
|
||||
}, [clone]);
|
||||
|
||||
return clone;
|
||||
return useMemo(() => object.clone(true) as T, [object]);
|
||||
}
|
||||
|
||||
@@ -16,6 +16,24 @@ interface TerrainHeightSampler {
|
||||
getHeight: (x: number, z: number) => number | null;
|
||||
}
|
||||
|
||||
interface CachedTerrainHeightSampler {
|
||||
key: string;
|
||||
sampler: TerrainHeightSampler;
|
||||
}
|
||||
|
||||
const terrainSamplerCache = new WeakMap<
|
||||
THREE.Object3D,
|
||||
CachedTerrainHeightSampler
|
||||
>();
|
||||
|
||||
function createTerrainSamplerCacheKey(
|
||||
position: Vector3Tuple,
|
||||
rotation: Vector3Tuple,
|
||||
scale: Vector3Tuple,
|
||||
): string {
|
||||
return `${position.join(",")}|${rotation.join(",")}|${scale.join(",")}`;
|
||||
}
|
||||
|
||||
function createTerrainHeightSampler(
|
||||
scene: THREE.Object3D,
|
||||
position: Vector3Tuple,
|
||||
@@ -64,10 +82,23 @@ export function useTerrainHeightSampler(): TerrainHeightSampler {
|
||||
const rotation = terrainNode?.rotation ?? DEFAULT_TERRAIN_ROTATION;
|
||||
const scale = terrainNode?.scale ?? DEFAULT_TERRAIN_SCALE;
|
||||
|
||||
return useMemo(
|
||||
() => createTerrainHeightSampler(scene, position, rotation, scale),
|
||||
[position, rotation, scale, scene],
|
||||
);
|
||||
return useMemo(() => {
|
||||
const key = createTerrainSamplerCacheKey(position, rotation, scale);
|
||||
const cached = terrainSamplerCache.get(scene);
|
||||
|
||||
if (cached?.key === key) {
|
||||
return cached.sampler;
|
||||
}
|
||||
|
||||
const sampler = createTerrainHeightSampler(
|
||||
scene,
|
||||
position,
|
||||
rotation,
|
||||
scale,
|
||||
);
|
||||
terrainSamplerCache.set(scene, { key, sampler });
|
||||
return sampler;
|
||||
}, [position, rotation, scale, scene]);
|
||||
}
|
||||
|
||||
export function useTerrainSnappedPosition(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { INSTANCED_MAP_EXCEPTIONS } from "@/world/vegetation/vegetationConfig";
|
||||
import { INSTANCED_MAP_EXCEPTIONS } from "@/data/world/vegetationConfig";
|
||||
import type { MapNode } from "@/types/map/mapScene";
|
||||
import {
|
||||
type MapNodeInstanceTransform,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { useCallback, useMemo, useRef, useState } from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { CHUNK_CONFIG } from "@/data/world/chunkStreamingConfig";
|
||||
|
||||
@@ -18,6 +18,7 @@ export function useVisibleWorldChunks<TChunk extends WorldChunkLike>(
|
||||
): readonly TChunk[] {
|
||||
const camera = useThree((state) => state.camera);
|
||||
const lastUpdateRef = useRef(-CHUNK_CONFIG.updateInterval);
|
||||
const activeChunkKeysRef = useRef<Set<string>>(new Set());
|
||||
const [activeChunkKeys, setActiveChunkKeys] = useState<Set<string>>(
|
||||
() => new Set(),
|
||||
);
|
||||
@@ -32,7 +33,7 @@ export function useVisibleWorldChunks<TChunk extends WorldChunkLike>(
|
||||
chunk.centerX - cameraX,
|
||||
chunk.centerZ - cameraZ,
|
||||
);
|
||||
const wasActive = activeChunkKeys.has(chunk.key);
|
||||
const wasActive = activeChunkKeysRef.current.has(chunk.key);
|
||||
const radius = wasActive
|
||||
? CHUNK_CONFIG.unloadRadius
|
||||
: CHUNK_CONFIG.loadRadius;
|
||||
@@ -42,10 +43,11 @@ export function useVisibleWorldChunks<TChunk extends WorldChunkLike>(
|
||||
}
|
||||
}
|
||||
|
||||
if (areSetsEqual(nextKeys, activeChunkKeys)) return;
|
||||
if (areSetsEqual(nextKeys, activeChunkKeysRef.current)) return;
|
||||
|
||||
activeChunkKeysRef.current = nextKeys;
|
||||
setActiveChunkKeys(nextKeys);
|
||||
}, [activeChunkKeys, camera, chunks]);
|
||||
}, [camera, chunks]);
|
||||
|
||||
useFrame(({ clock }) => {
|
||||
if (!streamingEnabled) return;
|
||||
@@ -57,18 +59,26 @@ export function useVisibleWorldChunks<TChunk extends WorldChunkLike>(
|
||||
updateActiveChunks();
|
||||
});
|
||||
|
||||
if (!streamingEnabled) return chunks;
|
||||
return useMemo(() => {
|
||||
if (!streamingEnabled) return chunks;
|
||||
|
||||
return chunks.filter((chunk) => {
|
||||
if (activeChunkKeys.size > 0) {
|
||||
return activeChunkKeys.has(chunk.key);
|
||||
}
|
||||
return chunks.filter((chunk) => {
|
||||
if (activeChunkKeys.size > 0) {
|
||||
return activeChunkKeys.has(chunk.key);
|
||||
}
|
||||
|
||||
return (
|
||||
Math.hypot(
|
||||
chunk.centerX - camera.position.x,
|
||||
chunk.centerZ - camera.position.z,
|
||||
) <= CHUNK_CONFIG.loadRadius
|
||||
);
|
||||
});
|
||||
return (
|
||||
Math.hypot(
|
||||
chunk.centerX - camera.position.x,
|
||||
chunk.centerZ - camera.position.z,
|
||||
) <= CHUNK_CONFIG.loadRadius
|
||||
);
|
||||
});
|
||||
}, [
|
||||
activeChunkKeys,
|
||||
camera.position.x,
|
||||
camera.position.z,
|
||||
chunks,
|
||||
streamingEnabled,
|
||||
]);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user