Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Tilemap.cellBounds.allPositionsWithin seems to be wrong after erase tile

Discussion in '2D' started by Rafarel, Aug 1, 2019.

  1. Rafarel

    Rafarel

    Joined:
    Jul 21, 2017
    Posts:
    199
    Hello there,

    I have a Tilemap with a script attached to it to display Handles.Label over my tiles.
    While adding tiles with the Paint tool, I paint too much out of the desired area so I decided to use Erase tool to erase the unwanted tiles. I seems that the Tilemap.cellBounds.allPositionsWithin returns me a wrong numbers of tiles because I have an empty column with Handles.Label over it while the column has no tiles !

    Here is a bit of code and a screenshot (see the empty right column with Labels?)

    Code (CSharp):
    1. private void OnDrawGizmos()
    2. {
    3.     foreach (Vector3Int position in Tilemap.cellBounds.allPositionsWithin)
    4.     {
    5.         Handles.Label(position, "0");
    6.     }
    7. }
    tilemap-bounds-error.png

    Thanks!
     
  2. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    When erasing Tiles from the Tilemap, the bounds do not automatically resize to the smallest region. To do that, you can use
    tilemap.CompressBounds
    . CompressBounds will compress the bounds to the extents of actual filled Tiles, and is a rather expensive operation so it is not called automatically. CompressBounds can be called from the menu dropdown of the Tilemap inspector (using the gear dropdown button).
     
  3. Rafarel

    Rafarel

    Joined:
    Jul 21, 2017
    Posts:
    199
    Hey @ChuanXin! Thanks for your clear answer! Your name is displayed all over my IDE because it seems you have worked on the 2d-techdemos git repo :) Congratulation for your work, i have a couple of questions and YOU are the man I need ;)

    I'm working on a Tower Defense game, the levels are designed in Photoshop in plain illustration, so I do not use Tilemaps to render tiles. I want to use Tilemap to be be data containers for path finding or to know if I can build on this particular tile / square.

    I've spend a lot of time to read the code of 2d extra and 2d tech demos to learn about custom tiles and custom brushes and I want to experiment more :)

    I made test with a basic CustomTile class like this one :

    Code (CSharp):
    1.  
    2. [CreateAssetMenu(fileName = "Custom Tile", menuName = "Tiles/CustomTile")]
    3. public class CustomTile : TileBase
    4. {
    5.     public bool Walkable;
    6.     public bool Swimmable;
    7.     public bool Buildable;
    8.     public int Height;
    9. }
    10.  
    Then I made a Tile Palette with only my custom tile in it.
    And I made a Custom brush to edit the properties of the Tile I wanted to place on the Tilemap with an exemple in the following script that just invert the walkable bollean just for test purpose.

    Code (CSharp):
    1. public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
    2. {
    3.     // Do not allow editing palettes
    4.     if (brushTarget.layer == 31)
    5.         return;
    6.  
    7.     Tilemap tilemap = brushTarget.GetComponent<Tilemap>();
    8.     if (tilemap != null)
    9.     {
    10.         SetWalkable(tilemap, position);
    11.     }
    12. }
    13.  
    14. private static void SetWalkable(Tilemap tilemap, Vector3Int position)
    15. {
    16.     CustomTile tile = tilemap.GetTile(position) as CustomTile;
    17.    
    18.     if (tile != null)
    19.     {
    20.         tile.Walkable = !tile.Walkable;
    21.         tilemap.RefreshTile(position);
    22.     }
    23. }
    The thing is that it changes the value of the Tile asset itself so all the Tiles placed on the Tilemap are impacted... I thought it would change the "tile instance", but it is a reference to an asset. Sounds totally logical but I was a little too optimist ^^ Maybe I'm thinking wrong about Tilemaps and they are not made to handle data that can change over time ?

    Do I have to create a custom Tile for every tile combination possible and then the brush will automatically select the right tile?

    Regarding the Height int value of my tiles, I was thinking using the Z value but if I do that I will not able to simply get tiles with
    GetTile(new Vector3(x, y, 0))
    as long as tiles will be on three different Z (0, 1, and 2). Some tile can be climbed depending of the height value.

    I do not know what is the smartest way to go, and I'm sure you're the best person I can ask that question to :)

    I know it is a lot of questions totally out of the main topic subject but I'm so happy you answered me that I can't miss this opportunity :)

    Here is a preview of the very first level, so it is quite simple to make a game tutorial.
    See, Tiles are not used for their rendering capabilities.

    w01l01-preview.png


    Have a nice day and keep up the good work, you're awesome Unity teams!
     
  4. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    Unfortunately, you are right where you retrieve the Tile Asset when you call `tilemap.GetTile(position)`. This will not allow you to set per instance data directly.

    Instead, you can check out `GridInformation` component in 2D Extras which could help with what you want (https://github.com/Unity-Technologies/2d-extras/blob/master/Documentation~/GridInformation.md). This allows you to store objects by position.

    For example:

    Code (CSharp):
    1. public override void Paint(GridLayout grid, GameObject brushTarget, Vector3Int position)
    2. {
    3.     var gi = brushTarget.GetComponent<GridInformation>();
    4.     var tilemap = brushTarget.GetComponent<Tilemap>();
    5.     if (gi != null && tilemap != null)
    6.     {
    7.         if (tilemap.GetTile(position) is CustomTile)
    8.         {
    9.             gi.SetPositionProperty(position, "Walkable", 1);
    10.             tilemap.RefreshTile(position);
    11.         }
    12.     }
    13. }
     
    eses likes this.
  5. Rafarel

    Rafarel

    Joined:
    Jul 21, 2017
    Posts:
    199
    Thank you, I'll give it a try as soon as I come back from vacations ^^