import React, { useEffect, useState, useRef } from "react"; import * as THREE from "three"; import { useFrame, useThree } from "@react-three/fiber"; import { NavMeshQuery } from "@recast-navigation/core"; import { Line } from "@react-three/drei"; import { useActiveTool } from "../../../store/store"; import { usePlayButtonStore } from "../../../store/usePlayButtonStore"; interface PathNavigatorProps { navMesh: any; selectedPoints: any; id: string; speed: number; bufferTime: number; hitCount: number; } export default function PathNavigator({ navMesh, selectedPoints, id, speed, bufferTime, hitCount, }: PathNavigatorProps) { const [path, setPath] = useState<[number, number, number][]>([]); const progressRef = useRef(0); const distancesRef = useRef([]); const totalDistanceRef = useRef(0); const currentSegmentIndex = useRef(0); const [stop, setStop] = useState(true); const { scene } = useThree(); const { isPlaying, setIsPlaying } = usePlayButtonStore(); const [startPoint, setStartPoint] = useState(new THREE.Vector3()); const isWaiting = useRef(false); // Flag to track waiting state const delayTime = bufferTime; const movingForward = useRef(true); // Tracks whether the object is moving forward // Compute distances and total distance when the path changes useEffect(() => { if (!scene || !id || path.length < 2) return; let totalDistance = 0; const distances: number[] = []; for (let i = 0; i < path.length - 1; i++) { const start = new THREE.Vector3(...path[i]); const end = new THREE.Vector3(...path[i + 1]); const segmentDistance = start.distanceTo(end); distances.push(segmentDistance); totalDistance += segmentDistance; } distancesRef.current = distances; totalDistanceRef.current = totalDistance; progressRef.current = 0; }, [path]); // Compute the path using NavMeshQuery useEffect(() => { if (!navMesh || selectedPoints.length === 0) return; const allPoints = selectedPoints.flat(); const computedPath: [number, number, number][] = []; for (let i = 0; i < allPoints.length - 1; i++) { const start = allPoints[i]; setStartPoint( new THREE.Vector3(allPoints[0].x, allPoints[0].y, allPoints[0].z) ); const end = allPoints[i + 1]; try { const navMeshQuery = new NavMeshQuery(navMesh); const { path: segmentPath } = navMeshQuery.computePath(start, end); if (!segmentPath || segmentPath.length === 0) { continue; } computedPath.push( ...segmentPath.map(({ x, y, z }): [number, number, number] => [ x, y + 0.1, z, ]) ); } catch (error) {} } if (computedPath.length > 0) { setPath(computedPath); currentSegmentIndex.current = 0; } }, [selectedPoints, navMesh]); useFrame((_, delta) => { if (!scene || !id || path.length < 2) return; // Find the object in the scene by its UUID const findObject = scene.getObjectByProperty("uuid", id); if (!findObject) return; if (isPlaying) { const fast = speed; progressRef.current += delta * fast; let coveredDistance = progressRef.current; let accumulatedDistance = 0; let index = 0; // Determine the current segment of the path while ( index < distancesRef.current.length && coveredDistance > accumulatedDistance + distancesRef.current[index] ) { accumulatedDistance += distancesRef.current[index]; index++; } if (index >= distancesRef.current.length) { progressRef.current = totalDistanceRef.current; if (!isWaiting.current) { isWaiting.current = true; // Set waiting flag if (movingForward.current) { // Moving forward: reached the end, wait for `delay` // console.log( // "Reached end position. Waiting for delay:", // delayTime, // "seconds" // ); setTimeout(() => { // After delay, reverse direction movingForward.current = false; progressRef.current = 0; // Reset progress path.reverse(); // Reverse the path distancesRef.current.reverse(); isWaiting.current = false; // Reset waiting flag }, delayTime * 1000); // Wait for `delay` seconds } } return; } // Interpolate position within the current segment const start = new THREE.Vector3(...path[index]); const end = new THREE.Vector3(...path[index + 1]); const segmentDistance = distancesRef.current[index]; const t = Math.min( (coveredDistance - accumulatedDistance) / segmentDistance, 1 ); // Clamp t to avoid overshooting const position = start.clone().lerp(end, t); findObject.position.copy(position); // Rotate the object to face the direction of movement const direction = new THREE.Vector3().subVectors(end, start).normalize(); const targetYRotation = Math.atan2(direction.x, direction.z); findObject.rotation.y += (targetYRotation - findObject.rotation.y) * 0.1; } else { findObject.position.copy(startPoint); } }); return ( <> {path.length > 0 && ( <> {selectedPoints.map((val: any, i: any) => ( ))} )} ); }