Additional layers of map generation?

I have this script here that generates the map on startup:

using UnityEngine;
using UnityEngine.AI;
public class Map_Generator : MonoBehaviour
{
    //EDIT VARIABLES
    private Vector3 _centre = new Vector3(0, 0, 0);
    private int _width = 1000;
    private int _length = 1000;
    private int _numberOfTrees = 7500;
    private int _numberOfBushes = 10000;
    private int _numberOfObstacles = 150;
    private int _numberOfLockboxes = 50;
    //VARIABLES
    public bool _buildMap = true;
    [SerializeField] private GameObject _entity;
    [SerializeField] private GameObject _treePrefab;
    [SerializeField] private GameObject _logPrefab;
    [SerializeField] private GameObject _lockboxPrefab;
    [SerializeField] private GameObject _bandagePrefab;
    [SerializeField] private GameObject _bushPrefab;
    private int _objectNumber = 0;
    private bool _space;
    private Vector3 _randomPosition;
    private Collider[] _colliders;
    [SerializeField] private GameObject _trees;
    [SerializeField] private GameObject _bushes;
    [SerializeField] private GameObject _obstacles;
    [SerializeField] private GameObject _lockboxes;
    //START
    private void Start()
    {
        if (_buildMap)
        {
            _mapBuild(); //Build map
            _buildMap = false;
        }
    }
    //VOIDS AND COROUTINES
    private void _mapBuild()
    {
        //Lockboxes
        _objectNumber = 0;
        while (_objectNumber < _numberOfLockboxes)
        {
            GameObject _object = Instantiate(_lockboxPrefab);
            _object.transform.position = new Vector3(0, 0, 0);
            _object.GetComponent<BoxCollider>().enabled = false;
            _space = false;
            while (!_space)
            {
                _space = true;
                _randomPosition = _centre + new Vector3(Random.Range(-_width / 2, (_width / 2) + 1), 0, Random.Range(-_length / 2, (_length / 2) + 1));
                _object.transform.position = _randomPosition;
                _object.transform.eulerAngles = new Vector3(_object.transform.eulerAngles.x, Random.Range(0, 361), _object.transform.eulerAngles.z);
                _colliders = Physics.OverlapBox(_randomPosition + new Vector3(0, 21, -0.2f), new Vector3(1, 25, 1), _object.transform.rotation);
                foreach (Collider _collider in _colliders)
                {
                    if (_collider.transform.tag == "Object" || _collider.transform.tag == "Map Edge")
                    {
                        _space = false;
                    }
                }
            }
            while (Physics.CheckBox(_object.transform.position + new Vector3(0, 21, -0.2f), new Vector3(1, 25, 1), _object.transform.rotation))
            {
                _object.transform.position += new Vector3(0, 0.1f, 0);
            }
            _object.transform.position += new Vector3(0, -1, 0);
            _object.GetComponent<BoxCollider>().enabled = true;
            _object.transform.SetParent(_lockboxes.transform);
            _object.name = "Lockbox";
            _objectNumber += 1;
            if (Random.Range(1, 6) >= 3) //Item spawning
            {
                GameObject _item = Instantiate(_bandagePrefab);
                _item.transform.position = _object.transform.position + _object.transform.TransformDirection(new Vector3(0, 0, -0.2f));
                _item.transform.eulerAngles = new Vector3(_item.transform.eulerAngles.x, Random.Range(0, 361), _item.transform.eulerAngles.z);
                _item.transform.SetParent(_object.transform);
                _item.name = "Bandages";
            }
        }
        //Obstacles
        _objectNumber = 0;
        while (_objectNumber < _numberOfObstacles)
        {
            GameObject _object = Instantiate(_logPrefab);
            _object.transform.position = new Vector3(0, 0, 0);
            _object.GetComponent<BoxCollider>().enabled = false;
            _space = false;
            while (!_space)
            {
                _space = true;
                _randomPosition = _centre + new Vector3(Random.Range(-_width / 2, (_width / 2) + 1), 0, Random.Range(-_length / 2, (_length / 2) + 1));
                _object.transform.position = _randomPosition;
                _object.transform.eulerAngles = new Vector3(_object.transform.eulerAngles.x, Random.Range(0, 361), _object.transform.eulerAngles.z);
                _colliders = Physics.OverlapBox(_randomPosition + new Vector3(0, 24, 0), new Vector3(1, 25, 7.75f), _object.transform.rotation);
                foreach (Collider _collider in _colliders)
                {
                    if (_collider.transform.tag == "Object" || _collider.transform.tag == "Map Edge")
                    {
                        _space = false;
                    }
                }
            }
            while (Physics.CheckBox(_object.transform.position + new Vector3(0, 24, 0), new Vector3(1, 25, 7.75f), _object.transform.rotation))
            {
                _object.transform.position += new Vector3(0, 0.1f, 0);
            }
            _object.transform.position += new Vector3(0, -1, 0);
            _object.GetComponent<BoxCollider>().enabled = true;
            _object.transform.SetParent(_obstacles.transform);
            _object.name = "Obstacle";
            _objectNumber += 1;
        }
        //Trees
        _objectNumber = 0;
        while (_objectNumber < _numberOfTrees)
        {
            GameObject _object = Instantiate(_treePrefab);
            _object.transform.position = new Vector3(0, 0, 0);
            _object.GetComponent<CapsuleCollider>().enabled = false;
            _space = false;
            while (!_space)
            {
                _space = true;
                _randomPosition = _centre + new Vector3(Random.Range(-_width / 2, (_width / 2) + 1), 0, Random.Range(-_length / 2, (_length / 2) + 1));
                _object.transform.position = _randomPosition;
                _object.transform.eulerAngles = new Vector3(_object.transform.eulerAngles.x, Random.Range(0, 361), _object.transform.eulerAngles.z);
                _colliders = Physics.OverlapCapsule(_randomPosition, _randomPosition + new Vector3(0, 24.5f, 0), 0.6f);
                foreach (Collider _collider in _colliders)
                {
                    if (_collider.transform.tag == "Object" || _collider.transform.tag == "Map Edge")
                    {
                        _space = false;
                    }
                }
            }
            while (Physics.CheckCapsule(_object.transform.position, _object.transform.position + new Vector3(0, 24.5f, 0), 0.6f))
            {
                _object.transform.position += new Vector3(0, 0.1f, 0);
            }
            _object.transform.position += new Vector3(0, -1, 0);
            _object.GetComponent<CapsuleCollider>().enabled = true;
            _object.transform.SetParent(_trees.transform);
            _object.name = "Tree";
            _objectNumber += 1;
        }
        //Bushes
        _objectNumber = 0;
        while (_objectNumber < _numberOfBushes)
        {
            GameObject _object = Instantiate(_bushPrefab);
            _object.transform.position = new Vector3(0, 0, 0);
            _space = false;
            while (!_space)
            {
                _space = true;
                _randomPosition = _centre + new Vector3(Random.Range(-_width / 2, (_width / 2) + 1), 0, Random.Range(-_length / 2, (_length / 2) + 1));
                _object.transform.position = _randomPosition;
                _object.transform.eulerAngles = new Vector3(_object.transform.eulerAngles.x, Random.Range(0, 361), _object.transform.eulerAngles.z);
                _colliders = Physics.OverlapSphere(_randomPosition, 2);
                foreach (Collider _collider in _colliders)
                {
                    if (_collider.transform.tag == "Object" || _collider.transform.tag == "Map Edge")
                    {
                        _space = false;
                    }
                }
            }
            while (Physics.CheckSphere(_object.transform.position, 2))
            {
                _object.transform.position += new Vector3(0, 0.1f, 0);
            }
            _object.transform.position += new Vector3(0, -1, 0);
            _object.transform.SetParent(_bushes.transform);
            _object.name = "Bush";
            _objectNumber += 1;
        }
        //Entity
        _entity.GetComponent<NavMeshAgent>().enabled = false;
        _entity.transform.position = new Vector3(0, 0, 0);
        _entity.GetComponent<CapsuleCollider>().enabled = false;
        _space = false;
        while (!_space)
        {
            _space = true;
            _randomPosition = _centre + new Vector3(Random.Range(-_width / 2, (_width / 2) + 1), 0, Random.Range(-_length / 2, (_length / 2) + 1));
            _entity.transform.position = _randomPosition;
            _entity.transform.eulerAngles = new Vector3(_entity.transform.eulerAngles.x, Random.Range(0, 361), _entity.transform.eulerAngles.z);
            _colliders = Physics.OverlapCapsule(_randomPosition + new Vector3(0, -5, 0), _randomPosition + new Vector3(0, 5, 0), 2.5f);
            foreach (Collider _collider in _colliders)
            {
                if (_collider.transform.tag == "Object" || _collider.transform.tag == "Map Edge")
                {
                    _space = false;
                }
            }
            if (_randomPosition.sqrMagnitude < 40000)
            {
                _space = false;
            }
        }
        while (Physics.CheckCapsule(_entity.transform.position + new Vector3(0, -5, 0), _entity.transform.position + new Vector3(0, 5, 0), 2.5f))
        {
            _entity.transform.position += new Vector3(0, 0.1f, 0);
        }
        _entity.GetComponent<CapsuleCollider>().enabled = true;
        _entity.GetComponent<NavMeshAgent>().enabled = true;
    }
}

