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*;*
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); }
}
}