Tilemap.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. using System;
  2. using System.Collections.Generic;
  3. using Microsoft.Xna.Framework;
  4. using MonoGame.Extended.Tiled;
  5. namespace MonoGame.Extended.Tilemaps
  6. {
  7. /// <summary>
  8. /// Represents a tilemap loaded from any supported format.
  9. /// </summary>
  10. /// <remarks>
  11. /// A tilemap is a collection of layers organized into a grid-based structure.
  12. /// Layers can contain tiles, objects, or images, and support various rendering
  13. /// features like parallax scrolling, opacity, and tinting.
  14. /// </remarks>
  15. public class Tilemap
  16. {
  17. /// <summary>
  18. /// Gets or sets the name of the tilemap.
  19. /// </summary>
  20. public string Name { get; set; }
  21. /// <summary>
  22. /// Gets the width of the tilemap in tiles.
  23. /// </summary>
  24. public int Width { get; }
  25. /// <summary>
  26. /// Gets the height of the tilemap in tiles.
  27. /// </summary>
  28. public int Height { get; }
  29. /// <summary>
  30. /// Gets the width of each tile in pixels.
  31. /// </summary>
  32. public int TileWidth { get; }
  33. /// <summary>
  34. /// Gets the height of each tile in pixels.
  35. /// </summary>
  36. public int TileHeight { get; }
  37. /// <summary>
  38. /// Gets the orientation of the tilemap.
  39. /// </summary>
  40. public TilemapOrientation Orientation { get; }
  41. /// <summary>
  42. /// Gets or sets the background color of the tilemap.
  43. /// </summary>
  44. public Color? BackgroundColor { get; set; }
  45. /// <summary>
  46. /// Gets the custom properties of the tilemap.
  47. /// </summary>
  48. public TilemapProperties Properties { get; }
  49. /// <summary>
  50. /// Gets the collection of tilesets used by this tilemap.
  51. /// </summary>
  52. public TilemapTilesetCollection Tilesets { get; }
  53. /// <summary>
  54. /// Gets the collection of layers in this tilemap.
  55. /// </summary>
  56. public TilemapLayerCollection Layers { get; }
  57. /// <summary>
  58. /// Gets the world-space bounds of the tilemap.
  59. /// </summary>
  60. public Rectangle WorldBounds
  61. {
  62. get
  63. {
  64. // For orthogonal maps, bounds are straightforward
  65. // For isometric/hexagonal maps, this is an axis-aligned bounding box
  66. return Orientation switch
  67. {
  68. TilemapOrientation.Orthogonal => new Rectangle(0, 0, Width * TileWidth, Height * TileHeight),
  69. // Isometric maps have a diamond shape, so we need to calculate the AABB
  70. TilemapOrientation.Isometric => CalculateIsometricBounds(),
  71. // For staggered and hexagonal, use orthogonal approximation for now
  72. // TODO: Implement precise bounds for staggered/hexagonal in future
  73. TilemapOrientation.Staggered => new Rectangle(0, 0, Width * TileWidth, Height * TileHeight),
  74. TilemapOrientation.Hexagonal => new Rectangle(0, 0, Width * TileWidth, Height * TileHeight),
  75. _ => new Rectangle(0, 0, Width * TileWidth, Height * TileHeight)
  76. };
  77. }
  78. }
  79. /// <summary>
  80. /// Initializes a new instance of the <see cref="Tilemap"/> class.
  81. /// </summary>
  82. /// <param name="name">The name of the tilemap.</param>
  83. /// <param name="width">The width of the tilemap in tiles.</param>
  84. /// <param name="height">The height of the tilemap in tiles.</param>
  85. /// <param name="tileWidth">The width of each tile in pixels.</param>
  86. /// <param name="tileHeight">The height of each tile in pixels.</param>
  87. /// <param name="orientation">The orientation of the tilemap.</param>
  88. public Tilemap(string name, int width, int height, int tileWidth, int tileHeight, TilemapOrientation orientation)
  89. {
  90. Name = name;
  91. Width = width;
  92. Height = height;
  93. TileWidth = tileWidth;
  94. TileHeight = tileHeight;
  95. Orientation = orientation;
  96. Properties = new TilemapProperties();
  97. Tilesets = new TilemapTilesetCollection();
  98. Layers = new TilemapLayerCollection();
  99. }
  100. /// <summary>
  101. /// Converts tile coordinates to world-space position.
  102. /// </summary>
  103. /// <param name="x">The tile X coordinate.</param>
  104. /// <param name="y">The tile Y coordinate.</param>
  105. /// <returns>The position in world space, where (0,0) represents the top-left corner of the map.</returns>
  106. /// <remarks>
  107. /// This transformation accounts for the map's orientation and tile dimensions.
  108. /// For isometric maps, the calculation differs from orthogonal maps.
  109. /// </remarks>
  110. public Point TileToWorldPosition(int x, int y)
  111. {
  112. return Orientation switch
  113. {
  114. TilemapOrientation.Orthogonal => TileToWorldOrthogonal(x, y),
  115. TilemapOrientation.Isometric => TileToWorldIsometric(x, y),
  116. TilemapOrientation.Staggered => TileToWorldStaggered(x, y),
  117. TilemapOrientation.Hexagonal => TileToWorldHexagonal(x, y),
  118. _ => TileToWorldOrthogonal(x, y)
  119. };
  120. }
  121. /// <summary>
  122. /// Converts world-space position to tile coordinates.
  123. /// </summary>
  124. /// <param name="worldPosition">The position in world space.</param>
  125. /// <returns>The tile coordinates.</returns>
  126. public Point WorldToTilePosition(Vector2 worldPosition)
  127. {
  128. return Orientation switch
  129. {
  130. TilemapOrientation.Orthogonal => WorldToTileOrthogonal(worldPosition),
  131. TilemapOrientation.Isometric => WorldToTileIsometric(worldPosition),
  132. TilemapOrientation.Staggered => WorldToTileStaggered(worldPosition),
  133. TilemapOrientation.Hexagonal => WorldToTileHexagonal(worldPosition),
  134. _ => WorldToTileOrthogonal(worldPosition),
  135. };
  136. }
  137. #region Helper Methods
  138. private Rectangle CalculateIsometricBounds()
  139. {
  140. // Isometric maps form a diamond shape
  141. // The width and height in world space are:
  142. // width = (mapWidth * mapHeight) * (tileWidth / 2)
  143. // height = (mapWidth * mapHeight) * (tileHeight / 2)
  144. int worldWidth = (Width + Height) * (TileWidth / 2);
  145. int worldHeight = (Width + Height) * (TileHeight / 2);
  146. return new Rectangle(0, 0, worldWidth, worldHeight);
  147. }
  148. private Point TileToWorldOrthogonal(int x, int y)
  149. {
  150. return new Point(x * TileWidth, y * TileHeight);
  151. }
  152. private Point TileToWorldIsometric(int x, int y)
  153. {
  154. // Isometric transformation:
  155. // worldX = (x - y) * (tileWidth / 2)
  156. // worldY = (x + y) * (tileHeight / 2)
  157. int worldX = (x - y) * (TileWidth / 2);
  158. int worldY = (x + y) * (TileHeight / 2);
  159. return new Point(worldX, worldY);
  160. }
  161. private Point TileToWorldStaggered(int x, int y)
  162. {
  163. // TODO: Implement staggered transformation in future
  164. // For now, use orthogonal as approximation
  165. return TileToWorldOrthogonal(x, y);
  166. }
  167. private Point TileToWorldHexagonal(int x, int y)
  168. {
  169. // TODO: Implement hexagonal transformation in future
  170. // For now, use orthogonal as approximation
  171. return TileToWorldOrthogonal(x, y);
  172. }
  173. private Point WorldToTileOrthogonal(Vector2 worldPosition)
  174. {
  175. int tileX = (int)(worldPosition.X / TileWidth);
  176. int tileY = (int)(worldPosition.Y / TileHeight);
  177. return new Point(tileX, tileY);
  178. }
  179. private Point WorldToTileIsometric(Vector2 worldPosition)
  180. {
  181. // Inverse isometric transformation:
  182. // x = (worldX / (tileWidth/2) + worldY / (tileHeight/2)) / 2
  183. // y = (worldY / (tileHeight/2) - worldX / (tileWidth/2)) / 2
  184. float halfTileWidth = TileWidth * 0.5f;
  185. float halfTileHeight = TileHeight * 0.5f;
  186. float normalizedX = worldPosition.X / halfTileWidth;
  187. float normalizedY = worldPosition.Y / halfTileHeight;
  188. int tileX = (int)((normalizedX + normalizedY) * 0.5f);
  189. int tileY = (int)((normalizedY - normalizedX) * 0.5f);
  190. return new Point(tileX, tileY);
  191. }
  192. private Point WorldToTileStaggered(Vector2 worldPosition)
  193. {
  194. // TODO: Implement staggered transformation in future
  195. // For now, use orthogonal as approximation
  196. return WorldToTileOrthogonal(worldPosition);
  197. }
  198. private Point WorldToTileHexagonal(Vector2 worldPosition)
  199. {
  200. // TODO: Implement hexagonal transformation in future
  201. // For now, use orthogonal as approximation
  202. return WorldToTileOrthogonal(worldPosition);
  203. }
  204. #endregion
  205. }
  206. }