- Home /
How to spawn enemies at different locations and avoid overlapping each other
Hi everyone,
This is my first question here.
Heres my problem: I want to spawn enemies at different location (I have already managed to do that) but I want each enemy to spawn in a different location where it doesn't overlap other enemies.
My code:
public class SpawnManager : MonoBehaviour {
[SerializeField] GameObject[] asteroidPrefab;
private float currentSpawn;
void Start () {
}
// Update is called once per frame
void Update () {
}
public void spawnAsteroids()
{
StartCoroutine(spawner());
}
IEnumerator spawner()
{
while (true)
{
Instantiate(asteroidPrefab[Random.Range(0, 3)], new Vector3(Random.Range(-7.0f, 7.0f), 7, 0), Quaternion.identity);
yield return new WaitForSeconds(5.0f);
}
}
}
This happens when the Random.range spawns enemies at close locations.
Is there a way that to avoid that?
Thank you.
$$anonymous$$aybe set a collision detection and just say if colliding with another enemy destroy(this).
Answer by Code1345 · Jul 30, 2018 at 11:05 PM
Just to clarify, you just don't want two enemies spawning at the same location right? Just making sure I understand your problem.
Great- $$anonymous$$y first idea would be to have a bool in place whenever your enemy spawns at a location- if it was true, no more enemies would spawn there until it was false. But to do that you would need a system to check if an enemy has spawned in there. Do the locations your enemies are spawning in at have colliders? Or are they just coordinates?
Thank you for your reply yes they have colliders!
Answer by Harinezumi · Jul 31, 2018 at 07:58 AM
I've recently did an implementation of this. Basically whenever I instantiate my prefab, I store it in a list of existing instances ( instances.Add(newInstance);
), and when it "dies", I remove it from this list. Then before instantiating I first generate the position, and go through the list of instances to check if the position overlaps. If it does, I generate a new position. However, after a set number of tries I just stop, because if there is no space, I don't want to get into an infinite loop.
In code:
public class SpawnManager : MonoBehaviour {
[SerializeField] private AsteroidLogic[] asteroidPrefabs = null; // assign asteroid prefabs to it from Editor
[SerializeField] private maxPositionRetries = 5;
private List<AsteroidLogic> existingAsteroids = new List<AsteroidLogic>();
public void AddAsteroid (AsteroidLogic value) { existingAsteroids.Add(value); }
public void RemoveAsteroidLogic (AsteroidLogic value) { existingAsteroids.Remove(value); }
public void Spawn () {
AsteroidLogic asteroidPrefab = asteroidPrefabs[Random.Range(0, asteoidPrefabs.Length)];
// probably check if the selected asteroidPrefab is not null!
int numTries = 0;
do {
// generate a position, and check if it valid
Vector3 position = new Vector3(Random.Range(-7.0f, 7.0f), 7, 0);
if (IsValidPosition(position, asteroidPrefab.transform.localScale)) { // assign the second parameter (size) correctly, for example from the extents of the asteroid's collider, or get it from the AsteroidLogic
AsteroidLogic newAsteroid = Instantiate(asteroidPrefab, position, Quaternion.identity);
newAsteroid.SetSpawnManager(this);
break; // new asteroid generated, break out of loop
}
} while (numTries++ < maxPositionRetries);
}
private bool IsValidPosition (Vector3 positionToCheck, Vector3 objectSize) {
for (int i = 0; i < existingAsteroids.Length; ++i) {
AsteroidLogic asteroid = existingAsteroids[i];
Vector3 asteroidPosition = asteroid.transform.position;
Vector3 asteroidSize = asteroid.transform.localScale; // assign this correctly, e.g. from the collider's extents
Vector3 positionDifference = positionToCheck - asteroidPosition;
Vector3 sizeSum = objectSize + asteroidSize;
// if the positions are less than the sum of the extents, they overlap; also check for z coordinate if you change that
if (Mathf.Abs(positionDifference.x) < sizeSum.x && Mathf.Abs(positionDifference.y) < sizeSum.y) { return false; }
}
return true;
}
}
public class AsteroidLogic : MonoBehaviour {
private SpawnManager spawnManager;
// automatically register itself when the manager is set
public void SetSpawnManager (SpawnManager value) {
spawnManager = value;
spawnManager.AddAsteroid(this);
}
// automatically unregister itself when it gets destroyed
private void OnDestroy () {
if (spawnManager != null) { spawnManager.RemoveAsteroid(this); }
}
}
Well, it is not copy-paste from Visual Studio, so there can be syntax errors, but the general logic is sound. Of course, the AsteroidLogic
class should be in its own source file called AsteroidLogic.cs
, and the asteroid prefabs need this component on them.
What doesn't work? It doesn't compile, or causes Unity to hang (infinite loop), or...?