Tilemap.RefreshTile doesn't call TileBase.RefreshTile

What I want to achieve: click at a tile and call the RefreshTile method of every connected tile.
What I get: only Tilemap.SetTile can trigger RefreshTile. Tilemap.RefreshTile() does nothing.
The relevant code:

public class Tester : MonoBehaviour
{
    public Tilemap map;
    public TileBase clickToSet;

    private void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            var cellPos = map.WorldToCell(Camera.main.ScreenToWorldPoint(Input.mousePosition));
            // Debug.Log($"mouse clicked at {cellPos}");
            var tile = map.GetTile(cellPos);

            if (tile != clickToSet)
            {
                map.SetTile(cellPos, clickToSet);
            }
            else
            {
                map.RefreshTile(cellPos);
            }
        }
        if (Input.GetKeyDown(KeyCode.T))
        {
            map.RefreshAllTiles();
        }
    }
}
        // in class DebugTIle derived from Tile
        public override void RefreshTile(Vector3Int position, ITilemap tilemap)
        {
            // base.RefreshTile(position, tilemap);

            Debug.Log($"Before Refresh tile on <b>{position}</b>");
            // Refresh connected Tiles
            for (int dx = -1; dx <= 1; dx++)
            {
                var temp = new Vector3Int(position.x + dx, position.y, position.z);
                if (IsNeighbor(temp, tilemap)) tilemap.RefreshTile(temp);
            }

            for (int dy = -1; dy <= 1; dy++)
            {
                var temp = new Vector3Int(position.x, position.y + dy, position.z);
                if (IsNeighbor(temp, tilemap)) tilemap.RefreshTile(temp);
            }
            Debug.Log($"Refresh tile on <b>{position}</b> finished.");
        }

        private bool IsNeighbor(Vector3Int pos, ITilemap tilemap)
        {
            var tile = tilemap.GetTile(pos);
            return tile && tile == this;
        }

I get this msg ONLY when I click on an empty cell and create a new tile. I get nothing when I click on the tile of type DebugTile.
7907713--1007989--upload_2022-2-18_23-42-57.png
What I have checked:

  • all tiles of DebugTile are connected

  • GetTile() at that position get an instance of DebugTile and I can directly call other methods

Yes, I can implement my own “Refresh” method which calls the same method at the neighbor positions. But this is certain to cause an infinite recursion.
I have read the documents about this method but still cannot find the critical difference between my code and The example.

As far as I know, at runtime (at least) tilemap.refreshtile calls Startup/GetTileData but never RefreshTile. It seems as if (and I could be incorrect) that it’s an editor-time method only, to be used with rule tiles and the like. It’s been several months since I tried this but I recall being befuddled by this behavior.

Its confusing! Lousy choice for a method name?

It makes sense if it is the case!
But there may be some more details to be found. I just draw some DebugTile on the Tilemap with Tile Palette, but still, DebugTile.RefreshTile() is called only at the positions of the new tiles.

I thought it was a similar process in Minecraft: a block can “refresh”(or update?) its neighboring blocks, then maybe the neighbors of its neighbors…
It seems that I have to implement my own “Refresh” method that can iterate through the connected area correctly.

IMO the name is confusing. If you stick some debug.log calls in RefreshTile, StartUp, and GetTileData (even if you just override those only to have the debug.log calls and you don’t forget to call base.StartUp etc) you can see that tilemap.refreshtile or refreshalltiles doesn’t actually call RefreshTile.

Cant agree more:(

In the document, both Tilemap.RefreshTile and ITilemap.RefreshTile has this description:
“The tile map will retrieve the rendering data, animation data and other data for the tile and update all relevant components.”
In my test, GetTileData is called in Tilemap.RefreshTile, but StartUp wasn’t It seems that this method only refresh the visual stuff at the given position.
But what does “all relevant components” mean is uncleared yet. There is only 1 TileBase instance for all tiles created from the same tile assets. As a Scriptable Object, you cannot attach any component to it. So it’s odd to say “component” of a tile.

it’s confusing language for sure.
“There is only 1 TileBase instance for all tiles created from the same tile assets.” is true unless you clone the tiles.

BTW I think “All relevant components” refers to the tilemap and/or tilemap renderer components.

What’s the situation that makes you need this? I had a similar need at some point but then fixed it by avoiding it in the first place.

I just want to retrieve a connected area to sync some properties or broadcast some events on the area, either than a box area. Then I thought I could use Tilemap.RefreshTile to avoid iterating them manually. Sadly this method doesn’t offer the function I thought.
In this situation, I can write some search algorithms like BFS or DFS, which could take some time to implement, but is pratical.

And thanks for your information!

Tilemap.RefreshTile will not trigger TileBase.RefreshTile or any of its overrided versions, and will only update the TileData for that position. The relation for this is actually the reverse, as TileBase.RefreshTile will generally call Tilemap.RefreshTile instead.

We will see what we can do to make the behaviour that you want easier for you.