pmndrs / react-three-rapier

🤺 Rapier physics in React
https://react-three-rapier.pmnd.rs
MIT License
1.03k stars 57 forks source link

How to get the position of a falling body #610

Closed rmr-code closed 7 months ago

rmr-code commented 7 months ago

I am using react-three/fiber with react-three-rapier. The following is my scene

import React, { Suspense, useEffect, useRef } from 'react';
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import { Physics, RigidBody } from "@react-three/rapier";

function Page() {
    const OrangeRing = (() => {
          const orangeref = useRef();    

          useFrame(() => {
             if(orangeref.current) {
                console.log(orangeref.current.position); // shows the same position
             }
          });

        return (
          <mesh ref={orangeref}>
             <torusGeometry args={[0.2, 0.1, 16, 100]} />
             <lineBasicMaterial color={"black"} linewidth={10} />
             <meshBasicMaterial color="orange" />
          </mesh>
       )
   });

  return (
    <>
      <Canvas camera={{ position: [3, 3, 2] }}>
        <ambientLight intensity={0.5} />
        <directionalLight position={[-10, 10, 0]} intensity={0.4} />
        <OrbitControls />
        <gridHelper args={[10, 10]} />
        <Suspense>
          <Physics colliders={false}>
            <RigidBody type={"dynamic"} colliders={"trimesh"} restitution={0.5} rotation={[-Math.PI / 2, 0, 0]} position={[0, 2, 0]}>
              <OrangeRing />
            </RigidBody>
            <RigidBody type={"fixed"} colliders={"cuboid"} restitution={0.8} position={[0, 0.02, 0]}>
              <mesh>
                <boxGeometry args={[5, 0.04, 5]} />
                <meshBasicMaterial color="#a8a8a8" />
              </mesh>
            </RigidBody>
          </Physics>
        </Suspense>
      </Canvas>
    </>
  );
}

export default Page;

The torus shape does fall but how do I access its changing position?

Thanks

wiledal commented 7 months ago

Hey @rrrepos,

When you wrap a threejs object in a RigidBody, there are new objects created which are moving, not the original object. You could get the position by instead asking for the worldPosition (https://threejs.org/docs/#api/en/core/Object3D.getWorldPosition), since when you are just checking the position of the object you are getting it relative to its parent, not the world.

The more robust solution might be to check the position of the RigidBody itself, which can be done as described in the Readme: https://github.com/pmndrs/react-three-rapier?tab=readme-ov-file#moving-things-around-and-applying-forces

rmr-code commented 7 months ago

Thanks @wiledal for your quick response. However, when I tried option 2 it gives me a function components cannot have string references. I looks like I am making some silly mistake but cannot figure it out.

import React, { Suspense, useEffect, useRef } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import { Physics, RapierRigidBody, RigidBody } from "@react-three/rapier";

function Page() {

  const orangeRef = useRef<RapierRigidBody>(null);

  return (
    <>
      <Canvas camera={{ position: [3, 3, 2] }}>
        <ambientLight intensity={0.5} />
        <directionalLight position={[-10, 10, 0]} intensity={0.4} />
        <OrbitControls />
        <gridHelper args={[10, 10]} />
        <Suspense>
          <Physics colliders={false}>
            <RigidBody ref={orangeRef} type={"dynamic"} colliders={"trimesh"} restitution={0.5} rotation={[-Math.PI / 2, 0, 0]} position={[0, 2, 0]}>
              <mesh>
                <torusGeometry args={[0.2, 0.1, 16, 100]} />
                <lineBasicMaterial color={"black"} linewidth={10} />
                <meshBasicMaterial color="orange" />
              </mesh>
            </RigidBody>

            <RigidBody type={"fixed"} colliders={"cuboid"} restitution={0.8} position={[0, 0.02, 0]}>
              <mesh>
                <boxGeometry args={[5, 0.04, 5]} />
                <meshBasicMaterial color="#a8a8a8" />
              </mesh>
            </RigidBody>
          </Physics>
        </Suspense>
      </Canvas>
    </>
  );
}

export default Page;

Thanks

rmr-code commented 7 months ago

The first suggestion worked @wiledal . Thanks. Below is the code.

import React, { Suspense, useEffect, useRef } from 'react';
import { Canvas, useFrame } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import { Physics, RapierRigidBody, RigidBody } from "@react-three/rapier";
import * as THREE from "three";

function Page() {

  const OrangeRing = (() => {
    const orangeRef = useRef(null);
    let vec = new THREE.Vector3(0, 0, 0);

    useFrame(() => {
      orangeRef.current.getWorldPosition(vec);
      console.log(vec.y); // to get only the y position
    });

    return (
      <mesh ref={orangeRef}>
        <torusGeometry args={[0.2, 0.1, 16, 100]} />
        <meshBasicMaterial color="orange" />
      </mesh>
    )
  });

  return (
    <>
      <Canvas camera={{ position: [3, 3, 2] }}>
        <ambientLight intensity={0.5} />
        <directionalLight position={[-10, 10, 0]} intensity={0.4} />
        <OrbitControls />
        <gridHelper args={[10, 10]} />
        <Suspense>
          <Physics colliders={false}>
            <RigidBody type={"dynamic"} colliders={"trimesh"} restitution={0.5} rotation={[-Math.PI / 2, 0, 0]} position={[0, 2, 0]}>
              <OrangeRing />
            </RigidBody>

            <RigidBody type={"fixed"} colliders={"cuboid"} restitution={0.8} position={[0, 0.02, 0]}>
              <mesh>
                <boxGeometry args={[5, 0.04, 5]} />
                <meshBasicMaterial color="#a8a8a8" />
              </mesh>
            </RigidBody>
          </Physics>
        </Suspense>
      </Canvas>
    </>
  );
}

export default Page;