import { PerspectiveCamera, PointerLockControls } from "@react-three/drei"; import { useFrame, useThree } from "@react-three/fiber"; import { useContext, useEffect, useRef } from "react"; import { AppContext } from "../App"; import { CapsuleCollider, RigidBody, useRapier, vec3 } from "@react-three/rapier"; import { Vector3 } from "three"; import { useBeforePhysicsStep } from "@react-three/rapier"; import { Raycaster } from "three"; const _movespeed = 3.0; export default function Player() { const controlsRef = useRef(); const { keys, setMessages } = useContext(AppContext); const rapier = useRapier(); const controller = useRef(); const collider = useRef(); const rigidbody = useRef(); const camera = useRef(); const raycaster = useRef(new Raycaster()); const { scene, pointer } = useThree(); const refState = useRef({ grounded: false, velocity: vec3(), }); const onClick = (_) => { raycaster.current.setFromCamera(pointer, camera.current); const intersect = raycaster.current.intersectObjects(scene.children).at(0); if (intersect && intersect.object.name === 'ground' && intersect.distance < 10 && controlsRef.current.isLocked) { const message = prompt(); if (message) { setMessages(messages => [...messages, { position: intersect.point, message: message}]); } controlsRef.current.lock(); } }; useEffect(() => { window.addEventListener("click", onClick); return () => window.removeEventListener("click", onClick); }, []); useEffect(() => { const c = rapier.world.createCharacterController(0.1); c.setApplyImpulsesToDynamicBodies(true); c.setCharacterMass(0.2); controller.current = c; }, [rapier]); useFrame((_, delta) => { const fov_axis = +(keys.current.includes('Minus')) - +(keys.current.includes('Equal')); if (fov_axis != 0) { camera.current.fov += 45 * fov_axis * delta; camera.current.updateProjectionMatrix(); } }); useBeforePhysicsStep((world) => { if (controller.current && rigidbody.current && collider.current) { const move_axis_x = +(keys.current.includes('KeyD')) - +(keys.current.includes('KeyA')); const move_axis_z = +(keys.current.includes('KeyW')) - +(keys.current.includes('KeyS')); const { velocity } = refState.current; const position = vec3(rigidbody.current.translation()); const movement = vec3(); const forward = new Vector3(); camera.current.getWorldDirection(forward); const left = new Vector3().crossVectors(forward, camera.current.up); movement.x += move_axis_z * world.timestep * _movespeed * forward.x; movement.z += move_axis_z * world.timestep * _movespeed * forward.z; movement.x += move_axis_x * world.timestep * _movespeed * left.x; movement.z += move_axis_x * world.timestep * _movespeed * left.z; if (refState.current.grounded) { velocity.y = 0; } else { velocity.y -= 9.81 * world.timestep * world.timestep; } movement.add(velocity); controller.current.computeColliderMovement(collider.current, movement); refState.current.grounded = controller.current.computedGrounded(); let correctedMovement = controller.current.computedMovement(); position.add(vec3(correctedMovement)); rigidbody.current.setNextKinematicTranslation(position); } }); return ( ); }