Spawning prefabs randomly on top of existing tiles

Good day everyone!

I’m currently in the process of building a 2D Game, and as for my next step I’d like to randomly spawn collectable gems (prefabs) on top of my tiles for the player to collect.

My tilemap is generated at runtime.

I’ve tried various solutions however I can’t seem to get it quite right. I know that I’m supposed to get the positions of the tiles and check whether the tile below is empty or not, however I’m not sure how to implement this, and I’d really appreciate the help! Thank you. :slight_smile:

Here’s my code for generating the tilemap incase anyone would like to see it.

    void GenerateMap()
    {
        float seed = UnityEngine.Random.Range(.1f, 1f);
        mapArray = GenerateArray(terrainWidth, terrainHeight, true);
        RenderMap(PerlinNoiseSmooth(mapArray, seed, 3), tilemap, tile);
    }


    public static int[,] GenerateArray(int width, int height, bool empty)
    {
        int[,] map = new int[width, height];
        for (int x = 0; x < map.GetUpperBound(0); x++)
        {
            for (int y = 0; y < map.GetUpperBound(1); y++)
            {
                if (empty)
                {
                    map[x, y] = 0;
                }
                else
                {
                    map[x, y] = 1;
                }
            }
        }
        return map;
    }

    public static void RenderMap(int[,] map, Tilemap tilemap, TileBase tile)
    {
        //Clear the map (ensures we dont overlap)
        tilemap.ClearAllTiles();
        //Loop through the width of the map
        for (int x = 0; x < map.GetUpperBound(0); x++)
        {
            //Loop through the height of the map
            for (int y = 0; y < map.GetUpperBound(1); y++)
            {
                // 1 = tile, 0 = no tile
                if (map[x, y] == 1)
                {
                    tilemap.SetTile(new Vector3Int(x, y, 0), tile);

                }
            }
        }
    }

    public static void UpdateMap(int[,] map, Tilemap tilemap) //Takes in our map and tilemap, setting null tiles where needed
    {
        for (int x = 0; x < map.GetUpperBound(0); x++)
        {
            for (int y = 0; y < map.GetUpperBound(1); y++)
            {
                //We are only going to update the map, rather than rendering again
                //This is because it uses less resources to update tiles to null
                //As opposed to re-drawing every single tile (and collision data)
                if (map[x, y] == 0)
                {
                    tilemap.SetTile(new Vector3Int(x, y, 0), null);
                }
            }
        }
    }

    public static int[,] PerlinNoise(int[,] map, float seed)
    {
        int newPoint;
        //Used to reduced the position of the Perlin point
        float reduction = 0.5f;
        //Create the Perlin
        for (int x = 0; x < map.GetUpperBound(0); x++)
        {
            newPoint = Mathf.FloorToInt((Mathf.PerlinNoise(x, seed) - reduction) * map.GetUpperBound(1));

            //Make sure the noise starts near the halfway point of the height
            newPoint += (map.GetUpperBound(1) / 2);
            for (int y = newPoint; y >= 0; y--)
            {
                map[x, y] = 1;
            }
        }
        return map;
    }

    public static int[,] PerlinNoiseSmooth(int[,] map, float seed, int interval)
    {
        //Smooth the noise and store it in the int array
        if (interval > 1)
        {
            int newPoint, points;
            //Used to reduced the position of the Perlin point
            float reduction = 0.5f;

            //Used in the smoothing process
            Vector2Int currentPos, lastPos;
            //The corresponding points of the smoothing. One list for x and one for y
            List<int> noiseX = new List<int>();
            List<int> noiseY = new List<int>();

            //Generate the noise
            for (int x = 0; x < map.GetUpperBound(0); x += interval)
            {
                newPoint = Mathf.FloorToInt((Mathf.PerlinNoise(x, (seed * reduction))) * map.GetUpperBound(1));
                noiseY.Add(newPoint);
                noiseX.Add(x);
            }

            points = noiseY.Count;

            //Start at 1 so we have a previous position already
            for (int i = 1; i < points; i++)
            {
                //Get the current position
                currentPos = new Vector2Int(noiseX_, noiseY*);*_

//Also get the last position
lastPos = new Vector2Int(noiseX[i - 1], noiseY[i - 1]);

//Find the difference between the two
Vector2 diff = currentPos - lastPos;

//Set up what the height change value will be
float heightChange = diff.y / interval;
//Determine the current height
float currHeight = lastPos.y;

//Work our way through from the last x to the current x
for (int x = lastPos.x; x < currentPos.x; x++)
{
for (int y = Mathf.FloorToInt(currHeight); y > 0; y–)
{
map[x, y] = 1;
}
currHeight += heightChange;
}
}
}
else
{
//Defaults to a normal Perlin gen
map = PerlinNoise(map, seed);
}

return map;
}

}

Thanks for the solution

Nox