I'm trying to show tiles on an isometric staggered tilemap in Unity, but some tiles are missing. I think the problem might be with where I'm placing the tiles or some floating-point precision issues.
The Main Issue:
I read tiles from a JSON file and try to put them on a Unity Tilemap. But, some tiles just don't show up. I've double-checked my math and even tried some small adjustments, but I still have missing tiles.
Here's the code I'm using:
MapLoader.cs - Responsible for loading the map data from a file path
using System.IO;
using UnityEngine;
public static class MapLoader
{
public static MapDataStructure.MapData LoadMapFromPath(string path)
{
if (!IsValidPath(path)) return null;
return DeserializeMap(File.ReadAllText(path));
}
private static bool IsValidPath(string path)
{
if (File.Exists(path)) return true;
Debug.LogError($"Cannot find or access map file at: {path}");
return false;
}
private static MapDataStructure.MapData DeserializeMap(string jsonData)
{
return JsonUtility.FromJson<MapDataStructure.MapData>(jsonData);
}
}
TilePlacer.cs - Contains logic for determining which tile to place and its world position.
using UnityEngine;
using UnityEngine.Tilemaps;
public class TilePlacer
{
private readonly Tilemap _tilemap;
private readonly Tile[] _tileAssets;
public TilePlacer(Tilemap tilemap, Tile[] tileAssets)
{
_tilemap = tilemap;
_tileAssets = tileAssets;
}
public void PlaceTileAtCoordinates(int[] tileData, int x, int y, MapDataStructure.MapData mapData)
{
int tileIndex = GetTileIndex(tileData, x, y, mapData);
if (IsValidTileIndex(tileIndex))
{
PlaceTile(tileIndex, x, y, mapData);
}
else
{
Debug.LogWarning($"Invalid tile index {tileIndex} at position {y},{x}");
}
}
private int GetTileIndex(int[] tileData, int x, int y, MapDataStructure.MapData mapData)
{
return tileData[y * mapData.width + x] - 1;
}
private bool IsValidTileIndex(int tileIndex)
{
return tileIndex >= 0 && tileIndex < _tileAssets.Length;
}
private void PlaceTile(int tileIndex, int x, int y, MapDataStructure.MapData mapData)
{
Vector3 tileWorldPosition = ComputeTileWorldPosition(x, y, mapData);
Vector3Int cellPosition = _tilemap.WorldToCell(tileWorldPosition);
_tilemap.SetTile(cellPosition, _tileAssets[tileIndex]);
}
private Vector3 ComputeTileWorldPosition(int x, int y, MapDataStructure.MapData mapData)
{
float worldX = x * mapData.tileWidth;
float worldY = y * mapData.tileHeight / 2;
// Adjust x for odd rows
if ((y & 1) == 1)
{
worldX += mapData.tileWidth / 2;
}
return new Vector3(worldX, worldY, 0);
}
}
TilemapRenderer.cs - Manages the process of rendering the tiles to the Tilemap.
using System.IO;
using UnityEngine;
using UnityEngine.Tilemaps;
public class TilemapRenderer : MonoBehaviour
{
[SerializeField]
private string mapFileName = "WorldMap.json";
[SerializeField]
private Tile[] tileAssets;
private Tilemap tilemap;
private TilePlacer tilePlacer;
private const int DEFAULT_LAYER_INDEX = 0;
private void Start()
{
tilemap = GetComponent<Tilemap>();
tilePlacer = new TilePlacer(tilemap, tileAssets);
LoadAndRenderMap();
}
private void LoadAndRenderMap()
{
string filePath = BuildFilePath();
MapDataStructure.MapData mapData = MapLoader.LoadMapFromPath(filePath);
if (mapData != null)
{
RenderTilesFromLayer(mapData, DEFAULT_LAYER_INDEX);
}
else
{
Debug.LogError("Failed to deserialize map data from the file.");
}
}
private string BuildFilePath()
{
return Path.Combine(Application.streamingAssetsPath, mapFileName);
}
private void RenderTilesFromLayer(MapDataStructure.MapData mapData, int layerIndex)
{
if (mapData.layers == null || layerIndex >= mapData.layers.Length)
{
Debug.LogError("Invalid map layer.");
return;
}
int[] tileData = mapData.layers[layerIndex].data;
for (int y = 0; y < mapData.height; y++)
{
for (int x = 0; x < mapData.width; x++)
{
tilePlacer.PlaceTileAtCoordinates(tileData, x, y, mapData);
}
}
}
}
Important Parts of the Code:
The ComputeTileWorldPosition function in TilePlacer.cs calculates the world position for a given tile based on its coordinates x and y. After calculating the world position, the function _tilemap.WorldToCell is used to convert this world position to a cell position.
Screenshots:
What I've Tried:
If I add a small offset (like 0.1) to where I place each tile, or if I change the grid's position to -0.5, it seems to fix it. But it feels like I'm just patching the problem instead of really fixing it.
Here's the adjusted ComputeTileWorldPosition function:
private Vector3 ComputeTileWorldPosition(int x, int y, MapDataStructure.MapData mapData)
{
float worldX = x * mapData.tileWidth;
float worldY = y * mapData.tileHeight / 2;
// Adjust x for odd rows
if ((y & 1) == 1)
{
worldX += mapData.tileWidth / 2;
}
worldX += 0.1f;
worldY += 0.1f;
return new Vector3(worldX, worldY, 0);
}
Has anyone had this problem before or see what I might be doing wrong? Any help would be great! Thanks!