Code Control

Quick guide to control your units manually in code.

Overview

Here you can follow steps on how to control formation movement manually without using the Unit Formation package for placement and unit management.

If you wish to avoid using manual control, check out the Unit Formation Control and follow steps there, but it is completely optional. You can use your own code to handle unit movement for full control.

Example on how to do this can be seen in GroupFormationDemo and MovingUnitFormation scripts.

Listeners

First we need to set up our code so that it knows when formation has been updated or moved using the FormationMovementManager.

private void Awake()
{
    // Attach a listener method for formation updated (moved)
    FormationMovementManager.Current.OnFormationUpdated += HandleFormationUpdated;
    
    // Attach a listener method for formation stopped moving.
    FormationMovementManager.Current.OnFormationStopped += HandleFormationUpdated;
}

private void HandleFormationUpdated(in ActiveFormation formation)
{
    // ...
}

Start Movement

Now that we have listeners set up, we can start moving units with the manager. Let's construct StartFormationParams used to start the movement.

// Use NavMeshAgent for movable objects, if you are using Native Unity navigation.
[SerializedField] private NavMeshAgent[] agents;

private void GoToDestination()
{
    // 1. Create a collection of moving units, defined by transform and max speed.
    var movingUnits = agents
        .Select(agent => new MovingUnit(agent.transform, maxSpeed))
        .ToList();

    // 2. Set your desired destination and facing direction when reaching destination.
    var destination = new Vector3(10, 0, -10);
    var direction = Vector3.forward;

    // 3. Create formation layout (see example below)
    FormationLayoutProvider layout = new(CreateFormationPositions);

    // 4. Create parameters
    var parameters = new StartParameters(
        movingUnits, layout, destination, direction);

    // 5. Start moving
    if (FormationMovementManager.Current.StartMoving(parameters, out var newGroup))
    {
        // Path successfully computed & formation is moving.
        _group = newGroup;
    }
}

// Define formations manually or use the provided ones.
private Vector3[] CreateFormationPositions(int count)
{
    // Create a collection of local positions here or use formations available
    // in package like RectangleFormation, RingFormation, etc.
    return new RectangleFormation(10, 1).GetPositions(count);
}

This will create an anchor for the formation and start moving it. But you will not see any movement yet, you now need to update each unit when changes have been made using the events set up in Awake method. The example above uses the basic ProvideFormationPositions delegate. Check out the Segmented Formation page for using ProvideFormationSlots delegate to provide positions for different unit type in a single formation group.

Update Units

Then you need to update the units to let them know about their new positions & speed. Speed is important if formation is allowed to slow down duo to lagging units, or lagging/steering units can speed up to catch up to the formation.

private Dictionary<int, NavMeshAgent> mappedAgents = new();

private void Awake() 
{
    // Map to dictionary for improved performance when updating units
    mappedAgents = agents
        .ToDictionary(agent => (agent.transform.GetInstanceID(), agent));
}

private void HandleFormationUpdated(in ActiveFormation formation)
{
    // Iterate through formation members and update their destinations.
    foreach (var member in formation.members)
    {
        var agent = mappedAgents[member.id];
        agent.speed = member.speed;
        agent.SetDestination(member.desiredPosition);
    }
}

Now this is a very straightforward approach and it will work, but navigation can be a heavy CPU load and it has a habit of reaching limits on how many units it can manage smoothly pretty fast. However workarounds and other solutions can provide great results as well when larger number of units needs to move at the same time. By using less intensive configuration (no sorting, obstacle avoidance, etc.) can still support 500 units in a single formation.

Stop Movement

You can stop movement of the formation at any time by using the StopMoving method. At this point all the formations within a group will stop moving.

// Use group reference received when starting the movement.
FormationGroup formationGroup = // ...
FormationMovementManager.Current.StopMoving(formationGroup);

Last updated