0

I've made a simple example with Rapier.js to illustrate my problem on a CodePen.

I'm trying to use the JavaScript API of the physics engine Rapier to render DOM elements with collision detection. If the elements are the same size then it works as expected, but if two rectangles of different sizes interact with each other then the alignment is off. In the CodePen you can see two boxes that fall to the ground with gravity. The bottom one stops correctly on the ground but the top one remains hovering above the bottom one. If I change the boxes to be the same height then they work as expected. What am I doing wrong?

import RAPIER from "https://cdn.skypack.dev/@dimforge/rapier2d-compat";

const createBox = (id) => {
  const element = document.getElementById(id);
  const rect = element.getBoundingClientRect();

  const rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic().setTranslation(
    rect.x,
    rect.y
  );

  const rigidBody = world.createRigidBody(rigidBodyDesc);

  const colliderDesc = RAPIER.ColliderDesc.cuboid(
    rect.width / 2,
    rect.height / 2
  );

  world.createCollider(colliderDesc, rigidBody);
  return [rigidBody, element];
};

await RAPIER.init();
const gravity = { x: 0.0, y: 9.81 };
const world = new RAPIER.World(gravity);

const groundRect = document.getElementById(`ground`).getBoundingClientRect();

const colliderDesc = RAPIER.ColliderDesc.cuboid(
  groundRect.width / 2,
  groundRect.height / 2
).setTranslation(groundRect.x, groundRect.y);

world.createCollider(colliderDesc);

const [oneBody, oneElement] = createBox(`one`);
const [twoBody, twoElement] = createBox(`two`);

const gameLoop = () => {
  world.step();

  const oneTranslation = oneBody.translation();
  oneElement.style.left = `${oneTranslation.x}px`;
  oneElement.style.top = `${oneTranslation.y}px`;

  const twoTranslation = twoBody.translation();
  twoElement.style.left = `${twoTranslation.x}px`;
  twoElement.style.top = `${twoTranslation.y}px`;

  requestAnimationFrame(gameLoop);
};

gameLoop();

1 Answer 1

2

The center of Rapier’s cuboids are located at the middle of the rectangle, not its top-left corner. So you need to account for this in your DOM element positioning:

import RAPIER from "https://cdn.skypack.dev/@dimforge/rapier2d-compat";

const createBox = (id) => {
  const element = document.getElementById(id);
  const rect = element.getBoundingClientRect();

  console.log(rect);
  const rigidBodyDesc = RAPIER.RigidBodyDesc.dynamic().setTranslation(
    rect.x + rect.width / 2,
    rect.y + rect.height / 2
  );

  const rigidBody = world.createRigidBody(rigidBodyDesc);

  const colliderDesc = RAPIER.ColliderDesc.cuboid(
    rect.width / 2,
    rect.height / 2
  );

  world.createCollider(colliderDesc, rigidBody);
  return [rigidBody, element, rect];
};

await RAPIER.init();
const gravity = { x: 0.0, y: 9.81 };
const world = new RAPIER.World(gravity);

const groundRect = document.getElementById(`ground`).getBoundingClientRect();

const colliderDesc = RAPIER.ColliderDesc.cuboid(
  groundRect.width / 2,
  groundRect.height / 2
).setTranslation(groundRect.x + groundRect.width / 2, groundRect.y + groundRect.height / 2);

world.createCollider(colliderDesc);

const [oneBody, oneElement, oneRect] = createBox(`one`);
const [twoBody, twoElement, twoRect] = createBox(`two`);

const gameLoop = () => {
  world.step();

  const oneTranslation = oneBody.translation();
  oneElement.style.left = `${oneTranslation.x - oneRect.width / 2}px`;
  oneElement.style.top = `${oneTranslation.y - oneRect.height / 2}px`;

  const twoTranslation = twoBody.translation();
  twoElement.style.left = `${twoTranslation.x - twoRect.width / 2}px`;
  twoElement.style.top = `${twoTranslation.y - twoRect.height / 2}px`;

  requestAnimationFrame(gameLoop);
};

gameLoop();

Also keep in mind that you are not taking the rotation into account here. It doesn’t mater for this particular example since they won’t rotate, but if they do, your rendering won’t match what the physics does.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.