Skip to content

RigidbodyWrapper

public class RigidbodyWrapper : IRetiringWrapper

Component-logic accessor over a physics BODY, exposed as Rendering.<Part>.Rigidbody on a part whose mesh carries a part-body marker. This is a COMPONENT-AGNOSTIC force primitive: it knows nothing about motors, thrusters or any device; it just applies torque and force and reports velocity, angle, joint and contact loads. Users compose a motor (torque from winding current), a thruster (force along an axis), a solenoid (force), a reaction wheel (torque) and more from this ONE handle.

It holds the DEFINITION node (the part’s mesh) and resolves the RUNTIME body lazily through the physics host each call. The body only exists while playing, so before Play (or on a body that never became physical) every force call is a silent no-op and every body read is zero; ReflectedInertia and ReflectedInertiaGearRatio are the exception, set in Initialize and kept for when Play builds the bodies. Torque and force are TRUE SI in WORLD space (N·m, N); velocities are rad/s and m/s. Nothing is scaled by the mm-display factor.

Applied values are SUSTAINED: the host re-applies the last-set torque or force every physics frame until it changes (the physics engine clears an applied torque each substep), so component logic sets it when it changes and sets it to zero to stop. A torque that depends on a body read (a motor’s current against its spin) belongs in PhysicsUpdate, not Update. Every vector in and out is a Vector3.

public Vector3 AngularVelocity { get; }

WORLD-space angular velocity (rad/s), absolute rather than relative to any hinge; on a part welded into a bigger body it is that body’s. Zero before Play (and after Reset) or on a part that is not physical; while paused it keeps the value it had at the pause.

public double SpinRate { get; }

Signed angular velocity (rad/s) about this part’s HINGE axis, relative to the body its hinge is on: the scalar read for a hinged part (the value a back-EMF term wants). Positive about HingeAxis. Zero for a part without a hinge axis, before Play (and after Reset) or on a part that is not physical; while paused it keeps the value it had at the pause.

public double HingeAngle { get; }

Accumulated signed rotation (rad) about this part’s HINGE axis, relative to the body its hinge is on, since Play began: CONTINUOUS (multi-turn unwrapped), 0 = the pose the part was baked in. The positional sibling of SpinRate: a servo’s feedback pot, an encoder disc, a limit switch are all reads of this one agnostic value. Drift-free (pose-derived each tick, not integrated). Read in PhysicsUpdate it is the pose the coming step starts from; elsewhere it is what the last tick left.

public Vector3 LinearVelocity { get; }

WORLD-space linear velocity of this part’s centre of mass (TRUE SI m/s). Zero before Play (and after Reset) or on a part that is not physical; while paused it keeps the value it had at the pause.

public Vector3 JointForce { get; }

The force this part’s joint puts on it (N, world space) over the last physics step: what a load cell between the part and the body it is jointed to would read. Zero before the first step, for a part with no joint, and on an engine build without joint reactions. Refreshed once per physics tick, by the first read in it; outside a tick (in Update) it returns what the last tick left.

public Vector3 JointForceLocal { get; }

The same force as JointForce along the joint’s own axes, as authored on the part and carried by the body it is jointed to: a load cell’s reading in its mount (Y is a socket’s axis when the joint is authored Y up).

public Vector3 JointTorque { get; }

The moment this part’s joint puts on it (N·m, world space) over the last step, apart from the force: what a torque sensor in the joint would read. A hinge’s own friction shows here.

public Vector3 HingeAxis { get; }

This part’s hinge axis in world space, unit length: the axis SpinRate, HingeAngle and ApplyTorque use. Zero for a part without a single hinge axis.

public Vector3 Weight { get; }

This body’s weight (N, world space): its mass times the gravity it feels, its gravity scale and any zone included, so no logic needs its own figure for g. A welded body’s is the whole body’s. Zero until the first physics step, and for a body that is not dynamic.

public Vector3 ContactForce { get; }

Everything the world’s contacts did to this part over the last physics step (N, world space), the pressing force and the friction together: what a load cell between the part and everything it touches would read. Zero out of contact and before the first step. Read it from PhysicsUpdate; outside a tick it returns what the last tick left.

public Vector3 ContactForceLocal { get; }

The same force as ContactForce along this part’s own axes: a sensor built into the part.

public Vector3 ContactTorque { get; }

The moment of the contact forces (N·m, world space) about this part’s own centre of mass, over the last physics step.

public double ContactNormalForce { get; }

The pressing force alone (N, never negative) over the last physics step: what a force-sensing resistor, a load cell under a foot or a bump switch responds to.

public Vector3 ContactPoint { get; }

The centre of pressure (m, world space); zero out of contact. On a soft patch it is the solved centre of the pressure. Where a rigid part rests on a few points, it is where the solver happened to put the load among them, which for a flat face on a flat face is not unique.

public Vector3 ContactPointLocal { get; }

The centre of pressure in this part’s own frame (m); zero out of contact.

