Selecting Tiles On A Tilemap By Dragging

Hey there,

I’m a novice Unity user, and I’m trying to create a feature where I can’t wrap my head around.

I’m using the Unity tilemap, and want to create a function where you can click and drag te select tiles (the blue squares in my image)

I’m getting the tiles by using a Vector3Int (WorldToCell) to make it easy to fill in the tiles between the “startTile” and “endTile”

In my exemple the values are:
startTile: -1,-2,0
endTile: -4, -4, 0

On release of the mousebutton I like to fire a function for each tile.
So far I tried someting like this, but I walk into issues fast, Like, of course, this only works in the + direction, and when you make the selection smaller, the tiles keep being set.

if (Input.GetMouseButtonDown(0))
{
    Debug.Log(tile);
    startTile = tile;
    isSelecting = true;
}

if (Input.GetMouseButtonUp(0))
{
    isSelecting = false;
}

if (isSelecting)
{
    endTile = tile;

    for (int x = startTile.x; x < endTile.x; x++)
    {
        for (int y = startTile.y; y < endTile.y; y++)
        {
            Vector3Int localTile = new Vector3Int(x, y, 0);
            digMap.SetTile(localTile, digTile);
        }
    }
}

I figure there must be a better method to do this. Any help or nudge in the right direction is greatly appreciated.

For now I fabricated something like this. Which is not the most elegant solution I guess. So tips or better solutions are still more than welcome :smile:

if (startTile.x <= endTile.x)
{
    for (int x = startTile.x; x <= endTile.x; x++)
    {
        if (startTile.y <= endTile.y)
        {
            for (int y = startTile.y; y <= endTile.y; y++)
            {
                Vector3Int localTile = new Vector3Int(x, y, 0);
                Function(localTile);
            }
        }
        else if (endTile.y <= startTile.y)
        {
            for (int y = startTile.y; y >= endTile.y; y--)
            {
                Vector3Int localTile = new Vector3Int(x, y, 0);
                Function(localTile);
            }
        }
    }
}

else if (startTile.x >= endTile.x)
{
    for (int x = startTile.x; x >= endTile.x; x--)
    {
        if (startTile.y <= endTile.y)
        {
            for (int y = startTile.y; y <= endTile.y; y++)
            {
                Vector3Int localTile = new Vector3Int(x, y, 0);
                Function(localTile);
            }
        }
        else if (endTile.y <= startTile.y)
        {
            for (int y = startTile.y; y >= endTile.y; y--)
            {
                Vector3Int localTile = new Vector3Int(x, y, 0);
                Function(localTile);
            }
        }
    }
}

For testing purposes I call a function, later I going to add the tiles to a list

You could try this:

public static BoundsInt GetMarqueeBounds(Vector3Int p1, Vector3Int p2)
{
    return new BoundsInt(
            Math.Min(p1.x, p2.x),
            Math.Min(p1.y, p2.y),
            Math.Min(p1.z, p2.z),
            Math.Abs(p2.x - p1.x) + 1,
            Math.Abs(p2.y - p1.y) + 1,
            Math.Abs(p2.z - p1.z) + 1
    );
}
// On Mouse Up
var bounds = GetMarqueeBounds(startTile, endTile);
for (var localTile in oldBounds.allPositionsWithin)
{
    ClearFunction(localTile); // Clear Selection
}
for (var localTile in bounds.allPositionsWithin)
{
    SetFunction(localTile); // Set Selection
}
oldBounds = bounds;

Using the following function might be helpful as well: Unity - Scripting API: Tilemaps.Tilemap.SetTilesBlock

Thanks you so much, going to experiment a little further with your suggestion!