Reputation: 11
I like react-three and I am trying to make an FPV character with a body: https://codesandbox.io/s/fpv-player-ers63 So the goal is that the camera is placed at the head level and the character moves around like in original example https://codesandbox.io/s/minecraft-vkgi6 by Paul Henschel drcmda
I have added PerspectiveCamera to change the camera position but now PointerLockControls doesn't work any more and I do not understand why and how to handle it.
Thank you
PerspectiveCamera usage
export const Player = (props) => {
const axe = useRef()
const [ref, api] = useSphere(() => ({ mass: 1, type: "Dynamic", position: [0, 10, 0], ...props }))
const { forward, backward, left, right, jump } = usePlayerControls()
const { camera } = useThree()
const velocity = useRef([0, 0, 0])
useEffect(() => api.velocity.subscribe((v) => (velocity.current = v)), [])
useFrame((state) => {
ref.current.getWorldPosition(camera.position)
frontVector.set(0, 0, Number(backward) - Number(forward))
sideVector.set(Number(left) - Number(right), 0, 0)
direction.subVectors(frontVector, sideVector).normalize().multiplyScalar(SPEED).applyEuler(camera.rotation)
speed.fromArray(velocity.current)
axe.current.children[0].rotation.x = THREE.MathUtils.lerp(
axe.current.children[0].rotation.x,
Math.sin((speed.length() > 1) * state.clock.elapsedTime * 10) / 20,
0.01,
)
axe.current.rotation.copy(camera.rotation)
axe.current.position.copy(camera.position).add(camera.getWorldDirection(rotation).multiplyScalar(1))
api.velocity.set(direction.x, velocity.current[1], direction.z)
if (jump && Math.abs(velocity.current[1].toFixed(2)) < 0.05) api.velocity.set(velocity.current[0], 10, velocity.current[2])
})
return (
<>
<mesh ref={ref} />
<group ref={axe} onPointerMissed={(e) => (axe.current.children[0].rotation.x = -0.5)}>
<group position={[0, 7, 0]}>
<PerspectiveCamera makeDefault={true} fov={75}>
<Body position={[0, -4, 1]} />
</PerspectiveCamera>
</group>
</group>
</>
)
}
PointerLockControls usage
export default function App() {
return (
<Canvas
shadows
gl={{ alpha: false }}
camera={{ fov: 45 }}
raycaster={{
computeOffsets: (e) => ({ offsetX: e.target.width / 2, offsetY: e.target.height / 2 }),
}}>
<Sky sunPosition={[100, 20, 100]} />
<ambientLight intensity={0.3} />
<pointLight castShadow intensity={0.8} position={[100, 100, 100]} />
<Physics gravity={[0, -30, 0]}>
<Ground />
<Player />
<Reflector
blur={[0, 0]} // Blur ground reflections (width, heigt), 0 skips blur
mixBlur={0.1} // How much blur mixes with surface roughness
mixStrength={0.25} // Strength of the reflections
resolution={1024} // Off-buffer resolution, lower=faster, higher=better quality
args={[10, 30]} // PlaneBufferGeometry arguments
position={[0, 0, -20]}
rotation={[0, 0, 2 * Math.PI]}
mirror={0.5} // Mirror environment, 0 = texture colors, 1 = pick up env colors
minDepthThreshold={0.25}
maxDepthThreshold={1}
depthScale={50}>
{(Material, props) => <Material metalness={0} roughness={0} {...props} />}
</Reflector>
</Physics>
<PointerLockControls />
</Canvas>
)
}
Browser: Firefox 94.0.2 (64-bit), macOS Monterey
"dependencies": { "@pmndrs/branding": "0.0.8", "@react-three/cannon": "4.0.1", "@react-three/drei": "7.17.2", "@react-three/fiber": "7.0.17", "@types/three": "0.133.1", "react": "17.0.2", "react-dom": "17.0.2", "react-scripts": "4.0.3", "simplex-noise": "3.0.0", "three": "0.133.1", "zustand": "3.6.1" },
https://codesandbox.io/s/fpv-player-ers63
Upvotes: 0
Views: 944
Reputation: 11
Well, I found that it is sufficient to offset the camera in useFrame update https://codesandbox.io/s/fpv-player-ers63
export const Player = (props) => {
const axe = useRef()
const [ref, api] = useSphere(() => ({ args: [1], mass: 1, type: "Dynamic", position: [0, 10, 0], ...props }))
const { forward, backward, left, right, jump } = usePlayerControls()
const { camera } = useThree()
const velocity = useRef([0, 0, 0])
useEffect(() => api.velocity.subscribe((v) => (velocity.current = v)), [])
useFrame((state) => {
ref.current.getWorldPosition(camera.position)
frontVector.set(0, 0, Number(backward) - Number(forward))
sideVector.set(Number(left) - Number(right), 0, 0)
direction.subVectors(frontVector, sideVector).normalize().multiplyScalar(SPEED).applyEuler(camera.rotation)
speed.fromArray(velocity.current)
axe.current.children[0].rotation.x = THREE.MathUtils.lerp(
axe.current.children[0].rotation.x,
Math.sin((speed.length() > 1) * state.clock.elapsedTime * 10) / 10,
0.1,
)
axe.current.rotation.copy(camera.rotation)
axe.current.position.copy(camera.position).add(camera.getWorldDirection(rotation).multiplyScalar(1))
camera.position.setY(camera.position.y + 8)
api.velocity.set(direction.x, velocity.current[1], direction.z)
if (jump && Math.abs(velocity.current[1].toFixed(2)) < 0.05) api.velocity.set(velocity.current[0], 10, velocity.current[2])
})
return (
<>
<mesh ref={ref} />
<group ref={axe} onPointerMissed={(e) => (axe.current.children[0].rotation.x = -0.5)}>
<Body position={[0, 4, 0]} />
</group>
</>
)
}
Upvotes: 1