RigidBody
A 3D physics rigid body component.
| Type | Name | Interface Description |
|---|---|---|
| Variables | angularDamping: number | • Function: Gets or sets the angular damping coefficient that reduces angular velocity over time. Value typically ranges from 0 (no damping) to 1 (maximum damping). Higher values cause the body to stop rotating faster. |
| Variables | angularVelocity: Vector3f | • Function: World-space angular velocity in radians per second. The vector points along the rotation axis, the getter returns the live simulator value, and writing overrides the current physics-computed angular velocity immediately. For gradual changes, use instead. |
| Variables | damping: number | • Function: Gets or sets the linear damping coefficient that reduces linear velocity over time. Value typically ranges from 0 (no damping) to 1 (maximum damping). Higher values cause the body to slow down faster. |
| Variables | eulerAngles: Vector3f | • Function: Current world-space rotation of the rigid body as Euler angles in radians (rotation order XYZ). The getter reflects the live orientation from the physics engine. Writing teleports the body immediately. For smooth rotation, use . Do NOT modify Transform.eulerAngles directly — it will be overwritten. Prefer over this property to avoid gimbal lock. |
| Variables | force: Vector3f | • Function: Gets or sets the continuous force applied to the rigid body in world space. Force is measured in Newtons and is applied continuously during physics simulation. Setting this replaces the previous continuous force value. This does NOT affect forces added via . To apply multiple forces cumulatively, use instead. |
| Variables | freezeX: boolean | • Function: Gets or sets whether movement along the X-axis is frozen. When frozen, the rigid body cannot move along the X-axis but can still rotate freely. Constraint is applied during the next physics simulation step. |
| Variables | freezeY: boolean | • Function: Gets or sets whether movement along the Y-axis is frozen. When frozen, the rigid body cannot move along the Y-axis but can still rotate freely. Constraint is applied during the next physics simulation step. |
| Variables | freezeZ: boolean | • Function: Gets or sets whether movement along the Z-axis is frozen. When frozen, the rigid body cannot move along the Z-axis but can still rotate freely. Constraint is applied during the next physics simulation step. |
| Variables | inertiaTensor: Vector3f | • Function: Gets or sets the diagonal inertia tensor of the rigid body. These values describe resistance to angular acceleration around the body's principal axes. They are usually computed automatically from mass and collider shape, but can be overridden for custom behavior. |
| Variables | mass: number | • Function: Gets or sets the mass of the rigid body in kilograms. Mass must be positive and greater than zero. Changing mass during simulation affects the body's inertia and response to forces. |
| Variables | physicsAnimation: boolean | • Function: Blend animation with physics. When enabled, a hidden kinematic "shadow body" follows the animation, and the real physics rigid body is pulled toward it via spring-like joints. - |
| Variables | physicsAnimationRate: number | • Function: Controls the blend between animation and physics when is enabled. Range [0, 1]: - |
| Variables | position: Vector3f | • Function: World-space position of the rigid body (meters). Reads from the physics engine; writing teleports the body immediately, bypassing velocity/force simulation. Do NOT modify Transform.position directly on a physics-controlled object — the physics engine will overwrite it on the next frame. Use this property or instead to move the body. |
| Variables | rotation: Quaternionf | • Function: Current world-space rotation of the rigid body, returned as a quaternion. Once the body is created, the getter reflects the live orientation from the physics engine. Writing teleports the body's rotation immediately, bypassing torque and angular-velocity simulation. Do NOT modify Transform.rotation directly on a physics-controlled object — the physics engine will overwrite it on the next frame. Use this property, , or instead. |
| Variables | static: boolean | • Function: Gets or sets whether the rigid body behaves as an immovable kinematic body. When enabled, the PBD simulator stops treating this body as dynamically simulated. Use this for environment geometry or authored placement that should not respond to forces. |
| Variables | torque: Vector3f | • Function: Gets or sets the continuous torque (the external force that causes a rigid body to rotate) applied to the rigid body in world space. Torque is measured in Newton-meters and is applied continuously during physics simulation. Setting this replaces the previous continuous torque value. This does NOT affect torques added via . To apply multiple torques cumulatively, use instead. |
| Variables | useGravity: boolean | • Function: Gets or sets whether scene gravity contributes to this body's continuous external force. When enabled, APJS adds |
| Variables | velocity: Vector3f | • Function: World-space linear velocity in meters per second (m/s). The getter returns the live simulator value, and writing overrides the current physics-computed velocity immediately. For gradual changes, use instead. |
| Functions | constructor() | |
| Functions | addForce(force: Vector3f, mode?: ForceMode3D): void | • Function: Adds force at the rigid body's center of mass. Force modes: - Parameters • • |
| Functions | addForceAt(force: Vector3f, position: Vector3f, isLocal?: boolean, mode?: ForceMode3D): ConstantForce3D | null | • Function: Applies a force at a specific point, which may also generate torque. If Parameters • • • • Returns A world-space handle for continuous modes; null for instantaneous modes. |
| Functions | addTorque(torque: Vector3f, mode?: ForceMode3D): void | • Function: Adds torque at the rigid body's center of mass. Force modes: - Parameters • • |
Examples
position: Vector3f
// Teleport a physics body to (0, 5, 0)
rigidBody.position = new APJS.Vector3f(0, 5, 0);
// For smooth movement, use forces:
rigidBody.addForce(new APJS.Vector3f(0, 10, 0), APJS.ForceMode3D.Impulse);
rotation: Quaternionf
// Teleport rotation (90 degrees around Y axis, 1.57 radians)
rigidBody.rotation = APJS.Quaternionf.makeFromEulerAngles(new APJS.Vector3f(0, 1.57, 0));
// For smooth rotation, use torque:
rigidBody.addTorque(new APJS.Vector3f(0, 10, 0), APJS.ForceMode3D.Impulse);
constructor()
let obj = new APJS.RigidBody();
addForce(force: Vector3f, mode?: ForceMode3D): void
const explosionForce = new APJS.Vector3f(0, 100, 0);
rigidBody.addForce(explosionForce, APJS.ForceMode3D.Impulse);
addForceAt(force: Vector3f, position: Vector3f, isLocal?: boolean, mode?: ForceMode3D): ConstantForce3D | null
push something at a specific point
// Apply a push at the edge of the object to make it swing
const pushForce = new APJS.Vector3f(0, 0, 10); // Push forward
const pushPoint = new APJS.Vector3f(1, 0, 0); // Right edge
rigidBody.addForceAt(pushForce, pushPoint, false, APJS.ForceMode3D.Impulse);
addTorque(torque: Vector3f, mode?: ForceMode3D): void
const explosionTorque = new APJS.Vector3f(0, 100, 0);
rigidBody.addTorque(explosionTorque, APJS.ForceMode3D.Impulse);
Use Case
Example 1 — On tap, apply random upward impulse to multiple 2D physics blocks using addForce with ForceMode2D.Impulse
@component()
export class TapPushBlocks extends APJS.BasicScriptComponent {
@serializeProperty
blocks: APJS.SceneObject[] = [];
private blockStartPos: APJS.Vector2f[] = [];
private blockStartGravity: boolean[] = [];
private inited = false;
private onTouch = (event: APJS.IEvent) => {
const touch = event.args[0] as APJS.TouchData;
if (touch.phase === APJS.TouchPhase.Began) {
for (const obj of this.blocks) {
const rb = obj.getComponent("RigidBody2D") as APJS.RigidBody2D;
if (rb) {
const randomX = (Math.random() - 0.5) * 200;
const upwardY = 300 + Math.random() * 200;
rb.addForce(new APJS.Vector2f(randomX, upwardY), APJS.ForceMode2D.Impulse);
}
}
}
};
// RecordStart: pool reset per Physics2D §"RecordStart Reset for 2D Physics" — iterate
// each block, apply velocity → anchoredPosition → useGravity. Cache per-block startPos
// and startGravity flag in the lazy init below. See GameState §"RecordStart / RecordEnd Lifecycle".
private onRecordStart = (_event: APJS.IEvent) => {
if (!this.inited) return;
for (let i = 0; i < this.blocks.length; i++) {
const obj = this.blocks[i];
if (!obj) continue;
const rb = obj.getComponent("RigidBody2D") as APJS.RigidBody2D;
const st = obj.getComponent("ScreenTransform") as APJS.ScreenTransform;
if (rb) rb.velocity = new APJS.Vector2f(0, 0);
if (st && this.blockStartPos[i]) {
st.anchoredPosition = new APJS.Vector2f(this.blockStartPos[i].x, this.blockStartPos[i].y);
}
if (rb && this.blockStartGravity[i] !== undefined) {
rb.useGravity = this.blockStartGravity[i];
}
}
};
onStart(): void {
APJS.EventManager.getGlobalEmitter().on(APJS.EventType.Touch, this.onTouch);
APJS.EventManager.getGlobalEmitter().on(APJS.EventType.RecordStart, this.onRecordStart);
}
onUpdate(_dt: number): void {
if (!this.inited && this.blocks.length > 0) {
for (const obj of this.blocks) {
if (!obj) continue;
const st = obj.getComponent("ScreenTransform") as APJS.ScreenTransform;
const rb = obj.getComponent("RigidBody2D") as APJS.RigidBody2D;
if (st) {
this.blockStartPos.push(new APJS.Vector2f(st.anchoredPosition.x, st.anchoredPosition.y));
} else {
this.blockStartPos.push(new APJS.Vector2f(0, 0));
}
this.blockStartGravity.push(rb ? rb.useGravity : true);
}
this.inited = true;
}
}
onDestroy(): void {
APJS.EventManager.getGlobalEmitter().off(APJS.EventType.Touch, this.onTouch);
APJS.EventManager.getGlobalEmitter().off(APJS.EventType.RecordStart, this.onRecordStart);
}
}
Example 2 — 3D physics: tap anywhere to apply an upward impulse to a RigidBody object.
@component()
export class TapImpulse extends APJS.BasicScriptComponent {
@serializeProperty
impulseStrength: number = 50;
private rb: APJS.RigidBody;
private inited = false;
private startPos!: APJS.Vector3f;
private startRot!: APJS.Quaternionf;
private startGravityOn = true;
private onTouch = (event: APJS.IEvent) => {
const touch = event.args[0] as APJS.TouchData;
if (touch.phase === APJS.TouchPhase.Began && this.rb) {
// Impulse: one-shot force, object falls back under gravity
this.rb.addForce(
new APJS.Vector3f(0, this.impulseStrength, 0),
APJS.ForceMode3D.Impulse
);
}
};
// RecordStart: reset per Physics3D §"RecordStart Reset for 3D Physics" — full 6-step
// single-body block (velocity → angularVelocity → position → rotation → useGravity →
// accumulators). See GameState §"RecordStart / RecordEnd Lifecycle".
private onRecordStart = (_event: APJS.IEvent) => {
if (!this.inited) return;
this.rb.velocity = new APJS.Vector3f(0, 0, 0);
this.rb.angularVelocity = new APJS.Vector3f(0, 0, 0);
this.rb.position = new APJS.Vector3f(this.startPos.x, this.startPos.y, this.startPos.z);
this.rb.rotation = this.startRot;
this.rb.useGravity = this.startGravityOn;
};
onUpdate(dt: number): void {
if (!this.inited) {
const obj = this.getSceneObject();
if (!obj) return;
this.rb = obj.getComponent("RigidBody") as APJS.RigidBody;
if (!this.rb) return;
this.startPos = new APJS.Vector3f(this.rb.position.x, this.rb.position.y, this.rb.position.z);
this.startRot = this.rb.rotation;
this.startGravityOn = this.rb.useGravity;
this.inited = true;
APJS.EventManager.getGlobalEmitter().on(APJS.EventType.Touch, this.onTouch);
APJS.EventManager.getGlobalEmitter().on(APJS.EventType.RecordStart, this.onRecordStart);
}
}
onDestroy(): void {
APJS.EventManager.getGlobalEmitter().off(APJS.EventType.Touch, this.onTouch);
APJS.EventManager.getGlobalEmitter().off(APJS.EventType.RecordStart, this.onRecordStart);
}
}