Tutorial: Constraining Movement¶
Keep a draggable inside a region — a tabletop, a circle, a sphere, an axis-aligned box — without writing any code. A Constrainer clamps a Transform each frame so it can never leave the volume you describe, even when a Rigidbody is pushed by physics.
By the end you'll have a cube that can be dragged anywhere inside a tabletop area, snaps to the surface on the Y axis, and is gently nudged back if a collision tries to shove it off.
Prerequisites: A scene with at least one Transform you want to constrain — for this tutorial, a Cube under an AttachingAgent.
When to Use a Constrainer¶
Use it when you need a hard spatial limit that applies every frame, regardless of who's writing the position:
- A grid-based puzzle where pieces must stay inside the board.
- A tabletop drag where Z must stay zero.
- A character carrying an object that must remain inside a "reach" sphere.
- A physics object on a slider rail.
A modifier on a transform operation only constrains that operation's output. The Constrainer runs after everything else — physics, joints, other operations — so it has the final say.
1. Add the Constrainer¶
Select the GameObject whose transform you want to clamp (the Cube). Add the Constrainer component.
Add Component → Octoputs → Constrainer
The component is organized into:
- Target — the Transform to constrain (defaults to this GameObject).
- Position — list of position constraints.
- Rotation — list of rotation constraints.
- Execution (inherited from
ControllableComponent) — when the constrainer is active.
2. Pick When the Constrainer is Active¶
The Constrainer is a ControllableComponent — its Begin/End can be tied to any frame. By default it begins on OnEnable and ends on OnDisable. For a piece that's only constrained while being dragged, swap the execution frame to a Targeted Cycle Frame subscribed to the AttachableObject's Attached cycle — the Constrainer only enforces while the cube is held.
For a fixed-area object that's always constrained, leave the default.
3. Add a Position Constraint¶
The Position Constraints list takes IPositionConstraint implementations. Each one is a step — they run in order, and the final clamped position is what gets written.
Click + and pick Position Constraint (axis-aligned bounding box). Configure:
| Field | Value |
|---|---|
| Min | (-2, 0, -2) |
| Max | (2, 0, 2) |
This keeps the cube inside a 4×4 horizontal patch with Y locked to 0.
Press Play. Try to drag the cube outside the patch — it gets clamped to the edge. Set Y manually in the Inspector during play — it snaps back to 0.
Combining constraints
Add another constraint, e.g. Sphere Constraint with radius 3, and now the cube is restricted to whatever both constraints allow. Constraints chain — each one further restricts the position. Use this to compose simple shapes into complex regions.
4. Add a Rotation Constraint¶
For rotation, the Rotation Constraints list takes IRotationConstraint. Add a Rotation Constraint with an angle range to limit how much the cube can rotate from its starting orientation.
Same pattern: constraints run in order, the final clamped rotation is written.
5. Local vs World Space¶
The Constrainer has a Space field (World or Local). World space is what you usually want for tabletop / scene-relative limits. Local space is useful when the parent moves — e.g. a tray that tilts: constraining the contents in local space keeps them in the tray frame regardless of the tray's world rotation.
6. Working With Rigidbodies¶
If the target has a Rigidbody, the Constrainer automatically switches to the last FixedUpdate channel. It writes Rigidbody.position / Rigidbody.rotation directly, before the physics solver integrates — joints and forces start each step from a constrained state.
There's one extra setting that matters for dynamic Rigidbodies:
- Project Velocity On Correction (on by default) — when a correction pushes the body back onto the constraint surface, the Constrainer projects
linearVelocityandangularVelocityonto the tangent of the constraint. This means a body shoved into the boundary keeps sliding along the boundary (instead of bouncing or sticking) — collisions, joints, and forces still push it around, just not through the surface.
Turn this off for the older "zero velocity on every correction" behaviour — useful for kinematic-style snapping but harsh on rigidbodies with joints.
7. Constraint Types Reference¶
The picker shows every available constraint. The set evolves; common ones include:
Position¶
| Constraint | What it bounds |
|---|---|
| Bounds Position Constraint | Axis-aligned box (Min / Max). |
| Sphere Position Constraint | Sphere around a center point. |
| Plane Position Constraint | Half-space defined by a plane. |
| Line Position Constraint | Single axis (slider rail). |
| Combinatory Position Constraint | Composite — combine children with All or Any. |
Rotation¶
| Constraint | What it bounds |
|---|---|
| Angle Range Rotation Constraint | Euler angle limits per axis. |
Browse the picker for the full set in your install.
8. Constrainer vs Transform Modifier¶
These two surfaces both shape position — when to use which?
| Use a... | When |
|---|---|
| Modifier on a transform operation | You want the constraint to apply only while that operation runs. Constraint is part of the move recipe. |
| Constrainer component | You want a hard cap that applies every frame regardless of who's writing the transform — physics included. |
A common combo: a modifier handles the during-drag feel (snap-to-grid, surface-slide), the Constrainer guarantees the never-leave-this-region invariant.
Next Steps¶
- Transform Operations tutorial for modifier-based shaping.
- Constrainer reference for the full API.
- Constraints overview for the constraint-type catalog.