Search Unity

  1. Unity Asset Manager is now available in public beta. Try it out now and join the conversation here in the forums.
    Dismiss Notice
  2. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  3. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Tilemap tile positions assistance

Discussion in '2D Experimental Preview' started by M0RD3CA1, Jul 30, 2017.

Thread Status:
Not open for further replies.
  1. M0RD3CA1

    M0RD3CA1

    Joined:
    Jul 30, 2017
    Posts:
    2
    Hi there!

    I am currently working on a game where we have designed the levels using the experimental Tilemap feature and painting with the brush.

    I am currently working on a pathfinder, and this requires examining each of the tiles in the tilemap.

    Problem is, I don't know how to do it. I've scanned the APIs over and over again but there doesn't seem to be any apparent way to acquire a TileBase's position

    The farthest I've gotten is acquiring an array of the TileBases using

    Code (CSharp):
    1. Tilemap tilemap = GetComponent<Tilemap>();
    2. BoundsInt size = tilemap.cellBounds
    3. TileBase[] allTiles = tilemap.GetTilesBlock(size);
    But there aren't any coordinates associated with that! I know the cellBounds provides some sort of positions but using the coordinates below I'm not sure I'm selecting the actual tiles. And if I were, how would I get the position in the world?

    Code (CSharp):
    1. int colNumbers = size.yMax - size.yMin;
    2. int rowNumbers = size.xMax - size.xMin;
    3. for (int col = colNumbers; col >= 0; col--)
    4. {
    5.    for (int row = 0; row <= rowNumbers; row++)
    6.    {
    7.        TileBase currentTile = terrain.GetTile(new Vector3Int(row, col, 0));
    8.    }
    9. }
    Thanks in advance.
     
    rakkarage likes this.
  2. danbrani

    danbrani

    Joined:
    Nov 22, 2012
    Posts:
    46
    TileBase doesn't have a position, it's a class that's representing the type of the tile.

    If you need to go thrugh all the tiles, simply iterate through all the coordinates on the grid beginning from the tilemap.origin up to tilemap.origin + tilemap.size. At each coordinate you can retrieve the TileBase that's associated with it, and do whatever you need to do.

    To get the position in the world, given the integer coordinates use the following.
    https://docs.unity3d.com/2017.2/Documentation/ScriptReference/Tilemaps.Tilemap-layoutGrid.html
    https://docs.unity3d.com/2017.2/Documentation/ScriptReference/GridLayout.CellToWorld.html
     
    rakkarage likes this.
  3. M0RD3CA1

    M0RD3CA1

    Joined:
    Jul 30, 2017
    Posts:
    2
    You are a lifesaver thank you so much
     
  4. DDaddySupreme

    DDaddySupreme

    Joined:
    Jun 3, 2017
    Posts:
    10
    I had a lot of trouble with this recently, but eventually managed to get it to work. Here's how I got a list of all positions on a tilemap:
    Code (CSharp):
    1.     public Tilemap tileMap = null;
    2.  
    3.     public List<Vector3> availablePlaces;
    4.  
    5.     void Start () {
    6.         tileMap = transform.GetComponentInParent<Tilemap>();
    7.         availablePlaces = new List<Vector3>();
    8.  
    9.         for (int n = tileMap.cellBounds.xMin; n < tileMap.cellBounds.xMax; n++)
    10.         {
    11.             for (int p = tileMap.cellBounds.yMin; p < tileMap.cellBounds.yMax; p++)
    12.             {
    13.                 Vector3Int localPlace = (new Vector3Int(n, p, (int)tileMap.transform.position.y));
    14.                 Vector3 place = tileMap.CellToWorld(localPlace);
    15.                 if (tileMap.HasTile(localPlace))
    16.                 {
    17.                     //Tile at "place"
    18.                     availablePlaces.Add(place);
    19.                 }
    20.                 else
    21.                 {
    22.                     //No tile at "place"
    23.                 }
    24.             }
    25.         }
    26.     }
    Feel free to use my code however you want!

    An alternative, which I did before I wrote that code, is creating a raycast from the sky, or anything that can detect collides, and testing each location in a given range with it.
    As for the pathfinding itself, I wrote some A* that uses "availablePlaces"
     
  5. keely

    keely

    Joined:
    Sep 9, 2010
    Posts:
    967
    Iterating BoundsInt or RectInt can be done like this too:

    Code (CSharp):
    1. foreach (var position in tilemap.cellBounds.allPositionsWithin)
    2. {
    3.     // Do stuff per position
    4. }
     
  6. KingRecycle

    KingRecycle

    Joined:
    Jul 20, 2013
    Posts:
    26
    Are the same tile but different locations not unique? So if I place a tile at 0,0 and the same tile at 0,1. Unity doesn't see that as 2 separate tiles but the same?

    I've spent over 6 hours trying to create a floodfill algorithm only to find out every time I "GetTile" it doesn't matter at what position it always returns the same tile.

    The way I'm doing it is covering the area with a RoomTile. RoomTiles have a Room variable, just says what Room that tile is a part of. So if my floodfill says this RoomTile needs to be a different Room I set that RoomTile's Room variable but this also affects every other RoomTile.
     
  7. keely

    keely

    Joined:
    Sep 9, 2010
    Posts:
    967
    Correct: It is the same Tile asset in every cell. We do not instantiate Tile asset for every cell, we simply refer to the same Tile asset in the project many times

    Your intuition of storing per-cell data into Tile is understandable, but it isn't how this system is designed to work. You have to store your per-cell data some other way. We will offer storage component called GridInformation as open-source add-on before 2017.2 comes out. You can also make your own custom component to store that data if you want, but Tile(s) are only meant for rendering & physics, not data.
     
    Last edited: Aug 3, 2017
  8. KingRecycle

    KingRecycle

    Joined:
    Jul 20, 2013
    Posts:
    26
    Okay cool. I mean that actually makes sense performance wise but it was just me being an idiot and not realizing what was happening. Thanks. Can't wait for GridInformation component.
     
  9. keely

    keely

    Joined:
    Sep 9, 2010
    Posts:
    967
  10. guilhermecorintho

    guilhermecorintho

    Joined:
    Sep 24, 2017
    Posts:
    11

    Hey thank you! That its working well for me, but this show all positions, even if its painted or not, how can I do something only if its a filled tile? I mean, if I paint a tile, and after that I use the erase tool, how can I ignore this erased tile in this kind of loop?

    I was not able to understand how to use GridInformation tho

    Thank you in advance

    @Edit

    Seems that the DDaddySupreme solution will help me, thank you
     
    Last edited: Aug 26, 2018
  11. opsuty

    opsuty

    Joined:
    Jul 12, 2019
    Posts:
    1
    Use Tilemap.hasTile:

    Code (CSharp):
    1.   foreach (var position in tilemap.cellBounds.allPositionsWithin) {
    2.             if (!tilemap.HasTile(position)) {
    3.                 continue;
    4.             }
    5.  
    6.             // Tile is not empty; do stuff
    7.         }
     
  12. Gabe-Tucker

    Gabe-Tucker

    Joined:
    Nov 26, 2015
    Posts:
    94
    I love you dude this saved me so much time
     
  13. YesYourRight

    YesYourRight

    Joined:
    Apr 18, 2020
    Posts:
    1
    foreach(var pos in ground.cellBounds.allPositionsWithin) {
    if (ground.HasTile(pos))
    {
    spawnPoints.Add(new Vector3(pos.x+0.5f,pos.y+0.5f));
    }

    }
    this is more correctly because tile's origin is basically set at bottom left
     
Thread Status:
Not open for further replies.