pmndrs / use-cannon

👋💣 physics based hooks for @react-three/fiber
https://cannon.pmnd.rs
2.76k stars 155 forks source link

PreStep support #67

Open Splact opened 4 years ago

Splact commented 4 years ago

Is there a way to define a preStep function on a body?

const [ref, api] = useBox(
  () => ({
    args: [size[0] / 2, size[1] / 2, size[2] / 2],
    preStep: () => {
      console.log("prestep");
    },
    ...props,
  })
);

I tried passing a callback to useBox (as above) but this leads to an error on the worker.

Failed to execute 'postMessage' on 'Worker': function preStep() {
  console.log("preStep");
} could not be cloned.

My goal is to recreate gravity towards a point in space, so I'm trying to apply a constant force through that point in the preStep function. Don't know if I'm following the right approach, I'm open to suggestions.

A super nice solution to this would be having a component that defines an attractor point. An example below:

import { Physics, Attractor } from "use-cannon";

// ...

return (
  <Physics gravity={[0, 0, 0]}>
    {/* ... objects with useCannon hooks ... */}

    <Attractor position={[0, 50, -20]} mass={4} />
    <Attractor position={[0, -20, 60]} mass={6} />
  </Physics>
);
codynova commented 4 years ago

Hey @Splact, sorry for the delayed response. The Cannon Body preStep and postStep methods are deprecated, and it's suggested that you use World events instead. However, we don't currently expose a way to add events to the world - and even if we did, we may still need to un-deprecate the Body preStep and postStep events to make the API a little more sane. This will need to be considered.

@drcmda Do you have any thoughts on this?

codynova commented 4 years ago

We should also add a way to update world props like gravity, tolerance, allowSleep, etc. through the Provider context. I will try to find some time for this soon.

Splact commented 4 years ago

The solution I implemented with the current api is to apply a constant force in the direction of the gravity center on each frame. This is not theoretically correct, since the forces sum up on each frame, but the end result is acceptable in my case.

useFrame(() => {
  // Calculate constant force to the gravity center
  // 1. get direction vector
  force.current
    .subVectors(ref.current.position, gravityCenterVector)
    .normalize();
  // 2. get the distance to the gravity center
  const distance = ref.current.position.distanceTo(
    gravityCenterVector
  );
  // 3. get force's magnitude following a quad curve on the distance
  const forceMagnitude =
    quadOut(Math.max(1 - distance / GRAVITY_MAX_DISTANCE, 0)) *
    GRAVITY_MAX_FORCE;
  force.current.multiplyScalar(-forceMagnitude);

  // ... update the force if needed, in my case here I've a force variation logic due to pointer movement

  // Apply force
  api.applyForce(force.current.toArray(), [0, 0, 0]);
}

Here I obtain the force's magnitude in relation to the distance to the gravity center. The GRAVITY_MAX_DISTANCE is the distance in which the gravity force applied to the object is 0.