this works fine, and everything looks fine, and i didnt notice anything wrong until the “entity” object (which has a navmesh agent) started saying it wasnt properly placed on a navmesh. i took a further look, and it was randomly floating way above the map. this happens with all generated items, as shows in the video:

note that this only happens to some of the items randomly, and the item type is random as the entity is not always placed in the sky, only sometimes.

i have no idea why it is doing this, though i suspect it is to do with this snippet:

//For all the item types, not just the entity
while (Physics.CheckCapsule(_entity.transform.position + new Vector3(0, -5, 0), _entity.transform.position + new Vector3(0, 5, 0), 2.5f))
        {
            _entity.transform.position += new Vector3(0, 0.1f, 0);
        }

which repeatedly moves the object up until it is not inside a collider (the terrain collider)

i really need to get this answer as quickly as possible, so id really appreciate any help
thankyou in advance :slight_smile:

I think it’d make more sense to do a single ray cast from an arbitrarily high point, downward and infinite (or very long) distance, to work out the height to place something.

That might work yeah! I’ll try it in the morning :+1:

after putting together some code (with significant trial and error ( :sweat_smile: ), i got this little script:

using UnityEngine;
using UnityEngine.AI;
public class Map_Generator : MonoBehaviour
{
    //EDIT VARIABLES
    private Vector3 _centre = new Vector3(0, 0, 0);
    private int _width = 1000;
    private int _length = 1000;
    private int _numberOfTrees = 7500;
    private int _numberOfBushes = 10000;
    private int _numberOfObstacles = 150;
    private int _numberOfLockboxes = 50;
    //VARIABLES
    public bool _buildMap = true;
    [SerializeField] private GameObject _entity;
    [SerializeField] private GameObject _treePrefab;
    [SerializeField] private GameObject _logPrefab;
    [SerializeField] private GameObject _lockboxPrefab;
    [SerializeField] private GameObject _bandagePrefab;
    [SerializeField] private GameObject _bushPrefab;
    private int _objectNumber = 0;
    private bool _space;
    private Vector3 _randomPosition;
    [SerializeField] private GameObject _trees;
    [SerializeField] private GameObject _bushes;
    [SerializeField] private GameObject _obstacles;
    [SerializeField] private GameObject _lockboxes;
    //START
    private void Start()
    {
        if (_buildMap)
        {
            _mapBuild(); //Build map
            _buildMap = false;
        }
    }
    //VOIDS AND COROUTINES
    private void _mapBuild()
    {
        RaycastHit _hit;
        Ray _ray;
        //Lockboxes
        _objectNumber = 0;
        while (_objectNumber < _numberOfLockboxes)
        {
            GameObject _object = Instantiate(_lockboxPrefab);
            _object.transform.position = new Vector3(0, 0, 0);
            _object.GetComponent<BoxCollider>().enabled = false;
            _space = false;
            while (!_space)
            {
                _space = true;
                _randomPosition = _centre + new Vector3(Random.Range(-_width / 2, (_width / 2) + 1), 0, Random.Range(-_length / 2, (_length / 2) + 1));
                _object.transform.position = _randomPosition;
                _object.transform.eulerAngles = new Vector3(_object.transform.eulerAngles.x, Random.Range(0, 361), _object.transform.eulerAngles.z);
                Collider[] _colliders = Physics.OverlapBox(_randomPosition + new Vector3(0, 21, -0.2f), new Vector3(1, 25, 1), _object.transform.rotation);
                foreach (Collider _collider in _colliders)
                {
                    if (_collider.transform.tag == "Object" || _collider.transform.tag == "Map Edge")
                    {
                        _space = false;
                    }
                }
            }
            _ray = new Ray(_randomPosition + new Vector3(0, 100, 0), Vector3.down);
            if (Physics.Raycast(_ray, out _hit))
            {
                _object.transform.position = _hit.point + new Vector3(0, 3.5f, 0);
            }
            _object.GetComponent<BoxCollider>().enabled = true;
            _object.transform.SetParent(_lockboxes.transform);
            _object.name = "Lockbox";
            _objectNumber += 1;
            if (Random.Range(1, 6) >= 3) //Item spawning
            {
                GameObject _item = Instantiate(_bandagePrefab);
                _item.transform.position = _object.transform.position + _object.transform.TransformDirection(new Vector3(0, 0, -0.2f));
                _item.transform.eulerAngles = new Vector3(_item.transform.eulerAngles.x, Random.Range(0, 361), _item.transform.eulerAngles.z);
                _item.transform.SetParent(_object.transform);
                _item.name = "Bandages";
            }
        }
        //Obstacles
        _objectNumber = 0;
        while (_objectNumber < _numberOfObstacles)
        {
            GameObject _object = Instantiate(_logPrefab);
            _object.transform.position = new Vector3(0, 0, 0);
            _object.GetComponent<BoxCollider>().enabled = false;
            _space = false;
            while (!_space)
            {
                _space = true;
                _randomPosition = _centre + new Vector3(Random.Range(-_width / 2, (_width / 2) + 1), 0, Random.Range(-_length / 2, (_length / 2) + 1));
                _object.transform.position = _randomPosition;
                _object.transform.eulerAngles = new Vector3(_object.transform.eulerAngles.x, Random.Range(0, 361), _object.transform.eulerAngles.z);
                Collider[] _colliders = Physics.OverlapBox(_randomPosition + new Vector3(0, 24, 0), new Vector3(1, 25, 7.75f), _object.transform.rotation);
                foreach (Collider _collider in _colliders)
                {
                    if (_collider.transform.tag == "Object" || _collider.transform.tag == "Map Edge")
                    {
                        _space = false;
                    }
                }
            }
            _ray = new Ray(_randomPosition + new Vector3(0, 100, 0), Vector3.down);
            if (Physics.Raycast(_ray, out _hit))
            {
                _object.transform.position = _hit.point + new Vector3(0, 0.5f, 0);
            }
            _object.GetComponent<BoxCollider>().enabled = true;
            _object.transform.SetParent(_obstacles.transform);
            _object.name = "Obstacle";
            _objectNumber += 1;
        }
        //Trees
        _objectNumber = 0;
        while (_objectNumber < _numberOfTrees)
        {
            GameObject _object = Instantiate(_treePrefab);
            _object.transform.position = new Vector3(0, 0, 0);
            _object.GetComponent<CapsuleCollider>().enabled = false;
            _space = false;
            while (!_space)
            {
                _space = true;
                _randomPosition = _centre + new Vector3(Random.Range(-_width / 2, (_width / 2) + 1), 0, Random.Range(-_length / 2, (_length / 2) + 1));
                _object.transform.position = _randomPosition;
                _object.transform.eulerAngles = new Vector3(_object.transform.eulerAngles.x, Random.Range(0, 361), _object.transform.eulerAngles.z);
                Collider[] _colliders = Physics.OverlapCapsule(_randomPosition, _randomPosition + new Vector3(0, 24.5f, 0), 0.6f);
                foreach (Collider _collider in _colliders)
                {
                    if (_collider.transform.tag == "Object" || _collider.transform.tag == "Map Edge")
                    {
                        _space = false;
                    }
                }
            }
            _ray = new Ray(_randomPosition + new Vector3(0, 100, 0), Vector3.down);
            if (Physics.Raycast(_ray, out _hit))
            {
                _object.transform.position = _hit.point + new Vector3(0, -1, 0);
            }
            _object.GetComponent<CapsuleCollider>().enabled = true;
            _object.transform.SetParent(_trees.transform);
            _object.name = "Tree";
            _objectNumber += 1;
        }
        //Bushes
        _objectNumber = 0;
        while (_objectNumber < _numberOfBushes)
        {
            GameObject _object = Instantiate(_bushPrefab);
            _object.transform.position = new Vector3(0, 0, 0);
            _space = false;
            while (!_space)
            {
                _space = true;
                _randomPosition = _centre + new Vector3(Random.Range(-_width / 2, (_width / 2) + 1), 0, Random.Range(-_length / 2, (_length / 2) + 1));
                _object.transform.position = _randomPosition;
                _object.transform.eulerAngles = new Vector3(_object.transform.eulerAngles.x, Random.Range(0, 361), _object.transform.eulerAngles.z);
                Collider[] _colliders = Physics.OverlapSphere(_randomPosition, 2);
                foreach (Collider _collider in _colliders)
                {
                    if (_collider.transform.tag == "Object" || _collider.transform.tag == "Map Edge")
                    {
                        _space = false;
                    }
                }
            }
            _ray = new Ray(_randomPosition + new Vector3(0, 100, 0), Vector3.down);
            if (Physics.Raycast(_ray, out _hit))
            {
                _object.transform.position = _hit.point + new Vector3(0, 0.5f, 0);
            }
            _object.transform.SetParent(_bushes.transform);
            _object.name = "Bush";
            _objectNumber += 1;
        }
        //Entity
        _entity.GetComponent<NavMeshAgent>().enabled = false;
        _entity.transform.position = new Vector3(0, 0, 0);
        _entity.GetComponent<CapsuleCollider>().enabled = false;
        _space = false;
        while (!_space)
        {
            _space = true;
            _randomPosition = _centre + new Vector3(Random.Range(-_width / 2, (_width / 2) + 1), 0, Random.Range(-_length / 2, (_length / 2) + 1));
            _entity.transform.position = _randomPosition;
            _entity.transform.eulerAngles = new Vector3(_entity.transform.eulerAngles.x, Random.Range(0, 361), _entity.transform.eulerAngles.z);
            Collider[] _colliders = Physics.OverlapCapsule(_randomPosition + new Vector3(0, -5, 0), _randomPosition + new Vector3(0, 5, 0), 2.5f);
            foreach (Collider _collider in _colliders)
            {
                if (_collider.transform.tag == "Object" || _collider.transform.tag == "Map Edge")
                {
                    _space = false;
                }
            }
            if (_randomPosition.sqrMagnitude < 40000)
            {
                _space = false;
            }
        }
        _ray = new Ray(_randomPosition + new Vector3(0, 100, 0), Vector3.down);
        if (Physics.Raycast(_ray, out _hit))
        {
            _entity.transform.position = _hit.point + new Vector3(0, 5, 0);
        }
        _entity.GetComponent<CapsuleCollider>().enabled = true;
        _entity.GetComponent<NavMeshAgent>().enabled = true;
    }
}

however… it does the same thing. i invesitgated and it seems that some objects are spawning on top of other objects, even though this loop:

_space = false;
            while (!_space)
            {
                _space = true;
                _randomPosition = _centre + new Vector3(Random.Range(-_width / 2, (_width / 2) + 1), 0, Random.Range(-_length / 2, (_length / 2) + 1));
                _object.transform.position = _randomPosition;
                _object.transform.eulerAngles = new Vector3(_object.transform.eulerAngles.x, Random.Range(0, 361), _object.transform.eulerAngles.z);
                Collider[] _colliders = Physics.OverlapBox(_randomPosition + new Vector3(0, 21, -0.2f), new Vector3(1, 25, 1), _object.transform.rotation);
                foreach (Collider _collider in _colliders)
                {
                    if (_collider.transform.tag == "Object" || _collider.transform.tag == "Map Edge")
                    {
                        _space = false;
                    }
                }
            }

should be preventing that, right? do you know why this is happening?

I don’t fully understand the issue as it’s a block of code that says “what’s happening”, from what I hear you should just be grabbing mesh points? Unity - Scripting API: Mesh.vertices

The script here is basically just spawning one item at a time, placing it in random positions until it find somewhere where it isn’t touching another object, and then it moves it up into a space above the terrain. The only issue is that some objects are stacking. That’s what I’ve figured out anyway. The issue causing multiple layers of generation is that some objects are stacking onto eachother for some reason, even though they shouldn’t be able to be placed inside another object

yep the its checking the collider, I don’t know exactly why you’re having your issue, but you can grab a mesh point and then spawn according, you won’t need to deal with overlapping physics.

Isn’t the above code going to fail 100% of the time if your actual terrain space is higher than the box of an object? By this I mean:

  • place one thing … yep, okay, move it up to terrain height that is quite high
  • place second thing right below it… nope, no collision, so move it up
  • voila, two stacked things

This is always the problem trying to involve colliders and other APIs in something that is as simple as deconflicting things.

The more-scalable approach, since I assume you’re considering the “ground space” as being an X/Z domain, regardless of height, is to store the positions in that X/Z domain, and consider them in that same X/Z domain, disregarding Y.

Therefore, instead of trying to involve Unity and colliders (which now ALSO depends on the colliders being correct), just store the positions of where you contemplate putting these things, and each time you put one down, check it against the previous ones until it fits.

REMEMBER: it will deadlock if you don’t account for the final inability to place that last object. That’s on you.

Deconflicted spawning is such a fundamental property of so many games and it seems to come up here over and over again, so I created even more examples in my MakeGeo repository.

This spawns in a grid:

This spawns in an area / volume:

Fully functional scenes are in the project and there are other examples of random creation in there too.

MakeGeo is presently hosted at these locations:

i havent worked with meshpoints before… this is only my third game. sorry :smiling_face_with_tear:

i THINK i understand what you were saying here:

Isn’t the above code going to fail 100% of the time if your actual terrain space is higher than the box of an object? By this I mean:

  • place one thing … yep, okay, move it up to terrain height that is quite high
  • place second thing right below it… nope, no collision, so move it up
  • voila, two stacked things

and im pretty sure i fixed it? sorry if i misunderstood but this is what i did:

//Trees
        _objectNumber = 0;
        while (_objectNumber < _numberOfTrees)
        {
            _object = Instantiate(_treePrefab);
            _object.transform.position = new Vector3(0, 0, 0);
            _object.GetComponent<CapsuleCollider>().enabled = false;
            foreach (Collider _collider in _object.GetComponentsInChildren<BoxCollider>())
            {
                _collider.enabled = false;
            }
            _space = false;
            while (!_space)
            {
                _space = true;
                _randomPosition = _centre + new Vector3(Random.Range(-_width / 2, (_width / 2) + 1), 0, Random.Range(-_length / 2, (_length / 2) + 1));
                _object.transform.position = _randomPosition;
                _ray = new Ray(_randomPosition + new Vector3(0, 100, 0), Vector3.down);
                if (Physics.Raycast(_ray, out _hit))
                {
                    _object.transform.position = _hit.point + new Vector3(0, -1, 0);
                }
                _object.transform.eulerAngles = new Vector3(_object.transform.eulerAngles.x, Random.Range(0, 361), _object.transform.eulerAngles.z);
                Collider[] _colliders = Physics.OverlapCapsule(_randomPosition, _randomPosition + new Vector3(0, 24.5f, 0), 0.6f);
                foreach (Collider _collider in _colliders)
                {
                    if (_collider.transform.tag == "Object" || _collider.transform.tag == "Map Edge")
                    {
                        _space = false;
                    }
                }
            }
            _object.GetComponent<CapsuleCollider>().enabled = true;
            foreach (Collider _collider in _object.GetComponentsInChildren<BoxCollider>())
            {
                _collider.enabled = true;
            }
            _object.transform.SetParent(_trees.transform);
            _object.name = "Tree";
            _objectNumber += 1;
        }

i made the objects also move into their correct height (ontop of the terrain) before checking for objects taking up that space already.

however this still doesnt work, and stacking is occuring. :sweat_smile:

im continuing to look into this further, but if you have any ideas then let me know!

Then you either failed to fix the bug, or you have another bug… and that means… time to start debugging!

By debugging you can find out exactly what your program is doing so you can fix it.

Use the above techniques to get the information you need in order to reason about what the problem is.

You can also use Debug.Log(...); statements to find out if any of your code is even running. Don’t assume it is.

Once you understand what the problem is, you may begin to reason about a solution to the problem.

Remember with Unity the code is only a tiny fraction of the problem space. Everything asset- and scene- wise must also be set up correctly to match the associated code and its assumptions.

I’m a big fan of procedural worlds. I found it’s often best to scan through the world placing objects down similar to how we write words on a page. Doing it this way means you’re not having to randomly search around hoping to find empty spaces to place things.

^ ^ ^ Big agree on this. The way I usually populate levels is to traverse one axis linearly, while choosing the other axis randomly.

At its simplest, you move far enough in one axis that no matter what the other axis randomizes to, you can’t hit each other, kinda like this:

// traverse x constantly
for (int x = -5; x <= +5; x++)
{
  // pick y randomly
  int y = Random.Range( -5, +6);

  Vector3 position = new Vector3( x, y, 0);

  var ball = GameObject.CreatePrimitive( PrimitiveType.Sphere);
  ball.transform.position = position;
}

what if i want to have many trees on one axis level?

Then deconflict on that line, which makes your search space tiny.

Either way, even if you use your original method, just put their positions into a List<Vector3>() and choose them all at once, checking distance from all previous ones, then when you have a list of “far enough apart positions,” iterate that list and spawn everything at once.

public GameObject[] items;

    void Start()
    {
        for (int x = -1000; x < 1000; x += 10)
            for (int z = -1000; z < 1000; z += 10)
                if (Random.value > 0.8f)
                    Instantiate(items[Random.Range(0, items.Length)], new Vector3(x + Random.value * 10, 0, z + Random.value * 10), Quaternion.Euler(0, Random.value * 360, 0));
    }

The above is just an example. In practice you would make sure an item doesn’t overlap a neighboring cell by adjusting the random range of the instantiate position.

Yes that sounds good actually! I’ll try that tomorrow! Hopefully it finally works :face_exhaling: (I’ll use that as a reference @zulo3d)

How would I actually go about doing this, factoring in having different items with different sizes and random orientations, as well as needing to place them on top of the terrain? :thinking:

As I stated above, make a list of positions.

Pick random positions, and for each one, check it against the ones you already made.

If it is “too close” then pick a new random position.

If it’s far enough away, add it to the list.

If you’re laying it out on a terrain, only check X,Z when deciding “too close”

When you’re done, go place them, raising each one to wherever the height is on the map.

Go!

Imphenzia: How Did I Learn To Make Games:

using UnityEngine;
using System.Collections.Generic;

public class RandomPositionOnMesh : MonoBehaviour
{
    public Mesh mesh; 
    public int numberOfPositions = 10; 

    void Start()
    {
        List<Vector3> randomPositions = GetRandomPositionsOnMesh(mesh, numberOfPositions);
        
        // Debug log to show the positions
        foreach (var position in randomPositions)
        {
            Debug.Log(position);
        }
    }

    public List<Vector3> GetRandomPositionsOnMesh(Mesh mesh, int numberOfPositions)
    {
        List<Vector3> randomPositions = new List<Vector3>();
        Vector3[] vertices = mesh.vertices;

        // Ensure that there are vertices to sample from
        if (vertices.Length == 0)
        {
            Debug.LogError("Mesh has no vertices.");
            return randomPositions;
        }

        // Generate random positions from the mesh vertices
        for (int i = 0; i < numberOfPositions; i++)
        {
            int randomIndex = Random.Range(0, vertices.Length); // Random index for vertex
            Vector3 randomVertex = vertices[randomIndex];

            // Convert the vertex position to world space (optional based on use case)
            randomVertex = transform.TransformPoint(randomVertex);

            randomPositions.Add(randomVertex);
        }

        return randomPositions;
    }
}

You can entire use the object bounds to see if theres any overlapping or you can keep the bounds in your data and just use the distance between verts.