using UnityEngine; using UnityEditor; using System; using System.IO; using System.Linq; using System.Xml.Linq; using System.Reflection; using System.Collections.Generic; namespace IsoTools.Tiled.Internal { class TiledMapPostprocessor : AssetPostprocessor { static void OnPostprocessAllAssets( string[] imported_assets, string[] deleted_assets, string[] moved_assets, string[] moved_from_asset_paths) { var tmx_assets = imported_assets .Where(p => Path.GetExtension(p).ToLower().Equals(".tmx")); foreach ( var tmx_asset in tmx_assets ) { TmxAssetProcess(tmx_asset); } } static void TmxAssetProcess(string tmx_asset) { var tile_map_data = LoadTiledMapFromTmxFile(tmx_asset); if ( tile_map_data != null ) { var new_asset_path = Path.ChangeExtension(tmx_asset, ".asset"); var new_asset = AssetDatabase.LoadAssetAtPath(new_asset_path); if ( !new_asset ) { new_asset = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(new_asset, new_asset_path); } new_asset.Data = tile_map_data; new_asset.Name = Path.GetFileNameWithoutExtension(new_asset_path); RemoveAllSubAsset(new_asset_path); EditorUtility.SetDirty(new_asset); AssetDatabase.SaveAssets(); } } static TiledMapData LoadTiledMapFromTmxFile(string tmx_path) { try { var tmx_root_elem = XDocument.Load(tmx_path).Document.Root; var tiled_map_data = new TiledMapData(); LoadTiledMapOptsFromTmxRootElem(tmx_root_elem, tiled_map_data); LoadTiledMapLayersFromTmxRootElem(tmx_root_elem, tiled_map_data); LoadTiledMapTilesetsFromTmxRootElem(tmx_root_elem, tiled_map_data); LoadTiledMapTilesetsTextures(tmx_path, tiled_map_data); return tiled_map_data; } catch ( Exception e ) { Debug.LogErrorFormat("Parsing TMX file error: {0}", e.Message); return null; } } static void LoadTiledMapOptsFromTmxRootElem(XElement root_elem, TiledMapData data) { data.Width = SafeLoadIntFromElemAttr(root_elem, "width" , data.Width); data.Height = SafeLoadIntFromElemAttr(root_elem, "height" , data.Height); data.TileWidth = SafeLoadIntFromElemAttr(root_elem, "tilewidth" , data.TileWidth); data.TileHeight = SafeLoadIntFromElemAttr(root_elem, "tileheight", data.TileHeight); var orientation_str = root_elem.Attribute("orientation").Value; switch ( orientation_str ) { case "isometric": data.Orientation = TiledMapOrientation.Isometric; break; default: throw new UnityException(string.Format( "unsupported orientation ({0})", orientation_str)); } SafeLoadPropertiesFromOwnerElem(root_elem, data.Properties); } // ----------------------------- // Layers // ----------------------------- static void LoadTiledMapLayersFromTmxRootElem(XElement root_elem, TiledMapData data) { foreach ( var layer_elem in root_elem.Elements("layer") ) { var layer = new TiledMapLayerData(); LoadTiledMapLayerFromTmxLayerElem(layer_elem, layer); data.Layers.Add(layer); } } static void LoadTiledMapLayerFromTmxLayerElem(XElement layer_elem, TiledMapLayerData layer) { layer.Name = SafeLoadStrFromElemAttr (layer_elem, "name" , layer.Name); layer.OffsetX = SafeLoadIntFromElemAttr (layer_elem, "offsetx", layer.OffsetX); layer.OffsetY = SafeLoadIntFromElemAttr (layer_elem, "offsety", layer.OffsetY); layer.Visible = SafeLoadBoolFromElemAttr(layer_elem, "visible", layer.Visible); LoadTiledMapLayerTilesFromTmxLayerElem(layer_elem, layer); SafeLoadPropertiesFromOwnerElem(layer_elem, layer.Properties); } static void LoadTiledMapLayerTilesFromTmxLayerElem(XElement layer_elem, TiledMapLayerData layer) { foreach ( var elem in layer_elem.Element("data").Elements("tile") ) { layer.Tiles.Add(SafeLoadIntFromElemAttr(elem, "gid", 0)); } } // ----------------------------- // Tilesets // ----------------------------- static void LoadTiledMapTilesetsFromTmxRootElem(XElement root_elem, TiledMapData data) { foreach ( var tileset_elem in root_elem.Elements("tileset") ) { var tileset = new TiledMapTilesetData(); LoadTiledMapTilesetFromTmxTilesetElem(tileset_elem, tileset); data.Tilesets.Add(tileset); } } static void LoadTiledMapTilesetFromTmxTilesetElem(XElement tileset_elem, TiledMapTilesetData tileset) { tileset.FirstGid = SafeLoadIntFromElemAttr(tileset_elem, "firstgid" , tileset.FirstGid); tileset.Name = SafeLoadStrFromElemAttr(tileset_elem, "name" , tileset.Name); tileset.Margin = SafeLoadIntFromElemAttr(tileset_elem, "margin" , tileset.Margin); tileset.Spacing = SafeLoadIntFromElemAttr(tileset_elem, "spacing" , tileset.Spacing); tileset.TileWidth = SafeLoadIntFromElemAttr(tileset_elem, "tilewidth" , tileset.TileWidth); tileset.TileHeight = SafeLoadIntFromElemAttr(tileset_elem, "tileheight", tileset.TileHeight); tileset.TileCount = SafeLoadIntFromElemAttr(tileset_elem, "tilecount" , tileset.TileCount); tileset.TileOffsetX = SafeLoadIntFromElemAttr(tileset_elem.Element("tileoffset"), "x" , tileset.TileOffsetX); tileset.TileOffsetY = SafeLoadIntFromElemAttr(tileset_elem.Element("tileoffset"), "y" , tileset.TileOffsetY); tileset.ImageSource = SafeLoadStrFromElemAttr(tileset_elem.Element("image" ), "source", tileset.ImageSource); SafeLoadPropertiesFromOwnerElem(tileset_elem, tileset.Properties); } // ----------------------------- // Textures // ----------------------------- static void LoadTiledMapTilesetsTextures(string tmx_path, TiledMapData data) { foreach ( var tileset in data.Tilesets ) { if ( !string.IsNullOrEmpty(tileset.ImageSource) ) { var base_path = Path.GetDirectoryName(tmx_path); var image_path = Path.Combine(base_path, tileset.ImageSource); var importer = AssetImporter.GetAtPath(image_path) as TextureImporter; if ( !importer ) { throw new UnityException(string.Format( "tileset ({0}) image importer not found ({1})", tileset.Name, image_path)); } var meta_data = new List(); var image_size = GetSizeFromTextureImporter(importer); for ( var i = image_size.y - tileset.TileHeight - tileset.Margin; i >= tileset.Margin; i -= tileset.TileHeight + tileset.Spacing ) { for ( var j = tileset.Margin; j <= image_size.x - tileset.Margin - tileset.TileWidth; j += tileset.TileWidth + tileset.Spacing ) { var meta_elem = new SpriteMetaData(); meta_elem.name = string.Format( "{0}_{1}", Path.GetFileNameWithoutExtension(image_path), meta_data.Count + tileset.FirstGid); meta_elem.rect = new Rect(j, i, tileset.TileWidth, tileset.TileHeight); meta_data.Add(meta_elem); } } importer.spritesheet = meta_data.ToArray(); importer.textureType = TextureImporterType.Sprite; importer.spriteImportMode = SpriteImportMode.Multiple; AssetDatabase.ImportAsset(image_path, ImportAssetOptions.ForceUpdate); } } } // ----------------------------- // Common // ----------------------------- static string SafeLoadStrFromElemAttr(XElement elem, string attr_name, string def_value) { if ( elem != null && elem.Attribute(attr_name) != null ) { return elem.Attribute(attr_name).Value; } return def_value; } static int SafeLoadIntFromElemAttr(XElement elem, string attr_name, int def_value) { int value; if ( elem != null && int.TryParse(SafeLoadStrFromElemAttr(elem, attr_name, ""), out value) ) { return value; } return def_value; } static bool SafeLoadBoolFromElemAttr(XElement elem, string attr_name, bool def_value) { int value; if ( elem != null && int.TryParse(SafeLoadStrFromElemAttr(elem, attr_name, ""), out value) ) { return value != 0; } return def_value; } static void SafeLoadPropertiesFromOwnerElem(XElement owner_elem, List props) { var props_elem = owner_elem != null ? owner_elem.Element("properties") : null; if ( props_elem != null ) { foreach ( var prop_elem in props_elem.Elements("property") ) { var prop_name = SafeLoadStrFromElemAttr(prop_elem, "name" , null); var prop_value = SafeLoadStrFromElemAttr(prop_elem, "value", null); if ( !string.IsNullOrEmpty(prop_name) && prop_value != null ) { props.Add(prop_name); props.Add(prop_value); } } } } static void RemoveAllSubAsset(string main_asset_path) { var subassets = AssetDatabase.LoadAllAssetsAtPath(main_asset_path); foreach ( var asset in subassets ) { if ( !AssetDatabase.IsMainAsset(asset) ) { GameObject.DestroyImmediate(asset, true); } } } static Vector2 GetSizeFromTextureImporter(TextureImporter importer) { var method_args = new object[2]{0,0}; typeof(TextureImporter) .GetMethod("GetWidthAndHeight", BindingFlags.NonPublic | BindingFlags.Instance) .Invoke(importer, method_args); return new Vector2((int)method_args[0], (int)method_args[1]); } } }