using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using MonoGame.Extended.Tiled; namespace MonoGame.Extended.Tilemaps { /// /// Represents a tilemap loaded from any supported format. /// /// /// A tilemap is a collection of layers organized into a grid-based structure. /// Layers can contain tiles, objects, or images, and support various rendering /// features like parallax scrolling, opacity, and tinting. /// public class Tilemap { /// /// Gets or sets the name of the tilemap. /// public string Name { get; set; } /// /// Gets the width of the tilemap in tiles. /// public int Width { get; } /// /// Gets the height of the tilemap in tiles. /// public int Height { get; } /// /// Gets the width of each tile in pixels. /// public int TileWidth { get; } /// /// Gets the height of each tile in pixels. /// public int TileHeight { get; } /// /// Gets the orientation of the tilemap. /// public TilemapOrientation Orientation { get; } /// /// Gets or sets the background color of the tilemap. /// public Color? BackgroundColor { get; set; } /// /// Gets the custom properties of the tilemap. /// public TilemapProperties Properties { get; } /// /// Gets the collection of tilesets used by this tilemap. /// public TilemapTilesetCollection Tilesets { get; } /// /// Gets the collection of layers in this tilemap. /// public TilemapLayerCollection Layers { get; } /// /// Gets the world-space bounds of the tilemap. /// public Rectangle WorldBounds { get { // For orthogonal maps, bounds are straightforward // For isometric/hexagonal maps, this is an axis-aligned bounding box return Orientation switch { TilemapOrientation.Orthogonal => new Rectangle(0, 0, Width * TileWidth, Height * TileHeight), // Isometric maps have a diamond shape, so we need to calculate the AABB TilemapOrientation.Isometric => CalculateIsometricBounds(), // For staggered and hexagonal, use orthogonal approximation for now // TODO: Implement precise bounds for staggered/hexagonal in future TilemapOrientation.Staggered => new Rectangle(0, 0, Width * TileWidth, Height * TileHeight), TilemapOrientation.Hexagonal => new Rectangle(0, 0, Width * TileWidth, Height * TileHeight), _ => new Rectangle(0, 0, Width * TileWidth, Height * TileHeight) }; } } /// /// Initializes a new instance of the class. /// /// The name of the tilemap. /// The width of the tilemap in tiles. /// The height of the tilemap in tiles. /// The width of each tile in pixels. /// The height of each tile in pixels. /// The orientation of the tilemap. public Tilemap(string name, int width, int height, int tileWidth, int tileHeight, TilemapOrientation orientation) { Name = name; Width = width; Height = height; TileWidth = tileWidth; TileHeight = tileHeight; Orientation = orientation; Properties = new TilemapProperties(); Tilesets = new TilemapTilesetCollection(); Layers = new TilemapLayerCollection(); } /// /// Converts tile coordinates to world-space position. /// /// The tile X coordinate. /// The tile Y coordinate. /// The position in world space, where (0,0) represents the top-left corner of the map. /// /// This transformation accounts for the map's orientation and tile dimensions. /// For isometric maps, the calculation differs from orthogonal maps. /// public Point TileToWorldPosition(int x, int y) { return Orientation switch { TilemapOrientation.Orthogonal => TileToWorldOrthogonal(x, y), TilemapOrientation.Isometric => TileToWorldIsometric(x, y), TilemapOrientation.Staggered => TileToWorldStaggered(x, y), TilemapOrientation.Hexagonal => TileToWorldHexagonal(x, y), _ => TileToWorldOrthogonal(x, y) }; } /// /// Converts world-space position to tile coordinates. /// /// The position in world space. /// The tile coordinates. public Point WorldToTilePosition(Vector2 worldPosition) { return Orientation switch { TilemapOrientation.Orthogonal => WorldToTileOrthogonal(worldPosition), TilemapOrientation.Isometric => WorldToTileIsometric(worldPosition), TilemapOrientation.Staggered => WorldToTileStaggered(worldPosition), TilemapOrientation.Hexagonal => WorldToTileHexagonal(worldPosition), _ => WorldToTileOrthogonal(worldPosition), }; } #region Helper Methods private Rectangle CalculateIsometricBounds() { // Isometric maps form a diamond shape // The width and height in world space are: // width = (mapWidth * mapHeight) * (tileWidth / 2) // height = (mapWidth * mapHeight) * (tileHeight / 2) int worldWidth = (Width + Height) * (TileWidth / 2); int worldHeight = (Width + Height) * (TileHeight / 2); return new Rectangle(0, 0, worldWidth, worldHeight); } private Point TileToWorldOrthogonal(int x, int y) { return new Point(x * TileWidth, y * TileHeight); } private Point TileToWorldIsometric(int x, int y) { // Isometric transformation: // worldX = (x - y) * (tileWidth / 2) // worldY = (x + y) * (tileHeight / 2) int worldX = (x - y) * (TileWidth / 2); int worldY = (x + y) * (TileHeight / 2); return new Point(worldX, worldY); } private Point TileToWorldStaggered(int x, int y) { // TODO: Implement staggered transformation in future // For now, use orthogonal as approximation return TileToWorldOrthogonal(x, y); } private Point TileToWorldHexagonal(int x, int y) { // TODO: Implement hexagonal transformation in future // For now, use orthogonal as approximation return TileToWorldOrthogonal(x, y); } private Point WorldToTileOrthogonal(Vector2 worldPosition) { int tileX = (int)(worldPosition.X / TileWidth); int tileY = (int)(worldPosition.Y / TileHeight); return new Point(tileX, tileY); } private Point WorldToTileIsometric(Vector2 worldPosition) { // Inverse isometric transformation: // x = (worldX / (tileWidth/2) + worldY / (tileHeight/2)) / 2 // y = (worldY / (tileHeight/2) - worldX / (tileWidth/2)) / 2 float halfTileWidth = TileWidth * 0.5f; float halfTileHeight = TileHeight * 0.5f; float normalizedX = worldPosition.X / halfTileWidth; float normalizedY = worldPosition.Y / halfTileHeight; int tileX = (int)((normalizedX + normalizedY) * 0.5f); int tileY = (int)((normalizedY - normalizedX) * 0.5f); return new Point(tileX, tileY); } private Point WorldToTileStaggered(Vector2 worldPosition) { // TODO: Implement staggered transformation in future // For now, use orthogonal as approximation return WorldToTileOrthogonal(worldPosition); } private Point WorldToTileHexagonal(Vector2 worldPosition) { // TODO: Implement hexagonal transformation in future // For now, use orthogonal as approximation return WorldToTileOrthogonal(worldPosition); } #endregion } }