Skip to content

Instantly share code, notes, and snippets.

@zaneclaes
Created March 20, 2016 19:26
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save zaneclaes/a187ac4af6298b0a48f9 to your computer and use it in GitHub Desktop.
Save zaneclaes/a187ac4af6298b0a48f9 to your computer and use it in GitHub Desktop.
Tiled Map Spawner
using UnityEngine;
using UnityEngine.Rendering;
using UnityEditor;
using IsoTools.Tiled;
using IsoTools.Internal;
using System;
using System.IO;
using System.Linq;
using System.Collections.Generic;
namespace IsoTools.Tiled {
class MapSpawner : MonoBehaviour {
public List<GameObject> tileTemplates = null;
public TiledMapAsset tiledMap = null;
public Vector2 spritePivotPoint = new Vector2(0.5f, 0);
// Map gid -> sprite and template:
private Dictionary<int, Sprite> spriteMap = new Dictionary<int, Sprite>();
private Dictionary<int, GameObject> templateMap = new Dictionary<int, GameObject>();
void Awake() {
var assetPath = AssetDatabase.GetAssetPath(tiledMap);
if (string.IsNullOrEmpty(assetPath)) {
throw new UnityException(string.Format("not found tiled map asset ({0}) path", tiledMap.name));
}
// Pre-load all the sprite objects for later usage...
for (int i = 0, e = tiledMap.Data.Tilesets.Count; i < e; ++i) {
TiledMapTilesetData tileset = tiledMap.Data.Tilesets [i];
var path = Path.Combine(Path.GetDirectoryName(assetPath), tileset.ImageSource);
// Force alignment of sprites...
TextureImporter textureImporter = AssetImporter.GetAtPath(path) as TextureImporter;
textureImporter.isReadable = true;
List<SpriteMetaData> newData = new List<SpriteMetaData>();
for (int k = 0; k < textureImporter.spritesheet.Length; k++) {
SpriteMetaData d = textureImporter.spritesheet [k];
d.alignment = (int)SpriteAlignment.Custom;
d.pivot = spritePivotPoint;
newData.Add(d);
}
textureImporter.spritesheet = newData.ToArray();
AssetDatabase.ImportAsset(path, ImportAssetOptions.ForceUpdate);
var assets = AssetDatabase.LoadAllAssetsAtPath(path);
var fn = Path.GetFileNameWithoutExtension(tileset.ImageSource);
// Load the sprite into the spriteMap:
for (int j = 0; j < tileset.TileCount; j++) {
int gid = tileset.FirstGid + j;
var name = string.Format("{0}_{1}", fn, gid);
spriteMap [gid] = assets.Where(p => p is Sprite && p.name == name).Select(p => p as Sprite).FirstOrDefault();
if (!spriteMap [gid]) {
throw new UnityException(string.Format("sprite ({0}) for tile ({1}) not found", name, gid));
}
templateMap [gid] = tileTemplates [0];
}
// Load the template into the templateMap:
foreach (TiledMapTileData tileData in tileset.Tiles) {
TiledMapProperties props = new TiledMapProperties(tileData.Properties);
String templateName = props.GetAsString("template");
foreach (GameObject template in tileTemplates) {
if (template.name == templateName) {
templateMap [tileData.Gid] = template;
}
}
}
}
// Spawn each layer in the map...
foreach (TiledMapLayerData layer in tiledMap.Data.Layers) {
SpawnLayer(tiledMap.Data, layer);
}
// Remove the spawner from the hierarichy.
Destroy(this);
}
/**
* Create all the tiles in a given layer
*/
private void SpawnLayer(TiledMapData mapData, TiledMapLayerData layer) {
TiledMapProperties props = new TiledMapProperties(layer.Properties);
// The following code assumes mapData.renderorder = right-down
float z = props.GetAsFloat("z");
for (int i = 0; i < layer.Tiles.Count; i++) {
int gid = layer.Tiles [i];
if (gid == 0) {
continue;
}
Vector3 pos = new Vector3(mapData.Height - i / mapData.Height - 1, mapData.Width - i % mapData.Width - 1, z);
GameObject template = templateMap [gid] as GameObject;
GameObject container = Instantiate(template);
container.name = template.name + "_" + pos.x + "x" + pos.y + "x" + pos.z;
SpriteRenderer spriteRenderer = container.GetComponent<SpriteRenderer>();
spriteRenderer.sprite = spriteMap [gid];
IsoObject isoObject = container.GetComponent<IsoObject>();
isoObject.position = pos;
// Correct the position of any rigidBody children:
IsoObject[] children = isoObject.GetComponentsInChildren<IsoObject>();
foreach (IsoObject child in children) {
child.position = pos;
}
}
}
}
}
using UnityEngine;
using System.Collections.Generic;
namespace IsoTools.Tiled {
public enum TiledMapOrientation {
Isometric
}
[System.Serializable]
public class TiledMapTileData {
public int Gid = 0;
public List<string> Properties = new List<string>();
}
[System.Serializable]
public class TiledMapLayerData {
public string Name = "";
public int OffsetX = 0;
public int OffsetY = 0;
public bool Visible = true;
public List<int> Tiles = new List<int>();
public List<string> Properties = new List<string>();
}
[System.Serializable]
public class TiledMapTilesetData {
public int FirstGid = 0;
public string Name = "";
public int Margin = 0;
public int Spacing = 0;
public int TileWidth = 0;
public int TileHeight = 0;
public int TileCount = 0;
public int TileOffsetX = 0;
public int TileOffsetY = 0;
public string ImageSource = "";
public List<string> Properties = new List<string>();
public List<TiledMapTileData> Tiles = new List<TiledMapTileData>();
}
[System.Serializable]
public class TiledMapData {
public int Width = 0;
public int Height = 0;
public int TileWidth = 0;
public int TileHeight = 0;
public TiledMapOrientation Orientation = TiledMapOrientation.Isometric;
public List<string> Properties = new List<string>();
public List<TiledMapLayerData> Layers = new List<TiledMapLayerData>();
public List<TiledMapTilesetData> Tilesets = new List<TiledMapTilesetData>();
}
public class TiledMapAsset : ScriptableObject {
[HideInInspector]
public TiledMapData Data = new TiledMapData();
public string Name = string.Empty;
public float PixelsPerUnit = 100.0f;
}
}
using UnityEngine;
using UnityEditor;
using IsoTools.Tiled;
using System;
using System.IO;
using System.Linq;
using System.Xml;
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<TiledMapAsset>(new_asset_path);
if (!new_asset) {
new_asset = ScriptableObject.CreateInstance<TiledMapAsset>();
AssetDatabase.CreateAsset(new_asset, new_asset_path);
}
new_asset.Data = tile_map_data;
new_asset.Name = Path.GetFileNameWithoutExtension(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);
foreach (var tile_elem in tileset_elem.Elements("tile")) {
var tile = new TiledMapTileData();
tile.Gid = tileset.FirstGid + SafeLoadIntFromElemAttr(tile_elem, "id", tileset.FirstGid + tile.Gid);
SafeLoadPropertiesFromOwnerElem(tile_elem, tile.Properties);
tileset.Tiles.Add(tile);
}
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 method_args = new object[2]{ 0, 0 };
typeof(TextureImporter)
.GetMethod("GetWidthAndHeight", BindingFlags.NonPublic | BindingFlags.Instance)
.Invoke(importer, method_args);
var image_width = (int)method_args [0];
var image_height = (int)method_args [1];
var meta_data = new List<SpriteMetaData>();
for (var i = image_height - tileset.TileHeight - tileset.Margin; i >= tileset.Margin; i -= tileset.TileHeight + tileset.Spacing) {
for (var j = tileset.Margin; j <= image_width - 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;
TextureImporterSettings texSettings = new TextureImporterSettings();
importer.ReadTextureSettings(texSettings);
texSettings.spriteAlignment = (int)SpriteAlignment.BottomCenter;
importer.SetTextureSettings(texSettings);
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<string> 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);
}
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment