Constraint Lifecycle
Explains validity, ownership, disabling, collision filtering, dependency invalidation and safe cleanup.
A managed JoltConstraint owns one world-scoped generational native handle. It references connected bodies but never owns their GML wrappers or gameplay data.
States#
| State | is_valid() | Meaning |
|---|---|---|
| Alive and enabled | true | The native constraint participates in solving. |
| Alive and disabled | true | The resource and settings remain, but it does not participate in solving. |
| Destroyed | false | The handle is released and becomes 0 on the wrapper. |
| Dependency destroyed | false | Destroying an attached body automatically removes its constraints first. |
| Runtime stopped | false | jolt_stop() invalidates resources from the old default world. |
Ownership rules#
- destroy() destroys only the constraint, never either body.
- destroy() is idempotent and safe to repeat on an already released wrapper.
- A stale generational handle cannot address a later constraint that reuses the same internal slot.
- All body-to-body constraints must remain in the same managed default world as their bodies.
- World constraints reference Jolt's immutable fixed body internally and one user body externally.
Disable versus destroy#
joint.set_enabled(false); // keep limits and motor settings
// Later:
joint.set_enabled(true);
// Permanent release:
joint.destroy();Disabling is appropriate for a mechanism that will be reused. Destroying is appropriate when the relationship has ended and should release its native solver resource.
Connected-body collision#
- collision:false or set_collision_enabled(false) suppresses only the connected user-body pair.
- Suppression is reference-counted: destroying one of several suppressing constraints does not re-enable the pair prematurely.
- Changing the setting invalidates cached contacts and wakes the affected bodies.
- World constraints always report collision disabled because there is no second user collision body.
Safe cleanup#
if (is_struct(joint) && joint.is_valid()) {
joint.destroy();
}
body_a.destroy();
body_b.destroy();
// Global managed cleanup is also ordered and idempotent.
jolt_stop();Error handling#
var joint = jolt_hinge(a, b, 0,0,2, 0,0,1);
if (is_undefined(joint)) {
var error = jolt_get_error();
show_debug_message(string(error.code) + " / " + error.context + ": " + error.message);
}Manual cleanup order#
Release dependent constraints before their bodies, then release reusable shape/layer resources before the world and runtime. jolt_stop() performs the managed order automatically and safely when repeated.
Verified operation contracts#
These contracts distinguish units, return values, errors and lifetime according to the current implementation. Check results: false, 0 and undefined can mean different things for different methods.
| Symbol / owner | Signature | Operation contract |
|---|---|---|
| JoltConstraint.destroy | JoltConstraint.destroy() | Boolean; destroys the constraint, not its bodies. Repeated destruction of an already invalid wrapper returns true; a native error can return false. |
| JoltConstraint.get_collision_enabled | JoltConstraint.get_collision_enabled() | Collision between the two attached bodies. Setter Boolean success, getter Boolean state; a world constraint always reads false and its setter succeeds without enabling collision. Not a global layer filter. |
| JoltConstraint.get_current_length | JoltConstraint.get_current_length() | Pulley only: minimum/maximum/current combined length in m (including pulley ratio). 0 on error. |
| JoltConstraint.get_enabled | JoltConstraint.get_enabled() | Constraint activation, not destruction. Setter returns Boolean success; getter Boolean state, also false on error. Requires a live constraint. |
| JoltConstraint.get_friction | JoltConstraint.get_friction(axis) | SixDOF axis 0..5; N for translation, N·m for rotation; 0 for invalid wrapper/type, inspect native errors separately. |
| JoltConstraint.get_impulses | JoltConstraint.get_impulses() | SixDOF: {translation, rotation, motor_translation, motor_rotation}, each {x, y, z}, latest solver impulses N·s / N·m·s. undefined for invalid wrapper/type; inspect native errors separately. |
| JoltConstraint.get_max_length | JoltConstraint.get_max_length() | Pulley only: minimum/maximum/current combined length in m (including pulley ratio). 0 on error. |
| JoltConstraint.get_min_length | JoltConstraint.get_min_length() | Pulley only: minimum/maximum/current combined length in m (including pulley ratio). 0 on error. |
| JoltConstraint.get_native_handle | JoltConstraint.get_native_handle() | Live constraint handle or 0. |
| JoltConstraint.get_position | JoltConstraint.get_position() | Slider displacement or Path distance along the path in m, not {x, y, z}. 0 on error. |
| JoltConstraint.get_sixdof_debug_frame | JoltConstraint.get_sixdof_debug_frame() | SixDOF diagnostic struct: world anchor and axis_x/y/z; relative_position/orientation and target_position/orientation in constraint space. Positions m, orientations XYZW. undefined for invalid wrapper/type; read-only, no solver change. |
| JoltConstraint.get_target_angular_velocity | JoltConstraint.get_target_angular_velocity() | SixDOF: {x, y, z} in constraint space in °/s. Getter does not capture every native error; validate type/liveness first and inspect the error state. |
| JoltConstraint.get_target_position | JoltConstraint.get_target_position() | SixDOF: {x, y, z} in constraint space in m. Getter does not capture every native error; validate type/liveness first and inspect the error state. |
| JoltConstraint.get_target_velocity | JoltConstraint.get_target_velocity() | SixDOF: {x, y, z} in constraint space in m/s. Getter does not capture every native error; validate type/liveness first and inspect the error state. |
| JoltConstraint.is_valid | JoltConstraint.is_valid() | Boolean checking the native constraint and original world; destruction of an attached body invalidates the constraint. |
| JoltConstraint.set_collision_enabled | JoltConstraint.set_collision_enabled(enabled) | Collision between the two attached bodies. Setter Boolean success, getter Boolean state; a world constraint always reads false and its setter succeeds without enabling collision. Not a global layer filter. |
| JoltConstraint.set_enabled | JoltConstraint.set_enabled(enabled) | Constraint activation, not destruction. Setter returns Boolean success; getter Boolean state, also false on error. Requires a live constraint. |
| JoltConstraint.set_friction | JoltConstraint.set_friction(axis,value) | SixDOF axis 0..5; finite nonnegative force limit (axes 0..2, N) or torque limit (3..5, N·m). Boolean. Setting a limit alone does not enable a motor. |
| JoltConstraint.set_length | JoltConstraint.set_length(minimum, maximum) | Pulley only: finite 0 ≤ min ≤ max in m for combined length. Boolean. |
| JoltConstraint.set_target_angular_velocity | JoltConstraint.set_target_angular_velocity(x,y,z) | SixDOF: finite x/y/z in constraint space in °/s; Boolean. Setting a target alone does not enable individual motors. |
| JoltConstraint.set_target_position | JoltConstraint.set_target_position(x,y,z) | SixDOF: finite x/y/z in constraint space in m; Boolean. Setting a target alone does not enable individual motors. |
| JoltConstraint.set_target_velocity | JoltConstraint.set_target_velocity(x,y,z) | SixDOF: finite x/y/z in constraint space in m/s; Boolean. Setting a target alone does not enable individual motors. |