public double ContactArea { get; }

The area of the soft patches this part touches with (m²); 0 on rigid contacts.

public double FrictionUse { get; }

How much of the grip the contacts are using, 0 to 1; 1 is sliding. Incipient-slip sensing: it climbs toward 1 before anything slides.

public bool InContact { get; }

Whether anything pressed on this part in the last physics step.

public double ReflectedInertia { get; set; }

Inertia this body carries that its own geometry cannot show (kg·m², about its HINGE axis): everything upstream of the joint that drives it, reflected through the drive ratio. A geared actuator’s rotor is the case that matters. Reflected through the ratio SQUARED it is usually many times the driven part’s own inertia (a 9 g servo’s rotor through its gear train is a few 10⁻⁴ kg·m² at the horn, against about 10⁻⁶ for the horn itself), and it is the flywheel that lets a real mechanism ride through a load reversal without the shaft speed, and so the current, collapsing. Without it the electro-mechanical loop is unphysically light, which is what every hand-tuned inertia has been compensating for.

Set it ONCE in Initialize from the actuator’s own parameters (its rotor inertia times the gear ratios it already knows), so it follows them when they change. At the bake the host gives it to a rotor of its own, tied to this part and to the body its hinge is on, so it resists their RELATIVE turn on whichever side moves (the case of a servo on a moving arm as much as its horn) and adds nothing about the axes the hinge holds; the hinge drive turns that rotor, as a real motor turns its own. Set ReflectedInertiaGearRatio too, for the exact figure when the case itself turns. On an engine build without the velocity gear, or with the host’s rotor switch off, it goes on the part’s own tensor on all three axes instead, as it did before the rotor, and so it does on a part whose hinge also carries a gear mesh the host applies as torques rather than solving (one with backlash, or on a frame that moves), until such meshes are solved with the rotor. Agnostic: any rotating drive has one. The joint’s breakaway friction, its Config’s and ApplyJointFriction’s, is shared with a friction row on the rotor by the two inertias, so it meets both at once. For a hinged part; zero or a negative value clears it. It is read only when Play builds the bodies, so a change made while playing applies at the next Play.

public double ReflectedInertiaGearRatio { get; set; }

The turns ratio of the drive whose rotor ReflectedInertia comes from: the rotor turns this many times for each turn of the part against the body its hinge is on (negative when the train reverses its direction). Optional. Left at 0 the host treats the ratio as large: exact while the case is still, and about 1/ratio off in what the rotor adds when the case itself turns, which on a light free case is most of what it does. A direct drive is 1, and should say so: left at 0 its rotor is taken to turn against the case, not with the part. A ratio that is not finite or is under 0.01 in size is treated as undeclared, with a warning. Set it with ReflectedInertia, once in Initialize; read when Play builds the bodies, like it. Agnostic: any geared drive has one (a servo, a gearmotor, a geared stepper, a winch).

public void ApplyTorque(Vector3 torqueNm);

Apply a sustained WORLD-space torque: the vector’s direction is the axis and its length the torque in N·m. One-sided: no other body takes a reaction (for a motor’s couple about the hinge use ApplyTorque). It holds until set again; zero stops it. Does nothing before Play.

public void ApplyTorque(double nm);

Apply a sustained torque (N·m) about this part’s HINGE axis, the ergonomic scalar form with no world-axis math: positive acts along HingeAxis (right-hand rule), the sense in which SpinRate and HingeAngle count positive. It is a motor’s couple: the body the hinge is on takes the equal and opposite reaction, as a motor’s case does. The axis is taken as it stands at the call, so a part whose hinge moves sets it again every PhysicsUpdate. Does nothing on a part without a single hinge axis.

public void ApplyJointFriction(double nm);

Add a sustained breakaway friction (N·m) to this part’s joint, on top of the friction its Config authored, and solved the same way: the joint stands still while everything else acting about it adds up to less, and slides against it when more. For friction that depends on what the part is doing: a brake pad pressed harder, a clutch, the tooth friction of a gearbox that grows with the torque going through it. On a joint free about several axes, a ball pressed into its socket, it resists the turn the same whichever way it goes. Sustained until changed, like ApplyTorque; zero removes it, and a negative value counts as zero. Set it from PhysicsUpdate: the physics step that follows is the one it acts on, and set before Play it does nothing. An axis with a spring keeps its spring and ignores this, with one warning, because a joint axis holds a spring or a friction, not both. On a part with a ReflectedInertia the friction meets the rotor too: the host shares it between the hinge and the rotor by their inertias, so a stop or a hold is held at once and never rocks past zero, and JointTorque then reads only the hinge’s share of it.

public void ApplyForce(Vector3 forceN);

Apply a sustained WORLD-space force (N) at this part’s own centre of mass. A thruster or a solenoid is just this. One-sided: no other body takes a reaction. It holds until set again; zero stops it. Does nothing before Play.

Generated from the CRUMB source, commit fcec660d, 2026-10-29.