three.jsreact-three-fiberreact-three-rapier

R3F / Rapier - How to use a Static Collider for First-Person Movement


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.


Solution

  • 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>