1
\$\begingroup\$
void Treetree()
{
    for (int i = 0; i < TreeCount; i++)
    {
        xPos = Random.Range(-140.5f, -13.51f);
        yPos = Random.Range(163.47f, 35.53f);
        Instantiate(treeFab, new Vector3(xPos, yPos, 1.6f), Quaternion.Euler(-90, 0, 0));
        Debug.Log(TreeCount);
    }
}

I set a perimeter to where I want it to spawn but I don't want these objects to spawn at a specific area that is within the xPos and yPos range. what should I do?

\$\endgroup\$

2 Answers 2

5
\$\begingroup\$

A simple solution which covers a wide variety of circumstances which could invalidate spawn locations can be to simply check if the randomly generated position is within a "forbidden zone" and when that's the case just roll it again and again until it is not.

do {
    xPos = Random.Range(-140.5f, -13.51f);
    yPos = Random.Range(163.47f, 35.53f);
} while PositionBlocked(xPos, yPos);
 

The problem with that method is that there might be circumstances where it is impossible to place the desired object. The above algorithm would be unable to detect that and get caught in an infinite loop.

OK, perhaps there is a way for you to detect if the forbidden zone covers the spawn area completely, so you can detect and handle the impossible case separately. But what about the nearly impossible case? Like one where there is a tiny 0.001% area of the designated rectangle that are valid spawn locations? The above algorithm would hit that spot eventually (assuming a good random number generator), but it could take a very, very long time until it does. Unless the "forbidden zones" in your game are so small and few that you can guarantee that this is never going to happen (and that's a pretty bold assumption in most games), you have to handle this case somehow.

A good way to do that is by adding a counter and throwing an exception when it exceeds a predefined maximum of tries. The exact value of MAX_TRIES is a tradeoff between performance and reliability of tree placement in situations where there are very few valid positions:

try {  
    int tries = 0;
    do {
        tries++;
        if (tries > MAX_TRIES) throw new CantPlaceTreeException();
        xPos = Random.Range(-140.5f, -13.51f);
        yPos = Random.Range(163.47f, 35.53f);
   } while PositionBlocked(xPos, yPos);

    Instantiate(treeFab, new Vector3(xPos, yPos, 1.6f), Quaternion.Euler(-90, 0, 0));
    Debug.Log(TreeCount);
}
catch (CantPlaceTreeException e) {
   // handle failure to find a valid spawn position for the tree
}

How exactly could you handle this failure condition in the catch-block? Well, there are several options. When the trees are mostly cosmetic and the exact number of trees in a region isn't that important, then you could simply not place it. But when it is crucial for the game that the tree spawns no matter what, then you could place it on the last rolled position anyway, either ignoring or removing whatever obstacle would prevent it from spawning there. Or you could find some other solution which is more appropriate for your particular game.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ If it's not critical that the tree be placed, I'd get rid of the exception throwing and simply use a break; if it fails enough times, then make sure to NOT place a tree if it fails to find a good spot for it. Throwing exceptions adds unnecessary overhead for something that seems fairly likely to occur (though how likely depends on how large the blocked area is relative to the total area.) \$\endgroup\$ Mar 9, 2022 at 15:35
0
\$\begingroup\$

I would like to write an alternative answer which would be superior to my previous one if you have a situation where you:

  1. Have a finite set of possible spawn locations. Like, for example, when you have a tilemap and each tile can only contain one object.
  2. Want to place a lot of objects
  3. Want to guarantee that all possible spawn locations are occupied before you start to deal with "impossible" spawns.

In this case you can enumerate all possible spawn locations, check each one if it is valid and then store them in a list. You can then place objects by taking a random element of the list and then removing it from the list so it won't be used again. You can do that until the list is empty. Now you can be certain that there are no more spawn positions available and can act accordingly.

This method is faster than the algorithm I describe in my other answer when you have a small number of available spawn locations, because it won't go into long trial-and-error loops. However, if you have a large number of possible spawn locations and a low number of objects to spawn, then the time it takes to create the list of possible spawn locations would probably take much longer.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .