- Home /
How do I make enemies not spawn in the same position?
So I am making a 3D game which has a perspective of a 2D game and I made an enemy spawner game object and gave it this script. And here's the problem, sometimes enemies randomly spawn in the same position. How do I prevent this from happening? Thanks for your support!
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class SpawnEnemy : MonoBehaviour
{
public GameObject[] enemyPrefabs;
public Vector3 spawnPos;
private int enemyCount = 0;
private float startDelay = 3;
public float minValuex = -8;
public float spaceBetweenSquares = 1f;
private BoxCollider boxCollider;
private int maxEnemies = 4;
Vector3[] spawnedEnemiesPosition;
private PlayerController playerControllerScript;
// Start is called before the first frame update
void Start()
{
InvokeRepeating("SpawnEnemies", startDelay, Random.Range(4f, 5f));
playerControllerScript = GameObject.Find("Player").GetComponent<PlayerController>();
for (int i = 0; i < maxEnemies; i++)
{
spawnedEnemiesPosition[i] = new Vector3(0, 0, 0);
}
}
// Update is called once per frame
void Update()
{
}
Vector3 RandomSpawnPosition()
{
float spawnPosX = minValuex + (RandomSquareIndex() * spaceBetweenSquares);
float spawnPosY = 0;
Vector3 spawnPosition = new Vector3(spawnPosX, spawnPosY, -2);
return spawnPosition;
}
// Generates random square index from 0 to 3, which determines which square the target will appear in
int RandomSquareIndex()
{
return Random.Range(-2, 0);
}
void SpawnEnemies()
{
if (playerControllerScript.gameOver == false && enemyCount <= 4)
{
int enemyIndex = Random.Range(0, enemyPrefabs.Length);
spawnPos = RandomSpawnPosition();
//bool CheckBox(Vector3 center = new Vector3(0.009f, 1.26f, -0.033f), Vector3 halfExtents, Quaternion orientation = Quaternion.identity, int layermask = DefaultRaycastLayers, QueryTriggerInteraction queryTriggerInteraction = QueryTriggerInteraction.UseGlobal);
Instantiate(enemyPrefabs[enemyIndex], spawnPos, enemyPrefabs[enemyIndex].transform.rotation);
enemyCount++;
}
}
}
Answer by Zaeran · Jan 04, 2021 at 12:29 AM
Easiest way would be to add the position of each enemy to a list when they spawn.
Before an enemy spawns, check each position in the list, and if the proposed spawn position is too close to a position in the list, change the proposed spawn position.
Answer by ddooms · Jan 04, 2021 at 01:17 AM
I second Zaeran's answer, but I'd like to add to it.
Add a List and then add to that anytime you have a new enemy (remove from it when you remove that enemy). Then, when you're checking for their distances, just use a sample position to test if there is an enemy at the chosen place for the enemy's next spawn - Loop through all current enemies and test their distances :
if((enemy.position - newSpawnPosition).sqrMagnitude > (some squared value)) continue; else { GenerateNewPosition() }
GenerateNewPosition is just a dummy function here, but it would just call the spawn function again to get a new position to test. You should have some fail safe so you don't have an endless loop. Perhaps if it fails after 100 times it just stops, because maybe there are too many enemies or something.
I wouldn't use InvokeRepeating because it's difficult to control. Try to use controllable loops that you can break anytime. While loops, or maybe forloops. You could also use the Update function to spawn enemies every few seconds.
float nextSpawn, spawnRate = 1;
void Update {
if(Time.time > nextSpawn){
nextSpawn = Time.time + spawnRate;
SpawnEnemy();
}
}
This would spawn an enemy every 1 second. You can utilize Time.time to control the rate of things easily -- just have a value that keeps the time, and add the amount of time you want to wait to it.
Thanks a lot! But i have a question, what do you mean by saying "some squared value" in the code?
if((enemy.position - newSpawnPosition).sqr$$anonymous$$agnitude > (some squared value))
I'm using a distance formula instead of the standard "Vector3.Distance(a,b)" It works with squares, rather than division. In order to get an accurate reading without negatives, you square it (multiply it by itself). It's the same as (enemy.position - newSpawnPosition) * (enemy.position - newSpawnPosition).
So the "some squared value" is a value that represents the distance between your enemy and the spawn point you're checking. We have to square it, since we are also squaring the distance formula.
So if we have a distance of 4, it was squared. That means if we want to CHECK for a distance of 3, we have to take 3 to the power of 2 (3 * 3) in order to keep it consistent between the two values.
float dist = 0;
float $$anonymous$$imumDistance = 5;
dist = (enemy.position - newSpawnPosition).sqr$$anonymous$$agnitude;
if(dist > ($$anonymous$$imumDistance * $$anonymous$$imumDistance)){
// spawn
}
The reason I do this is because Vector3.Distance is ever so slightly less efficient than doing the formula yourself. Sorry to complicate things lol.
Your answer
Follow this Question
Related Questions
[Help] How Do I Randomly Spawn Game Objects On Specific Coordinates? 3 Answers
Pickup & Drop Objects + Spawn items HELP 0 Answers
Different way to spawn prefabs 0 Answers
Spawning help 1 Answer
Navmesh problem with SpawnSystem 0 Answers