What's stored in a tilemap

Simple question: when you use tilemap.SetTile() does the tilemap store a reference to the tile (a scriptable object) or does it create a clone of the tile and store that?

This part of tilemaps is opaque, since it’s in the actual engine and not the C# code that one can look at.

From examining scene files, it -appears- that the tilemap is storing clones (instances?) of the tile asset, but I’m curious mostly due to my creating a custom brush that places tiles and prefabs at the same time and links them together using a custom-scripted Tile. It all works fine.

It’d be helpful if someone from Unity could clear this up for me as what’s happening in the “hidden zone” appears to be undocumented. It’s possible I’m just misunderstanding someone.

A reference to the tile (not a clone of the instance!) is stored in the Tilemap. This is part of what makes Tilemaps so much less memory intensive than a GameObject with a SpriteRenderer.

Associated with each position/cell, there’s the Tile and its TileData, which is a struct with color, flags, (optional) gameobject reference, sprite and Matrix4x4 transform information. Unity - Scripting API: TileData

The Tile exists to help control what TileData fields are in different situations, e.g. a RuleTile will change the sprite or transform depending on what tiles exist in neighboring cells.

One common source of confusion is that there are no per-instance properties per placed tile on the tilemap - aside from the TileData. For example, if you wanted health/hp per-tile then there’s no place to put it. Where you want to have per-instance fields, many people use GameObjects instead. Others keep a parallel data structure (array/dictionary) where that kind of information can be stored. Complication: If you really wanted to, you could create a clone of every tile then place it to the Tilemap… but that’s not the default behavior.

If that were true then modifying the tile would modify the tile asset ( scriptable object), wouldn’t it?

What I see is that I can click on a tile with the pick tool from the tilemap palette and see the tile’s fields in an inspector panel. This is done with an intermediate bit of code called a ‘GridSelection’ (part of the tilemap editor IIRC).

What you actually see in the inspector is determined by the brush code’s
OnSelectionInspectorGUI implementation which that method has access to when it’s called. I added code to display properties/fields that are specific to my custom tile. If I modify one of these values, or even a base value like Color, the scriptable object AKA tile asset isn’t changed. This is in editor mode ie not playing, but the same thing happens during runtime.

So although I was pretty sure that tilemaps stored a reference to a tile (Scriptable Object) to use as a template for creating data to send to the tilemap renderer, I’m no longer sure of what’s actually being stored, or more specifically, what’s serialized when the tilemap is saved as part of a scene.

“One common source of confusion is that there are no per-instance properties per placed tile on the tilemap - aside from the TileData”

I’m not confused by that. But GetTIleData and GetTileAnimationData seem to be used when the tile is refreshed. This is why, for example, you can start and stop (a modified version of) the AnimatedTile with some simple code that calls refreshtile on that grid location.

It’s also possible that I’ve confused myself!

theres something extra, when you set a tile you are drawing on a mesh( tilemap ) and even if you change the sprite of the tile it wont update the rest of the tilemap until you refresh that position.

You can essentially draw your whole map with just 1 tile by changing the sprite field in runtime as long as you dont refresh the already placed ones. This works with animated tiles aswell.

good to know. Someone should write a lorebook for this stuff! :slight_smile:

[quote]
If that were true then modifying the tile would modify the tile asset ( scriptable object), wouldn’t it?
[/quote]Yes. GetTile(someCell).someProperty = 2 will change it with the refresh caveat that raarc stated.

Though I would caution against changing tiles in such a way that they are not “refresh stable” - b/c it can lead to hidden bugs or strange behavior. (I once had such a bug and it took quite a while to figure out.) This is why many tile scripts, e.g. RandomTile, use the cell-position as a seed which is Perlin’d or hashed to select an index from an array of sprites. By doing it that way, rather than using Random.Range(), it is refresh-stable.

Simple test:

using UnityEngine;
using UnityEngine.Tilemaps;

public class PainterTest : MonoBehaviour
{
    public Tile tile;
    public Tilemap tilemap;

