How to spawn gameobjects around the edge of a circle radius randomly ?

Hi guys!

I have the following problem:

The idea would be to have an item, in this case a small castle. Every time I click on the item, a speciifed number of soldiers will spawn around that mincastle. The thing is I want to keep clicking in the castle and spawning more soldiers, so I don’t want that to spawn again the soldiers in the position where there are already spawned soldiers.

I really suck at math, and I could find some code, but this one only reparts the soldier at an equal distance in the edge of the circle, and the spawn positions will be always the same.

Any tips about how to improve this code so I can keep spawn soldiers at new positions everytime and not being spawned in repeated positions? I know I have to check the position with Physics.overlap , but the thing is I don’t know how to spawn the soldiers in the edge of the radius randomly :frowning:

I also tried with Random.insideCircle / Random.insideSphere but with those solutions the soldier spawned in the top of the castle… so Im a little lost

Here is a little sketch about the idea I have on mind and I will attach my current code

The current code

 using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;
public class ForgeSpawn : MonoBehaviour
{
     public LayerMask layerGround;
     private RaycastHit hit;
     public GameObject soldierPrefab;
     public int amountSoldiers;
     public float radius;
     private void Update()
     {
         Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
         if (Physics.Raycast(ray, out hit, 100, layerGround))
         {
             if (Input.GetKeyDown(KeyCode.Mouse0))
             {
                 SpawnSoldiersAround(hit.transform.position);
             }
         }
        
       
     }
    
     private void SpawnSoldiersAround(Vector3 point)
     {
         bool incorrectSpawn = true;
        
         for (int i = 0; i < amountSoldiers; i++)
         {
                 /* Distance around the circle */
                 var radians = 2 * MathF.PI / amountSoldiers * i;
                 /* Get the vector direction */
                 var vertical = MathF.Sin(radians);
                 var horizontal = MathF.Cos(radians);
                
                 var spawnDir = new Vector3(horizontal, 0, vertical);
            
                 /* Get the spawn position */
                 var spawnPos = point + spawnDir * radius; // Radius is just the distance away from the point
                 /* Now spawn */
                 var soldier = Instantiate(soldierPrefab, spawnPos, Quaternion.identity) as GameObject;
                 /* Rotate the enemy to face the opposite direction of the castle */
                 soldier.transform.rotation = Quaternion.LookRotation(soldier.transform.position - transform.position);
                 /* Adjust height */
                 soldier.transform.Translate(new Vector3(0, soldier.transform.localScale.y / 2, 0));
            
           
         }
        
     }
}

I would appreciate any help… As Im a little frustrated with this :frowning:

I think you’re very close here. Based on your picture I would assume this is 2D (which is X and Y generally speaking), but based on line 39 above, you are setting X and Z in that spawnDir vector as if you were on a flat plane like the ground.

For EITHER 2D or 3D, you could actually just do this:

// this is to replace lines 33 to 39 above:
Vector3 spawnDir = Random.onUnitSphere; // could be anywhere on a sphere

// now we flatten it!

// Choose ONLY ONE of the following lines of code:
spawnDir.y = 0;    // flatten it to the X/Z plane
// OR choose:
spawnDir.z = 0;    // flatten it to the X/Y plane (for 2D use)

// (do not do both of the above!)

// now normalize:
spawnDir.Normalize();

// and now use it in your computation for line 42 above!

Hi Kurt.

Thanks for your answer! At the end I used something similar. My project is in 3D, and with this code I was able to get it.

Thanks for the help anyways! Much appreciated :slight_smile:

 private void SpawnSoldiersAround()
    {
        bool incorrectSpawn = true;
       
        for (int i = 0; i < amountSoldiers; i++)
        {
            /* Get the spawn position */
                var spawnPos = RandomPointOnCircleEdge(radius);

                /* Now spawn */
                var soldier = Instantiate(soldierPrefab, spawnPos, Quaternion.identity) as GameObject;

                /* Rotate the enemy to face the opposite direction of the castle */
                soldier.transform.rotation = Quaternion.LookRotation(soldier.transform.position - transform.position);

                /* Adjust height */
                soldier.transform.Translate(new Vector3(0, soldier.transform.localScale.y / 2, 0));
           
          
        }
    }

    private Vector3 RandomPointOnCircleEdge(float radius)
    {

        bool correctSpawn = false;
       
        Vector2 vector2 = new Vector2(0, 0);

        //Number of times we will try to search for an empty position
        int searchCount = 10;

        while (searchCount-- > 0 && !correctSpawn)
        {
            vector2 = Random.insideUnitCircle.normalized * radius;

            correctSpawn = CheckIfPositionIsOcuppied(vector2);
        }

        return new Vector3(vector2.x, 0, vector2.y);
    }

    private static bool CheckIfPositionIsOcuppied(Vector2 vector2)
    {
        bool correctSpawn = true;

        Collider[] collidersDetected = Physics.OverlapBox(new Vector3(vector2.x, 0, vector2.y), new Vector3(1, 2, 1f));

        if (collidersDetected.Length != 0)
        {
            foreach (Collider col in collidersDetected)
            {
                if (col.CompareTag("Soldier"))
                {
                    correctSpawn = false;
                }
            }
        }

        return correctSpawn;
    }
1 Like