///<summary> ///Create and configure main Grid GameObject. ///</summary> static GameObject CreateGrid(AssetImportContext context, Map map) { var name = Path.GetFileNameWithoutExtension(context.assetPath); // Instantiate a prefab named after the tilemap name, if any. var prefab = PrefabHelper.Load(name, context, map.properties.Length > 0); GameObject gameObject; if (prefab) { gameObject = PrefabUtility.InstantiatePrefab(prefab) as GameObject; foreach (var component in gameObject.GetComponentsInChildren <MonoBehaviour>()) { Property.Apply(map.properties, component); } } else { gameObject = new GameObject(name); } var grid = gameObject.AddComponent <Grid>(); gameObject.isStatic = true; switch (map.orientation) { case "orthogonal": grid.cellLayout = CellLayout.Rectangle; break; case "isometric": grid.cellLayout = CellLayout.Isometric; break; case "hexagonal": grid.cellLayout = CellLayout.Hexagon; break; default: throw new NotImplementedException($"Orientation: {map.orientation}"); } // Grid cells are always 1 unit wide; their height respects the aspect ratio. grid.cellSize = new Vector3(1f, (float)map.tileheight / map.tilewidth, 1f); gameObject.AddComponent <Rigidbody2D>().bodyType = RigidbodyType2D.Static; gameObject.AddComponent <CompositeCollider2D>(); context.AddObjectToAsset("Grid", gameObject); context.SetMainObject(gameObject); return(gameObject); }
///<summary> ///Instantiate a prefab or create a GameObject for each layer. ///</summary> static GameObject[] CreateLayers(AssetImportContext context, Map map) { var animators = new Dictionary <uint, AnimatorController>(); var layerObjects = new GameObject[map.layers.Length]; // A layer can be either a tile layer (has chunks), or an object layer (no chunks). for (var i = 0; i < layerObjects.Length; ++i) { var layer = map.layers[i]; // Instantiate a prefab named after the layer name, if any. var prefab = PrefabHelper.Load(layer.name, context, layer.properties.Length > 0); if (prefab) { layerObjects[i] = PrefabUtility.InstantiatePrefab(prefab) as GameObject; foreach (var component in layerObjects[i].GetComponentsInChildren <MonoBehaviour>()) { Property.Apply(layer.properties, component); } } else { layerObjects[i] = new GameObject(layer.name); } if (layer.chunks.Length > 0) { CreateTileLayer(layerObjects[i], map, layer, order: i); } else { CreateObjectLayer(context, layerObjects[i], map, layer, order: i, animators); } layerObjects[i].isStatic = true; } return(layerObjects); }
///<summary>Construct a tilemap from Tiled, adding named prefab instances based on Type.</summary> public override void OnImportAsset(AssetImportContext context) { var assetPathPrefix = Path.GetDirectoryName(assetPath) + Path.DirectorySeparatorChar; // TODO: Support ../source.tsx var tmx = new TMX(XDocument.Load(assetPath).Element("map"), assetPathPrefix); // if (tmx.infinite) { //throw new NotImplementedException("Infinite: " + tmx.infinite); // } var layout = tmx.orientation == "orthogonal" ? GridLayout.CellLayout.Rectangle : tmx.orientation == "isometric" ? GridLayout.CellLayout.Isometric : GridLayout.CellLayout.Hexagon; var gids = new uint[tmx.layers.Length][][]; var gidSet = new HashSet <uint>(); // TODO: & 0x1fffffff the gidSet values from tile gids[] for (var i = 0; i < tmx.layers.Length; ++i) { var layer = tmx.layers[i]; if (layer.data.chunks != null) // Tile layer { gids[i] = new uint[layer.data.chunks.Length][]; for (var j = 0; j < gids[i].Length; ++j) { gids[i][j] = ParseGIDs( layer.data.encoding, layer.data.compression, layer.data.chunks[j].value, layer.width, layout ); gidSet.UnionWith(gids[i][j]); } } else // Object layer { foreach (var @object in layer.objects) { gidSet.Add(@object.gid & 0x1fffffff); } } } // Tilesets var tiles = new Tile[1]; // Global Tile IDs start from 1 foreach (var tsx in tmx.tilesets) { Tileset tileset = null; if (tsx.source == null) // Embedded tileset { tileset = TSXImporter.Load(context, tsx); } else // External tileset { var tilesetSource = "Assets" + Path.GetFullPath(assetPathPrefix + tsx.source).Substring(Application.dataPath.Length); tileset = AssetDatabase.LoadAssetAtPath <Tileset>(tilesetSource); context.DependsOnSourceAsset(tilesetSource); } var tilesetTiles = new Tile[tileset.tiles.Length]; for (var i = 0; i < tilesetTiles.Length; ++i) { if (!gidSet.Contains((uint)(i + tsx.firstgid))) { continue; } tilesetTiles[i] = tileset.tiles[i].Instantiate(tmx.tilewidth); context.AddObjectToAsset($"tile{i + tsx.firstgid:0000}", tilesetTiles[i]); context.AddObjectToAsset($"sprite{i + tsx.firstgid:0000}", tilesetTiles[i].sprite); for (var j = 0; j < tilesetTiles[i].sprites.Length; ++j) { context.AddObjectToAsset($"frame{i + tsx.firstgid:0000}:{j}", tilesetTiles[i].sprites[j]); } } ArrayUtility.AddRange(ref tiles, tilesetTiles); } // Grid var grid = new GameObject(Path.GetFileNameWithoutExtension(assetPath), typeof(Grid)); grid.AddComponent <Rigidbody2D>().bodyType = RigidbodyType2D.Static; grid.isStatic = true; grid.GetComponent <Grid>().cellLayout = layout; grid.GetComponent <Grid>().cellSize = new Vector3(1f, (float)tmx.tileheight / tmx.tilewidth, 1f); context.AddObjectToAsset("grid", grid); context.SetMainObject(grid); var collider = grid.AddComponent <CompositeCollider2D>(); collider.generationType = CompositeCollider2D.GenerationType.Manual; // Layers PrefabHelper.cache.Clear(); for (var i = 0; i < tmx.layers.Length; ++i) { var layer = tmx.layers[i]; var layerObject = new GameObject(layer.name); layerObject.transform.parent = grid.transform; layerObject.isStatic = true; // Tile layer if (layer.data.chunks != null) { // Tilemap var tilemap = layerObject.AddComponent <Tilemap>(); var renderer = layerObject.AddComponent <TilemapRenderer>(); layerObject.AddComponent <TilemapCollider2D>().usedByComposite = true; tilemap.color = new Color(1f, 1f, 1f, layer.opacity); tilemap.orientation = layout == GridLayout.CellLayout.Hexagon ? Tilemap.Orientation.Custom : Tilemap.Orientation.XY; tilemap.orientationMatrix = Matrix4x4.TRS( Vector3.zero, Quaternion.Euler(0f, 180f, 180f), Vector3.one ); tilemap.transform.localScale = layout == GridLayout.CellLayout.Hexagon ? new Vector3(1f, -1f, 1f) : Vector3.one; // Chunks for (var j = 0; j < layer.data.chunks.Length; ++j) { var layerTiles = new Tile[gids[i][j].Length]; for (var k = 0; k < layerTiles.Length; ++k) { layerTiles[k] = tiles[gids[i][j][k] & 0x1ffffff]; // 3 MSB are for flipping } var size = new Vector3Int(layer.data.chunks[j].width, layer.data.chunks[j].height, 1); var bounds = new BoundsInt( new Vector3Int( layer.width - layer.data.chunks[j].width + layer.data.chunks[j].x, layer.height - layer.data.chunks[j].height - layer.data.chunks[j].y, 0 ), size); tilemap.SetTilesBlock(bounds, layerTiles); // Flipped tiles for (var k = 0; k < gids[i][j].Length; ++k) { var diagonal = (gids[i][j][k] >> 29) & 1; var vertical = (gids[i][j][k] >> 30) & 1; var horizontal = (gids[i][j][k] >> 31) & 1; var flips = new Vector4(diagonal, vertical, horizontal, 0); if (flips.sqrMagnitude > 0f) { var position = new Vector3Int( layer.width - layer.data.chunks[j].width + k % layer.data.chunks[j].width, layer.height - layer.data.chunks[j].height + k / layer.data.chunks[j].width, 0 ); var transform = Matrix4x4.TRS( Vector3.zero, Quaternion.AngleAxis(diagonal * 180, new Vector3(1, 1, 0)), Vector3.one - (diagonal == 1 ? new Vector3(flips.y, flips.z, flips.w) : new Vector3(flips.z, flips.y, flips.w)) * 2 ); tilemap.SetTransformMatrix(position, transform); } } } renderer.sortOrder = layout == GridLayout.CellLayout.Hexagon ? TilemapRenderer.SortOrder.BottomLeft : TilemapRenderer.SortOrder.TopLeft; renderer.sortingOrder = i; // Object layer } else { var objects = layer.objects; //layer.Elements("object").ToArray(); foreach (var @object in objects) { var tile = tiles[@object.gid & 0x1fffffff]; string icon = null; GameObject gameObject; // Doubles as prefab when object has type // Default instantiation when object has no set type if (string.IsNullOrEmpty(@object.type)) { gameObject = new GameObject($"{@object.name} {@object.id}".Trim()); icon = "sv_label_0"; // Warn instantiation when object has type but no prefab was found } else if (null == (gameObject = PrefabHelper.Load(@object.type, context))) { var gameObjectName = string.IsNullOrEmpty(@object.name) ? @object.type : @object.name; gameObject = new GameObject($"{gameObjectName} {@object.id}".Trim()); icon = "sv_label_6"; // Prefab instantiation based on object type } else { gameObject = PrefabUtility.InstantiatePrefab(gameObject) as GameObject; gameObject.name = $"{@object.name} {@object.id}".Trim(); foreach (var component in gameObject.GetComponentsInChildren <MonoBehaviour>()) { ApplyProperties(@object.properties, component); } } gameObject.transform.parent = layerObject.transform; // Object sprite var sprite = tile?.sprite; if (sprite) { var diagonal = ((@object.gid >> 29) & 1) == 1 ? true : false; var vertical = ((@object.gid >> 30) & 1) == 1 ? true : false; var horizontal = ((@object.gid >> 31) & 1) == 1 ? true : false; gameObject.transform.localRotation *= Quaternion.Euler(0f, 0f, [email protected]); gameObject.transform.localRotation *= Quaternion.Euler(vertical ? 180f : 0f, horizontal ? 180f : 0f, 0f); gameObject.transform.localPosition = new Vector3(@object.x / tmx.tilewidth, [email protected] / tmx.tileheight + tmx.height, 0); gameObject.transform.localPosition += horizontal ? -gameObject.transform.right * @object.width / tmx.tilewidth : Vector3.zero; gameObject.transform.localPosition += vertical ? -gameObject.transform.up * @object.height / tmx.tileheight : Vector3.zero; var renderer = new GameObject("Renderer").AddComponent <SpriteRenderer>(); renderer.transform.SetParent(gameObject.transform, worldPositionStays: false); renderer.sprite = sprite; renderer.sortingOrder = i; renderer.spriteSortPoint = SpriteSortPoint.Pivot; renderer.drawMode = SpriteDrawMode.Sliced; // HACK: Makes renderer.size work renderer.size = new Vector2(@object.width, @object.height) / tmx.tilewidth; renderer.color = new Color(1f, 1f, 1f, layer.opacity); renderer.transform.localPosition = new Vector3(@object.width / tmx.tilewidth / 2f, @object.height / tmx.tileheight / 2f); } else { gameObject.transform.localPosition = new Vector3(@object.x / tmx.tileheight, [email protected] / tmx.tileheight + tmx.height - @object.height / tmx.tileheight, 0); } // Icon if (icon != null) { InternalEditorGUIHelper.SetIconForObject( gameObject, EditorGUIUtility.IconContent(icon).image ); } } } } collider.generationType = CompositeCollider2D.GenerationType.Synchronous; }
///<summary> ///Load prefab based on object or tile type, if any, or create a GameObject otherwise. ///</summary> static GameObject CreateObject( AssetImportContext context, TMX.Layer.Object @object, Tile tile ) { var tileName = tile?.prefab ? tile.prefab.name : string.Empty; var name = $"{@object.name ?? @object.type ?? tileName}"; name = $"{name} {@object.id}".Trim(); GameObject gameObject = null; var properties = Property.Merge(@object.properties, tile?.properties); var icon = string.Empty; if (!string.IsNullOrEmpty(@object.type)) { // Instantiate prefab based on object type. var prefab = PrefabHelper.Load(@object.type, context); if (prefab) { gameObject = PrefabUtility.InstantiatePrefab(prefab) as GameObject; } } else if (tile?.prefab) { // Instantiate GameObject based on tile type. gameObject = PrefabUtility.InstantiatePrefab(tile.prefab) as GameObject; } else { // Default instantiation when object and tile have no set type gameObject = new GameObject(name); icon = "sv_label_0"; } if (gameObject != null) { // Apply properties to all behaviours. gameObject.name = name; var components = gameObject.GetComponentsInChildren <MonoBehaviour>(); foreach (var component in components) { Property.Apply(properties, component); } } else { // Warn on instantiation when object has type but no prefab was found. gameObject = new GameObject(name); icon = "sv_label_6"; } // Apply icon to GameObject, if any. if (!string.IsNullOrEmpty(icon)) { InternalEditorGUIHelper.SetIconForObject( gameObject, EditorGUIUtility.IconContent(icon).image ); } return(gameObject); }