Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice
  4. Dismiss Notice

Bug None of the Fill methods (FloodFill, BoxFill, SetTilesBlock) work

Discussion in '2D' started by QuariYune, Jan 16, 2023.

  1. QuariYune

    QuariYune

    Joined:
    Jul 1, 2021
    Posts:
    20
    Hey all, experiencing this issue on the very latest version of unity (2022.2.1f1), but it was also occurring on the latest version of 2021 unity which I upgrade this project from to try and get it working.

    As the title says, none of the Tilemap.FloodFill, BoxFill, or SetTilesBlock work for filling in a large portion of the tilemap with the same tile. For all 3 of the code snippets below, I get null in the second Debug:

    FloodFill
    Code (CSharp):
    1. BoundsInt zone = new BoundsInt(startX, startY, 0, sizeX, sizeY, 0); // startX, startY = 0, sizeX, sizeY = 8. But same issue occurs with any valid parameters
    2. Tilemaps[0].SetTile(zone.min, BackgroundTile);
    3. Tilemaps[0].SetTile(zone.max, BackgroundTile);
    4. Tilemaps[0].FloodFill(zone.min, BackgroundTile);
    5. Debug.Log(Tilemaps[0].GetTile(zone.min)); // Outputs name of the background tile
    6. Debug.Log(Tilemaps[0].GetTile(zone.min + new Vector3Int(1, 1, 1))); // Outputs null
    BoxFill
    Code (CSharp):
    1. BoundsInt zone = new BoundsInt(startX, startY, 0, sizeX, sizeY, 0); // startX, startY = 0, sizeX, sizeY = 8. But same issue occurs with any valid parameters
    2. Tilemaps[0].SetTile(zone.min, BackgroundTile);
    3. Tilemaps[0].SetTile(zone.max, BackgroundTile);
    4. Tilemaps[0].BoxFill(zone.min, BackgroundTile, 0, 0, sizeX - 1, sizeY - 1);
    5. Debug.Log(Tilemaps[0].GetTile(zone.min)); // Outputs name of the background tile
    6. Debug.Log(Tilemaps[0].GetTile(zone.min + new Vector3Int(1, 1, 1))); // Outputs null
    SetTilesBlock
    Code (CSharp):
    1. BoundsInt zone = new BoundsInt(startX, startY, 0, sizeX, sizeY, 0); // startX, startY = 0, sizeX, sizeY = 8. But same issue occurs with any valid parameters
    2. TileBase[] background = new TileBase[sizeY * sizeY];
    3. Array.Fill(background, BackgroundTile);
    4. Tilemaps[0].SetTile(zone.min, BackgroundTile);
    5. Tilemaps[0].SetTile(zone.max, BackgroundTile);
    6. Tilemaps[0].SetTilesBlock(zone, background);
    7. Debug.Log(Tilemaps[0].GetTile(zone.min)); // Outputs name of the background tile
    8. Debug.Log(Tilemaps[0].GetTile(zone.min + new Vector3Int(1, 1, 1))); // Outputs null
    The only way I can achieve filling in a large part of the map with the same tile is with SetTiles
    Code (CSharp):
    1. BoundsInt zone = new BoundsInt(startX, startY, 0, sizeX, sizeY, 0); // startX, startY = 0, sizeX, sizeY = 8. But same issue occurs with any valid parameters
    2. TileBase[] background = new TileBase[sizeY * sizeY];
    3. Array.Fill(background, BackgroundTile);
    4. Vector3Int[] backgroundPositions = new Vector3Int[sizeY * sizeY];
    5. int index = 0;
    6. for (int y = startY; y < startY + sizeY; y++)
    7. {
    8.     for (int x = startX; x < startX + sizeX; x++)
    9.     {
    10.         backgroundPositions[index] = new Vector3Int(x, y, 0);
    11.         index++;
    12.     }
    13. }
    14. Tilemaps[0].SetTile(zone.min, BackgroundTile);
    15. Tilemaps[0].SetTile(zone.max, BackgroundTile);
    16. Tilemaps[0].SetTiles(backgroundPositions, background);
    17. Debug.Log(Tilemaps[0].GetTile(zone.min)); // Outputs name of the background tile
    18. Debug.Log(Tilemaps[0].GetTile(zone.min + new Vector3Int(1, 1, 1))); // Outputs name of background tile
    However, this is not ideal due to just the amount of looping, especially when generating large maps (1024*1024) or having to perform this on many smaller tilemap layers. Any ideas what is causing this issue?
     
    Last edited: Jan 16, 2023
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,780
    This looks like it is just a nullref. Figure out why there's no tile at that address.

    How to fix a NullReferenceException error

    https://forum.unity.com/threads/how-to-fix-a-nullreferenceexception-error.1230297/

    Three steps to success:
    - Identify what is null <-- any other action taken before this step is WASTED TIME
    - Identify why it is null
    - Fix that
     
  3. karliss_coldwild

    karliss_coldwild

    Joined:
    Oct 1, 2020
    Posts:
    530
    Code (CSharp):
    1. zone.min + new Vector3Int(1, 1, 1)
    Shouldn't this be Vector3Int(1, 1, 0) or Vector3Int(1, 0, 1) instead of Vector3Int(1, 1, 1)? Not sure if it's xy or xz plane but 1, 1, 1 is most likely wrong.
     
  4. QuariYune

    QuariYune

    Joined:
    Jul 1, 2021
    Posts:
    20
    My bad yea, it should be Vector3Int(1, 1, 0). I included the Debug to only show that none of the Fill methods are doing anything in Editor. The below picture is essentially what I see in the editor after running those snippets, and Vector3Int(1, 1, 0) also shows null.

    The issue with null ref is exactly why I posted this question.

    What is null:
    The spot in the Tilemap that should have a background tileset. The background asset going into the Unity API is not null.

    Why is it null:
    Unity API doesn't seem to be filling it in and doing what it is supposed to? If the inputs are not null and are valid, the API shouldn't be nullifying the output/results.

    Fix that:
    No idea how / what the cause is. Potentially a bug with the Unity api. Which is the reason for the post.
     

    Attached Files:

    Last edited: Jan 16, 2023
  5. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    Hi, could you try the following to set up the bounds and check if that helps?

    BoundsInt zone = new BoundsInt(startX, startY, 0, sizeX, sizeY, 1);
     
  6. QuariYune

    QuariYune

    Joined:
    Jul 1, 2021
    Posts:
    20
    Sorry for the late reply. This doesn't seem to work either, still getting the same thing:
    upload_2023-1-19_11-17-9.png
     
  7. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    @QuariYune

    Thanks for the update, I think I understand the issue better now!

    This will not work for Tilemap.FloodFill or BoxFill, as it will replace Tiles based on the initial position set. This works similarly to Paint in Windows, where if you try to fill a red pixel with a black pixel, it will only change the red pixels at the location to black and so on.

    As you have set the target position (zone.min) with the BackgroundTile, when calling Tilemap.FloodFill at zone.min, it will attempt to change the target Tile at that position (BackgroundTile) to the BackgroundTile. It will then check its neighbours to see if they are the original target Tile and then change them. As the neighbours are not the target Tile (null), nothing will happen and the FloodFill algorithm will stop.

    Instead of calling SetTile with zone.min and zone.max, you could change the Tilemap's origin and size with the values first, before calling FloodFill. Using FloodFill with zone.min + Vector3Int.right should work too.

    The bounds should work for SetTilesBlock as that requires a depth of at least 1 (size.z).
     
  8. QuariYune

    QuariYune

    Joined:
    Jul 1, 2021
    Posts:
    20
    Looks like the SetTilesBlock indeed works for filling the area with a depth of 1. Though its a bit unfortunate that the floodfill and boxfill don't treat "null / empty" as a valid target for replacement. While I understand operations on null values can get finicky, I just tested 3 different art programs that all allowed me to bucket fill a newly opened unaltered file (MS Paint, Aseprite, Krita). In any case thanks for the help @ChuanXin