Skip to content
🎉 Angular Three v4 is here! Read the announcement

NgtrRigidBody

NgtrRigidBody creates a rigid body in the physics simulation. Rigid bodies can be dynamic (simulated), fixed (static), or kinematic (user-controlled).

Use ngt-object3D with the rigidBody attribute. The rigid body type is specified as the attribute value:

<!-- Dynamic (default when empty) -->
<ngt-object3D rigidBody [position]="[0, 5, 0]">
<ngt-mesh>
<ngt-box-geometry />
<ngt-mesh-standard-material />
</ngt-mesh>
</ngt-object3D>
<!-- Fixed (static) -->
<ngt-object3D rigidBody="fixed" [position]="[0, -1, 0]">
<ngt-mesh>
<ngt-box-geometry *args="[20, 1, 20]" />
<ngt-mesh-standard-material />
</ngt-mesh>
</ngt-object3D>
<!-- Kinematic -->
<ngt-object3D rigidBody="kinematicPosition">
<ngt-mesh>
<ngt-sphere-geometry />
<ngt-mesh-standard-material />
</ngt-mesh>
</ngt-object3D>
TypeDescription
'' or 'dynamic'Fully simulated body affected by forces and collisions
'fixed'Static, immovable body that can be collided with
'kinematicPosition'User-controlled body moved by setting position
'kinematicVelocity'User-controlled body moved by setting velocity

Configure rigid body behavior via the options input:

<ngt-object3D
rigidBody
[options]="{
colliders: 'ball',
ccd: true,
gravityScale: 0.5,
linearVelocity: [0, 10, 0],
angularVelocity: [0, 1, 0]
}"
>
...
</ngt-object3D>
OptionTypeDefaultDescription
canSleepbooleantrueWhether the body can sleep when inactive
linearVelocity[x, y, z][0, 0, 0]Initial linear velocity
angularVelocity[x, y, z][0, 0, 0]Initial angular velocity
gravityScalenumber1Scaling factor for gravity
dominanceGroupnumber0Dominance group for collision immunity
ccdbooleanfalseEnable Continuous Collision Detection
softCcdPredictionnumber0Soft CCD prediction distance
linearDampingnumberundefinedLinear velocity damping
angularDampingnumberundefinedAngular velocity damping
lockRotationsbooleanundefinedLock all rotations
lockTranslationsbooleanundefinedLock all translations
enabledRotations[x, y, z]undefinedEnable rotation per axis
enabledTranslations[x, y, z]undefinedEnable translation per axis
additionalSolverIterationsnumberundefinedExtra solver iterations
colliders'ball' | 'cuboid' | 'hull' | 'trimesh' | falseinheritedAuto-collider type
frictionnumberundefinedFriction for auto-colliders
restitutionnumberundefinedBounciness for auto-colliders
collisionGroupsInteractionGroupsundefinedCollision group bitmask
solverGroupsInteractionGroupsundefinedSolver group bitmask
activeCollisionTypesActiveCollisionTypesundefinedActive collision types
contactSkinnumber0Contact skin width
includeInvisiblebooleanfalseInclude invisible meshes in collider creation

Listen to physics events on rigid bodies:

<ngt-object3D
rigidBody
(collisionEnter)="onCollisionEnter($event)"
(collisionExit)="onCollisionExit($event)"
(intersectionEnter)="onIntersectionEnter($event)"
(intersectionExit)="onIntersectionExit($event)"
(contactForce)="onContactForce($event)"
(sleep)="onSleep()"
(wake)="onWake()"
>
...
</ngt-object3D>
EventPayloadDescription
collisionEnterNgtrCollisionEnterPayloadCollision started
collisionExitNgtrCollisionExitPayloadCollision ended
intersectionEnterNgtrIntersectionEnterPayloadSensor intersection started
intersectionExitNgtrIntersectionExitPayloadSensor intersection ended
contactForceNgtrContactForcePayloadContact force applied
sleepvoidBody went to sleep
wakevoidBody woke up
interface NgtrCollisionEnterPayload {
target: {
rigidBody?: RigidBody;
collider: Collider;
rigidBodyObject?: THREE.Object3D;
colliderObject?: THREE.Object3D;
};
other: {
rigidBody?: RigidBody;
collider: Collider;
rigidBodyObject?: THREE.Object3D;
colliderObject?: THREE.Object3D;
};
manifold: TempContactManifold;
flipped: boolean;
}
interface NgtrContactForcePayload extends NgtrCollisionPayload {
totalForce: Vector;
totalForceMagnitude: number;
maxForceDirection: Vector;
maxForceMagnitude: number;
}

