refactor feature folders by code type
This commit is contained in:
@@ -0,0 +1,126 @@
|
||||
import {
|
||||
useRef,
|
||||
useEffect,
|
||||
useCallback,
|
||||
forwardRef,
|
||||
useImperativeHandle,
|
||||
type ElementRef,
|
||||
} from "react";
|
||||
import { useFrame, useThree } from "@react-three/fiber";
|
||||
import { OrbitControls } from "@react-three/drei";
|
||||
import * as THREE from "three";
|
||||
|
||||
type OrbitControlsRef = ElementRef<typeof OrbitControls>;
|
||||
|
||||
interface FlyControllerProps {
|
||||
speed?: number;
|
||||
verticalSpeed?: number;
|
||||
onPositionChange?: (position: THREE.Vector3) => void;
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
interface FlyControllerRef {
|
||||
controls: OrbitControlsRef | null;
|
||||
}
|
||||
|
||||
export const FlyController = forwardRef<FlyControllerRef, FlyControllerProps>(
|
||||
(
|
||||
{ speed = 10, verticalSpeed = 5, onPositionChange, disabled = false },
|
||||
ref,
|
||||
) => {
|
||||
const { camera: rawCamera } = useThree();
|
||||
const cameraRef = useRef(rawCamera);
|
||||
const keys = useRef<{ [key: string]: boolean }>({});
|
||||
const controlsRef = useRef<OrbitControlsRef | null>(null);
|
||||
const lastPosition = useRef(new THREE.Vector3());
|
||||
|
||||
useImperativeHandle(ref, () => ({
|
||||
controls: controlsRef.current,
|
||||
}));
|
||||
|
||||
const handleKeyDown = useCallback((e: KeyboardEvent) => {
|
||||
keys.current[e.code] = true;
|
||||
}, []);
|
||||
|
||||
const handleKeyUp = useCallback((e: KeyboardEvent) => {
|
||||
keys.current[e.code] = false;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener("keydown", handleKeyDown);
|
||||
window.addEventListener("keyup", handleKeyUp);
|
||||
return () => {
|
||||
window.removeEventListener("keydown", handleKeyDown);
|
||||
window.removeEventListener("keyup", handleKeyUp);
|
||||
};
|
||||
}, [handleKeyDown, handleKeyUp]);
|
||||
|
||||
useFrame((_, delta) => {
|
||||
// Disabled mode keeps OrbitControls active without keyboard movement.
|
||||
if (disabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Supports AZERTY, QWERTY, and arrow-key movement.
|
||||
const isForward =
|
||||
keys.current["KeyW"] || keys.current["KeyZ"] || keys.current["ArrowUp"];
|
||||
const isBackward = keys.current["KeyS"] || keys.current["ArrowDown"];
|
||||
const isLeft =
|
||||
keys.current["KeyQ"] ||
|
||||
keys.current["KeyA"] ||
|
||||
keys.current["ArrowLeft"];
|
||||
const isRight = keys.current["KeyD"] || keys.current["ArrowRight"];
|
||||
|
||||
const direction = new THREE.Vector3();
|
||||
const frontVector = new THREE.Vector3(
|
||||
0,
|
||||
0,
|
||||
Number(isBackward) - Number(isForward),
|
||||
);
|
||||
const sideVector = new THREE.Vector3(
|
||||
Number(isRight) - Number(isLeft),
|
||||
0,
|
||||
0,
|
||||
);
|
||||
|
||||
direction.subVectors(frontVector, sideVector);
|
||||
if (direction.lengthSq() > 0) {
|
||||
direction.normalize().multiplyScalar(speed * delta);
|
||||
direction.applyQuaternion(cameraRef.current.quaternion);
|
||||
cameraRef.current.position.add(direction);
|
||||
}
|
||||
|
||||
// Space moves up; Shift moves down.
|
||||
if (keys.current["Space"]) {
|
||||
cameraRef.current.position.y += verticalSpeed * delta;
|
||||
}
|
||||
if (keys.current["ShiftLeft"] || keys.current["ShiftRight"]) {
|
||||
cameraRef.current.position.y -= verticalSpeed * delta;
|
||||
}
|
||||
|
||||
if (
|
||||
onPositionChange &&
|
||||
!cameraRef.current.position.equals(lastPosition.current)
|
||||
) {
|
||||
lastPosition.current.copy(cameraRef.current.position);
|
||||
onPositionChange(cameraRef.current.position);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<OrbitControls
|
||||
ref={controlsRef}
|
||||
makeDefault
|
||||
enableDamping
|
||||
dampingFactor={0.05}
|
||||
mouseButtons={{
|
||||
LEFT: THREE.MOUSE.ROTATE,
|
||||
MIDDLE: THREE.MOUSE.DOLLY,
|
||||
RIGHT: THREE.MOUSE.PAN,
|
||||
}}
|
||||
/>
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
FlyController.displayName = "FlyController";
|
||||
Reference in New Issue
Block a user