0
\$\begingroup\$

I've been experimenting with Tilemap to display tiles (with random colors) in script with SetColor() and other methods:

x = tilemap.WorldToCell(cur);  

tilemap.SetTile(x, tile);
tilemap.SetTileFlags(x, TileFlags.None);
tilemap.SetColor(x, UtilityFunc.GetRandColor());

But as the number of tiles increased to a certain point, the colors just glitched out (this is a 200x400 hexagon, pointed-top grid):

enter image description here

Full code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Tilemaps;

public class TEST : MonoBehaviour
{
    [SerializeField] Tilemap tilemap;
    [SerializeField] Tile tile;

    [Space(15)]
    [SerializeField] private float hexSize = 0.1f;

    [Space(15)]
    [SerializeField] private int hexLevel = 20;
    [SerializeField] private int perLevel = 20;

    private float hexWidth, hexHeight;
    [SerializeField] private float delay = 0.2f;

    Vector3Int x;

    private void Start()
    {
        StartCoroutine(CreatBoard());
    }

    IEnumerator CreatBoard()
    {
        tilemap.transform.localScale = new Vector3(hexSize, hexSize, 1f);
        hexHeight = hexSize; hexWidth = hexHeight/2f * Mathf.Sqrt(3f);

        bool goLeft = true; Vector2 original = new Vector2(0f, 0f);
        original += new Vector2(-hexWidth*perLevel/2f, hexLevel*hexHeight*0.75f/2f);

        //create board
        for (int i = 0; i < hexLevel; i++)
        {
            yield return new WaitForSeconds(delay);
            original = (goLeft) ? GetBottomLeftPos(original) : GetBottomRightPos(original); goLeft = !goLeft; Vector2 cur = original;
            for (int j = 0; j < perLevel; j++)
            {
                x = tilemap.WorldToCell(cur);

                tilemap.SetTile(x, tile);
                tilemap.SetTileFlags(x, TileFlags.None);
                tilemap.SetColor(x, UtilityFunc.GetRandColor());

                cur = GetRightPos(cur);
            }
        }
    }

    private Vector2 GetTopRightPos(Vector2 pos) { return new Vector2(pos.x + hexWidth/2, pos.y + hexHeight*0.75f); }
    private Vector2 GetTopLeftPos(Vector2 pos) { return new Vector2(pos.x - hexWidth/2, pos.y + hexHeight*0.75f); }

    private Vector2 GetLeftPos(Vector2 pos) { return new Vector2(pos.x - hexWidth, pos.y); }
    private Vector2 GetRightPos(Vector2 pos) { return new Vector2(pos.x + hexWidth, pos.y); }

    private Vector2 GetBottomRightPos(Vector2 pos) { return new Vector2(pos.x + hexWidth/2, pos.y - hexHeight*0.75f); }
    private Vector2 GetBottomLeftPos(Vector2 pos) { return new Vector2(pos.x - hexWidth/2, pos.y - hexHeight*0.75f); }
}

And random color function:

public static Color GetRandColor()
{
    return new Color(UnityEngine.Random.Range(0f, 1f),
        UnityEngine.Random.Range(0f, 1f),
        UnityEngine.Random.Range(0f, 1f),
    1f);
}

Why is the Tilemap system behaving like this?

Additional info: Video

\$\endgroup\$
2
  • \$\begingroup\$ Can you give us a Minimal Complete Verifiable Example, including the implementations of all functions like GetRightPos and GetRandColor? \$\endgroup\$
    – DMGregory
    Apr 27, 2022 at 11:51
  • \$\begingroup\$ @DMGregory Sure, let me edit \$\endgroup\$
    – silverfox
    Apr 27, 2022 at 11:51

1 Answer 1

2
\$\begingroup\$

*Since the C++ source code of Unity is not public, what I am talking about here is speculation

From the video, the wrong part of the tile color changes together. There must be some kind of resource limit. To save memory, tilemap has some flyweight-like design. This means that instead of each tile having its own complete resource, there is something like a resource index. Such as sprites and colors.

Just like:

HashTable<int,Color> _colorTable;
void SetTileColor(Vector3Int position, Color color){
    int colorKey = GetColorKey(color);
    Tile tile = GetTile(position);
    tile.colorIndex = colorKey;
}
int GetColorKey(Color color){
    int colorKey = some_hash_strategy(color);
    if(_colorTable.CoutainsKey(colorKey)){
        return colorKey;
    }
    if(_colorTable.Count >= COLOR_NUM_MAX){
        //ooops! color run out, I'll just give you the last one, what ever
        colorKey = lastColorKey;
    }
    _colorTable[colorKey]=color;
    return colorKey;
}

Proof: Compare the memory addresses of the colors returned by tilemap.GetColor. The color passed in when tilemap.SetColor is called is all new object, but tilemap.GetColor returned some duplicate addresses.:-)

Fix: Limit the number of colors so that color resources do not run out.

private Color GetRandColor()
{
    return new Color(randFloat(), randFloat(), randFloat(), 1f);
}
private float randFloat()
{
    return Random.Range(0, 16) / 16f;
}
\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .