Handling formations

This is a key algorithm for creating flocks or a group of military agents. It is designed to be flexible enough to give you the chance to create your own formations.

The end result from this recipe will be a set of target positions and rotations for each agent in the formation. Then, it is up to you to create the necessary algorithms to move the agent to the previous targets.

Note

We can use the movement algorithms learnt in Chapter 1, Movement, in order to target those positions.

Getting ready

We will need to create three base classes that are the data types to be used by the high-level classes and algorithms. The Location class is very similar to the Steering class and is used to define a target position and rotation given the formation's anchor point and rotation. The SlogAssignment class is a data type to match a list's indices and agents. Finally, the Character class component holds the target Location class.

The following is the code for the Location class:

using UnityEngine;
using System.Collections;

public class Location
{
    public Vector3 position;
    public Quaternion rotation;

    public Location ()
    {
        position = Vector3.zero;
        rotation = Quaternion.identity;
    }

    public Location(Vector3 position, Quaternion rotation)
    {
        this.position = position;
        this.rotation = rotation;
    }
}

The following is the code for the SlotAssignment class:

using UnityEngine;
using System.Collections;

public class SlotAssignment
{
    public int slotIndex;
    public GameObject character;

    public SlotAssignment()
    {
        slotIndex = -1;
        character = null;
    }
}

The following is the code for the Character class:

using UnityEngine;
using System.Collections;

public class Character : MonoBehaviour
{
    public Location location;

    public void SetTarget (Location location)
    {
        this.location = location;
    }
}

How to do it…

We will implement two classes—FormationPattern and FormationManager:

  1. Create the FormationPattern pseudo-abstract class:
    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    
    public class FormationPattern: MonoBehaviour
    {
        public int numOfSlots;
        public GameObject leader;
    }
  2. Implement the Start function:
    void Start()
    {
        if (leader == null)
            leader = transform.gameObject;
    }
  3. Define the function for getting the position for a given slot:
    public virtual Vector3 GetSlotLocation(int slotIndex)
    {
        return Vector3.zero;
    }
  4. Define the function for retrieving whether a given number of slots is supported by the formation:
    public bool SupportsSlots(int slotCount)
    {
        return slotCount <= numOfSlots;
    }
  5. Implement the function for setting an offset in the locations, if necessary:
    public virtual Location GetDriftOffset(List<SlotAssignment> slotAssignments)
    {
        Location location = new Location();
        location.position = leader.transform.position;
        location.rotation = leader.transform.rotation;
        return location;
    }
  6. Create the appropriate class for managing the formation:
    using UnityEngine;
    using System.Collections;
    using System.Collections.Generic;
    
    public class FormationManager : MonoBehaviour
    {
        public FormationPattern pattern;
        private List<SlotAssignment> slotAssignments;
        private Location driftOffset;
    }
  7. Implement the Awake function:
    void Awake()
    {
        slotAssignments = new List<SlotAssignment>();
    }
  8. Define the function for updating the slot assignments given the list's order:
    public void UpdateSlotAssignments()
    {
        for (int i = 0; i < slotAssignments.Count; i++)
        {
            slotAssignments[i].slotIndex = i;
        }
        driftOffset = pattern.GetDriftOffset(slotAssignments);
    }
  9. Implement the function for adding a character in the formation:
    public bool AddCharacter(GameObject character)
    {
        int occupiedSlots = slotAssignments.Count;
        if (!pattern.SupportsSlots(occupiedSlots + 1))
            return false;
        SlotAssignment sa = new SlotAssignment();
        sa.character = character;
        slotAssignments.Add(sa);
        UpdateSlotAssignments();
        return true;
    }
  10. Implement the function for removing a character in the formation:
    public void RemoveCharacter(GameObject agent)
    {
        int index = slotAssignments.FindIndex(x => x.character.Equals(agent));
        slotAssignments.RemoveAt(index);
        UpdateSlotAssignments();
    }
  11. Implement the function for updating the slots:
    public void UpdateSlots()
    {
        GameObject leader = pattern.leader;
        Vector3 anchor = leader.transform.position;
        Vector3 slotPos;
        Quaternion rotation;
        rotation = leader.transform.rotation;
        foreach (SlotAssignment sa in slotAssignments)
        {
            // next step
        }
    }
  12. Finally, implement the foreach loop:
    Vector3 relPos;
    slotPos = pattern.GetSlotLocation(sa.slotIndex);
    relPos = anchor;
    relPos += leader.transform.TransformDirection(slotPos);
    Location charDrift = new Location(relPos, rotation);
    Character character = sa.character.GetComponent<Character>();
    character.SetTarget(charDrift);

How it works…

The FormationPattern class contains the relative positions to a given slot. For example, a child CircleFormation class will implement the GetSlotLocation class, given the number of slots and its locations over 360 degrees. It is intended to be a basic class, so it is up to the manager to add a layer for permissions and rearrangement. That way, the designer can focus on simple formation scripting, deriving from the base class.

The FormationManager class, as stated earlier, handles the high-level layer and arranges the locations in line with the formation's needs and permissions. The calculations are based on the leader's position and rotation, and they apply the necessary transformations given the pattern's principles.

There is more…

It is worth mentioning that the FormationManager and FormationPattern classes are intended to be components of the same object. When the leader field in the manager is set to null, the leader is the object itself. That way, we could have a different leader object in order to have a clean inspector window and class modularity.

See also

  • Refer to Chapter 1, Movement, the Arriving and leaving recipe
  • For further information on drift offset and how to play with this value, please refer to Ian Millington's book, Artificial Intelligence for Games
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.226.251.206