Skip to content

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 linearVelocity and angularVelocity onto 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