Export the directive to access the underlying Rapier rigid body:

<ngt-object3D rigidBody #body="rigidBody">...</ngt-object3D>
@Component({...})
export class MyComponent {
body = viewChild.required<NgtrRigidBody>('body');
applyForce() {
const rb = this.body().rigidBody();
if (rb) {
rb.applyImpulse({ x: 0, y: 10, z: 0 }, true);
}
}
}

By default, rigid bodies automatically generate colliders from child meshes. Control this behavior:

<!-- Use cuboid colliders (default) -->
<ngt-object3D rigidBody [options]="{ colliders: 'cuboid' }">
<ngt-mesh>
<ngt-box-geometry />
</ngt-mesh>
</ngt-object3D>
<!-- Use ball colliders -->
<ngt-object3D rigidBody [options]="{ colliders: 'ball' }">
<ngt-mesh>
<ngt-sphere-geometry />
</ngt-mesh>
</ngt-object3D>
<!-- Use convex hull colliders -->
<ngt-object3D rigidBody [options]="{ colliders: 'hull' }">
<ngt-mesh>
<ngt-torus-geometry />
</ngt-mesh>
</ngt-object3D>
<!-- Use triangle mesh colliders (expensive) -->
<ngt-object3D rigidBody [options]="{ colliders: 'trimesh' }">
<ngt-mesh>
<ngt-torus-knot-geometry />
</ngt-mesh>
</ngt-object3D>
<!-- Disable auto-colliders -->
<ngt-object3D rigidBody [options]="{ colliders: false }">
<!-- Add manual colliders -->
<ngt-object3D [ballCollider]="[0.5]" />
</ngt-object3D>

Kinematic bodies are controlled by you rather than the physics engine:

@Component({
template: `
<ngt-object3D rigidBody="kinematicPosition" #kinematic="rigidBody">
<ngt-mesh>
<ngt-box-geometry />
</ngt-mesh>
</ngt-object3D>
`,
})
export class KinematicExample {
kinematic = viewChild.required<NgtrRigidBody>('kinematic');
constructor() {
beforePhysicsStep(() => {
const rb = this.kinematic().rigidBody();
if (rb) {
// Move to a new position
rb.setNextKinematicTranslation({ x: Math.sin(Date.now() / 1000), y: 0, z: 0 });
}
});
}
}
@Component({
template: `
<ngt-object3D rigidBody="kinematicVelocity" #kinematic="rigidBody">
<ngt-mesh>
<ngt-box-geometry />
</ngt-mesh>
</ngt-object3D>
`,
})
export class KinematicVelocityExample {
kinematic = viewChild.required<NgtrRigidBody>('kinematic');
constructor() {
beforePhysicsStep(() => {
const rb = this.kinematic().rigidBody();
if (rb) {
// Set velocity
rb.setLinvel({ x: 1, y: 0, z: 0 }, true);
}
});
}
}

Restrict movement to specific axes:

<!-- Lock all rotations -->
<ngt-object3D rigidBody [options]="{ lockRotations: true }">...</ngt-object3D>
<!-- Lock all translations -->
<ngt-object3D rigidBody [options]="{ lockTranslations: true }">...</ngt-object3D>
<!-- Enable only specific axes -->
<ngt-object3D
rigidBody
[options]="{
enabledRotations: [false, true, false],
enabledTranslations: [true, true, false]
}"
>
...
</ngt-object3D>

Enable CCD to prevent fast objects from tunneling through thin objects:

<ngt-object3D rigidBody [options]="{ ccd: true }">...</ngt-object3D>
<!-- Or use soft CCD for moderate speeds -->
<ngt-object3D rigidBody [options]="{ softCcdPrediction: 2 }">...</ngt-object3D>

Control which bodies are affected by collisions:

<!-- Higher dominance = immune to forces from lower groups -->
<ngt-object3D rigidBody [options]="{ dominanceGroup: 10 }">...</ngt-object3D>
<ngt-object3D rigidBody [options]="{ dominanceGroup: 0 }">...</ngt-object3D>