Spawn system that doesn't instantiated enemies on top of player or each other (C#)

Hello my fellow great Unites. I have an enemy spawn system that spawns out enemies using Ienumerator inside a set of borders. Now the only problem is that the enemies will sometimes spawn out on the player, or another object…

How can I fix this problem? Using a for loop? Any code example?

Here’s the code:

 using UnityEngine;
     using System.Collections;
     using System.Collections.Generic;
     
     public class EnemySpawn : MonoBehaviour 
     {
         // EnemyPrefabs
         public GameObject EnemyBouncingPrefab;
         public GameObject EnemyGrowerPrefab;
         public GameObject EnemyMinePrefab;
     
         //Show where spawn is prefab
         public GameObject showEnemySpawn;
         public GameObject showEnemyFlyBySpawn;
         public GameObject showEnemyGrowerSpawn;
         public GameObject showEnemyMineSpawn;
     
     
         // Array of spawn points 
         public Transform[] spawnPointsEnemyFollow; 
         public Transform[] spawnPointsEnemyExplode;
         public Transform[] spawnPointsEnemyFlyBy;
         
         // Borders
         public Transform borderTop;
         public Transform borderBottom;
         public Transform borderLeft;
         public Transform borderRight;
         
     
         //Used to put active enemies in a list
         public static List<GameObject> activeEnemies;
     
         void Start () 
         {
             // Starts a coroutine function for spawning bouncing enemies and fly by enemy
             StartCoroutine(SpawnBouncingEnemy());
             StartCoroutine(SpawnGrowerEnemy());
             StartCoroutine(SpawnEnemyFlyBy());
             StartCoroutine(SpawnMineEnemy());
         }
     
         void Awake()
         {
             // Create the list
             activeEnemies = new List<GameObject>();
     
         }
         
         // Spawn a Bouncing enemy
         IEnumerator SpawnBouncingEnemy() 
         {
             //Wait 3 to 5 seconds when game starts to spawn a ball
             yield return new WaitForSeconds(Random.Range(1, 3));
     
             while(true)
             {
                 //Calls the function to set random position
                 Vector2 spawnPoint = RandomPointWithinBorders();
                 
                 // Show spawn location for one second
                 Object marker = Instantiate(showEnemySpawn, spawnPoint, Quaternion.identity);
                 yield return new WaitForSeconds(1);
                 Destroy(marker);
                 
                 // Spawn enemy
                 GameObject newEnemy = (GameObject) Instantiate(EnemyBouncingPrefab, spawnPoint, Quaternion.identity);
     
                 activeEnemies.Add(newEnemy);
     
                 yield return new WaitForSeconds(Random.Range(4, 6));
     
             }
             
         }    
     
         // Spawn a Grower enemy
         IEnumerator SpawnGrowerEnemy() 
         {
             //Wait 3 to 5 seconds when game starts to spawn a "grower"
             yield return new WaitForSeconds(Random.Range(20, 40));
             
             while(true)
             {
                 //Calls the function to set random position
                 Vector2 spawnPoint = RandomPointWithinBorders();
                 
                 // Show spawn location for one second
                 Object marker = Instantiate(showEnemyGrowerSpawn, spawnPoint, Quaternion.identity);
                 yield return new WaitForSeconds(1);
                 Destroy(marker);
                 
                 // Spawn enemy
                 GameObject newEnemy = (GameObject) Instantiate(EnemyGrowerPrefab, spawnPoint, Quaternion.identity);
                 
                 activeEnemies.Add(newEnemy);
                 
                 yield return new WaitForSeconds(Random.Range(20, 50));
                 
             }
             
         }
     
         // Spawn a Mine enemy
         IEnumerator SpawnMineEnemy() 
         {
             //Wait 40 to 60 seconds when game starts to spawn a "Mine_enemy"
             yield return new WaitForSeconds(Random.Range(45, 60));
             
             while(true)
             {
                 //Calls the function to set random position
                 Vector2 spawnPoint = RandomPointWithinBorders();
                 
                 // Show spawn location for one second
                 Object marker = Instantiate(showEnemyMineSpawn, spawnPoint, Quaternion.identity);
                 yield return new WaitForSeconds(2);
                 Destroy(marker);
                 
                 // Spawn enemy
                 GameObject newEnemy = (GameObject) Instantiate(EnemyMinePrefab, spawnPoint, Quaternion.identity);
                 
                 activeEnemies.Add(newEnemy);
                 
                 yield return new WaitForSeconds(Random.Range(50, 60));
                 
             }
             
         }
     
         public Vector2 RandomPointWithinBorders()
         {
             //Code that will spawn a bouncing ball and ShowSpawn at a random position inside the borders 
             Vector2 random = new Vector2();
             random.x = (int)Random.Range(borderLeft.position.x, borderRight.position.x);
             random.y = (int)Random.Range(borderBottom.position.y, borderTop.position.y);
             return random;
         }
     }

