[Tilemap] Problem calling tilemap.RefreshTile multiple times in one call, different tiles.

Below is my code for my custom tile that updates when a globalstate bool is invoked
In this case I want a few tiles to change after you talk to a character. I have 3 tiles this even is connected too. When the event is call I can see when I put a debug.log in that it gets called 3 times on the 3 tiles.

But only the bottom right most tile updates. Does calling tilemap.Refreshtile have some sort of protection against this?

public class TileDataOverride : TileBase
    {
        private Sprite sprite;

        [SerializeField]
        private Sprite offSprite;

        [SerializeField]
        private Sprite onSprite;

        [SerializeField]
        private GlobalStateBool state;

        private Vector3Int position;
        private ITilemap tilemap;
        private UnityEngine.Tilemaps.Tilemap tmap;

        public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject instantiatedGameObject)
        {
            this.tilemap = tilemap;
            this.position = position;
            Debug.Log($"Set {position} sprite");
            sprite = (state?.Get() ?? false) ? onSprite : offSprite;

            var thingy = tilemap.GetComponent<UnityEngine.Tilemaps.Tilemap>();
            if (thingy)
            {
                tmap = thingy;
                Debug.Log("There is thingy");
            }
            if (state != null)
            {
                state.AddListener(SetTile);
            }
            return base.StartUp(position, tilemap, instantiatedGameObject);
        }

        private void SetTile(bool value)
        {
            sprite = value ? onSprite : offSprite;
            tilemap.RefreshTile(position);
            if (tmap)
            {
                //tmap.RefreshAllTiles();
            }
        }

        public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
        {
            var iden = Matrix4x4.identity;

            tileData.sprite = sprite ?? offSprite;
            tileData.colliderType = sprite != null ? Tile.ColliderType.Sprite : Tile.ColliderType.None;
            tileData.flags = TileFlags.LockTransform;
            tileData.transform = iden;
        }
    }

To change just a few tiles, after that GlobalBool state changes, you should get the specific tilemap and do tilemap.SetTile(cell, whateverNewTile);

This is more in line with how tiles are intended to be used. It’s odd to have instance-like properties on a tile (position, tilemap) when there’s only 1 tile instance for a kind of a tile (1 tile for all the grass, 1 tile for all the dirt, etc). To get any other behavior, you gotta resort to hacky wizardry, which kind of looks like you’re discovering.

If you strongly prefer hacking the tile class, you’re using StartUp where I think you mean to be using GetTileData. Here’s a template you could adapt to your situation, replacing the static bool with your GlobalBool:

public class SummerWinterTile : TileBase
{
    public static bool showSummer = true; // set externally, then tilemap.RefreshAllTiles()
    public Sprite m_summerSprite;
    public Sprite m_winterSprite;

    public override void GetTileData(Vector3Int cell, ITilemap tilemap, ref TileData tileData)
    {
       tileData.sprite = ShouldShowSummer() ? this.m_summerSprite : m_winterSprite;
    }

    protected bool ShouldShowSummer() => m_winterSprite == null || showSummer;
}

Ahhh I see.

I have done some refactoring. As I see what you mean about the tile reference. I now make a list of positions to update in start up. But when I call ITilemap.refreshTile(position) GetTileData is never called. Which is strange to me.

public class TileDataOverride : TileBase
    {
        private Sprite sprite;

        [SerializeField]
        private Sprite offSprite;

        [SerializeField]
        private Sprite onSprite;

        [SerializeField]
        private GlobalStateBool state;

        private Dictionary<ITilemap, List<Vector3Int>> positionsCache = new Dictionary<ITilemap, List<Vector3Int>>();

        private void SetTiles(bool value)
        {
            Debug.Log("Set tiles event called");
            sprite = value ? onSprite : offSprite;
            foreach (var positionList in positionsCache)
            {
                Debug.Log($"We have {positionList.Value.Count} positions");
                foreach (var position in positionList.Value)
                {
                    Debug.Log($"Refresh {position}");
                    var tilemap = positionList.Key.GetComponent<UnityEngine.Tilemaps.Tilemap>();
                    tilemap.RefreshTile(position);
                }
            }
        }

        private void OnDisable()
        {
            positionsCache.Clear();
            if(state)
                state.RemoveListener(SetTiles);
        }

        private void OnEnable()
        {
            if(state)
                state.AddListener(SetTiles);
            sprite = (state?.Get() ?? false) ? onSprite : offSprite;
        }

        public override bool StartUp(Vector3Int position, ITilemap tilemap, GameObject instantiatedGameObject)
        {
            List<Vector3Int> positionList;
            positionsCache.TryGetValue(tilemap, out positionList);
            if(positionList == null)
            {
                positionList = new List<Vector3Int>();
                positionsCache.Add(tilemap, positionList);
            }

            Debug.Log($"Setup {position}");

            positionList.Add(position);

            return base.StartUp(position, tilemap, instantiatedGameObject);
        }

        public override void RefreshTile(Vector3Int position, ITilemap tilemap)
        {
            Debug.Log($"Whattttt {position}");
            base.RefreshTile(position, tilemap);
        }

        public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
        {
            Debug.Log($"Grid data {position}");
            if (!Application.isPlaying)
            {
                tileData.sprite = offSprite;
            }
            else
            {
                tileData.sprite = sprite ?? offSprite;
            }
            tileData.colliderType = sprite != null ? Tile.ColliderType.Sprite : Tile.ColliderType.None;
            tileData.flags = TileFlags.LockTransform;
            tileData.transform = Matrix4x4.identity;
        }
}