rapier icon indicating copy to clipboard operation
rapier copied to clipboard

Connect KinematicPositionBased and Dynamic bodies with a Joint so that they can be moved as one. There is a lack of synchronicity.

Open arkonsolutions opened this issue 6 months ago • 3 comments

Good afternoon.

There is a KinematicPositionBased body that can be moved using a KinematicCharacterController, and I want to attach an additional component to it using a Joint to properly move them as one. Let's say there is a cylinder - it's a KinematicPositionBased (Character controller) and I want to attach a hammer to it. It's like an axis coming out of a cylinder, to which the hammer's head is attached through a lever. As planned, the player will be able to spin the hammer and hit the ball with it.

Avatar Composition:

  • Avatar body (rigid body: KinematicPositionBased)
    • Collider (Collider cylinder)
  • Hammer (rigid body: Dynamic)
    • Axle (Collider cylinder)
    • Lever (Collider cube)
    • Headband (Collider cylinder)
  • Joint (Revolute on the X-axis. It can simply spin around the axis)

The problem occurs when translating the Avatar's Body (via .setNextKinematicTranslation). When the avatar moves, there is a noticeable delay in the movement of the hammer following the character. Depending on the direction of movement, either a gap appears or the body bumps into the hammer.

I would like to understand how it is possible to implement a rigid bundle so that the movements of the connected bodies are synchronous.

Image Image Image

demo (movement with WSAD keys): https://l2l62c.csb.app/ sandbox: https://codesandbox.io/p/sandbox/l2l62c

With respect, Sergey

UPD: Changed call from setNextKinematicTranslation to setTranslation. As result joint the connection has become stable. But I'm worried that using setNextKinematicTranslation was recommended by the developers, and my hack may lead to unpredictable side effects in the future, in some cases. demo: https://9d36n3.csb.app/ sandbox: https://codesandbox.io/p/sandbox/9d36n3

arkonsolutions avatar Jul 09 '25 21:07 arkonsolutions

Thanks for sharing ; for the noticeable delay, I'd try 2 things:

  • change simulation parameters (increase simulation steps amount / numSolverIterations or reduce the deltatime...)
  • try a multibody joint (rather than an impulse joint)

for the collision with the other body, I'd setup different collision layer masks so the 2 bodies do not collide together.

Good luck keep us posted!

ThierryBerger avatar Jul 10 '25 12:07 ThierryBerger

Vrixyz, thanks for you attantion. I want to experiment further and try MultibodyJoint as you suggested. But I didn't find any information or examples. Could you throw off an example of initializing and using such a joint? Or maybe you know the link with examples or a description?

There is some success, but it is still doubtful. I described why in the conclusions below. Corrected code: https://codesandbox.io/p/sandbox/9d36n3

Сhanges:

  • Collision Groups

    • Identified the avatar group

      const AVATAR_GROUPS_MASK =
        (AVATAR_MASK << 16) + (0b1111111111111111 ^ AVATAR_MASK);
      
    • Attached all colliders associated with the avatar to this group: chControllerColliderRef.setCollisionGroups(AVATAR_GROUPS_MASK);

    • Removed shaftCollider.setSensor(true); which was used to filter out collisions when calculating the .computeColliderMovement. Instead, there are now groups.

  • Set up of the character controller's position

    • Commented out chControllerBodyRef.setNextKinematicTranslation(newPosition); and inserted chControllerBodyRef.setTranslation(newPosition) instead of this line. As a result, the impulse joint has become more stable.

Conclusions: setNextKinematicTranslation, based on the name, sets the coordinate as if for the future or something.. And such a coordinate is not suitable for an impulse Joint. But I suspect that the developers knowingly recommend using setNextKinematicTranslation for the character controller. And if you just use setTranslation, it can cause unexpected side effects... But here and now, joint is working.

arkonsolutions avatar Jul 11 '25 19:07 arkonsolutions

I found createMultibodyJoint method exists! And a try to create joint withi it:

jointRef = world.createMultibodyJoint(
    RAPIER.JointData.revolute(
      { x: characterRadius + shaftLength / 2, y: 0.2, z: 0.0 }, // First body anchor
      { x: 0.0, y: 0.0, z: 0.0 }, // Second body anchor
      { x: 1.0, y: 0.0, z: 0.0 } // Axis X
    ),
    chControllerBodyRef,
    hammerBody,
    false
  )

The problem is that now, when I try to call the jointRef.configureMotorPosition(Math.PI, 10000, 100.0); on this joint, I get an error: "jointRef.configureMotorPosition is not a function"

arkonsolutions avatar Jul 15 '25 06:07 arkonsolutions