@Internetman … Just shooting from the hip here :wink:

First … You might try adding a collider to the spawner object and then move it to the intended spawn point every iteration. If the collider is triggered (e.g. it is in a collision with something) then you can test if that is OK … if not then regenerate the intended spawn point … rinse and repeat until a spawn point is generated that is not in collision with anything undesirable.

OR … If you have a small set of spawned objects, you could test the distance from the generated spawn point to each object in the collection of spawned objects and if it’s too small/close then step out and regenerate another spawn point.

OR … you could cast a ray down from some point directly above the intended spawn point …

Vector3 RayCastPoint = new Vector3(SpawnPoint.x, SpawnPoint.y + 100f, SpawnPoint.z);

… if the ray collides with something undesirable then regenerate the intended spawn point and test again. I do this in my spawner to test for collisions with water layers and other world objects. Only problem with this is the ray is infinitely narrow and could miss collision with something by an infinitesimally small amount.

Finally … you could leave the spawner code alone and add logic to the spawned object in it’s Awake/Start methods to check where it spawned in at. If the location is undesirable then it will destroy itself and decrement any counters. If it’s OK then it will initialize as needed and go about its business. Note that the spawner should keep track of how many it can and has spawned (e.g. 3 of 5 have spawned in). As the spawner still has more items to spawn things move on without incident.

Try this modified version of your code. This will get a valid spawn point before the first Instantiate() because it is calculated in RandomPointWithinBorders().

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

public class EnemySpawn : MonoBehaviour 
{
    // EnemyPrefabs
    public GameObject EnemyBouncingPrefab;
    public GameObject EnemyGrowerPrefab;
    public GameObject EnemyMinePrefab;

    //Show where spawn is prefab
    public GameObject showEnemySpawn;
    public GameObject showEnemyFlyBySpawn;
    public GameObject showEnemyGrowerSpawn;
    public GameObject showEnemyMineSpawn;


    // Array of spawn points 
    public Transform[] spawnPointsEnemyFollow; 
    public Transform[] spawnPointsEnemyExplode;
    public Transform[] spawnPointsEnemyFlyBy;

    // Borders
    public Transform borderTop;
    public Transform borderBottom;
    public Transform borderLeft;
    public Transform borderRight;


    //Used to put active enemies in a list
    public static List<GameObject> activeEnemies;

    public Transform player;   // player transform
    public float minDistance;  // minimum distance from spawnpoint to object

    void Start()
    {
        // if the player has not been set in the inspector log it
        if (player == null)
        {
            Debug.LogWarning("EnemySpawn.Start(): There is no player set in the inspector. Please set this.");
        }

        // if the player has been set in the inspector but the Tag is not set to "Player" log it
        if ((player != null) && (player.tag != "Player"))
        {
            Debug.LogWarning("EnemySpawn.Start(): The Player does not have its Tag set to 'Player'.");
        }

        // if the player has not been set in the inspector and can find by Tag set it
        if ((player == null) && (GameObject.FindGameObjectsWithTag("Player") != null))
        {
            Debug.LogWarning("EnemySpawn.Start(): Setting the missing player.");
            player = GameObject.FindGameObjectsWithTag("Player")[0].transform;
        }

        // Starts a coroutine function for spawning bouncing enemies and fly by enemy
        StartCoroutine(SpawnBouncingEnemy());
        StartCoroutine(SpawnGrowerEnemy());
        // StartCoroutine(SpawnEnemyFlyBy());  // this does not exist yet
        StartCoroutine(SpawnMineEnemy());
    }

