static TMX.Object ApplyTemplate(TMX.Object mapObject, out ImportedTileset replacementTileset) { // Template path seems to be relative to the TMX string templatePath = Path.GetFullPath(Path.Combine(TiledTMXImporter.s_tmxParentFolder, mapObject.template)); ImportedTemplate importedTemplate; if (s_importedTemplates.ContainsKey(templatePath)) { importedTemplate = s_importedTemplates[templatePath]; } else { importedTemplate = TiledTXImporter.LoadTXFile(templatePath, TiledTMXImporter.s_tilesetDir, TiledTMXImporter.s_cellWidth, TiledTMXImporter.s_cellHeight, TiledTMXImporter.s_pixelsPerUnit); s_importedTemplates.Add(templatePath, importedTemplate); } if (!mapObject.gid.HasValue) { replacementTileset = importedTemplate.m_importedTileset; } else { replacementTileset = null; // If the instance has a gid, assume it uses the normal map tilesets } return(mapObject.GetVersionWithTemplateApplied(importedTemplate.m_template.templateObject)); }
public static void FindTileDataAndMatrix(uint gid, ImportedTileset[] importedTilesets, int cellWidth, int cellHeight, out ImportedTile importedTile, out TSX.Tile tilesetTile, out Matrix4x4 matrix) { importedTile = null; tilesetTile = null; matrix = Matrix4x4.identity; bool flippedHorizontally = (gid & FLIPPED_HORIZONTALLY_FLAG) != 0; bool flippedVertically = (gid & FLIPPED_VERTICALLY_FLAG) != 0; bool flippedDiagonally = (gid & FLIPPED_DIAGONALLY_FLAG) != 0; gid &= ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG); ImportedTileset tilesetContainingID = null; for (int j = importedTilesets.Length - 1; j >= 0; --j) { int firstGID = importedTilesets[j].firstGID; if (firstGID <= gid) { tilesetContainingID = importedTilesets[j]; break; } } if (tilesetContainingID != null) { int relativeID = (int)gid - tilesetContainingID.firstGID; if (tilesetContainingID.tileset.IsSingleImageTileset()) { // A single-image-tileset will just order tiles from 0-n importedTile = tilesetContainingID.tiles[relativeID]; tilesetTile = null; } else { for (int t = 0; t < tilesetContainingID.tileset.tiles.Length; t++) { TSX.Tile tile = tilesetContainingID.tileset.tiles[t]; int id = tile.id; if (id == relativeID) { importedTile = tilesetContainingID.tiles[t]; tilesetTile = tile; break; } } } if (importedTile != null) { if (importedTile.tile == null) // Load the tile asset if we haven't already { importedTile.tile = AssetDatabase.LoadAssetAtPath <Tile>(importedTile.path); } if (flippedHorizontally || flippedVertically || flippedDiagonally) { if (flippedDiagonally) { matrix = Matrix4x4.Rotate(Quaternion.Euler(0.0f, 0.0f, 90.0f)); matrix = Matrix4x4.Scale(new Vector3(1.0f, -1.0f, 1.0f)) * matrix; } if (flippedHorizontally) { matrix = Matrix4x4.Scale(new Vector3(-1.0f, 1.0f, 1.0f)) * matrix; } if (flippedVertically) { matrix = Matrix4x4.Scale(new Vector3(1.0f, -1.0f, 1.0f)) * matrix; } Rect rect = importedTile.tile.sprite.rect; rect.x = -cellWidth * 0.5f; rect.y = -cellHeight * 0.5f; Vector2[] corners = new Vector2[4] { new Vector2(rect.x, rect.y), new Vector2(rect.x + rect.width, rect.y), new Vector2(rect.x, rect.y + rect.height), new Vector2(rect.x + rect.width, rect.y + rect.height) }; for (int i = 0; i < corners.Length; i++) { corners[i] = matrix * corners[i]; } Vector2 bottomLeftCorner = corners[0]; for (int i = 1; i < corners.Length; i++) { if (corners[i].x < bottomLeftCorner.x) { bottomLeftCorner.x = corners[i].x; } if (corners[i].y < bottomLeftCorner.y) { bottomLeftCorner.y = corners[i].y; } } Vector2 offsetNeededUnits = new Vector2(-0.5f, -0.5f) - new Vector2(bottomLeftCorner.x / (float)cellWidth, bottomLeftCorner.y / (float)cellHeight); matrix = Matrix4x4.Translate(offsetNeededUnits) * matrix; } } } }
public ImportedTemplate(TXTypes.Template template, ImportedTileset tileset) { m_template = template; m_importedTileset = tileset; }
public static bool ImportTMXFile(string path, string tilesetDir, Grid targetGrid) { string tmxParentFolder = Path.GetDirectoryName(path); string filename = Path.GetFileNameWithoutExtension(path); TMX.Map map = ImportUtils.ReadXMLIntoObject <TMX.Map>(path); if (map == null) { return(false); } if (map.backgroundcolor != null) { Color backgroundColor; if (ColorUtility.TryParseHtmlString(map.backgroundcolor, out backgroundColor)) { Camera.main.backgroundColor = backgroundColor; } } if (map.tilesets != null) { // First we need to load (or import) all the tilesets referenced by the TMX file... int cellWidth = map.tilewidth; int cellHeight = map.tileheight; int pixelsPerUnit = Mathf.Max(map.tilewidth, map.tileheight); ImportedTileset[] importedTilesets = new ImportedTileset[map.tilesets.Length]; for (int i = 0; i < map.tilesets.Length; i++) { importedTilesets[i] = TiledTSXImporter.ImportFromTilesetReference(map.tilesets[i], tmxParentFolder, tilesetDir, cellWidth, cellHeight, pixelsPerUnit); if (importedTilesets[i] == null || importedTilesets[i].tiles == null || importedTilesets[i].tiles[0] == null) { Debug.LogError("Imported tileset is incomplete"); return(false); } } // Set up the Grid to store everything in GameObject newGrid = null; if (targetGrid != null) { newGrid = targetGrid.gameObject; for (int i = newGrid.transform.childCount - 1; i >= 0; --i) { Undo.DestroyObjectImmediate(newGrid.transform.GetChild(i).gameObject); } } else { newGrid = new GameObject(filename, typeof(Grid)); newGrid.GetComponent <Grid>().cellSize = new Vector3(1.0f, 1.0f, 0.0f); Undo.RegisterCreatedObjectUndo(newGrid, "Import map to new Grid"); } ITilemapImportOperation[] importOperations = ImportUtils.GetObjectsThatImplementInterface <ITilemapImportOperation>(); TilemapRenderer.SortOrder sortOrder = TilemapRenderer.SortOrder.TopLeft; if (map.renderorder.Equals("right-down")) { sortOrder = TilemapRenderer.SortOrder.TopLeft; } else if (map.renderorder.Equals("right-up")) { sortOrder = TilemapRenderer.SortOrder.BottomLeft; } else if (map.renderorder.Equals("left-down")) { sortOrder = TilemapRenderer.SortOrder.TopRight; } else if (map.renderorder.Equals("left-up")) { sortOrder = TilemapRenderer.SortOrder.BottomRight; } // Load all the tile layers if (map.layers != null) { for (int i = 0; i < map.layers.Length; i++) { TMX.Layer layer = map.layers[i]; bool success = ImportTileLayer(layer, newGrid, i, importedTilesets, importOperations, sortOrder, cellWidth, cellHeight, map.infinite); if (!success) { return(false); } } } Dictionary <string, ImportedTemplate> importedTemplates = new Dictionary <string, ImportedTemplate>(); // Load all the objects if (map.objectgroups != null) { for (int g = 0; g < map.objectgroups.Length; g++) { TMX.ObjectGroup objectGroup = map.objectgroups[g]; if (objectGroup != null && objectGroup.objects != null && objectGroup.objects.Length > 0) { GameObject newObjectLayer = new GameObject(objectGroup.name); newObjectLayer.transform.SetParent(newGrid.transform, false); Undo.RegisterCreatedObjectUndo(newObjectLayer, "Import object layer"); int sortingLayer = (map.layers == null ? 0 : map.layers.Length) + g; for (int i = 0; i < objectGroup.objects.Length; i++) { TMX.Object mapObject = objectGroup.objects[i]; bool success = ImportMapObject(mapObject, importedTilesets, importedTemplates, newObjectLayer, map.tilewidth, map.tileheight, sortingLayer, importOperations, tilesetDir, cellWidth, cellHeight, pixelsPerUnit, tmxParentFolder); if (!success) { return(false); } } Color objectColour = new Color(1.0f, 1.0f, 1.0f, 1.0f); if (objectGroup.color != null) { ColorUtility.TryParseHtmlString(objectGroup.color, out objectColour); } if (objectGroup.opacity != 1.0f) { objectColour.a = objectGroup.opacity; } SpriteRenderer[] renderers = newObjectLayer.GetComponentsInChildren <SpriteRenderer>(); foreach (SpriteRenderer r in renderers) { r.color = objectColour; } if (!objectGroup.visible) { foreach (SpriteRenderer r in renderers) { r.enabled = false; } } IDictionary <string, string> properties = (objectGroup.properties == null ? new Dictionary <string, string>() : objectGroup.properties.ToDictionary()); foreach (ITilemapImportOperation operation in importOperations) { operation.HandleCustomProperties(newObjectLayer, properties); } } } } } return(true); }
static bool ImportMapObject(TMX.Object mapObject, ImportedTileset[] importedTilesets, Dictionary <string, ImportedTemplate> importedTemplates, GameObject newObjectLayer, int mapTileWidth, int mapTileHeight, int sortingLayer, ITilemapImportOperation[] importOperations, string tilesetDir, int cellWidth, int cellHeight, int pixelsPerUnit, string tmxParentFolder) { ImportedTileset replacementTileset = null; if (mapObject.template != null) { // Fill out empty object fields with data from the template. // The template could have a tile from it's own tileset reference, so in that case, use the template tileset instead, and the template object GID TMX.Object combinedMapObject = ApplyTemplate(mapObject, tmxParentFolder, tilesetDir, cellWidth, cellHeight, pixelsPerUnit, importedTemplates, out replacementTileset); if (combinedMapObject == null) { Debug.LogError("Could not load template for map object " + mapObject); return(false); } else { mapObject = combinedMapObject; } } mapObject.InitialiseUnsetValues(); // We need special code to set defaults here, because setting them before merging with a template would give incorrect results. // Use the template's tileset (and the gid that's been set by ApplyTemplate) if (replacementTileset != null) { importedTilesets = new ImportedTileset[] { replacementTileset }; } ImportedTile importedTile; TSX.Tile tilesetTile; // Unused Matrix4x4 matrix; TiledUtils.FindTileDataAndMatrix(mapObject.gid.Value, importedTilesets, cellWidth, cellHeight, out importedTile, out tilesetTile, out matrix); Vector2 pixelsToUnits = new Vector2(1.0f / mapTileWidth, -1.0f / mapTileHeight); GameObject newObject = new GameObject(mapObject.name); newObject.transform.SetParent(newObjectLayer.transform, false); // So we gain the tile rotation/flipping newObject.transform.FromMatrix(matrix); Vector2 corner = Vector2.Scale(new Vector2(mapObject.x.Value, mapObject.y.Value), pixelsToUnits); if (importedTile != null) { Tile unityTile = importedTile.tile; Vector2 pivotProportion = new Vector2(unityTile.sprite.pivot.x / unityTile.sprite.rect.width, unityTile.sprite.pivot.y / unityTile.sprite.rect.height); Vector3 pivotWorldPosition = corner + Vector2.Scale(new Vector2(mapObject.width.Value * pivotProportion.x, mapObject.height.Value * -pivotProportion.y), pixelsToUnits); newObject.transform.localPosition += pivotWorldPosition; SpriteRenderer renderer = newObject.AddComponent <SpriteRenderer>(); renderer.sprite = unityTile.sprite; renderer.sortingOrder = sortingLayer; if (unityTile.colliderType == Tile.ColliderType.Sprite) { newObject.AddComponent <PolygonCollider2D>(); } else if (unityTile.colliderType == Tile.ColliderType.Grid) { newObject.AddComponent <BoxCollider2D>(); } Vector2 scale = new Vector2(mapObject.width.Value / unityTile.sprite.rect.width, mapObject.height.Value / unityTile.sprite.rect.height); newObject.transform.localScale = Vector3.Scale(newObject.transform.localScale, new Vector3(scale.x, scale.y, 1.0f)); } else { Vector3 pivotWorldPosition = corner + Vector2.Scale(new Vector2(mapObject.width.Value * 0.5f, mapObject.height.Value * 0.5f), pixelsToUnits); newObject.transform.localPosition += pivotWorldPosition; // If no tile used, must be a non-tile object of some sort (collision or text) if (mapObject.ellipse != null) { EllipseCollider2D collider = newObject.AddComponent <EllipseCollider2D>(); collider.RadiusX = (mapObject.width.Value * 0.5f) / mapTileWidth; collider.RadiusY = (mapObject.height.Value * 0.5f) / mapTileHeight; } else if (mapObject.polygon != null) { PolygonCollider2D collider = newObject.AddComponent <PolygonCollider2D>(); string points = mapObject.polygon.points; collider.points = ImportUtils.PointsFromString(points, pixelsToUnits); } else if (mapObject.polyline != null) { EdgeCollider2D collider = newObject.AddComponent <EdgeCollider2D>(); string points = mapObject.polyline.points; collider.points = ImportUtils.PointsFromString(points, pixelsToUnits); } else if (mapObject.text != null) { TextMesh textMesh = newObject.AddComponent <TextMesh>(); textMesh.text = mapObject.text.text; textMesh.anchor = TextAnchor.MiddleCenter; Color color = Color.white; if (mapObject.text.color != null) { ColorUtility.TryParseHtmlString(mapObject.text.color, out color); } textMesh.color = color; // Saving an OS font as an asset in unity (through script) is seemingly impossible right now in a platform independent way // So we'll skip fonts for now textMesh.fontSize = (int)mapObject.text.pixelsize; // Guess a good resolution for the font float targetWorldTextHeight = (float)mapObject.text.pixelsize / (float)mapTileHeight; textMesh.characterSize = targetWorldTextHeight * 10.0f / textMesh.fontSize; Renderer renderer = textMesh.gameObject.GetComponent <MeshRenderer>(); renderer.sortingOrder = sortingLayer; renderer.sortingLayerID = SortingLayer.GetLayerValueFromName("Default"); } else { // Regular box collision BoxCollider2D collider = newObject.AddComponent <BoxCollider2D>(); collider.size = new Vector2(mapObject.width.Value / mapTileWidth, mapObject.height.Value / mapTileHeight); } } if (mapObject.rotation != 0.0f) { newObject.transform.RotateAround(corner, new Vector3(0.0f, 0.0f, 1.0f), -mapObject.rotation.Value); } if (mapObject.visible == false) { Renderer renderer = newObject.GetComponent <Renderer>(); if (renderer != null) { renderer.enabled = false; } } IDictionary <string, string> properties = (mapObject.properties == null ? new Dictionary <string, string>() : mapObject.properties.ToDictionary()); foreach (ITilemapImportOperation operation in importOperations) { operation.HandleCustomProperties(newObject, properties); } return(true); }