Formation Flying Script

Hi All,

I’m doing a space sim and was having a hard time getting formation flying to work. A lot of the suggestions I read pointed towards boids but it wasn’t the path that I wanted to take. After a bit of wrestling yesterday I prototyped a system that enables formation flying without having to parent/unparent game objects or work with boids.

It consists of a FlightController which simply assigns flight leaders and flight members, and a follow function in a movement controller assigned to each ship GameObject. This is very rough and still in the early prototype phase (a lot of stuff is hardcoded still) but I wanted to get it out there so people trying to handle similar tasks had something to reference.

Right now I’m using each ships “flight position” to determine what their lateral offset from the flight leader should be.

FlightController.cs

using UnityEngine;
using System.Collections;
using System.Collections.Generic;

public class FlightController : MonoBehaviour {
	
	public List<Transform> ships = new List<Transform>();
	public Transform currentLeader;

	// Use this for initialization
	void Start () {
		
		//Get a List of all Ships this Flight Controller is Responsible For
		GameObject[] gos = GameObject.FindGameObjectsWithTag("Ship");
		foreach(GameObject go in gos)
		{
			if(go.GetComponent<FighterAI>().faction.ToString() == "Imperial")
			{
				ships.Add(go.transform);
			}
		}
		
		//Sort List by Name ie Alpha 1, Alpha 2, ect
		ships.Sort(
		    delegate(Transform i1, Transform i2) 
		    { 
		       return i1.name.CompareTo(i2.name); 
		    }
		);
	
	}
	
	// Update is called once per frame
	void Update () {
		setLeadersAndFollowers();
	}
	
	// Creates 3 ship flight groups out of the list. Ships assigned a leader will enter Follow state. Ships without leader will enter
	// Patrol state.
	void setLeadersAndFollowers()
	{
		int j = 1;
		
		for(int i=0; i<ships.Count; i++)
		{
			if(i%3==0)
			{
				currentLeader = ships[i];
				j=1;
			} else {
				ships[i].GetComponent<FighterAI>().flightLeader = currentLeader;
				ships[i].GetComponent<FighterAI>().flightPosition = j;
				j++;
			}
		}
	}
}

Follow function inside ship movement controller

// Follows Flight Leader in Patrol
	public void FollowTarget(Transform target, int xOffset)
	{
		// Sets the ship back and to the right or left of the leader
		Vector3 targetPosition = target.position + (target.right * xOffset) + (target.forward * Math.Abs(xOffset) * -1);
		
		float distance = Vector3.Distance(targetPosition,transform.position);

		//Ensures we never actually hit our targetPosition, we'll always be at least 20 units away
		//We need this so that Quaternion.RotateTowards doesn't create weird rotations
		if(distance < 20 ) {
			_myShip.currSpeed = 0;
		} else if(distance > 20  distance < 40) {
			_myShip.currSpeed = 2 * target.GetComponent<Ship>().currSpeed;
		} else {
			_myShip.currSpeed = 2 * _myShip.maxSpeed;
		}
		
		//Rotate Towards Target
		Quaternion newRotation = Quaternion.LookRotation(targetPosition - transform.position);
		transform.rotation = Quaternion.RotateTowards(transform.rotation, newRotation, _myShip.manuverability * Time.deltaTime);
		
		//Bank Towards Target
		Vector3 tarPosition = (targetPosition - transform.position);
		float dir = Vector3.Dot(tarPosition.normalized, transform.right.normalized);
		
		//Add a bit of dampening
		if(Math.Abs(dir) > _myShip.bankDampening) {
			transform.Rotate(0,0, -dir * _myShip.bank * Time.deltaTime);
		}
		
		//Move Towards Target		
		transform.position += transform.forward * _myShip.currSpeed * Time.deltaTime;
		
	}

Hope this helps somebody else!

Happy developing!

2 Likes

Hi did you get it to work i was trying to parent and un-parent but your way seems better …how is ur ship movement control structured?

you code really helped me get started i got it working now i just need to arrange the formation positions

This isn’t exactly what you are looking for but this allows flocking.

http://wiki.unity3d.com/index.php?title=Flocking

Hey, wanted to say THANK YOU!! … and also ask a couple questions.

I know this is old, so you may have moved on but I wondered what the deal is with _myShip… If I read this right, you probably declared it as:

ship _myship = this;

which brings me to this block of code:

        //Ensures we never actually hit our targetPosition, we'll always be at least 20 units away
        //We need this so that Quaternion.RotateTowards doesn't create weird rotations
        if(distance < 20 ) {
            _myShip.currSpeed = 0;
        } else if(distance > 20  distance < 40) {
            _myShip.currSpeed = 2 * target.GetComponent<Ship>().currSpeed;
        } else {
            _myShip.currSpeed = 2 * _myShip.maxSpeed;
        }

I have played with it a bit, and I am getting two weird results. As is, the “Followers” stay behind the leader, and “jump” when the distance passes the threshold. If I change the code to match the speed, the followers take off ahead of the leader and disappear into the abyss. If you have any suggestions, or would even share the rest of the script, I would be very grateful.

Thank you!