How to Build Readable Unit Formations in Unity with Procedural Layouts and Runtime Behaviours
In an RTS, tactics game or tower defense project, moving a group of units is more difficult than moving a single character.
A useful formation system needs to solve several problems at once:
- generate positions for the whole group;
- assign units to those positions;
- keep the formation readable while it moves;
- support different layouts for different gameplay situations;
- react to targets, paths and obstacles;
- avoid rebuilding expensive data every frame;
- make the result easy to preview and debug.
A common mistake is to treat every unit as an independent agent. That approach can work for small groups, but it becomes harder to control as the number of units grows. A formation gives the group a shared structure while still allowing individual units to move toward their assigned positions.
Start with a formation anchor
The first useful abstraction is a formation anchor.
The anchor represents the group as a whole. It can contain:
- a position;
- a rotation or facing direction;
- a destination;
- a list of local formation positions;
- the current group state.
The units do not need to calculate the entire formation independently. Instead, the system generates local slots around the anchor, and each unit receives a target slot.
The local slot positions can then be transformed into world space using the anchor's position and rotation.
Conceptually, the process looks like this:
Formation layout → Local slots → World-space targets → Unit movement
This separation makes the system easier to extend. You can change the layout without rewriting the movement code, or change the movement behaviour without changing how the slots are generated.
Procedural formation layouts
A formation does not have to be a hardcoded list of transforms. It can be generated from a small set of parameters such as:
- unit count;
- spacing;
- width;
- depth;
- radius;
- direction;
- custom seed;
- target position.
For example, a simple grid formation can be generated from rows and columns:
using UnityEngine;
public static class FormationLayout
{
public static Vector3[] CreateGrid(int unitCount, int columns, float spacing)
{
Vector3[] slots = new Vector3[unitCount];
if (columns <= 0)
{
columns = 1;
}
for (int i = 0; i < unitCount; i++)
{
int row = i / columns;
int column = i % columns;
float x = (column - (columns - 1) * 0.5f) * spacing;
float z = -row * spacing;
slots[i] = new Vector3(x, 0f, z);
}
return slots;
}
}
The result is a set of positions relative to the formation anchor. The same movement system can use those positions for an RTS squad, a tactical group or a tower defense wave.
Different layouts serve different gameplay roles
A grid is useful for compact groups, but different gameplay situations benefit from different shapes.
Examples include:
- line formations for advancing units;
- wedges for attacking or moving through space;
- walls for defensive positioning;
- circles and rings for surrounding a target;
- arcs for readable front-facing groups;
- columns for narrow paths;
- spirals and radial patterns for visual effects;
- custom positions for designed encounters.
The important design decision is to keep the layout independent from unit movement. A unit should not need to know whether its target came from a grid, a wedge or a ring.
Assign units to slots predictably
Once the slots are generated, the system needs to assign units to them.
A simple approach is to assign units by index. This is easy to implement, but it can create unnecessary movement when the formation changes or units are selected in a different order.
More advanced assignment can consider:
- the unit's current position;
- the distance to each slot;
- the unit's role;
- the previous slot assignment;
- the direction of movement;
- whether the unit is already close to a suitable slot.
Stable assignment is important. If units constantly exchange slots, the group can look disorganized even when the target positions are correct.
Move units toward formation targets
Each unit can use its assigned world-space slot as a movement target.
A basic movement loop might look like this:
using UnityEngine;
public class FormationUnit : MonoBehaviour
{
[SerializeField] private float moveSpeed = 4f;
[SerializeField] private float turnSpeed = 8f;
private Vector3 targetPosition;
public void SetFormationTarget(Vector3 position)
{
targetPosition = position;
}
private void Update()
{
Vector3 offset = targetPosition - transform.position;
offset.y = 0f;
if (offset.sqrMagnitude < 0.01f)
{
return;
}
Vector3 direction = offset.normalized;
transform.position += direction * moveSpeed * Time.deltaTime;
Quaternion targetRotation = Quaternion.LookRotation(direction);
transform.rotation = Quaternion.Slerp(
transform.rotation,
targetRotation,
turnSpeed * Time.deltaTime
);
}
}
This is only the final movement layer. A production system may add navigation, obstacle avoidance, local steering, animation state changes and different movement speeds.
The key idea remains the same: the formation system provides a meaningful target, and the unit movement layer decides how to reach it.
Keep the formation readable during movement
Teleporting units directly from one layout to another can look unnatural. It is usually better to transition between the old and new slot positions.
For a transition from one formation to another, the system can interpolate each slot:
Vector3 currentSlot = Vector3.Lerp(
oldSlot,
newSlot,
transitionProgress
);
The transition can be triggered when:
- the player changes formation type;
- the group starts attacking;
- the group reaches a waypoint;
- a target appears;
- the group changes from movement to defense;
- units are added or removed.
Formation morphing also works well for non-combat visuals, such as spell patterns, boss attacks and simulation displays.
Add behaviours as independent layers
A formation layout describes where the units should be. A behaviour describes how that layout changes over time.
Useful behaviours include:
- rotation;
- pulse and wave motion;
- phase shifts;
- organic noise;
- flow noise;
- swirl or vortex movement;
- follow-target movement;
- path and waypoint following;
- local cluster splitting;
- boids-style steering;
- topology changes.
Keeping behaviours separate makes them reusable. A rotation behaviour can be applied to a ring, a grid or a custom pattern without duplicating the layout code.
A behaviour stack can also define the order in which modifications are applied:
Base formation
↓
Morphing
↓
Rotation
↓
Noise or pulse
↓
Movement and steering
The order matters. Applying rotation before morphing can produce a different result from applying it after morphing.
Do not rebuild expensive work every frame
A formation system can become expensive if it recalculates everything for every unit on every frame.
In many projects, the following data does not need to be rebuilt continuously:
- the basic formation layout;
- unit-to-slot assignments;
- path requests;
- neighbour lists;
- cached references;
- unchanged behaviour parameters.
Instead, update structural data when something meaningful changes:
- the unit count changes;
- the formation type changes;
- spacing changes;
- the group receives a new destination;
- the current path becomes invalid;
- a unit joins or leaves the group.
Then use lightweight movement and steering between structural updates.
Profiling is important here. The best update frequency depends on the project, the number of units, the navigation system and the complexity of the behaviours.
Formation movement and pathfinding are different problems
A formation layout does not automatically solve navigation.
The group may need to move around obstacles, follow a path or keep units from getting stuck. It helps to separate the problems:
- The group chooses a destination or path.
- The formation generates local positions around the group.
- Each unit moves toward its assigned position.
- Local steering handles small corrections.
- The group updates or rebuilds the formation when the situation changes.
This approach avoids forcing every unit to solve the same high-level navigation problem independently.
For large groups, avoid restarting pathfinding because the target moved by a very small amount. Use meaningful thresholds and cached paths where appropriate.
Use editor previews and debug tools
Formation systems are much easier to develop when the result can be previewed without entering Play Mode.
Useful editor features include:
- drawing slot gizmos;
- previewing formation rotation;
- showing the formation anchor;
- displaying unit assignment lines;
- previewing enabled behaviours;
- editing waypoints visually;
- testing different unit counts;
- saving and loading example presets.
Debug visualization can reveal problems that are difficult to see from code alone. For example, a formation may appear to move incorrectly because the anchor rotation is wrong, the slot order changes unexpectedly or the assignment method produces crossings.
A reusable ScriptableObject architecture
Formation definitions and behaviours can be stored as reusable assets instead of being hardcoded into individual scenes.
A ScriptableObject-based workflow can store:
- formation type;
- spacing and dimensions;
- behaviour parameters;
- movement settings;
- preset names;
- demo configurations.
This makes it easier to reuse a formation across several scenes and to create variations without duplicating controller code.
It also makes the workflow more accessible to designers who need to tune formations in the Unity Inspector.
Building a reusable formation toolkit
A complete toolkit for Unity group movement can combine:
- procedural formation generators;
- runtime formation controllers;
- behaviour assets;
- formation morphing;
- target and leader following;
- path and waypoint movement;
- boids-style group movement;
- editor previews;
- custom inspectors;
- debug gizmos;
- interactive demo scenes;
- reusable runtime presets.
RomaSoft's Dynamic Formation System follows this structure. It includes 17 formation types, 15 behaviour assets, runtime formation generation, formation morphing, path and waypoint movement, editor previews and an interactive demo browser.
The included formation types cover layouts such as arcs, circles, grids, hex grids, rings, spirals, stars, walls, wedges, paths, text formations and custom positions.
The behaviour assets include options such as boids-style movement, follow target, path following, rotation, pulse, phase shift, organic noise, swirl vortex and topology changes.
The package requires Unity 2022.3 LTS or newer, the Unity Input System and TextMeshPro. The runtime system is render-pipeline agnostic; the included demo is designed for the Built-in Render Pipeline and may require material adjustments when adapted to URP or HDRP.
You can see the system in action in the Dynamic Formation System demo video or check the Unity Asset Store listing for current pricing, compatibility and package details.
Final checklist
Before using a formation system in a production project, check the following:
- Is the group anchor separate from individual unit movement?
- Are formation slots generated independently from steering?
- Are unit assignments stable during normal movement?
- Can the formation change without teleporting units?
- Are expensive calculations updated only when necessary?
- Can the group follow targets and waypoints?
- Are there debug gizmos or editor previews?
- Can designers tune formations without changing code?
- Have you tested the system with realistic unit counts?
- Have you profiled CPU time, allocations and frame time?
Readable group movement is the result of several small design decisions rather than one special algorithm. Start with a clear formation model, separate layout from movement, update expensive work deliberately and add behaviours as reusable layers.
That foundation can support RTS squads, tactical units, tower defense waves, boss attack patterns, spell formations and simulation prototypes without turning every group into a collection of unrelated movement scripts.
For more Unity development guides, visit the RomaSoft Guides.
Top comments (0)