    void Awake()
    {
        // Create the list
        activeEnemies = new List<GameObject>();
    }

    // Spawn a Bouncing enemy
    IEnumerator SpawnBouncingEnemy() 
    {
        //Wait 3 to 5 seconds when game starts to spawn a ball
        yield return new WaitForSeconds(Random.Range(1, 3));

        while(true)
        {
            //Calls the function to set random position
            Vector3 spawnPoint = RandomPointWithinBorders();

            // Show spawn location for one second
            Object marker = Instantiate(showEnemySpawn, spawnPoint, Quaternion.identity);
            yield return new WaitForSeconds(1);
            Destroy(marker);

            // Spawn enemy
            GameObject newEnemy = (GameObject) Instantiate(EnemyBouncingPrefab, spawnPoint, Quaternion.identity);

            activeEnemies.Add(newEnemy);

            yield return new WaitForSeconds(Random.Range(4, 6));
        }
    }    

    // Spawn a Grower enemy
    IEnumerator SpawnGrowerEnemy() 
    {
        //Wait 3 to 5 seconds when game starts to spawn a "grower"
        yield return new WaitForSeconds(Random.Range(20, 40));

        while(true)
        {
            //Calls the function to set random position
            Vector3 spawnPoint = RandomPointWithinBorders();

            // Show spawn location for one second
            Object marker = Instantiate(showEnemyGrowerSpawn, spawnPoint, Quaternion.identity);
            yield return new WaitForSeconds(1);
            Destroy(marker);

            // Spawn enemy
            GameObject newEnemy = (GameObject) Instantiate(EnemyGrowerPrefab, spawnPoint, Quaternion.identity);

            activeEnemies.Add(newEnemy);

            yield return new WaitForSeconds(Random.Range(20, 50));
        }
    }

    // Spawn a Mine enemy
    IEnumerator SpawnMineEnemy() 
    {
        //Wait 40 to 60 seconds when game starts to spawn a "Mine_enemy"
        yield return new WaitForSeconds(Random.Range(45, 60));

        while(true)
        {
            //Calls the function to set random position
            Vector3 spawnPoint = RandomPointWithinBorders();

            // Show spawn location for one second
            Object marker = Instantiate(showEnemyMineSpawn, spawnPoint, Quaternion.identity);
            yield return new WaitForSeconds(2);
            Destroy(marker);

            // Spawn enemy
            GameObject newEnemy = (GameObject) Instantiate(EnemyMinePrefab, spawnPoint, Quaternion.identity);

            activeEnemies.Add(newEnemy);

            yield return new WaitForSeconds(Random.Range(50, 60));
        }
    }

    public Vector3 RandomPointWithinBorders()
    {
        bool done = false;
        Vector3 randomPosition = new Vector3();
    
        while (!done)
        {
            //Code that will spawn a bouncing ball and ShowSpawn at a random position inside the borders 
            randomPosition.x = (int)UnityEngine.Random.Range(borderLeft.position.x, borderRight.position.x);
            randomPosition.y = (int)UnityEngine.Random.Range(borderBottom.position.y, borderTop.position.y);
            randomPosition.z = 0;

            done = ((minDistance == 0) || ValidMinimumDistance(randomPosition));
        }
        return randomPosition;
    }

    bool ValidMinimumDistance(Vector3 enemyPosition)
    {
        bool isValid = true;
        minDistance = Mathf.Abs(minDistance);
    
        if (player != null)
        {
            isValid = (Vector3.Distance(player.position, enemyPosition) > minDistance);
        }
    
        if (isValid && (activeEnemies.Count > 0))
        {
            for (int i = 0; i < activeEnemies.Count; i++)
            {
                if (Vector3.Distance(activeEnemies*.transform.position, enemyPosition) < minDistance)*

{
isValid = false;
break;
}
}
}
return isValid;
}
}
You do not need the modified Start() as long as you set Player in the inspector.