HashSets and Tilemaps

If you're working with Tilemaps, get familiar with HashSet.

A HashSet can be thought of as just the key part of a Dictionary. Many times, if you're working with Tilemaps, you load a scene and you want to scan the tilemaps and look for static (non-moving) obstacles. You don't care what's in that position, but you know that these positions are off-limits to characters and/or NPCs in your game.

Here's an example of some code that can be used to load a HashSet with all the occupied positions on a Tilemap:


public HashSet<Vector3Int> GetAllPositionsForMap(Tilemap map)
{
    if (map == null)
        return null;
    HashSet<Vector3Int> output = new HashSet<Vector3Int>();

    //make the map a little easier to deal with by compressing the bounds.
    map.CompressBounds();
    var bounds = map.cellBounds;
    //get the block of tiles within the bounds
    var block  = map.GetTilesBlock(bounds);
    var width  = bounds.size.x;
    var height = bounds.size.y;
    //loop thru the block
    for (var x = 0; x < width; x++)
    {
        for (var y = 0; y < height; y++)
        {
            var pTile = block[x + (y * width)]; //any tile here?
            //might be nothing at that position
            if (pTile == null)
                continue; //no
            //compute proper position relative to bounds
            var pos = new Vector3Int(x + bounds.xMin, y + bounds.yMin, 0);
            output.Add(pos); //note that hashset ignores duplicates
        }
    }

    return output;
}

HashSets are amazing when you just need to know if something exists. If you examine your code you may find places where you're using a dictionary but really only need the keys most of the time.

Tip: for Dictionaries and HashSets the simpler the key the faster the key lookup becomes. For example, if you wanted to make a Dictionary that maps Tilemaps as Keys and strings as values you would declare it as

Dictionary <Tilemap, string> m_TilemapToStringDictionary;

but since Tilemaps are Unity objects, you can use the Tilemap's instance ID as the key instead:

Dictionary<int,string> m_TilemapToStringDictionary.