Tiled
This page is up to date for MonoGame.Extended 4.0.4
. If you find outdated information, please open an issue.
Tiled
The MonoGame.Extended.Tiled
namespace allows you to load and render map files (.tmx
) created with the Tiled Map Editor.
Supported Features
MonoGame.Extended Tiled support includes:
- ✅ Orthogonal and Isometric maps
- ✅ Multiple layer types (Tile, Object, Image, Group)
- ✅ All object types (Rectangle, Ellipse, Polygon, Polyline, Tile)
- ✅ Tile animations
- ✅ Tile flipping and rotation
- ✅ External tilesets (.tsx files)
- ✅ Custom properties on maps, layers, and objects
- ✅ Image layers with positioning
- ✅ Group layers (nested layers)
Limitations
Current limitations include:
- ❌ Hexagonal and Staggered map orientations
- ❌ Infinite maps (chunked data)
- ❌ Wang tiles/terrain sets
- ❌ Collection of Images tilesets (use single image tilesets instead)
Prerequisites
The following prerequisites are required when using Tiled maps with MonoGame.Extended:
- A MonoGame project with MonoGame.Extended installed.
- Content Pipeline Extensions configured (see Installation Guide: Setup up MGC BEditor).
- Basic understanding of the Tiled Map Editor
File Organization
Tiled projects consist of multiple interconnected files:
.tmx
files - The main tilemap file containing layer and map data.tsx
files - External tileset files referenced by the tilemap.png
files - Image files used by the tilesets
These files use relative paths to reference each other:
- The
.tmx
file contains relative paths to.tsx
files - The
.tsx
files contain relative paths to.png
image files
Important File Management
When adding Tiled assets to your MonoGame project, you must maintain the same relative file structure that exists in your Tiled project. The MonoGame.Extended Content Pipeline reads these relative paths during processing and expects the referenced files to exist in the correct locations.
Best Practice: Keep all your Tiled project files (.tmx, .tsx, .png) in the same folder structure when you add them to the MGCB Editor. This ensures the relative paths remain valid during content processing.
All three file types (.tmx, .tsx, .png) must be added to the MGCB Editor for the content pipeline to process them correctly.
Adding Files to MGCB Editor
When working with Tiled maps in MonoGame.Extended, you need to add your files to the MGCB Editor content project carefully:
Files to Add to MGCB Editor
.tmx
files - The main tilemap files.tsx
files - External tileset files
Files to Copy (But Not Add)
.png
files - Tileset image files
The .png
tileset images must be copied to your content directory maintaining the same relative path structure as your Tiled project, but they should not be added as content items in the MGCB Editor. The MonoGame.Extended Content Pipeline will automatically detect and process these images when it processes the .tsx
files that reference them.
For reference, here are the assets from the demo files setup in the MGCB Editor and what it looks like in the directory structure
Step-by-Step Process
- Copy your entire Tiled project folder to your MonoGame Content directory, maintaining the folder structure
- Add only the .tmx and .tsx files to the MGCB Editor content project
- Leave the .png files in the content directory but do not add them to the MGCB Editor
- Build your content project - the pipeline will automatically process the referenced .png files
Usage
The assets used in this example can be downloaded here
Setting up Namespaces
Start by including the required Tiled namespaces:
using MonoGame.Extended.Tiled;
using MonoGame.Extended.Tiled.Renderers;
Loading and Rendering a Map
Define your TiledMap and TiledMapRenderer fields:
private TiledMap _tiledMap;
private TiledMapRenderer _tiledMapRenderer;
Initialize them in the LoadContent
method:
protected override void LoadContent()
{
_tiledMap = Content.Load<TiledMap>("samplemap");
_tiledMapRenderer = new TiledMapRenderer(GraphicsDevice, _tiledMap);
_spriteBatch = new SpriteBatch(GraphicsDevice);
}
Update and draw the map:
protected override void Update(GameTime gameTime)
{
_tiledMapRenderer.Update(gameTime);
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
_tiledMapRenderer.Draw();
base.Draw(gameTime);
}
Transparency Rendering Issue
If you run the game now using the demo assets, you'll notice a rendering issue where tiles that should have transparent backgrounds appear with black backgrounds instead.
This happens because the TiledMapRenderer
operates directly on the GraphicsDevice
and doesn't automatically set the proper blend state for transparency.
Unlike SpriteBatch
, which manages its own rendering state, TiledMapRenderer
relies on the current GraphicsDevice
state. By default, the graphics device uses BlendState.Opaque
, which doesn't handle transparency correctly, causing transparent pixels to render as black.
To fix this, we need to set the proper blend state before drawing the map:
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
// Save current blend state and set to AlphaBlend for proper transparency
BlendState previousBlendState = GraphicsDevice.BlendState;
GraphicsDevice.BlendState = BlendState.AlphaBlend;
_tiledMapRenderer.Draw();
// Restore previous blend state
GraphicsDevice.BlendState = previousBlendState;
base.Draw(gameTime);
}
Now when you run the game, transparent areas should render correctly without the black backgrounds.
Adding Camera Support
For maps larger than your screen, you'll want to add camera support for navigation.
Camera Setup
Include the camera namespaces:
using MonoGame.Extended;
using MonoGame.Extended.ViewportAdapters;
Add camera fields:
private OrthographicCamera _camera;
private Vector2 _cameraPosition;
Initialize the camera in Initialize
:
protected override void Initialize()
{
BoxingViewportAdapter viewportAdapter = new BoxingViewportAdapter(Window, GraphicsDevice, 800, 600);
_camera = new OrthographicCamera(viewportAdapter);
// Set initial camera position to show map from top-left
_cameraPosition = _camera.Origin;
_camera.LookAt(_cameraPosition);
base.Initialize();
}
Viewport Adapter Options
MonoGame.Extended provides several viewport adapter types for different scaling needs:
BoxingViewportAdapter
- Maintains aspect ratio with letterboxing/pillarboxing (recommended for most games)DefaultViewportAdapter
- Uses the current graphics device viewport as-isScalingViewportAdapter
- Stretches to fill the entire screen (may distort aspect ratio)WindowViewportAdapter
- Adapts to window size changes
For Tiled maps, BoxingViewportAdapter
is typically the best choice as it maintains proper aspect ratios while handling different screen sizes.
Camera Movement
Add camera movement methods:
private Vector2 GetMovementDirection()
{
Vector2 movementDirection = Vector2.Zero;
KeyboardState state = Keyboard.GetState();
if (state.IsKeyDown(Keys.Down))
{
movementDirection += Vector2.UnitY;
}
if (state.IsKeyDown(Keys.Up))
{
movementDirection -= Vector2.UnitY;
}
if (state.IsKeyDown(Keys.Left))
{
movementDirection -= Vector2.UnitX;
}
if (state.IsKeyDown(Keys.Right))
{
movementDirection += Vector2.UnitX;
}
// Normalize to prevent faster diagonal movement
if (movementDirection != Vector2.Zero)
{
movementDirection.Normalize();
}
return movementDirection;
}
private void MoveCamera(GameTime gameTime)
{
float speed = 200f;
float deltaTime = (float)gameTime.ElapsedGameTime.TotalSeconds;
Vector2 movementDirection = GetMovementDirection();
_cameraPosition += speed * movementDirection * deltaTime;
}
Using the Camera
Update your Update
and Draw
methods to use the camera:
protected override void Update(GameTime gameTime)
{
_tiledMapRenderer.Update(gameTime);
MoveCamera(gameTime);
_camera.LookAt(_cameraPosition);
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.Black);
_tiledMapRenderer.Draw(_camera.GetViewMatrix());
base.Draw(gameTime);
}
Working with Layers
TiledMap provides access to different layer types:
Getting Layers
// Get a layer by name
TiledMapLayer backgroundLayer = _tiledMap.GetLayer("Background");
// Get a specific layer type
TiledMapTileLayer tileLayer = _tiledMap.GetLayer<TiledMapTileLayer>("Ground");
TiledMapObjectLayer objectLayer = _tiledMap.GetLayer<TiledMapObjectLayer>("Collision");
TiledMapImageLayer imageLayer = _tiledMap.GetLayer<TiledMapImageLayer>("Parallax Background");
Layer Collections
Access collections of specific layer types:
// All layers
foreach (TiledMapLayer layer in _tiledMap.Layers)
{
Console.WriteLine($"Layer: {layer.Name}, Type: {layer.GetType().Name}");
}
// Only tile layers
foreach (TiledMapTileLayer tileLayer in _tiledMap.TileLayers)
{
Console.WriteLine($"Tile Layer: {tileLayer.Name}");
}
// Only object layers
foreach (TiledMapObjectLayer objectLayer in _tiledMap.ObjectLayers)
{
Console.WriteLine($"Object Layer: {objectLayer.Name}");
}
Working with Tiles
Accessing Individual Tiles
TiledMapTileLayer tileLayer = _tiledMap.GetLayer<TiledMapTileLayer>("Ground");
// Get tile at specific coordinates
if (tileLayer.TryGetTile(10, 5, out TiledMapTile? tile))
{
if (tile.HasValue && !tile.Value.IsBlank)
{
Console.WriteLine($"Tile Global ID: {tile.Value.GlobalIdentifier}");
Console.WriteLine($"Flipped Horizontally: {tile.Value.IsFlippedHorizontally}");
}
}
Working with Tilesets
// Get tileset for a specific tile
TiledMapTileset tileset = _tiledMap.GetTilesetByTileGlobalIdentifier(tile.Value.GlobalIdentifier);
if (tileset != null)
{
Console.WriteLine($"Tileset: {tileset.Name}");
// Get local tile ID within the tileset
int firstGid = _tiledMap.GetTilesetFirstGlobalIdentifier(tileset);
int localId = tile.Value.GlobalIdentifier - firstGid;
}
Working with Objects
Accessing Object Layers
TiledMapObjectLayer objectLayer = _tiledMap.GetLayer<TiledMapObjectLayer>("Enemies");
foreach (TiledMapObject mapObject in objectLayer.Objects)
{
Console.WriteLine($"Object: {mapObject.Name} at ({mapObject.Position.X}, {mapObject.Position.Y})");
// Check object type
switch (mapObject)
{
case TiledMapRectangleObject rectangle:
Console.WriteLine($"Rectangle: {rectangle.Size.Width} x {rectangle.Size.Height}");
break;
case TiledMapEllipseObject ellipse:
Console.WriteLine($"Ellipse: Center {ellipse.Center}, Radius {ellipse.Radius}");
break;
case TiledMapPolygonObject polygon:
Console.WriteLine($"Polygon with {polygon.Points.Length} points");
break;
case TiledMapTileObject tileObject:
Console.WriteLine($"Tile Object using tileset: {tileObject.Tileset.Name}");
break;
}
}
Working with Properties
Both maps and individual objects support custom properties:
Map Properties
// Access map-level properties
if (_tiledMap.Properties.TryGetValue("gravity", out string gravityValue))
{
float gravity = float.Parse(gravityValue);
Console.WriteLine($"Map gravity: {gravity}");
}
Object Properties
// Access object properties
foreach (TiledMapObject mapObject in objectLayer.Objects)
{
if (mapObject.Properties.TryGetValue("health", out string healthValue))
{
int health = int.Parse(healthValue);
Console.WriteLine($"Object {mapObject.Name} has {health} health");
}
}
Map Information
Access basic map information:
Console.WriteLine($"Map Size: {_tiledMap.Width} x {_tiledMap.Height} tiles");
Console.WriteLine($"Tile Size: {_tiledMap.TileWidth} x {_tiledMap.TileHeight} pixels");
Console.WriteLine($"Map Size in Pixels: {_tiledMap.WidthInPixels} x {_tiledMap.HeightInPixels}");
Console.WriteLine($"Orientation: {_tiledMap.Orientation}");
Console.WriteLine($"Render Order: {_tiledMap.RenderOrder}");
if (_tiledMap.BackgroundColor.HasValue)
{
Console.WriteLine($"Background Color: {_tiledMap.BackgroundColor.Value}");
}
Performance Optimization
Tileset Organization
The way you organize your tilesets in Tiled can significantly impact rendering performance. For optimal performance:
- Use fewer, larger tilesets rather than many small ones
- Combine related tiles into single tileset images when possible
- Avoid excessive tileset switching during rendering
Each tileset requires a separate texture binding operation during rendering. Having many small tilesets forces the renderer to switch textures frequently, which can create performance bottlenecks.
Managing Layer Visibility
You can control which layers are rendered to improve performance in areas where not all content needs to be visible:
TiledMapLayer backgroundLayer = _tiledMap.GetLayer("Background");
backgroundLayer.IsVisible = false; // This layer won't be rendered
// Later, you can re-enable it
backgroundLayer.IsVisible = true;
This is particularly useful for:
- Debug layers that shouldn't be visible in release builds
- Layers that are only relevant in certain game states
- Background layers that might be obscured by foreground content
Animated Tile Updates
The TiledMapRenderer.Update()
method is specifically for processing animated tiles. If your map doesn't contain any animated tiles, you can skip calling this method entirely:
protected override void Update(GameTime gameTime)
{
// Only call this if you have animated tiles in your map
if (HasAnimatedTiles)
{
_tiledMapRenderer.Update(gameTime);
}
base.Update(gameTime);
}
Automatic Optimizations
MonoGame.Extended includes several built-in optimizations that work automatically:
- Camera-based culling: Only tiles visible within the camera's view are rendered, regardless of map size
- Efficient vertex batching: Tiles are batched together to minimize draw calls
- Texture atlasing: Multiple tiles from the same tileset are rendered in a single draw operation
Troubleshooting
Content Pipeline Issues
Problem: "Could not find ContentTypeReader" error.
Solution: Ensure MonoGame.Extended.Content.Pipeline is added to your MGCB Editor references. If you did not use the <MonoGameExtendedPipelineReferencePath>
method as described in the Installation Guide: Setup up MGC BEditor and instead added the dll directly from the NuGet download directory, ensure it is the .dll
found in the tools
directory of the NuGet package, not the one in the lib
directory.
Problem: "File not found" errors during content processing.
Solution: Verify all .tmx, .tsx, and .png files are added to the MGCB Editor and maintain proper relative paths.
Runtime Issues
Problem: Map renders incorrectly or tiles are missing.
Solution: Check that all required tileset images are included in your content project and have been processed by the pipeline.
Problem: Poor performance with large maps.
Solution: Consider using fewer, larger tilesets and ensure you're only updating animated tiles when necessary.