Move Ammo.js Rigid Body Without Breaking Physics
Moving an active rigid body in ammo.js (the JavaScript
port of the Bullet physics engine) requires careful handling to prevent
glitches, clipping, or breaking the physics solver. This article
explains the three safest methods to translate or teleport an active
rigid body: resetting its world transform with state activation,
applying physical forces, and utilizing kinematic states for manual
control.
Method 1: Safe Teleportation (Direct Position Update)
Directly modifying a rigid body’s position during an active simulation can cause severe physics glitches, such as objects passing through walls or experiencing extreme velocity spikes. To safely teleport a rigid body, you must update its world transform, update its motion state, reset its velocities, and force the body to wake up.
function teleportBody(body, position, rotation) {
// 1. Create a new transform
const transform = new Ammo.btTransform();
transform.setIdentity();
// 2. Set the new origin (position)
const ammoPosition = new Ammo.btVector3(position.x, position.y, position.z);
transform.setOrigin(ammoPosition);
// 3. Set the rotation (quaternion)
const ammoRotation = new Ammo.btQuaternion(rotation.x, rotation.y, rotation.z, rotation.w);
transform.setRotation(ammoRotation);
// 4. Apply to both the world transform and the motion state
body.setWorldTransform(transform);
if (body.getMotionState()) {
body.getMotionState().setWorldTransform(transform);
}
// 5. Reset velocities to prevent "slingshot" physics bugs
const zeroVector = new Ammo.btVector3(0, 0, 0);
body.setLinearVelocity(zeroVector);
body.setAngularVelocity(zeroVector);
// 6. Force the physics engine to re-evaluate the body
body.activate(true);
// Clean up memory
Ammo.destroy(transform);
Ammo.destroy(ammoPosition);
Ammo.destroy(ammoRotation);
Ammo.destroy(zeroVector);
}Method 2: Physics-Based Movement (Forces and Impulses)
If you want the body to move naturally while interacting with the environment, do not set the position directly. Instead, apply forces or impulses. This allows the solver to calculate collisions and friction realistically.
- Continuous Force: Use
applyCentralForcefor constant movement (like a thruster). - Instantaneous Impulse: Use
applyCentralImpulsefor sudden movement (like a jump or explosion).
// Apply a continuous push (run this in your physics update loop)
const force = new Ammo.btVector3(x, y, z);
body.applyCentralForce(force);
body.activate(true); // Ensure the body is awake
Ammo.destroy(force);Method 3: Direct Velocity Manipulation
For precise control (such as a player character or a moving platform) where you want instantaneous movement without ignoring physics collisions, set the linear velocity directly.
// Set a specific velocity vector
const velocity = new Ammo.btVector3(velocityX, velocityY, velocityZ);
body.setLinearVelocity(velocity);
body.activate(true);
Ammo.destroy(velocity);Method 4: Switch to a Kinematic Body
If you need to manually control an object’s position frame-by-frame while still allowing it to push other dynamic objects out of the way, convert the body to a Kinematic Object. Kinematic bodies are not affected by gravity or forces; they are moved solely by your code.
To set up a kinematic body during initialization:
// Get existing collision flags and add the kinematic flag
const kinematicFlag = 2; // btCollisionObject::CF_KINEMATIC_OBJECT
body.setCollisionFlags(body.getCollisionFlags() | kinematicFlag);
// Disable gravity influence
body.setActivationState(4); // DISABLE_DEACTIVATIONTo move a kinematic body, update its motion state transform inside your render/update loop:
function moveKinematicBody(body, position) {
const transform = new Ammo.btTransform();
transform.setIdentity();
const ammoPosition = new Ammo.btVector3(position.x, position.y, position.z);
transform.setOrigin(ammoPosition);
body.getMotionState().setWorldTransform(transform);
Ammo.destroy(transform);
Ammo.destroy(ammoPosition);
}