Hi, need some help figuring out a problem here, see what I’m getting wrong.
First a bit of context, we have a game, a platformer, with different worlds. We have a tilemap for the first world and will have several different tilemaps for each subsequent world.
We wanted to run some user tests and thus set-dressed prototype levels of the second world with the tiles of the first tile palette. But now we want to re-setdress those prototype levels to the new tiles from second world tile palette.
Therefore, I went to make a tool that swaps tiles of the tilemap from the tiles of the tile palette of the first world to the ones of the second world. Of course, the tiles are placed at the exact same position in both tile “atlases”:
(By the way, I’m aware that some tiles are off, but this is just a test.)
So this is how I went about this “reskinner” component (basis code is from another Unity Answer from some years ago):
using System;
using System.Linq;
using UnityEngine;
using UnityEngine.Tilemaps;
namespace ExampleNamespace
{
public class Reskinner : MonoBehaviour
{
[SerializeField]
private string _skinReskinName;
[SerializeField]
private TileBase[] _otherTiles;
Tilemap[] tilemaps;
public void SetSkin()
{
SetSkin(_skinReskinName);
}
public void SetSkin(string name)
{
if (tilemaps == null)
tilemaps = GetComponentsInChildren<Tilemap>();
foreach (Tilemap tilemap in tilemaps)
{
for (int x = (int)tilemap.localBounds.min.x; x < tilemap.localBounds.max.x; x++)
{
for (int y = (int)tilemap.localBounds.min.y; y < tilemap.localBounds.max.y; y++)
{
TileBase tb = tilemap.GetTile(new Vector3Int(x, y, 0));
if (tb == null || tb.name.Length < 1)
{
continue;
}
string numberInOriginalName = GetNumberAtEndOfName(tb.name);
// if (numberInOriginalName == "0") continue;
TileBase replace = getSubTileByName(name, numberInOriginalName);
tilemap.SwapTile(tb, replace);
}
}
}
Debug.Log("Successfully swapped tiles");
}
TileBase getSubTileByName(string expectedTileName, string numberInOriginalName)
{
string expectedName = expectedTileName + "_" + numberInOriginalName;
foreach (TileBase s in _otherTiles)
{
if (s.name == expectedName)
{
return s;
}
}
Debug.LogError($"There is not tile of name {expectedName} in the sub tiles list.");
return null;
}
private string GetNumberAtEndOfName(string input)
{
string number = "";
string auxString = input;
char[] digits = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' };
int firstNumber = input.IndexOfAny(digits);
number = input.Substring(firstNumber);
return number;
}
}
}
Essentially I rename the tiles to something predictable that ends with a number ID, and then call SwapTile. This is bound to a button made on an Editor button:
This reskins appropiately:
As it can be seen, the tiles were replaced to the “Sand_Tile” named tiles replaced by the skinner. However, this doesn’t affect the .meta file, which means this isn’t a “permanent” change. The moment I reload the scene, the changes are gone.
I’d much prefer that this reskinner would persist the swaps as the target platform is Mobile Web and I don’t want to swap the sprites on runtime, as this would unnecessarily extend the load times.
Clearly I don’t understand how the tilemaps work behind when being persisted. If someone would help me understand I would much appreciate it. Thanks a bunch!