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

Tile's color won't update when it's color is changed in overriden GetTileData

Discussion in '2D' started by Artas, Jun 14, 2020.

  1. Artas

    Artas

    Joined:
    Jun 3, 2020
    Posts:
    8
    Hi!

    I'm trying to change the color of a custom-class tile. The tile's color changes when it's done in void Update.

    Code (CSharp):
    1.  void Update
    2. {
    3.        var vec = new Vector3Int { x = 0, y = 0, z = 0 };
    4.  
    5.         if (TileMap.GetColor(vec) == Color.blue) TileMap.SetColor(vec, Color.yellow);
    6.         else TileMap.SetColor(vec, Color.blue);
    7.  
    8.          TileMap.RefreshTile(vec);
    9. }
    However when I'm trying to do it in an overridden GetTileData it just simply doesn't work

    Code (CSharp):
    1. public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    2. {
    3.         if (color == Color.yellow)
    4.         {
    5.             color = Color.blue;
    6.             Debug.Log(color);
    7.             tileData.color = color;
    8.             tileData.sprite = sprite;
    9.  
    10.             return;
    11.         }
    12.  
    13.         if (color == Color.blue)
    14.         {
    15.             color = Color.yellow;
    16.             Debug.Log(color);
    17.             tileData.color = color;
    18.             tileData.sprite = sprite;
    19.  
    20.             return;
    21.         }
    22. }
    My question is: Why is it so? I understand why the code works in void Update() but I do not understand why it doesn't work in GetTileData. When I tried to debug it using Debug.Log the color's *raw* value did change but the tile looked the same all the time(In my case: The starting color's value was yellow. When I entered play mode the tile changed it's color to blue but then stayed like it forever. It didn't change it's color (the one seen in playmode) to yellow although the color property was equal to Color.yellow).
     
  2. brigas

    brigas

    Joined:
    Oct 4, 2014
    Posts:
    522
    probably because it doesnt call refreshtile
     
  3. Artas

    Artas

    Joined:
    Jun 3, 2020
    Posts:
    8
    Hi!

    Thanks for fast reply. Could you please elaborate on "probably because it doesn't call refreshtile"? I am calling TileMap.RefreshTile(vec); in void Update(). Should I call it also somewhere else?
     
  4. brigas

    brigas

    Joined:
    Oct 4, 2014
    Posts:
    522
    I mean that TileMap.RefreshTile(vec) is what causes the tilemap to update that tile to the current color, if you change the color but dont call refreshtile the tile wont change on the tilemap
     
  5. Artas

    Artas

    Joined:
    Jun 3, 2020
    Posts:
    8
    Hi!

    Oh yes, that's true but I'm calling it. The current void Update() looks like this:
    Code (CSharp):
    1.     void Update()
    2.     {
    3.              var vec = new Vector3Int { x = 0, y = 0, z = 0 };
    4.              TileMap.RefreshTile(vec);
    5.     }
    Should I call it somewhere else or the problem is within RefreshTile method and i should override it?
     
  6. brigas

    brigas

    Joined:
    Oct 4, 2014
    Posts:
    522
    and where are you calling GetTileData?
     
  7. Artas

    Artas

    Joined:
    Jun 3, 2020
    Posts:
    8
    Hi!

    It's called whenever TileMap.RefreshTile(vec) is called (at least from my observations using debug.log)
     
  8. brigas

    brigas

    Joined:
    Oct 4, 2014
    Posts:
    522
    alright try this

    Code (CSharp):
    1.     public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    2.     {
    3.             if (color == Color.yellow)
    4.             {
    5.                 color = Color.blue;
    6.                 Debug.Log(color);
    7.                 tileData.color = color;
    8.                 tileData.sprite = sprite;
    9.  
    10.                 return;
    11.             }else if (color == Color.blue)
    12.             {
    13.                 color = Color.yellow;
    14.                 Debug.Log(color);
    15.                 tileData.color = color;
    16.                 tileData.sprite = sprite;
    17.  
    18.                 return;
    19.             }
    20.     }
    21.  
    and

    Code (CSharp):
    1.  void Start()
    2.     {
    3.              var vec = new Vector3Int { x = 0, y = 0, z = 0 };
    4.              TileMap.RefreshTile(vec);
    5.              Debug.Log(TileMap.GetColor(vec));
    6.     }
    And see if the color changes

    by the way, in which script are you running void Update? In a separate object? If you can post your full scripts that you are using
     
    Last edited: Jun 14, 2020
  9. Artas

    Artas

    Joined:
    Jun 3, 2020
    Posts:
    8
    Hi!

    Nope, it didn't work unfortunately :( (I put your void Start code in void Update, and added else if to GetTileData)

    So in my project there are at the moment 2 scripts: GridCreator.cs and CheesersTile.cs.

    GridCreator - create grid (but because of the problem i changed it to create only one tile)
    ChessersTile - custom Tile class

    GridCreator.cs:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Tilemaps;
    3.  
    4. enum TilesTypes
    5. {
    6.     TileA,
    7.     TileB,
    8. }
    9.  
    10. public class GridCreator : MonoBehaviour
    11. {
    12.     public static int Size = 2;
    13.  
    14.     public Sprite[] ChessersTilesSprites = new Sprite[Size];
    15.     public ChessersTile[] Tiles = new ChessersTile[Size];
    16.  
    17.     public Sprite DefaultSprite;
    18.  
    19.     public Tilemap TileMap;
    20.  
    21.     // Start is called before the first frame update
    22.     void Start()
    23.     {
    24.         for(int i = 0; i<Size; i++)
    25.         {
    26.             Tiles[i] = ScriptableObject.CreateInstance<ChessersTile>();
    27.             Tiles[i].sprite = ChessersTilesSprites[i];
    28.             Tiles[i].Start();
    29.         }
    30.  
    31.         for (int i = 0; i < 1; i++)
    32.         {
    33.             for (int j = 0; j < 1; j++)
    34.             {
    35.                 var tileType = (TilesTypes)((i+j)%2);
    36.  
    37.                 int x = j * 9; int y = i * 9; int z = 0; //size in pixels: 900x900
    38.                 var vector = new Vector3Int(x, y, z);
    39.  
    40.                 TileMap.SetTile(vector, GetTile(tileType)); //Tiles((i+j)%2)
    41.             }
    42.         }
    43.     }
    44.  
    45.     // Update is called once per frame
    46.     void Update()
    47.     {
    48.  
    49.         var vec = new Vector3Int { x = 0, y = 0, z = 0 };
    50.  
    51.         //if (TileMap.GetColor(vec) == Color.blue) TileMap.SetColor(vec, Color.yellow);
    52.         //else TileMap.SetColor(vec, Color.blue);
    53.         //TileMap.RefreshAllTiles();
    54.      
    55.         TileMap.RefreshTile(vec);
    56.         Debug.Log(TileMap.GetColor(vec));
    57.         //Debug.Log("refresh");
    58.     }
    59.  
    60.     ChessersTile GetTile(TilesTypes tile)
    61.     {
    62.         switch(tile)
    63.         {
    64.             case TilesTypes.TileA:
    65.                 return Tiles[0];
    66.             case TilesTypes.TileB:
    67.                 return Tiles[1];
    68.             default:
    69.                 return new ChessersTile() { sprite = DefaultSprite };        
    70.         }
    71.     }
    72. }
    73.  
    ChessersTile.cs:
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Tilemaps;
    3.  
    4. public class ChessersTile : Tile
    5. {
    6.     public void Start()
    7.     {
    8.         Debug.Log(Color.yellow);
    9.         Debug.Log(Color.blue);
    10.         Debug.Log("START");
    11.  
    12.         color = Color.yellow;
    13.     }
    14.  
    15.     public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    16.     {
    17.         if (color == Color.yellow)
    18.         {
    19.             color = Color.blue;
    20.             Debug.Log(color);
    21.             tileData.color = color;
    22.             tileData.sprite = sprite;
    23.  
    24.             return;
    25.         }
    26.  
    27.         if (color == Color.blue)
    28.         {
    29.             color = Color.yellow;
    30.             Debug.Log(color);
    31.             tileData.color = color;
    32.             tileData.sprite = sprite;
    33.  
    34.             return;
    35.         }
    36.     }
    37. }
     
  10. brigas

    brigas

    Joined:
    Oct 4, 2014
    Posts:
    522
    what are you getting from the debug logs?

    add little appendix so you know where it comes from
    Code (CSharp):
    1.  Debug.Log("gettiledata - "+color);
    also try this in update instead:

    Code (CSharp):
    1.  void Update()
    2.     {
    3.         var vec = new Vector3Int { x = 0, y = 0, z = 0 };
    4.  
    5.         Tile st = TileMap.GetTile(vec);
    6. TileMap.SetTile(vec,null);
    7. TileMap.SetTile(vec,st);
    8.         Debug.Log("color of tile on -"+vec +"="+TileMap.GetColor(vec));
    9.  
    10.     }
     
  11. Artas

    Artas

    Joined:
    Jun 3, 2020
    Posts:
    8
    Hi!

    Thanks it works now. However, could you please elaborate on why it works? I don't really understand why SetTile fixes (i mean it does... but why??) the problem and why the solution that uses TileMap.RefreshTile(vec) doesn't work.
     
  12. brigas

    brigas

    Joined:
    Oct 4, 2014
    Posts:
    522
    Well I dont use refresh tile myself but there are some possibilities why it doesnt work, the first is that refresh tile may not actually be calling gettiledata, and so when i do Tilemap.GetTile it does call gettiledata. The second possibility is that somehow tiles are getting mixed up and gettiledata is getting called on a different tile than the one you refreshed, but this is something that you would have to debug more extensively to find out.

    The set tile works because it makes sure that you are working with the tile you got from gettile and that gettiledata was called for that tile.

    edit: and also theres a 3rd possibility, is that your previous setup is calling gettiledata twice instead of only once, which with your override would make it always the starting color, but I cant be sure because Im not positive how refresh tile works
     
  13. Artas

    Artas

    Joined:
    Jun 3, 2020
    Posts:
    8
    Hi!

    Sorry for not responding, but I was trying to investigate the issue further and it seems that the color not changing is Unity's fault. After tileData.color = color; I added Debug.Log(tilemap.GetColor(position)); [it informs about the color before the change] to see the logs. The color always remained the same in logs.

    Code (CSharp):
    1.     public override void GetTileData(Vector3Int position, ITilemap tilemap, ref TileData tileData)
    2.     {
    3.         Debug.Log("data");
    4.         if (color == Color.yellow)
    5.         {
    6.             Debug.Log("now blue!");
    7.             color = Color.blue;
    8.             //Debug.Log(color);
    9.             tileData.color = color;
    10.             tileData.sprite = sprite;
    11.  
    12.             Debug.Log(tilemap.GetColor(position));
    13.  
    14.             return;
    15.         }
    16.  
    17.         if (color == Color.blue)
    18.         {
    19.             Debug.Log("now yellow");
    20.             color = Color.yellow;
    21.             //Debug.Log(color);
    22.             tileData.color = color;
    23.             tileData.sprite = sprite;
    24.  
    25.             Debug.Log(tilemap.GetColor(position));
    26.             return;
    27.         }
    28.     }
    However, after changing tileData.sprite (for an instance tileData.sprite = secondSprite) the sprite did change (the color.. didn't though).

    I decided to use visual studio debuger and it seems that the color is changed then, and only then, when the SetTile is called to create new instance (when creating the Tile first time or when using your SetTile(null) trick [because it is creating new instance of the Tile])
     
  14. ChuanXin

    ChuanXin

    Unity Technologies

    Joined:
    Apr 7, 2015
    Posts:
    1,068
    You could try adding:

    Code (CSharp):
    1. tileData.flags = TileFlags.LockColor;
    This flag will lock the color to that passed in by the Tile asset. Otherwise, the color set previously on the Tilemap will take precedence.
     
    Atik1n, Jamisco and Artas like this.
  15. Artas

    Artas

    Joined:
    Jun 3, 2020
    Posts:
    8
    Hi!
    Sorry for not responding. Yes, that solves the issue, thank you for your help and explanation
     
    Jamisco likes this.