Last active
April 2, 2020 11:24
-
-
Save ZeronSix/7772309 to your computer and use it in GitHub Desktop.
Simple Unity3D TMX parser.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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