Search code examples
c#monogame

Drawing a tiled map with Monogame


I've been trying to implement a function that lets me draw the tiles inside a tiled file (.tmx). I've looked around and found a kind of working piece of code but it stretches out the tiles.
This is the code I found and edited a bit:

private void DrawLayer(int index, SpriteBatch batch) {
        for (var i = 0; i < Map.Layers[index].Tiles.Count; i++) {
            //Get the identification of the tile
            int gid = Map.Layers[index].Tiles[i].Gid;

            // Empty tile, do nothing
            if (gid == 0) { }
            else {
                int tileFrame = gid - 1 ;
                int column = tileFrame % (tileset.Width / tileWidth);
                int row = tileFrame / (tileset.Height / tileHeight);

                float x = (i % Map.Width) * Map.TileWidth;
                float y = (float)Math.Floor(i / (double)Map.Width) * Map.TileHeight;

                //Put all the data together in a new rectangle
                Rectangle tilesetRec = new Rectangle(tileWidth * column, tileHeight * row, tileWidth, tileHeight);

                //Draw the tile that is within the tilesetRec
                batch.Draw(tileset, new Rectangle((int)x, (int)y, tileWidth, tileHeight), tilesetRec, Color.White);
            }
        }
    }

Solution

  • The MonoGame.Extended library has support for loading and rendering Tiled (.tmx) maps. It's open source so you can check out how it works if you want.

    The layer rendering code supports different map types (orthogonal, isometric), different rendering order (right down, right up, left down, left up) and multiple tilesets so it's not boiled down into a single method like yours.

    If you where to extract the relevant bits of code you might end up with something like this:

    for (var y = 0; y < layerHeight; y++)
    {
        for (var x = 0; x < layerWidth; x++)
        {
            var region = tile.Id == 0 ? null : _regions[tile.Id];
    
            if (region != null)
            {
                var tx = tile.X * _map.TileWidth;
                var ty = tile.Y * _map.TileHeight;
                var sourceRectangle = region.Bounds;
                var destinationRectangle = new Rectangle(tx, ty, region.Width, region.Height);
    
                _spriteBatch.Draw(region.Texture, destinationRectangle, sourceRectangle, Color.White);
            }
        }
    }
    

    Of course, there's still a few missing bits, like the dictionary of texture regions created when loading the tileset.

    _regions = new Dictionary<int, TextureRegion2D>();
    
    for (var y = Margin; y < texture.Height - Margin; y += TileHeight + Spacing)
    {
        for (var x = Margin; x < texture.Width - Margin; x += TileWidth + Spacing)
        {
            _regions.Add(id, new TextureRegion2D(Texture, x, y, TileWidth, TileHeight));
            id++;
        }
    }
    

    And the definition of what a texture region actually is.

    public class TextureRegion2D
    {
        public Texture2D Texture { get; protected set; }
        public int X { get; private set; }
        public int Y { get; private set; }
        public int Width { get; private set; }
        public int Height { get; private set; }
        public Rectangle Bounds { get { return new Rectangle(X, Y, Width, Height); } }
    }
    

    Keep in mind that I've mostly copy and pasted code out of MonoGame.Extended. It won't work exactly as it's written here but I think I've provided enough detail to figure out what all the other variables do if you want to write your own Tiled rendering code.