    void Update()
    {
        var pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        var c = new Vector3(pos.x, pos.y);
        Vector3Int cell = tilemap.WorldToCell(c);

        // MODIFY THE TILE ASSET ITSELF
        if (Input.GetKeyDown(KeyCode.U))
        {
            tile.color = new Color(Random.value, Random.value, Random.value);
            tilemap.SetTile(cell, tile);
        }

        // MODIFY THE ASSET VIA GETTING IT FROM THE TILEMAP
        if (Input.GetKeyDown(KeyCode.I))
        {
            (tilemap.GetTile(cell) as Tile).color = new Color(Random.value, Random.value, Random.value);
            tilemap.SetTile(cell, null); // clear b/c tilemap seems to do a "if already has this tile by ref equality" check
            tilemap.SetTile(cell, tile);
        }
    }
}

Works as I said. Changes the tile asset (you can observe this in the Project / Inspector). If it stored a copy, then we would not observe such a change.

That’s not what I’m doing. I’ll try to make up a simple example and post it.

What you’re seeing is correct, in editor. It doesn’t happen in a build. I recreated your example with a few minor differences and for sure you see the asset change in editor. If you run it in Windows (example attached) then the color changes, but when you re-run the example the color is back at the original. Which is what you’d expect, since this sort of source-asset modification is editor only, as far as I can tell.

zip file of built project for windows:
https://drive.google.com/file/d/1hq0EWv5EGcYmfKZ1_mH-D-um7wDobRtC/view?usp=sharing

See Unity - Manual: Modifying Source Assets Through Scripting

on that page they’re talking about Play mode which is an editor mode.

using UnityEngine;
using UnityEngine.Tilemaps;
public class Test : MonoBehaviour
{
    public Tilemap tilemap;
    void Update()
    {
        var        pos  = Camera.main.ScreenToWorldPoint(Input.mousePosition);
        var        c    = new Vector3(pos.x, pos.y);
        Vector3Int cell = tilemap.WorldToCell(c);
        if(Input.GetKeyDown(KeyCode.Escape))
            Application.Quit();
        // MODIFY THE ASSET VIA GETTING IT FROM THE TILEMAP
        if (Input.GetKeyDown(KeyCode.X))
        {
            var tile = tilemap.GetTile(cell) as Tile;
            if (tile != null)
            {
                tile.color = new Color(Random.value, Random.value, Random.value);
                tilemap.RefreshTile(cell);
            }
            else
            {
                print("nothing at this position");
            }
        }
    }
}

A simple in-editor test:

set the inspector to debug mode
examine a tile asset, change flags to none if not already like that, so you can change color.
shut off debug mode
paint that tile.
use the pallet’s “select” (arrow) button, click on the tile you just painted
the inspector should now show a Grid Selection.
Click on the color box in the inspector and change the tile’s color, you’ll see the change in the scene window
Select the tile asset in the Project window
the inspector shows the tile asset
the tile asset color isn’t changed.

I just tried this several times…

and that’s because the code in GridBrushEditor uses
tilemap.SetColor which doesn’t involve the asset at all but changes the tilemap itself. So that change persists w/o changing the original tile.

Yes, b/c as you said GridSelection targets TileData.color, which isn’t targetting the tile.color property (which isn’t even a necessarily a property of a tile b/c Tile : TileBase and adds that property). I’m not sure I understand what’s the problem anymore. :face_with_spiral_eyes:

If by “re-run the example” you mean “launch the TilesTest.exe again” then what you’re observing is an aspect of how ScriptableObjects work. They do not serialize the changes you make to them when running the build. Every time you relaunch the .exe it deserializes it from whatever state it was in when you created the build. When exiting the build, it doesn’t re-serialize those changes to disk. To persist that kind of change, you need to use serialization tools like JsonUtility or a more formal serializer like JSON.NET.

Though perhaps I misunderstand your example b/c you didn’t explain what the difference between the two tiles - one (on the right) can have its color changed, whereas the other (on the left) does not.

No I agree with you. I do understand how scriptable objects work, it’s just different in editor and in a build.
The object on the left is just a sprite with the same image as the tile.
No need to respond anymore, thanks. Wasn’t trying to confuse anyone.