Hi, after some researching on the internet, I got some C# code that lets me generate a dynamic circle formation based on the units selected, circles come in different sizes as there are different units (tanks, infantry) with different sizes, the code works, but units with larger sizes seems seems to overlap and when there are few units like around 4 or 6 active, the generated circles/spheres starts overlapping alot, can’t seem to find a way to pass this >_<, but I think I already kinda understand what I’m doing wrong, its generating the circle formation radius, maybe I’m doing it wrong?
script/code:
[System.Serializable]
public class UnitFormationPoint
{
public Vector3 point;
public float radius;
public float angle;
}
[System.Serializable]
public class UnitCirclePoint
{
public Vector3 point;
public float radius;
public float angle;
}
public class TestFormationGeneratorV3 : MonoBehaviour
{
public List<UnitFormationPoint> formationPoints = new List<UnitFormationPoint>();
// thinginfo is the base monobehaviour script which all units has, and AIThingInfo stores the nav mesh agent
public List<ThingInfo> expThings = new List<ThingInfo>();
public float mainRadius;
public float PIMultiply = 2;
public List<GameObject> testGameObjects = new List<GameObject>();
// Start is called before the first frame update
void Start()
{
GenCircles();
}
void GenCircles()
{
expThings.Clear();
foreach (AIThingInfo _aiThing in GameObject.FindObjectsOfType<AIThingInfo>())
{
if (_aiThing.gameObject.activeSelf)
{
expThings.Add(_aiThing.GetComponent<ThingInfo>());
}
}
foreach (GameObject _go in testGameObjects) { GameObject.Destroy(_go); } testGameObjects.Clear();
formationPoints.Clear();
float _radius = 0;
foreach (ThingInfo _expThing in expThings)
{
_radius += _expThing.aiThingInfo.agent.radius;
}
GenerateCircles(expThings, transform.position, _radius / (Mathf.PI * PIMultiply), 0, expThings.Count);
}
// Update is called once per frame
void Update()
{
PlayerController player = PlayerController.Get;
//formationPoints = AIDefs.GenerateFormationRectangle(player.selectedThings, transform.position, transform.forward);
//formationPoints = AIDefs.GenerateFormationCircle(player.selectedThings, transform.position, transform.forward);
//Debug.Log($"PI Value: {Mathf.PI * 1}");
if (Input.GetKeyDown(KeyCode.T))
{
//CircledCircle(transform.position.x, transform.position.z, 100, 0.1f, 100, 1, 10);
}
GenCircles();
}
//-----------------------------------------------------------------------------------------------------------------
void GenerateCircles(List<ThingInfo> _things, Vector3 position, float radius, float margin, int circleCount)
{
List<ThingInfo> things = new List<ThingInfo>();
things.AddRange(_things);
List<ThingInfo> things_assigned = new List<ThingInfo>();
// to store circles
List<UnitCirclePoint> circles = new List<UnitCirclePoint>();
// circle half count
int halfCount = Mathf.FloorToInt(circleCount / 2);
// current angle & total angle
float current_angle = 0;
float total_angle = 0;
// add circles() - top
for (int i = 0; i < halfCount; i++)
{
if(things.Count > 0)
{
ThingInfo thing = things[0];
// get thing radius & do a calculation
float thing_radius = thing.aiThingInfo.agent.radius;
float thing_angle = Mathf.Atan2(2 * thing_radius + margin, radius);
// add circle
circles.Add(new UnitCirclePoint
{
radius = thing_radius,
angle = current_angle + thing_angle / 2
});
current_angle += thing_angle;
// add thing to assigned things & remove it from the default list
things_assigned.Add(things[0]);
things.RemoveAt(0);
}
}
total_angle = current_angle;
// re-center top circles
float correction = total_angle / 2 + Mathf.PI / 2;
for (int i = 0; i < halfCount; i++)
{
circles[i].angle -= correction;
}
current_angle = 0;
total_angle = 0;
// add circles() - bottom
for (int i = 0; i < (circleCount - halfCount); i++)
{
if (things.Count > 0)
{
ThingInfo thing = things[0];
// get thing radius & do a calculation
float thing_radius = thing.aiThingInfo.agent.radius;
float thing_angle = Mathf.Atan2(2 * thing_radius + margin, radius);
// add circle
circles.Add(new UnitCirclePoint
{
radius = thing_radius,
angle = current_angle + thing_angle / 2
});
current_angle += thing_angle;
// add thing to assigned things & remove it from the default list
things_assigned.Add(things[0]);
things.RemoveAt(0);
}
}
total_angle = current_angle;
correction = total_angle / 2 - Mathf.PI / 2;
for (int i = halfCount; i < circleCount; i++)
{
circles[i].angle -= correction;
}
// create formation points
formationPoints.Clear();
foreach(UnitCirclePoint _circle in circles)
{
float circleX = position.x + radius * Mathf.Cos(_circle.angle);
float circleZ = position.z + radius * Mathf.Sin(_circle.angle);
formationPoints.Add(new UnitFormationPoint
{
point = new Vector3(circleX, position.y, circleZ),
radius = _circle.radius
});
GameObject testSphere = GameObject.CreatePrimitive(PrimitiveType.Sphere);
testSphere.transform.position = new Vector3(circleX, position.y, circleZ);
testSphere.transform.localScale = new Vector3(_circle.radius * 2, _circle.radius * 2, _circle.radius * 2);
testGameObjects.Add(testSphere);
}
}
private void OnDrawGizmos()
{
if(formationPoints.Count == 0) { return; }
foreach(UnitFormationPoint _circle in formationPoints)
{
Gizmos.DrawSphere(_circle.point, _circle.radius);
}
}
}
topics explored:
https://stackoverflow.com/questions/25470730/position-different-size-circles-around-a-circular-path-with-no-gaps
https://discussions.unity.com/t/how-to-calculate-required-radius-of-a-circle/586886/5
etc...