Search code examples
c#monogametexture2d

Drawing tile-maps monogame performance


In a given situation:
- A 32x32 Tile map is about to be drawn in monogame with C#.
- Every texture is 64x64 pixels.
- There is only one kind of texture (i.e one .png file) repeating itself over and over to fill the map.
- Every tile is and object from the class Tile.cs

Example 1:
Creating a static/abstract class containing the one texture that is to be drawn, so that every tile-object can use this when drawing.

    public void Draw(){
        //Texture.GrassTexture is from the static class
        tile.Draw(Textures.GrassTexture);
    }

Example 2:
Another approach would be to either set a texture-variable for each tile when creating a new object from it.

    Texture2D tileTexture;
    public Tile(Texture texture){
        tileTexture = texture;
    }

When drawing the tile, the tileTexture variable can be used.

Example 3:
Sending a texture as a parameter for every tile when drawing the tile.

    public void Draw(Texture2D texture){
        tile.draw(texture)
    }


From a performace perspective, what of the examples would be the best practise? Any other ideas are welcome!


The code is made for this post.


Solution

  • From a rendering performance perspective the important thing is that you're not using to many different textures during a single draw of the map.

    In my experience switching textures to often is really going to hurt performance. So, what you're really interested in is keeping all of the tiles for your map on a single texture (this is often called a texture atlas).

    MonoGame doesn't come with texture atlas support out of the box. However, you can take a look at how we implemented it in MonoGame.Extended.

    The core idea is to implement a class to represent a texture region that represents a single tile from your multi-tile texture.

    public class TextureRegion2D
    {
        public TextureRegion2D(string name, Texture2D texture, int x, int y, int width, int height)
        {
            if (texture == null) throw new ArgumentNullException("texture");
    
            Name = name;
            Texture = texture;
            X = x;
            Y = y;
            Width = width;
            Height = height;
        }
    
        public string Name { get; private set; }
        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); }
        }
    }
    

    Then when you render your tiles you use the overload of sprite batch that takes a source rectangle.

    var sourceRectangle = textureRegion.Bounds;
    spriteBatch.Draw(textureRegion.Texture, position, sourceRectangle, color);
    

    Of course, getting this to all work nicely still requires a few other steps.

    1. You'll need to pack your tiles onto a single texture. You can do this manually or use a tool like TexturePacker.
    2. You'll need a way to load your tiles into texture regions. If the tiles are packed in a simple grid pattern this code is fairly trivial to write by hand. If you've used texture packer you'll need to write an importer.

    Lastly, if you're already using Tiled Map Editor to create your maps you might be interested to know that MonoGame.Extended already has support for loading and rendering them.