diff --git a/src/TiledCS.csproj b/src/TiledCS.csproj index bf71f54..ee7f82a 100644 --- a/src/TiledCS.csproj +++ b/src/TiledCS.csproj @@ -1,12 +1,12 @@ - 2.5.0 + 3.0.0 Ruben Labruyere Ruben Labruyere TiledCS - TiledCS is a library for loading Tiled maps and tilesets. This version supports Tiled 1.5.0 + TiledCS is a library for loading Tiled maps and tilesets. This version supports Tiled 1.8.4 https://github.com/thebonejarmer/tiledcs.git tiled,tmx,tsx diff --git a/src/TiledMap.cs b/src/TiledMap.cs index 6c001ad..b06bea8 100755 --- a/src/TiledMap.cs +++ b/src/TiledMap.cs @@ -26,60 +26,82 @@ public class TiledMap /// Returns the Tiled version used to create this map /// public string TiledVersion { get; set; } + /// /// Returns an array of properties defined in the map /// public TiledProperty[] Properties { get; set; } + /// /// Returns an array of tileset definitions in the map /// public TiledMapTileset[] Tilesets { get; set; } + /// /// Returns an array of layers or null if none were defined /// public TiledLayer[] Layers { get; set; } + /// /// Returns an array of groups or null if none were defined /// public TiledGroup[] Groups { get; set; } + /// /// Returns the defined map orientation as a string /// public string Orientation { get; set; } + /// /// Returns the render order as a string /// public string RenderOrder { get; set; } + /// /// The amount of horizontal tiles /// public int Width { get; set; } + /// /// The amount of vertical tiles /// public int Height { get; set; } + /// /// The tile width in pixels /// public int TileWidth { get; set; } + /// /// The tile height in pixels /// public int TileHeight { get; set; } + + /// + /// The parallax origin x + /// + public float ParallaxOriginX { get; set; } + + /// + /// The parallax origin y + /// + public float ParallaxOriginY { get; set; } + /// /// Returns true if the map is configured as infinite /// public bool Infinite { get; set; } + /// /// Returns the defined map background color as a hex string /// public string BackgroundColor { get; set; } + /// /// Returns an empty instance of TiledMap /// public TiledMap() { - } /// @@ -94,7 +116,7 @@ public TiledMap(string path) { throw new TiledException($"{path} not found"); } - + var content = File.ReadAllText(path); if (path.EndsWith(".tmx")) @@ -127,6 +149,8 @@ public void ParseXml(string xml) var nodesObjectGroup = nodeMap.SelectNodes("objectgroup"); var nodesTileset = nodeMap.SelectNodes("tileset"); var nodesGroup = nodeMap.SelectNodes("group"); + var attrParallaxOriginX = nodeMap.Attributes["parallaxoriginx"]; + var attrParallaxOriginY = nodeMap.Attributes["parallaxoriginy"]; this.TiledVersion = nodeMap.Attributes["tiledversion"].Value; this.Orientation = nodeMap.Attributes["orientation"].Value; @@ -143,6 +167,8 @@ public void ParseXml(string xml) if (nodesTileset != null) Tilesets = ParseTilesets(nodesTileset); if (nodesLayer != null) Layers = ParseLayers(nodesLayer, nodesObjectGroup, nodesImageLayer); if (nodesGroup != null) Groups = ParseGroups(nodesGroup); + if (attrParallaxOriginX != null) ParallaxOriginX = float.Parse(attrParallaxOriginX.Value, CultureInfo.InvariantCulture); + if (attrParallaxOriginY != null) ParallaxOriginY = float.Parse(attrParallaxOriginY.Value, CultureInfo.InvariantCulture); } catch (Exception ex) { @@ -205,18 +231,19 @@ private TiledGroup[] ParseGroups(XmlNodeList nodeListGroups) var tiledGroup = new TiledGroup(); tiledGroup.id = int.Parse(node.Attributes["id"].Value); tiledGroup.name = node.Attributes["name"].Value; - + if (attrVisible != null) tiledGroup.visible = attrVisible.Value == "1"; if (attrLocked != null) tiledGroup.locked = attrLocked.Value == "1"; if (nodesProperty != null) tiledGroup.properties = ParseProperties(nodesProperty); if (nodesGroup != null) tiledGroup.groups = ParseGroups(nodesGroup); if (nodesLayer != null) tiledGroup.layers = ParseLayers(nodesLayer, nodesObjectGroup, nodesImageLayer); - + result.Add(tiledGroup); } - + return result.ToArray(); } + private TiledLayer[] ParseLayers(XmlNodeList nodesLayer, XmlNodeList nodesObjectGroup, XmlNodeList nodesImageLayer) { var result = new List(); @@ -231,6 +258,8 @@ private TiledLayer[] ParseLayers(XmlNodeList nodesLayer, XmlNodeList nodesObject var attrTint = node.Attributes["tintcolor"]; var attrOffsetX = node.Attributes["offsetx"]; var attrOffsetY = node.Attributes["offsety"]; + var attrParallaxX = node.Attributes["parallaxx"]; + var attrParallaxY = node.Attributes["parallaxy"]; var tiledLayer = new TiledLayer(); tiledLayer.id = int.Parse(node.Attributes["id"].Value); @@ -243,8 +272,10 @@ private TiledLayer[] ParseLayers(XmlNodeList nodesLayer, XmlNodeList nodesObject if (attrVisible != null) tiledLayer.visible = attrVisible.Value == "1"; if (attrLocked != null) tiledLayer.locked = attrLocked.Value == "1"; if (attrTint != null) tiledLayer.tintcolor = attrTint.Value; - if (attrOffsetX != null) tiledLayer.offsetX = int.Parse(attrOffsetX.Value); - if (attrOffsetY != null) tiledLayer.offsetY = int.Parse(attrOffsetY.Value); + if (attrOffsetX != null) tiledLayer.offsetX = float.Parse(attrOffsetX.Value); + if (attrOffsetY != null) tiledLayer.offsetY = float.Parse(attrOffsetY.Value); + if (attrParallaxX != null) tiledLayer.offsetX = float.Parse(attrParallaxX.Value); + if (attrParallaxY != null) tiledLayer.offsetY = float.Parse(attrParallaxY.Value); if (nodesProperty != null) tiledLayer.properties = ParseProperties(nodesProperty); if (encoding == "csv") @@ -261,10 +292,11 @@ private TiledLayer[] ParseLayers(XmlNodeList nodesLayer, XmlNodeList nodesObject var hor = ((rawID & FLIPPED_HORIZONTALLY_FLAG)); var ver = ((rawID & FLIPPED_VERTICALLY_FLAG)); var dia = ((rawID & FLIPPED_DIAGONALLY_FLAG)); - tiledLayer.dataRotationFlags[i] = (byte)((hor | ver | dia) >> SHIFT_FLIP_FLAG_TO_BYTE); + tiledLayer.dataRotationFlags[i] = (byte) ((hor | ver | dia) >> SHIFT_FLIP_FLAG_TO_BYTE); // assign data to rawID with the rotation flags cleared - tiledLayer.data[i] = (int)(rawID & ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG)); + tiledLayer.data[i] = (int) (rawID & ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | + FLIPPED_DIAGONALLY_FLAG)); } } else if (encoding == "base64") @@ -287,10 +319,12 @@ private TiledLayer[] ParseLayers(XmlNodeList nodesLayer, XmlNodeList nodesObject var hor = ((rawID & FLIPPED_HORIZONTALLY_FLAG)); var ver = ((rawID & FLIPPED_VERTICALLY_FLAG)); var dia = ((rawID & FLIPPED_DIAGONALLY_FLAG)); - tiledLayer.dataRotationFlags[i] = (byte)((hor | ver | dia) >> SHIFT_FLIP_FLAG_TO_BYTE); + tiledLayer.dataRotationFlags[i] = (byte) ((hor | ver | dia) >> SHIFT_FLIP_FLAG_TO_BYTE); // assign data to rawID with the rotation flags cleared - tiledLayer.data[i] = (int)(rawID & ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG)); + tiledLayer.data[i] = (int) (rawID & ~(FLIPPED_HORIZONTALLY_FLAG | + FLIPPED_VERTICALLY_FLAG | + FLIPPED_DIAGONALLY_FLAG)); } } else if (compression == "zlib") @@ -300,24 +334,28 @@ private TiledLayer[] ParseLayers(XmlNodeList nodesLayer, XmlNodeList nodesObject // Should an external library be used instead of this hack? base64DataStream.ReadByte(); base64DataStream.ReadByte(); - - using (var decompressionStream = new DeflateStream(base64DataStream, CompressionMode.Decompress)) + + using (var decompressionStream = + new DeflateStream(base64DataStream, CompressionMode.Decompress)) { // Parse the raw decompressed bytes and update the inner data as well as the data rotation flags var decompressedDataBuffer = new byte[4]; // size of each tile var dataRotationFlagsList = new List(); var layerDataList = new List(); - - while (decompressionStream.Read(decompressedDataBuffer, 0, decompressedDataBuffer.Length) == decompressedDataBuffer.Length) + + while (decompressionStream.Read(decompressedDataBuffer, 0, + decompressedDataBuffer.Length) == decompressedDataBuffer.Length) { var rawID = BitConverter.ToUInt32(decompressedDataBuffer, 0); var hor = ((rawID & FLIPPED_HORIZONTALLY_FLAG)); var ver = ((rawID & FLIPPED_VERTICALLY_FLAG)); var dia = ((rawID & FLIPPED_DIAGONALLY_FLAG)); - dataRotationFlagsList.Add((byte)((hor | ver | dia) >> SHIFT_FLIP_FLAG_TO_BYTE)); + dataRotationFlagsList.Add((byte) ((hor | ver | dia) >> SHIFT_FLIP_FLAG_TO_BYTE)); // assign data to rawID with the rotation flags cleared - layerDataList.Add((int)(rawID & ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG))); + layerDataList.Add((int) (rawID & ~(FLIPPED_HORIZONTALLY_FLAG | + FLIPPED_VERTICALLY_FLAG | + FLIPPED_DIAGONALLY_FLAG))); } tiledLayer.data = layerDataList.ToArray(); @@ -326,24 +364,28 @@ private TiledLayer[] ParseLayers(XmlNodeList nodesLayer, XmlNodeList nodesObject } else if (compression == "gzip") { - using (var decompressionStream = new GZipStream(base64DataStream, CompressionMode.Decompress)) + using (var decompressionStream = + new GZipStream(base64DataStream, CompressionMode.Decompress)) { // Parse the raw decompressed bytes and update the inner data as well as the data rotation flags var decompressedDataBuffer = new byte[4]; // size of each tile var dataRotationFlagsList = new List(); var layerDataList = new List(); - - while (decompressionStream.Read(decompressedDataBuffer, 0, decompressedDataBuffer.Length) == decompressedDataBuffer.Length) + + while (decompressionStream.Read(decompressedDataBuffer, 0, + decompressedDataBuffer.Length) == decompressedDataBuffer.Length) { var rawID = BitConverter.ToUInt32(decompressedDataBuffer, 0); var hor = ((rawID & FLIPPED_HORIZONTALLY_FLAG)); var ver = ((rawID & FLIPPED_VERTICALLY_FLAG)); var dia = ((rawID & FLIPPED_DIAGONALLY_FLAG)); - - dataRotationFlagsList.Add((byte)((hor | ver | dia) >> SHIFT_FLIP_FLAG_TO_BYTE)); + + dataRotationFlagsList.Add((byte) ((hor | ver | dia) >> SHIFT_FLIP_FLAG_TO_BYTE)); // assign data to rawID with the rotation flags cleared - layerDataList.Add((int)(rawID & ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG))); + layerDataList.Add((int) (rawID & ~(FLIPPED_HORIZONTALLY_FLAG | + FLIPPED_VERTICALLY_FLAG | + FLIPPED_DIAGONALLY_FLAG))); } tiledLayer.data = layerDataList.ToArray(); @@ -400,13 +442,13 @@ private TiledLayer[] ParseLayers(XmlNodeList nodesLayer, XmlNodeList nodesObject var attrTint = node.Attributes["tintcolor"]; var attrOffsetX = node.Attributes["offsetx"]; var attrOffsetY = node.Attributes["offsety"]; - + var tiledLayer = new TiledLayer(); tiledLayer.id = int.Parse(node.Attributes["id"].Value); tiledLayer.name = node.Attributes["name"].Value; tiledLayer.type = "imagelayer"; tiledLayer.visible = true; - + if (attrVisible != null) tiledLayer.visible = attrVisible.Value == "1"; if (attrLocked != null) tiledLayer.locked = attrLocked.Value == "1"; if (attrTint != null) tiledLayer.tintcolor = attrTint.Value; @@ -414,13 +456,13 @@ private TiledLayer[] ParseLayers(XmlNodeList nodesLayer, XmlNodeList nodesObject if (attrOffsetY != null) tiledLayer.offsetY = int.Parse(attrOffsetY.Value); if (nodesProperty != null) tiledLayer.properties = ParseProperties(nodesProperty); if (nodeImage != null) tiledLayer.image = ParseImage(nodeImage); - + result.Add(tiledLayer); } return result.ToArray(); } - + private TiledImage ParseImage(XmlNode node) { var tiledImage = new TiledImage(); @@ -465,8 +507,10 @@ private TiledObject[] ParseObjects(XmlNodeList nodeList) for (var i = 0; i < vertices.Length; i++) { - polygon.points[(i * 2) + 0] = float.Parse(vertices[i].Split(',')[0], CultureInfo.InvariantCulture); - polygon.points[(i * 2) + 1] = float.Parse(vertices[i].Split(',')[1], CultureInfo.InvariantCulture); + polygon.points[(i * 2) + 0] = + float.Parse(vertices[i].Split(',')[0], CultureInfo.InvariantCulture); + polygon.points[(i * 2) + 1] = + float.Parse(vertices[i].Split(',')[1], CultureInfo.InvariantCulture); } obj.polygon = polygon; @@ -536,6 +580,7 @@ public TiledMapTileset GetTiledMapTileset(int gid) return new TiledMapTileset(); } + /// /// Loads external tilesets and matches them to firstGids from elements within the Tilesets array /// @@ -564,6 +609,7 @@ public Dictionary GetTiledTilesets(string src) return tilesets; } + /// /// Locates a specific TiledTile object /// @@ -584,6 +630,7 @@ public TiledTile GetTiledTile(TiledMapTileset mapTileset, TiledTileset tileset, return null; } + /// /// This method can be used to figure out the x and y position on a Tileset image for rendering tiles. /// @@ -592,7 +639,8 @@ public TiledTile GetTiledTile(TiledMapTileset mapTileset, TiledTileset tileset, /// An element within a TiledLayer.data array /// An int array of length 2 containing the x and y position of the source rect of the tileset image. Multiply the values by the tile width and height in pixels to get the actual x and y position. Returns null if the gid was not found /// This method currently doesn't take margin into account - [Obsolete("Please use GetSourceRect instead because with future versions of Tiled this method may no longer be sufficient")] + [Obsolete( + "Please use GetSourceRect instead because with future versions of Tiled this method may no longer be sufficient")] public int[] GetSourceVector(TiledMapTileset mapTileset, TiledTileset tileset, int gid) { var tileHor = 0; @@ -602,7 +650,7 @@ public int[] GetSourceVector(TiledMapTileset mapTileset, TiledTileset tileset, i { if (i == gid - mapTileset.firstgid) { - return new[] { tileHor, tileVert }; + return new[] {tileHor, tileVert}; } // Update x and y position @@ -667,6 +715,7 @@ public bool IsTileFlippedHorizontal(TiledLayer layer, int tileHor, int tileVert) { return IsTileFlippedHorizontal(layer, tileHor + (tileVert * layer.width)); } + /// /// Checks is a tile is flipped horizontally /// @@ -677,6 +726,7 @@ public bool IsTileFlippedHorizontal(TiledLayer layer, int dataIndex) { return (layer.dataRotationFlags[dataIndex] & (FLIPPED_HORIZONTALLY_FLAG >> SHIFT_FLIP_FLAG_TO_BYTE)) > 0; } + /// /// Checks is a tile is flipped vertically /// @@ -688,6 +738,7 @@ public bool IsTileFlippedVertical(TiledLayer layer, int tileHor, int tileVert) { return IsTileFlippedVertical(layer, tileHor + (tileVert * layer.width)); } + /// /// Checks is a tile is flipped vertically /// @@ -698,6 +749,7 @@ public bool IsTileFlippedVertical(TiledLayer layer, int dataIndex) { return (layer.dataRotationFlags[dataIndex] & (FLIPPED_VERTICALLY_FLAG >> SHIFT_FLIP_FLAG_TO_BYTE)) > 0; } + /// /// Checks is a tile is flipped diagonally /// @@ -709,6 +761,7 @@ public bool IsTileFlippedDiagonal(TiledLayer layer, int tileHor, int tileVert) { return IsTileFlippedDiagonal(layer, tileHor + (tileVert * layer.width)); } + /// /// Checks is a tile is flipped diagonally /// diff --git a/src/TiledModels.cs b/src/TiledModels.cs index 8916c92..23d303e 100644 --- a/src/TiledModels.cs +++ b/src/TiledModels.cs @@ -9,6 +9,7 @@ public class TiledMapTileset /// The first gid defines which gid matches the tile with source vector 0,0. Is used to determine which tileset belongs to which gid /// public int firstgid; + /// /// The tsx file path as defined in the map file itself /// @@ -24,10 +25,12 @@ public class TiledProperty /// The property name or key in string format /// public string name; + /// /// The property type as used in Tiled. Can be bool, number, string, ... /// public string type; + /// /// The value in string format /// @@ -43,46 +46,67 @@ public class TiledLayer /// The layer id /// public int id; + /// /// The layer name /// public string name; + /// /// Total horizontal tiles /// public int width; + /// /// Total vertical tiles /// public int height; + /// /// The layer type. Usually this is "objectgroup" or "tilelayer". /// public string type; + /// /// The tint color set by the user in hex code /// public string tintcolor; + /// /// Defines if the layer is visible in the editor /// public bool visible; + /// /// Is true when the layer is locked /// public bool locked; + /// /// The horizontal offset /// - public int offsetX; + public float offsetX; + /// /// The vertical offset /// - public int offsetY; + public float offsetY; + + /// + /// The parallax x position + /// + public float parallaxX; + + /// + /// The parallax y position + /// + public float parallaxY; + /// /// An int array of gid numbers which define which tile is being used where. The length of the array equals the layer width * the layer height. Is null when the layer is not a tilelayer. /// public int[] data; + /// /// A parallel array to data which stores the rotation flags of the tile. /// Bit 3 is horizontal flip, @@ -91,10 +115,12 @@ public class TiledLayer /// Is null when the layer is not a tilelayer. /// public byte[] dataRotationFlags; + /// /// The list of objects in case of an objectgroup layer. Is null when the layer has no objects. /// public TiledObject[] objects; + /// /// The layer properties if set /// @@ -112,50 +138,62 @@ public class TiledObject /// The object id /// public int id; + /// /// The object's name /// public string name; + /// /// The object type if defined. Null if none was set. /// public string type; + /// /// The object's x position in pixels /// public float x; + /// /// The object's y position in pixels /// public float y; + /// /// The object's rotation /// public int rotation; + /// /// The object's width in pixels /// public float width; + /// /// The object's height in pixels /// public float height; + /// /// The tileset gid when the object is linked to a tile /// public int gid; + /// /// An array of properties. Is null if none were defined. /// public TiledProperty[] properties; + /// /// If an object was set to a polygon shape, this property will be set and can be used to access the polygon's data /// public TiledPolygon polygon; + /// /// If an object was set to a point shape, this property will be set /// public TiledPoint point; + /// /// If an object was set to an ellipse shape, this property will be set /// @@ -201,7 +239,6 @@ public class TiledPolyline /// public class TiledPoint { - } /// @@ -209,7 +246,6 @@ public class TiledPoint /// public class TiledEllipse { - } /// @@ -222,27 +258,33 @@ public class TiledTile /// The tile id /// public int id; + /// /// The custom tile type, set by the user /// public string type; + /// /// The terrain definitions as int array. These are indices indicating what part of a terrain and which terrain this tile represents. /// /// In the map file empty space is used to indicate null or no value. However, since it is an int array I needed something so I decided to replace empty values with -1. public int[] terrain; + /// /// An array of properties. Is null if none were defined. /// public TiledProperty[] properties; + /// /// An array of tile animations. Is null if none were defined. /// public TiledTileAnimation[] animation; + /// /// An array of tile objects created using the tile collision editor /// public TiledTileObject[] objects; + /// /// The individual tile image /// @@ -258,12 +300,12 @@ public class TiledImage /// The image width /// public int width; - + /// /// The image height /// public int height; - + /// /// The image source path /// @@ -279,6 +321,7 @@ public class TiledTileAnimation /// The tile id within a tileset /// public int tileid; + /// /// The duration in miliseconds /// @@ -294,6 +337,7 @@ public class TiledTerrain /// The terrain name /// public string name; + /// /// The tile used as icon for the terrain editor /// @@ -309,14 +353,17 @@ public class TiledSourceRect /// The x position in pixels from the tile location in the source image /// public int x; + /// /// The y position in pixels from the tile location in the source image /// public int y; + /// /// The width in pixels from the tile in the source image /// public int width; + /// /// The height in pixels from the tile in the source image /// @@ -332,30 +379,37 @@ public class TiledGroup /// The group's id /// public int id; + /// /// The group's name /// public string name; + /// /// The group's visibility /// public bool visible; + /// /// The group's locked state /// public bool locked; + /// /// The group's user properties /// public TiledProperty[] properties; + /// /// The group's layers /// public TiledLayer[] layers; + /// /// The group's objects /// public TiledObject[] objects; + /// /// The group's subgroups ///