Constrainer¶
Continuously enforces position and rotation constraints on a target transform while active.
Namespace: Octoputs.Constraints
Inherits: ControllableComponent → MonoBehaviour
Implements: ILastLateUpdatable, ILastFixedUpdatable
Overview¶
Constrainer keeps a target Transform inside spatial limits — bounds, spheres, planes, lines, angle ranges. Each frame the target's position runs through every position constraint in order, then its rotation through every rotation constraint.
When the target has a Rigidbody, Constrainer corrects twice per physics step: in the last FixedUpdate channel, after every other system has applied forces but before the step, and again in the post-physics channel right after the step, so the drawn pose never leaves the constraint. On dynamic bodies it also projects linearVelocity and angularVelocity onto the constraint tangent, so collisions, joints, and forces can push the body along the constraint surface but never through it.
With Rigid Contacts on, collisions also see the body as immovable in the directions its constraints block: a heavy box dropped on a drawer rests on it instead of shoving it off its rail and falling through. A push along the rail at an angle moves the body a little less than it should.
Without a Rigidbody, Constrainer runs in the last LateUpdate channel so it has the final say after every other transform write.
Inherits its lifecycle from ControllableComponent: the constraining window can be tied to OnEnable/OnDisable, an attachment cycle, an external event, or manual Begin/End.
Properties¶
| Property | Type | Description |
|---|---|---|
| target | Transform |
Transform to constrain. Falls back to this GameObject when empty. |
| space | Space |
Whether constraints operate on world or local position/rotation. |
| projectVelocityOnCorrection | bool |
On dynamic Rigidbody targets, project velocity onto the constraint tangent on correction (preserve motion along the surface). Disable for legacy "zero velocity on correction" behaviour. |
| correctAfterPhysics | bool |
On Rigidbody targets, corrects again right after each physics step. On by default. |
| rigidContacts | bool |
On dynamic Rigidbody targets, collisions treat the body as immovable in the directions its constraints block. On by default. |
| positionConstraints | List<IPositionConstraint> |
Position constraints applied in order. Empty = position not constrained. |
| rotationConstraints | List<IRotationConstraint> |
Rotation constraints applied in order. Empty = rotation not constrained. |
Constraint Lifecycle¶
On OnBegin (the lifecycle entry point), Constrainer calls Initialize() on each position constraint and then each rotation constraint before registering for the update loop. This is the hook custom constraints implementing IPositionConstraint / IRotationConstraint use to resolve their Smart* fields (CopiedOnInit mode in particular needs this — without it the field would never run its resolver). Both interfaces provide a default no-op Initialize(), so most constraints don't need to override it; override only if your constraint holds smart fields or other init-once state.