How to make Camera follow a react three rapier object,

Hi All,

I’m experimenting with using react-three/rapier as my physics engine, however I can’t get a camera to follow the position of a Rapier object. The camera follows the object as it falls down the z axis, but as soon as it hits the ground and bounces around the camera bounces around on a seemingly different target, I’ve created a sandbox to show this here:https://codesandbox.io/s/solitary-snowflake-199g3k

As you can see from the below gif, the camera follows the box as it falls, but then pans wildly after impact with the floor

component is as below:

import { OrbitControls, PerspectiveCamera } from "@react-three/drei";
import { useFrame } from "@react-three/fiber";
import { RigidBody, CuboidCollider } from "@react-three/rapier";

import * as THREE from "three";

import { useRef } from "react";

const Scene = () => {
  const ref = useRef();
  const bodyRef = useRef();
  const cameraRef = useRef();

  const lookAtVec = new THREE.Vector3(0, 0, 0);
  const cameraVector = new THREE.Vector3(0, 0, 0);

  useFrame((state) => {
    const boxPos = bodyRef.current.translation();
    console.log(boxPos);
    lookAtVec.set(boxPos.x, boxPos.y, boxPos.z);
    cameraVector.lerp(lookAtVec, 0.1);
    state.camera.lookAt(cameraVector);
    state.camera.updateProjectionMatrix();
  });

  return (
    <>
      <ambientLight />
      <PerspectiveCamera position={[10, 4, 10]} makeDefault ref={cameraRef} />
      <OrbitControls />
      <RigidBody ref={bodyRef} colliders={"cuboid"} restitution={1.1}>
        <mesh ref={ref} position={[0, 4, 0]}>
          <boxGeometry args={[0.5, 0.5, 0.5]} />
          <meshStandardMaterial color={"green"} />
        </mesh>
      </RigidBody>
      <CuboidCollider
        position={[0, -2, 0]}
        args={[20, 0.5, 20]}
        color={"green"}
      ></CuboidCollider>
    </>
  );
};

export default Scene;

boxdrop

@willmadd Indeed strange - what if instead you follow the mesh position?

Check out this rapier + vanilla three example i cooked up for reference:

The Mesh position never changes, it simply stays at the initial values so this isn’t an option unfortunately,

Thanks for the demo @notchris , however looking at the demo am I right in thinking that in your demo the camera position is just tied to the y axis, so the camera ‘falls’ in time with the object, but the lookAt is never changed?

  const vec = new THREE.Vector3()
  const target = new THREE.Vector3(0, 0, 0)
  const ref = useRef()
  useFrame((state) => {
    target.lerp(ref.current.getWorldPosition(vec), 0.02)
    state.camera.lookAt(target)
  })
  return (
    <RigidBody colliders="ball" restitution={0.15}>
      <mesh ref={ref} castShadow receiveShadow {...props}>

what does translation do? i also thought it should be the position but it seems to be something else.

1 Like

Thank you @drcmda , that’s the solution… You’ve saved me a lot of head scratching,

Yeah I was under the impression that translate() got the position, which it does on the Y axis, strange

Hey guys,

There’s a couple issues at play here and I wanted to show you the correct way to resolve what’s happening.
First here’s a updated sandbox: r3f-rapier updated camera follow - CodeSandbox

First thing I did differently is just to clean it up I imported the Box shape from Drei. This is the same thing as the React-three-Rapier demos, to help it be cohesive.
Next I moved the position props off the mesh and onto the RidgedBody where they belonged

so:

<RigidBody ref={bodyRef} colliders={"cuboid"} restitution={1.1}>
        <mesh ref={ref} position={[0, 4, 0]}>
          <boxGeometry args={[0.5, 0.5, 0.5]} />
          <meshStandardMaterial color={"green"} />
        </mesh>
      </RigidBody>

Became:

<RigidBody ref={bodyRef} colliders={"cuboid"} restitution={1.1} position={[0, 10, 10]}>
        <Box scale={0.5} receiveShadow castShadow>
          <meshPhysicalMaterial color={"green"} />
        </Box>
      </RigidBody>

The main thing to remember is that there’s two worlds happening in parallel. Rapier to simulate and test/solve collisions, and ThreeJS to render.
So WHILE YOU CAN take vector, position, and other data from one sim to the other I’d be very careful about it.

In the first example the position of the ridged body was never correctly set the first time (was at 0) so of course it never fell. (translate didnt update)

But the mesh inside it did? I dont know that part gets confusing
Then, USING THREE you animated it falling in space instead of letting Rapier do it…

Also, there’s an invisible floor collider on the scene, so it’s just got a thing to bounce off of but nothing is setup to render in Three (I left that)

Anyway, moving props to the ridged body correctly initializes rapier which then updates correctly with translation() so the rest works as it should

1 Like