Skip to content

Instantly share code, notes, and snippets.

@ZeronSix
Last active April 2, 2020 11:24
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ZeronSix/7772309 to your computer and use it in GitHub Desktop.
Save ZeronSix/7772309 to your computer and use it in GitHub Desktop.
Simple Unity3D TMX parser.
using UnityEngine;
using UnityEditor;
using System;
using System.Xml.Linq;
using System.Collections.Generic;
public enum MapType {
Isometric,
Orthogonal
}
public struct Tileset {
public string name;
public int firstGID;
public Vector2 offset;
public float imageWidth, imageHeight;
public float tileWidth, tileHeight;
public Texture2D image;
public List<Sprite> sprites;
}
public struct TileLayer {
public string name;
public int layer; // unity GO layer
public int sortingLayer;
public List<int> data;
}
public class TMXImporter: EditorWindow {
private MapType _mapType;
private float _width, _height, _tileWidth, _tileHeight, _pixelsInUnits;
private List<TileLayer> layers;
private Tileset _tileset; // TODO: multiple tilesets
private Vector2 _windowScroll = Vector2.zero;
public static void ImportTMX() {
// open file dialog for selecting the map file
string path = EditorUtility.OpenFilePanel("Open .tmx file", "", "tmx");
if (path.Length != 0) { // if the map file has selected...
XDocument doc;
try {
doc = XDocument.Load(path);
} catch (Exception ex) {
EditorUtility.DisplayDialog("Error!", ex.Message, "OK");
return;
}
TMXImporter window = EditorWindow.GetWindow<TMXImporter>("TMX Importer");
window.SetupWindow(doc);
}
}
public void SetupWindow(XDocument doc) {
XElement map = doc.Element("map");
// get map params
string mapOrientation = map.Attribute("orientation").Value;
_width = float.Parse(map.Attribute("width").Value);
_height = float.Parse(map.Attribute("height").Value);
_tileWidth = float.Parse(map.Attribute("tilewidth").Value);
_tileHeight = float.Parse(map.Attribute("tileheight").Value);
_tileset = new Tileset() {
name = map.Element("tileset").Attribute("name").Value,
offset = new Vector2(float.Parse(map.Element("tileset").Element("tileoffset").Attribute("x").Value),
float.Parse(map.Element("tileset").Element("tileoffset").Attribute("y").Value)),
tileWidth = float.Parse(map.Element("tileset").Attribute("tilewidth").Value),
tileHeight = float.Parse(map.Element("tileset").Attribute("tileheight").Value),
imageWidth = float.Parse(map.Element("tileset").Element("image").Attribute("width").Value),
imageHeight = float.Parse(map.Element("tileset").Element("image").Attribute("height").Value),
sprites = new List<Sprite>()
};
layers = new List<TileLayer>();
foreach (XElement layer in map.Elements("layer")) {
TileLayer newLayer = new TileLayer() {
name = layer.Attribute("name").Value,
layer = 0,
sortingLayer = 0,
data = new List<int>()
};
foreach (XElement tile in layer.Element("data").Elements("tile")) {
newLayer.data.Add(int.Parse(tile.Attribute("gid").Value));
}
layers.Add(newLayer);
}
switch (mapOrientation) {
case "isometric":
_mapType = MapType.Isometric;
break;
case "orthogonal":
_mapType = MapType.Orthogonal;
break;
default:
EditorUtility.DisplayDialog("Error!", "Map type \"" + mapOrientation + "\" is not supported yet!", "OK");
return;
}
}
void OnGUI() {
if (layers == null) Close();
// window drawing code
_windowScroll = GUILayout.BeginScrollView(_windowScroll);
GUILayout.BeginVertical();
GUILayout.Label("Map Settings", EditorStyles.boldLabel);
EditorGUILayout.LabelField("Orientation: ", (_mapType == MapType.Isometric) ? "isometric": "orthogonal");
EditorGUILayout.LabelField("Width: ", _width.ToString());
EditorGUILayout.LabelField("Height: ", _height.ToString());
EditorGUILayout.LabelField("Tile Width: ", _tileWidth.ToString());
EditorGUILayout.LabelField("Tile Height: ", _tileHeight.ToString());
_pixelsInUnits = EditorGUILayout.FloatField("Pixels To Units", _pixelsInUnits);
GUILayout.Space(GUI.skin.label.fontSize);
GUILayout.Label("Layer Settings", EditorStyles.boldLabel);
for (int i = 0; i < layers.Count; i++) {
GUILayout.Label("Layer " + (i + 1).ToString() + " Settings", EditorStyles.boldLabel);
EditorGUILayout.LabelField("Name: ", layers[i].name);
TileLayer newLayer = layers[i];
newLayer.layer = EditorGUILayout.LayerField("GameObject Layer", layers[i].layer);
newLayer.sortingLayer = EditorGUILayout.IntField("Sorting Layer", layers[i].sortingLayer);
layers[i] = newLayer;
}
// select tileset image here
GUILayout.Label("Tilesets", EditorStyles.boldLabel);
GUILayout.Label("Tileset 1 Settings", EditorStyles.boldLabel);
EditorGUILayout.LabelField("Name: ", _tileset.name);
EditorGUILayout.LabelField("Tile Offset: ", _tileset.offset.ToString());
EditorGUILayout.LabelField("Tile Width: ", _tileset.tileWidth.ToString());
EditorGUILayout.LabelField("Tile Height: ", _tileset.tileWidth.ToString());
EditorGUILayout.LabelField("Image Width: ", _tileset.imageWidth.ToString());
EditorGUILayout.LabelField("Image Height: ", _tileset.imageHeight.ToString());
GUILayout.Label("Image source:");
_tileset.image = EditorGUILayout.ObjectField(_tileset.image, typeof(Texture2D), false) as Texture2D;
// check if this texture size is same as tileset's source
if (_tileset.image) {
if (_tileset.image.width != _tileset.imageWidth || _tileset.image.height != _tileset.imageHeight) {
EditorUtility.DisplayDialog("Error!", "Source texture size must be the same as tileset source image's one!", "OK");
_tileset.image = null;
}
}
if (GUILayout.Button("OK")) {
if (_tileset.image && _pixelsInUnits > 0) {
this.Close();
InstantiateMap();
} else {
if (!_tileset.image)
EditorUtility.DisplayDialog("Error!", "Each tileset source image must be setup!", "OK");
if (_pixelsInUnits <= 0)
EditorUtility.DisplayDialog("Error!", "Pixels to units must be greater than zero!", "OK");
}
}
GUILayout.EndVertical();
GUILayout.EndScrollView();
}
void InstantiateMap() {
// setup tileset sprites
UnityEngine.Object[] spriteAssets = AssetDatabase.LoadAllAssetsAtPath(AssetDatabase.GetAssetPath(_tileset.image));
int spriteCount = spriteAssets.Length - 1; // one asset is texture by itself
// we can't use more sprites than we have in our asset!
spriteCount = Mathf.Max(spriteCount, (int)((_tileset.imageWidth / _tileset.tileWidth) * (_tileset.imageHeight / _tileset.tileHeight)));
for (int i = 1; i <= spriteCount; i++) {
_tileset.sprites.Add(spriteAssets[i] as Sprite);
}
// instantiate sprites here
float unitOffsetX = _tileWidth / _pixelsInUnits;
float unitOffsetY = _tileHeight / _pixelsInUnits;
GameObject map = new GameObject("tiled_map"); // parent for hierarchy
foreach (TileLayer l in layers) {
GameObject parent = new GameObject(l.name);
parent.transform.parent = map.transform;
for (int i = 0; i < _width; i++) {
for (int j = 0; j < _height; j++) {
int gid = (int)(i * _height + j);
if (l.data[gid] != 0) {
Sprite spr = GetSprite(l.data[gid]);
GameObject tile = new GameObject(l.name + "_" + i.ToString() + "_" + j.ToString(), new Type[] { typeof(SpriteRenderer) });
SpriteRenderer sr = tile.GetComponent<SpriteRenderer>();
sr.sprite = spr;
sr.sortingLayerID = l.sortingLayer;
sr.sortingOrder = j + i + i * j;
tile.layer = l.layer;
tile.transform.parent = parent.transform;
tile.transform.localPosition = new Vector3(j * unitOffsetX / 2 - i * unitOffsetX / 2, -i * unitOffsetY / 2 - j * unitOffsetY / 2);
}
}
}
}
}
Sprite GetSprite(int gid) {
return _tileset.sprites[gid - 1];
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment