I want to create a custom PlayerController for react-three-fiber by using rapier.
I'm using a MouseMovement combined with KeyControls (WASD). I want a simple CapsuleCollider, to preventing the player from passing through other Rigidbodies. But the Collider should be not affected by physics forces (like gravity or velocity).
Problem
When moving around, the CapsuleCollider seems to be affected from Physics and moves around, see https://imgur.com/ztMIzAn. When I'm using type="kinematicVelocity" it moves correctly, but then the collision detection does not work.
Experience.jsx
<Physics debug>
<Floor />
<Player />
</Physics>
Player.jsx
// other code here
useEffect(() => {
window.addEventListener('keydown', handleKeyDown);
window.addEventListener('keyup', handleKeyUp);
window.addEventListener('mousedown', handleMouseDown);
window.addEventListener('mouseup', handleMouseUp);
const handleMouseMove = (e) => {
if (isMousePressed) {
yaw.current -= e.movementX * mouseSensitivity;
pitch.current = Math.max(
Math.min(pitch.current - e.movementY * mouseSensitivity, Math.PI / 2),
-Math.PI / 2
);
}
};
window.addEventListener('mousemove', handleMouseMove);
return () => {
window.removeEventListener('keydown', handleKeyDown);
window.removeEventListener('keyup', handleKeyUp);
window.removeEventListener('mousedown', handleMouseDown);
window.removeEventListener('mouseup', handleMouseUp);
window.removeEventListener('mousemove', handleMouseMove);
};
}, [isMousePressed]); // Re-run when `isMousePressed` changes
// Movement logic
useFrame((_, delta) => {
if (!body.current || !cameraRef.current) return;
const impulse = new Vector3();
const rotation = new Euler(0, yaw.current, 0, 'YXZ');
const forward = new Vector3(0, 0, -1).applyEuler(rotation);
const right = new Vector3(1, 0, 0).applyEuler(rotation);
if (keysPressed.forward) impulse.add(forward);
if (keysPressed.backward) impulse.sub(forward);
if (keysPressed.leftward) impulse.sub(right);
if (keysPressed.rightward) impulse.add(right);
impulse.normalize().multiplyScalar(speed * (isRunning ? runMultiplier : 1));
// Apply movement to RigidBody
body.current.setLinvel(impulse, true);
// Update camera rotation
const quaternion = new Quaternion();
quaternion.setFromEuler(new Euler(pitch.current, yaw.current, 0, 'YXZ'));
cameraRef.current.quaternion.copy(quaternion);
// Sync camera position with RigidBody
const bodyPosition = body.current.translation();
cameraRef.current.position.set(bodyPosition.x, bodyPosition.y + 1.5, bodyPosition.z);
});
return (
<>
<PerspectiveCamera
ref={cameraRef}
position={position}
makeDefault
/>
<RigidBody
colliders={false}
ref={body}
position={position}
friction={0.5}
restitution={0}>
<CapsuleCollider args={[0.6, 0.8]} />
</RigidBody>
</>
);
Floor.jsx:
import { RigidBody } from '@react-three/rapier';
import { useEffect, useRef } from 'react';
export function Floor() {
const floor = useRef();
return (
<>
<RigidBody type="fixed">
<mesh
position={[0, -0.25, 0]}
ref={floor}
receiveShadow={true}>
<boxGeometry args={[50, 0.5, 50]} />
<meshStandardMaterial color="#D9CABD" opacity={0} transparent={true} />
</mesh>
</RigidBody>
</>
);
}
I would be thankful for any help.
I fixed it. I had to set the prop lockRotations={true}
:
<RigidBody
colliders={false}
ref={body}
position={position}
friction={0.3}
restitution={0}
lockRotations={true}
type="dynamic"
gravityScale={0}
>
<CapsuleCollider args={[0.6, 0.8]} />
</RigidBody>