How to sort out places where things can spawn

So I have this code which spawns bananas every 3 seconds or so in my 2D game. There are a maxY-minY and maxX-minX which is the bounding box where bananas can spawn.

Vector2 pos = new Vector2(Random.Range(xMin, xMax), Random.Range(yMin, yMax));

Then I also have an array with different bananas that can spawn, and it chooses a random one from the array using

GameObject randomBanan = olikaBananer[Random.Range(0, olikaBananer.Length)];

then the banana spawns using var banan = Instantiate(randomBanan, pos, transform.rotation); And these bananas also get parented to a emptyObject with ```
banan.transform.parent = BananParent.transform;

The thing is, there are some ponds and other stuff that I don't want the bananas to spawn on, the ponds are being placed out with a tilemap but on a different tilemap than the grass.

So are there a way where I can sort out the places where it cannot spawn on? 
Thanks in advance.

After you select a random position, all you need to then do is to prune away invalid random positions.

Roughly, this is:

  • select random position
  • check if invalid tile (water) in any tilemap which may have an invalid tile (water tilemap, other tilemaps)
  • if position valid, spawn it just like you are doing already
  • if position invalid, select another random position, and keep trying until a valid position is found

If it’s highly likely to get an invalid position, perhaps you can put a counter to make sure you only try to select a valid position 10x before giving up and not spawning anything.

Okay! Thanks for the reply! I should’ve specified that I just got into unity and C#. The first thing(select random position) is already done, but how would I check what tilemap or tile is located at the position?

It should be possible to grab the tile and compare it with illegal tiles, which is one way, or you could make a secondary tilemap where you put a basic tile that are below the illegal tiles and tag the tilemap then you just check if tilemap has tile and if it does it is an illegal tile position, this has it so it does not matter what tile id placed only the areas that are illegal

That second one sounds really nice. I am sorry but I am very new to c# so it would be nice to have an example code to work with. Thanks for the reply anyway! I’ve tried to search but I got no luck.

well it pretty simple create a tilemap, place a tile at all position that are illegal does not matter which one, then you just take the position you want to check.
then use world to cell function (Unity - Scripting API: GridLayout.WorldToCell)
Vector3Int cellPosition = tilemap.WorldToCell(position)
then when you have the cellposition you check if there is a tile on that position with (Unity - Scripting API: Tilemaps.Tilemap.HasTile)
if(tilemap.HasTile(cellpos)) look for a new position
else
place tile.

1 Like

The tilemap.WorldToCell thing. What do I put in “tilemap”. I have a tilemap called “Invalid Tiles” and the tile pallet doesn’t matter right?

Shouldn’t this work?

Vector2 pos = new Vector2(Random.Range(xMin, xMax), Random.Range(yMin, yMax));
        Vector3Int cellPosition = InvalidTile.WorldToCell(pos);
        if(InvalidTile.HasTile(cellPosition))
        {
            Debug.Log("Despawn Banan, " + pos);
        }
        else
        {
            GameObject randomBanan = olikaBananer[Random.Range(0, olikaBananer.Length)];
            var banan = Instantiate(randomBanan, pos, transform.rotation);
            banan.transform.parent = BananParent.transform;
        }

Random bananas that aren’t inside the invalid tiles are getting despawned. I don’t know why, here is a video(Bananas doesn't spawn on some tiles. - Album on Imgur, I don’t know how to link with text). The bananas don’t spawn on the invalid tiles, but they also don’t spawn on some other tiles. The purple blocks are the only thing on the InvalidTile tilemap. Any ideas why?

Ok sorry I’m hella stupid and I’ve painted a lot of tiles with the invalid tilemap. Sorry

Untested:

    public Grid grid; // parent to tilemaps
    public Tilemap[] tilemapsToCheckForInvalids;
    public TileBase[] invalidTiles;
    public float xMin, xMax, yMin, yMax;

    public void TryPlaceBanana()
    {
        int attemptCount = 10;
        Vector3Int cell;
        Vector2 worldspacePos;
        do
        {
            cell = GetRandomTileCell(out worldspacePos);
            attemptCount--;
        } while (!HasValidTile(cell) && attemptCount > 0); // keep trying until found valid tile, unless out of attempts

        if(HasValidTile(cell)) // double-check didn't run out of attempts
        {
            GameObject randomBanan = olikaBananer[Random.Range(0, olikaBananer.Length)];
            var banan = Instantiate(randomBanan, worldspacePos, transform.rotation);
        }
    }

    Vector3Int GetRandomTileCell(out Vector2 worldspacePos)
    {
        worldspacePos = new Vector2(Random.Range(xMin, xMax), Random.Range(yMin, yMax));
        return grid.WorldToCell(worldspacePos); // convert from world-space to cell-space
    }

    bool HasValidTile(Vector3Int cell)
    {
        foreach (var tilemap in tilemapsToCheckForInvalids)
        {
            foreach (var badTile in invalidTiles)
            {
                if (tilemap.GetTile(cell) == badTile) // reference comparison, does THIS kind of tile (water) exist in this cell?
                    return false;
            }
        }
        return true;
    }

Do-while loops are a bit rare in c# but felt appropriate here. Basically, we will always try to enter the while-loop at least once (“do once…while true”). If the cell found to be valid (thus, while = false), then we stop looping and use that worldspace position to spawn a banana. If invalid, then we keep looping looking for a valid tile to place the banana. If no valid cells found in 10 attempts, then nothing spawned we wait 3 more seconds to call TryPlaceBanana().

public Tilemap[ ] tilemapsToCheckForInvalids;
public TileBase[ ] invalidTiles;

These are arrays because it’s more extensible. If you later decide you cannot spawn bananas on more than just water, then you just add another tile to the invalidTiles array and/or tilemap to check for an invalid cell.