Search Unity

Question How does Tilemap.GetTilesRangeNonAlloc work?

Discussion in '2D' started by JasonBricco, Feb 4, 2022.

  1. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    According to the documentation, it retrieves non-null tiles within the range given to it.

    So, how come if I have something like this

    Code (CSharp):
    1. private TileBase[] tiles = new TileBase[32768];
    2. private Vector3Int[] positions = new Vector3Int[32768];
    3.  
    4. BoundsInt bounds = new(0, 0, 0, 5, 5, 1);
    5. int count = map.GetTilesRangeNonAlloc(bounds.min, bounds.max, positions, tiles);
    for a tilemap with cellBounds = 128x20x1, I get a count returned of 553? I do not think it is possible to have 553 tiles within this range. It returns tiles well out of the range given.

    If I use GetTilesBlock() with the same bounds, it returns the correct size of 25. What's GetTilesRangeNonAlloc doing?

    Unity version: 2021.2.10.
     
    Last edited: Feb 4, 2022
  2. vonchor

    vonchor

    Joined:
    Jun 30, 2009
    Posts:
    249
    It doesn't say "retrieves non-null tiles within the range given to it" but rather "Retrieves an array of Tiles within the given range."

    The numbers do seem wrong. Have you tries not using a boundsInt but specifying the positions directly as in

    int count = map.GetTilesRangeNonAlloc(Vector3Int.zero, new Vector3(5,5,1), positions, tiles)
     
  3. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    Yes, doing so gives the same results. And true about null, though it does appear to not return null tiles. Otherwise, requiring an array of positions would be strange I would think.
     
  4. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    I made a new 2D URP project to test this in isolation. All I did is I added a tilemap to the scene and a test script with this code:

    Code (CSharp):
    1. public class TilemapTest : MonoBehaviour
    2. {
    3.     private Tilemap map;
    4.  
    5.     private void Awake()
    6.     {
    7.         map = GetComponentInChildren<Tilemap>();
    8.  
    9.         BoundsInt bounds = new(Vector3Int.zero, new(128, 128, 1));
    10.  
    11.         Tile[] tiles = new Tile[bounds.size.x * bounds.size.y];
    12.  
    13.         for (int i = 0; i < tiles.Length; ++i)
    14.             tiles[i] = ScriptableObject.CreateInstance<Tile>();
    15.  
    16.         map.SetTilesBlock(bounds, tiles);
    17.     }
    18.  
    19.     private void Update()
    20.     {
    21.         if (Input.GetKeyDown(KeyCode.Space))
    22.         {
    23.             Vector3Int start = Vector3Int.zero;
    24.             Vector3Int end = new(5, 5, 1);
    25.  
    26.             int count = map.GetTilesRangeCount(start, end);
    27.             Debug.Log("GetTilesRangeCount: " + count);
    28.  
    29.             TileBase[] tiles = new TileBase[ushort.MaxValue];
    30.             Vector3Int[] positions = new Vector3Int[ushort.MaxValue];
    31.  
    32.             count = map.GetTilesRangeNonAlloc(start, end, positions, tiles);
    33.             Debug.Log("GetTilesRangeNonAlloc: " + count);
    34.  
    35.             int nonNull = 0;
    36.  
    37.             for (int i = 0; i < tiles.Length; ++i)
    38.             {
    39.                 if (tiles[i] != null)
    40.                     ++nonNull;
    41.             }
    42.  
    43.             Debug.Log("Non-Null Returned: " + nonNull);
    44.         }
    45.     }
    46. }
    Upon pressing space I get this:

    GetTilesRangeCount: 646
    GetTilesRangeNonAlloc: 646
    Non-Null Returned: 646

    This must be a bug, right?
     
  5. vonchor

    vonchor

    Joined:
    Jun 30, 2009
    Posts:
    249
    I don't know if it's a bug. I haven't use GetTilesRangeNonAlloc in my asset so I'm just guessing here...

    GetTilesRangeCount doc says that the range is inclusive. So 0->5 is a range of 6. The example that they show has the Z param set to zero for both start and end, but you have it at 1. Still, 646 is weird.

    Use a debugger and examine exactly what's returned in the arrays. Sometimes, and this is hidden internally and only my opinion since I don't have visibility into the engine code, an empty position is returned as a TileBase instance. You can see this if you use the TilePalette in select mode and view the selection inspector when you click on an empty position. A TileBase instance is what's there. Hard to tell if this is just for display or not, even by examining the editor code.

    It certainly is confusing that GetTilesRangeCount returns 646 here.

    Try using End = 5,5,0 ??

    Aside from that I'd probably do a bug report at this point. Or the docs may not reflect what's really going on. Hard to tell since I can't find any usage of this method anywhere in the tilemap editor, etc.
     
  6. MelvMay

    MelvMay

    Unity Technologies

    Joined:
    May 24, 2013
    Posts:
    11,474
    I mentioned this post to the dev on the 2D team who's responsible for this. I'm sure they'll post here when they're around. A bug report never hurts though, maybe post the bug case number here too.
     
    JasonBricco likes this.
  7. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    It looks like it returned every tile from the rows at y = 0, 1, 2, 3, and 4. Not bounded by the range specified on the x axis. But then it stopped with the last tile being at 5, 5, 0. Since the map is 128 wide, that would be 128 * 5 + 6 tiles from row 5, giving 646. So it appears it's simply ignoring the x boundary.

    If I use end as 5, 5, 0 nothing changes (which it wouldn't if what I said above is correct).
     
    Last edited: Feb 5, 2022
    vonchor likes this.
  8. vonchor

    vonchor

    Joined:
    Jun 30, 2009
    Posts:
    249
    Really odd.
     
  9. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    Tilemap.GetTilesRangeNonAlloc returns the Tiles from the starting position to the ending position, iterating from the given Z position, then X position and then the Y position where possible.

    For the example where the Tilemap has Tiles set in the Bounds (128, 128, 1), using Tilemap.GetTilesRangeNonAlloc with a starting position of (0, 0, 0) and an ending position of (5, 5, 1) will include the following Tiles:
    • 128 Tiles from (0, 0, 0) to (127, 0, 0)
    • 128 Tiles from (0, 1, 0) to (127, 1, 0)
    • 128 Tiles from (0, 2, 0) to (127, 2, 0)
    • 128 Tiles from (0, 3, 0) to (127, 3, 0)
    • 128 Tiles from (0, 4, 0) to (127, 4, 0)
    • 6 Tiles from (0, 5, 0) to (5, 5, 0)
    • A total of 646 Tiles.
    This differs from Tilemap.GetTilesBlock which is bounded by the given Bounds rather than ranging from the full extents.

    Hope this clarifies!
     
  10. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    Thanks for the clarification. This means, then, that there isn’t a way to have the behavior of GetTilesBlock without array allocation? Or did I miss it?

    If not, that’s a shame. I need exactly that for several things I’m doing.
     
  11. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    Unfortunately, it is not possible right now, but we will correct this!

    Would it be possible to share which version of the Unity Editor you are using? Thanks!
     
  12. JasonBricco

    JasonBricco

    Joined:
    Jul 15, 2013
    Posts:
    956
    I’m on 2021.2.10. Glad to hear this option will be added!
     
  13. vonchor

    vonchor

    Joined:
    Jun 30, 2009
    Posts:
    249
    that's a remarkable explanation.
     
  14. FaithlessOne

    FaithlessOne

    Joined:
    Jun 19, 2017
    Posts:
    318
    Yep, thanks for the explanation. I would also appreciate a version of GetTilesRangeNonAlloc which behaves like GetTilesBlock in regard of the tiles returned.
     
  15. FaithlessOne

    FaithlessOne

    Joined:
    Jun 19, 2017
    Posts:
    318
    JasonBricco likes this.