using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class SquadFormation : MonoBehaviour
{
enum Formation
{
Square, Circle, Triangle
}
[Header("Main Settings")]
[Space(5)]
public Transform squadMemeberPrefab;
[Range(4, 100)]
public int numberOfSquadMembers = 20;
[Range(1, 20)]
public int numberOfSquads = 1;
[Range(0, 4)]
public int columns = 4;
public int gaps = 10;
public int circleRadius = 10;
public float yOffset = 0;
[Range(3, 50)]
public float moveSpeed = 3;
[Range(3, 50)]
public float rotateSpeed = 1;
public float threshold = 0.1f;
public bool randomSpeed = false;
[Range(1, 100)]
public int randSpeedMin = 1;
[Range(1, 100)]
public int randSpeedMax = 1;
public bool startRandomFormation = false;
public string currentFormation;
private Formation formation;
private List<Quaternion> quaternions = new List<Quaternion>();
private List<Vector3> newpositions = new List<Vector3>();
private bool move = false;
private bool squareFormation = false;
private List<GameObject> squadMembers = new List<GameObject>();
private float[] step;
private int[] randomSpeeds;
private int index = 0;
private int numofobjects = 0;
// Use this for initialization
void Start()
{
numofobjects = numberOfSquadMembers;
if (startRandomFormation)
{
formation = (Formation)UnityEngine.Random.Range(0, Enum.GetNames(typeof(Formation)).Length);
}
else
{
formation = Formation.Square;
}
currentFormation = formation.ToString();
ChangeFormation();
foreach (Transform child in gameObject.transform)
{
if (child.tag == "Squad Member")
squadMembers.Add(child.gameObject);
}
randomSpeeds = RandomNumbers(randSpeedMin, randSpeedMax, squadMembers.Count);
step = new float[squadMembers.Count];
}
// Update is called once per frame
void Update()
{
if (numofobjects != numberOfSquadMembers)
{
numofobjects = 0;
numofobjects = numberOfSquadMembers;
squadMembers = new List<GameObject>();
FormationSquare();
}
if (Input.GetKeyDown(KeyCode.F))
{
randomSpeeds = RandomNumbers(randSpeedMin, randSpeedMax, squadMembers.Count);
foreach (int speedV in randomSpeeds)
{
if (index == randomSpeeds.Length)
index = 0;
step[index] = speedV * Time.deltaTime;
index++;
}
ChangeFormation();
}
if (move == true)
{
MoveToNextFormation();
}
}
private void ChangeFormation()
{
switch (formation)
{
case Formation.Square:
FormationSquare();
break;
case Formation.Circle:
FormationCircle();
break;
}
}
private Vector3 FormationSquarePositionCalculation(int index) // call this func for all your objects
{
float posX = (index % columns) * gaps;
float posY = (index / columns) * gaps;
return new Vector3(posX, posY);
}
private void FormationSquare()
{
//newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
Transform go = squadMemeberPrefab;
for (int i = 0; i < numofobjects; i++)
{
if (squadMembers.Count == 0)
go = Instantiate(squadMemeberPrefab);
Vector3 pos = FormationSquarePositionCalculation(i);
if (newpositions.Count > 0)
{
go.position = new Vector3(newpositions[newpositions.Count -1].x + pos.x, 0, newpositions[newpositions.Count - 1].y + pos.y);
}
else
{
go.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
}
go.Rotate(new Vector3(0, -90, 0));
go.tag = "Squad Member";
go.transform.parent = gameObject.transform;
newpositions.Add(go.transform.position);
}
move = true;
squareFormation = true;
formation = Formation.Circle;
}
private Vector3 FormationCirclePositionCalculation(Vector3 center, float radius, int index, float angleIncrement)
{
float ang = index * angleIncrement;
Vector3 pos;
pos.x = center.x + radius * Mathf.Sin(ang * Mathf.Deg2Rad);
pos.z = center.z + radius * Mathf.Cos(ang * Mathf.Deg2Rad);
pos.y = center.y;
return pos;
}
private void FormationCircle()
{
newpositions = new List<Vector3>();
quaternions = new List<Quaternion>();
Vector3 center = transform.position;
float radius = (float)circleRadius / 2;
float angleIncrement = 360 / (float)numberOfSquadMembers;
for (int i = 0; i < numberOfSquadMembers; i++)
{
Vector3 pos = FormationCirclePositionCalculation(center, radius, i, angleIncrement);
var rot = Quaternion.LookRotation(center - pos);
pos.y = Terrain.activeTerrain.SampleHeight(pos);
pos.y = pos.y + yOffset;
newpositions.Add(pos);
quaternions.Add(rot);
}
move = true;
squareFormation = false;
formation = Formation.Square;
}
private void MoveToNextFormation()
{
if (randomSpeed == false)
{
step[0] = moveSpeed * Time.deltaTime;
}
for (int i = 0; i < squadMembers.Count; i++)
{
squadMembers_.transform.LookAt(newpositions*);*_
if (randomSpeed == true)
{
squadMembers*.transform.position =*
Vector3.MoveTowards(squadMembers.transform.position, newpositions_, step*);
}
else
{_
_squadMembers.transform.position =
Vector3.MoveTowards(squadMembers.transform.position, newpositions, step[0]);
}
if (Vector3.Distance(squadMembers.transform.position, newpositions) < threshold)
{
if (squareFormation == true)
{
Vector3 degrees = new Vector3(0, 0, 0);
Quaternion quaternion = Quaternion.Euler(degrees);
squadMembers.transform.rotation = Quaternion.Slerp(squadMembers.transform.rotation, quaternion, rotateSpeed * Time.deltaTime);
}
else
{
squadMembers.transform.rotation = Quaternion.Slerp(squadMembers.transform.rotation, quaternions, rotateSpeed * Time.deltaTime);
}
}
}
}*_
private static int[] RandomNumbers(int min, int max, int howMany)
{
int[] myNumbers = new int[howMany];
for (int i = 0; i < howMany; i++)
{
myNumbers = UnityEngine.Random.Range(min, max);
}
return myNumbers;
}
}
Since the code is a bit long I will also show the places where the problem is:
In the Update:
if (numofobjects != numberOfSquadMembers)
{
numofobjects = 0;
numofobjects = numberOfSquadMembers;
squadMembers = new List();
FormationSquare();
}
For example numberOfSquadMembers value is 7
Now I change it to 8
Now numofobjects is 8
And it’s going to the FormationSquare method:
private Vector3 FormationSquarePositionCalculation(int index) // call this func for all your objects
{
float posX = (index % columns) * gaps;
float posY = (index / columns) * gaps;
return new Vector3(posX, posY);
}
private void FormationSquare()
{
//newpositions = new List();
quaternions = new List();
Transform go = squadMemeberPrefab;
for (int i = 0; i < numofobjects; i++)
{
if (squadMembers.Count == 0)
go = Instantiate(squadMemeberPrefab);
Vector3 pos = FormationSquarePositionCalculation(i);
if (newpositions.Count > 0)
{
go.position = new Vector3(newpositions[newpositions.Count -1].x + pos.x, 0, newpositions[newpositions.Count - 1].y + pos.y);
}
else
{
go.position = new Vector3(transform.position.x + pos.x, 0, transform.position.y + pos.y);
}
go.Rotate(new Vector3(0, -90, 0));
go.tag = “Squad Member”;
go.transform.parent = gameObject.transform;
newpositions.Add(go.transform.position);
}
move = true;
squareFormation = true;
formation = Formation.Circle;
}
In the FormationSquare mode before I used to make new instance for newpositions List but now my logic say to keep the positions all the time and when creating new squad member/s to add it to the last position of the formation. But also to keep the formation shape.
But what it does now it first adding much more objects. If numofobjects is 8 it will not add new 8 but more. Second problem it’s adding the new once on the old once positions so in the end there are places there is 2 or 3 objects at the same position. But I want it to add the new once to last position/s so the formation square will get bigger.
Same idea with the circle formation. I didn’t do it yet not sure how but if it’s in the circle formation mode make the circle bigger show the current objects moving a bit to the sides or backward and make the circle bigger for the new once.
The main goal is when changing the numberOfSquadMembers while the game is running to update the currently formation.