diff --git a/src/Ecctrl.tsx b/src/Ecctrl.tsx index 052552c..7118980 100644 --- a/src/Ecctrl.tsx +++ b/src/Ecctrl.tsx @@ -12,7 +12,7 @@ import { import { useEffect, useRef, useMemo, useState, useImperativeHandle, forwardRef, type ReactNode, type ForwardRefRenderFunction } from "react"; import * as THREE from "three"; import { useControls } from "leva"; -import { useFollowCam } from "./hooks/useFollowCam"; +import { updateFollowCamRotation, useFollowCam } from "./hooks/useFollowCam"; import { useGame } from "./stores/useGame"; import { useJoystickControls } from "./stores/useJoystickControls"; import { QueryFilterFlags } from "@dimforge/rapier3d-compat"; @@ -138,6 +138,7 @@ const Ecctrl: ForwardRefRenderFunction = ({ if (characterRef.current) { characterRef.current.rotateCamera = rotateCamera; characterRef.current.rotateCharacterOnY = rotateCharacterOnY; + characterRef.current.setCameraRotation = setCameraRotation; return characterRef.current!; } return null; @@ -871,10 +872,11 @@ const Ecctrl: ForwardRefRenderFunction = ({ */ const rotateCamera = (x: number, y: number) => { pivot.rotation.y += y; - followCam.rotation.x = Math.min( + const limitedX = Math.min( Math.max(followCam.rotation.x + x, camLowLimit), camUpLimit ); + updateFollowCamRotation(followCam, limitedX); }; /** @@ -884,6 +886,20 @@ const Ecctrl: ForwardRefRenderFunction = ({ modelEuler.y += rad; }; + /** + * Set camera rotation + * @param {number} x - The x angle in radians + * @param {number} y - The y angle in radians + */ + const setCameraRotation = (x: number, y: number) => { + pivot.rotation.y = y; + const limitedX = Math.min( + Math.max(x, camLowLimit), + camUpLimit + ); + updateFollowCamRotation(followCam, limitedX); + }; + useEffect(() => { // Initialize directional light if (followLight) { @@ -1542,6 +1558,7 @@ export type camListenerTargetType = "document" | "domElement"; export interface CustomEcctrlRigidBody extends RapierRigidBody { rotateCamera?: (x: number, y: number) => void; rotateCharacterOnY?: (rad: number) => void; + setCameraRotation?: (x: number, y: number) => void; } export interface EcctrlProps extends RigidBodyProps { diff --git a/src/hooks/useFollowCam.tsx b/src/hooks/useFollowCam.tsx index 9e2710b..7dfa705 100644 --- a/src/hooks/useFollowCam.tsx +++ b/src/hooks/useFollowCam.tsx @@ -4,6 +4,17 @@ import { useEffect, useMemo, useRef } from "react"; import * as THREE from "three"; import type { camListenerTargetType } from "../Ecctrl"; +export const updateFollowCamRotation = (followCam: THREE.Object3D, vy: number) => { + const cameraDistance = followCam.position.length(); + followCam.rotation.x = vy; + updateFollowCamPosition(followCam, cameraDistance, vy); +} + +export const updateFollowCamPosition = (followCam: THREE.Object3D, distance: number, vy: number) => { + followCam.position.y = -distance * Math.sin(-vy); + followCam.position.z = -distance * Math.cos(-vy); +} + export const useFollowCam = function ({ disableFollowCam = false, disableFollowCamPos = null, @@ -70,9 +81,7 @@ export const useFollowCam = function ({ cameraDistance = followCam.position.length(); if (vy >= camLowLimit && vy <= camUpLimit) { - followCam.rotation.x = vy; - followCam.position.y = -cameraDistance * Math.sin(-vy); - followCam.position.z = -cameraDistance * Math.cos(-vy); + updateFollowCamRotation(followCam, vy); } } return false; @@ -85,8 +94,7 @@ export const useFollowCam = function ({ if (vz >= camMaxDis && vz <= camMinDis) { originZDis.current = vz; - followCam.position.z = originZDis.current * Math.cos(-vy); - followCam.position.y = originZDis.current * Math.sin(-vy); + updateFollowCamPosition(followCam, originZDis.current, vy); } return false; }; @@ -120,9 +128,7 @@ export const useFollowCam = function ({ cameraDistance = followCam.position.length(); if (vy >= camLowLimit && vy <= camUpLimit) { - followCam.rotation.x = vy; - followCam.position.y = -cameraDistance * Math.sin(-vy); - followCam.position.z = -cameraDistance * Math.cos(-vy); + updateFollowCamRotation(followCam, vy); } } @@ -142,8 +148,7 @@ export const useFollowCam = function ({ if (vz >= camMaxDis && vz <= camMinDis) { originZDis.current = vz; - followCam.position.z = originZDis.current * Math.cos(-vy); - followCam.position.y = originZDis.current * Math.sin(-vy); + updateFollowCamPosition(followCam, originZDis.current, vy); } } @@ -161,9 +166,7 @@ export const useFollowCam = function ({ cameraDistance = followCam.position.length(); if (vy >= camLowLimit && vy <= camUpLimit) { - followCam.rotation.x = vy; - followCam.position.y = -cameraDistance * Math.sin(-vy); - followCam.position.z = -cameraDistance * Math.cos(vy); + updateFollowCamRotation(followCam, vy); } }