- Home /
How to prevent 3D object spawn overlapping?
Hello to all. In my game, a bunch of prefabs are randomly placed throughout the map. However, they tend to often spawn on top of each other. I couldn't find a tutorial explaining a fix for 3D objects, so if someone could give me a hand, I would be thankful. Here is my current code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObstaclePlacer : MonoBehaviour
{
public int ObsToPlace = 10;
public GameObject[] Obstacles = new GameObject[0];
GameObject Obstacle;
void Awake()
{
for (int i = 0; i < ObsToPlace; i++)
{
Obstacle = Obstacles[Random.Range(0, Obstacles.Length)];
Vector3 position = new Vector3(Random.Range(-50.0f, 50.0f), 0, Random.Range(-50.0f, 50.0f));
Instantiate(Obstacle, position + Obstacle.transform.position, Quaternion.identity);
}
}
}
Thanks!
Answer by Darkforge317 · May 16, 2018 at 06:18 AM
I'm on my phone so I can't write code for you. But I can tell you what to do...
Use Physics.OverlapSphere() to check the spawn position before spawning. This method will collect all colliders in the specified area and return them all in an array. Just go through the array in a loop to look for items that you don't want to spawn on top of... Then if you find an item you don't want to spawn on top of, then pick a new random spawn location and repeat the process. (loop)
EDIT:
Try this. You're going to have to play around with the obstacleCheckRadius variable. It determines how big our check area is.
One warning about my code
My code will not spawn an obstacle after 10 attempts. Therefore even if you want 10 obstacles, you might only get 9 if there's not enough space to spawn the last one. I did this to prevent an infinite loop from happening, which would crash your Unity and game.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ObstaclePlacer : MonoBehaviour
{
public int ObsToPlace = 10;
public GameObject[] Obstacles = new GameObject[0];
GameObject Obstacle;
public float obstacleCheckRadius = 3f;
public int maxSpawnAttemptsPerObstacle = 10;
void Awake()
{
for (int i = 0; i < ObsToPlace; i++)
{
Obstacle = Obstacles[Random.Range(0, Obstacles.Length)];
// Create a position variable
Vector3 position = Vector3.zero;
// whether or not we can spawn in this position
bool validPosition = false;
// How many times we've attempted to spawn this obstacle
int spawnAttempts = 0;
// While we don't have a valid position
// and we haven't tried spawning this obstable too many times
while(!validPosition && spawnAttempts < maxSpawnAttemptsPerObstacle)
{
// Increase our spawn attempts
spawnAttempts++;
// Pick a random position
position = new Vector3(Random.Range(-50.0f, 50.0f), 0, Random.Range(-50.0f, 50.0f));
// This position is valid until proven invalid
validPosition = true;
// Collect all colliders within our Obstacle Check Radius
Collider[] colliders = Physics.OverlapSphere(position, obstacleCheckRadius);
// Go through each collider collected
foreach(Collider col in colliders)
{
// If this collider is tagged "Obstacle"
if(col.tag == "Obstacle")
{
// Then this position is not a valid spawn position
validPosition = false;
}
}
}
// If we exited the loop with a valid position
if(validPosition)
{
// Spawn the obstacle here
Instantiate(Obstacle, position + Obstacle.transform.position, Quaternion.identity);
}
}
}
}
@Darkforge317 I tried doing what you said many times, playing around and whatnot, but I just couldn't wrap my head around the concepts, nor could I figure out a way to gt the methods to all work together in the code. It was just too confusing. $$anonymous$$aybe you could give me more specific instructions or some example code to help? I'm rather new to Unity, thank you for understanding.
It's ok! Thanks for asking! :) I added code to my answer and commented every line that I've added (except for the two extra variables I added on the top of the class).
Let me know if this works and let me know if you're ever confused about it! :)
Just made a few more edits to it. So if you've already copied it before reading this comment, then just re-copy it. I fixed an infinite loop...
Thank you for putting in the work to share this it really helped me in my project. Cheers!
Answer by Cynikal · May 16, 2018 at 10:36 PM
As darkforge mentioned, overlapsphere would work out.
It really depends on the size/shape of the object you're trying to spawn.
On, on your instantiate (psuedo):
Physics.OverlapSphere(or box) at the desired location. Check the collission. If false, you're good to go. If it fails, loop through it again.
Also, add a FailCount, as there will come a point you just need to fail the operation when too many loops fail, if you don't, you'll eventually freeze your game due to a constant running process.
Answer by Eno-Khaon · May 16, 2018 at 11:13 PM
Take a look at Physics.ComputePenetration().
The example script includes the general information necessary to be able to check for nearby objects, then if they are nearby, determine the minimum separation required to keep them apart from each other.