MonoGame Tiled – Extract Texture2D

NEZ, an open source MonoGame framework extension, provides the functionality to import and render a tiled map. (.tmx) If MonoGame and NEZ isn’t a term for you, consider reading an introduction article first.

Recap: Following code does allow us to render a tiledmap with the NEZ’s ECS system

var tmx = Core.Content.Load<TiledMap>("Map");
Entity mapEntity = new Entity();            
TiledMapComponent bgComponent = new TiledMapComponent(tmx, "collision", true);
AddComponent(bgComponent);
mapEntity.AddComponent(bgComponent);
Core.Scene.AddEntity(mapEntity);

But how do we only render one specific layer? And what if we would like to manipulate the Texture2D of that layer. E.g.: Applying a material, shader, matrix manipulation, …

Rendering only a specific layer is already supported by NEZ:

bgComponent.SetLayersToRender(RenderLayerNames);

Manipulating the Texture2D however, isn’t. The Texture2D behind each and every layer equals the whole Texture2D of the tileset. Hence, we need to implement our own logic.

Option 1 – RenderTarget2D

Create a new Texture2D, limit the TiledMapComponent’s render layers and call the spriteBatch.Draw within an active RenderTarget2D. Done

Option 2 – Texture2D.GetData

A RenderTarget2D requires a spriteBatch which we maybe won’t have access to. In that case, we can make use of the Texture2D’s .GetData method.

    public static class TiledTileLayerExtensions {
     public static Texture2D ExtractTexture2D(this TiledTileLayer layer) {
      List < TiledTile > tiles = layer.Tiles.Where(t => t != null).ToList();

      var mostLeft = tiles.Min(c => c.X) * Constants.TILE_WIDTH;
      var mostRight = (tiles.Max(c => c.X) * Constants.TILE_WIDTH) + Constants.TILE_WIDTH;
      var mostTop = tiles.Min(c => c.Y) * Constants.TILE_HEIGHT;
      var mostBottom = tiles.Max(c => c.Y) * Constants.TILE_HEIGHT + +Constants.TILE_HEIGHT;

      var width = mostRight - mostLeft;
      var height = mostBottom - mostTop;
      var subTextureRect = new Rectangle(mostLeft, mostTop, width, height);

      Color[] subTextureColor = new Color[width * height];

      for (int currentTile = 0; currentTile < tiles.Count; currentTile++) {
       TiledTile tileDetails = tiles[currentTile];

       Color[] currentTileColor = new Color[Constants.TILE_WIDTH * Constants.TILE_HEIGHT];
       tileDetails.TextureRegion.Texture2D.GetData(0, tileDetails.TextureRegion.SourceRect, currentTileColor, 0, currentTileColor.Length);

       int tX = tileDetails.X * Constants.TILE_WIDTH;
       int tY = tileDetails.Y * Constants.TILE_HEIGHT;

       for (int x = 0; x < Constants.TILE_WIDTH; x++)
        for (int y = 0; y < Constants.TILE_HEIGHT; y++)
         subTextureColor[(y + tY) * width + (x + tX)] = currentTileColor[y * Constants.TILE_WIDTH + x];
      }

      Texture2D subTexture = new Texture2D(Core.GraphicsDevice, width, height);
      subTexture.SetData(subTextureColor);
      return subTexture;
     }
    }

Call the extension method with

var textureLayer = tmx?.Layers.Where(l => l.Name == "LayerToExtract").SingleOrDefault();
return (textureLayer as TiledTileLayer)?.ExtractTexture2D();
0 0 vote
Article Rating
Subscribe
Notify of
0 Comments
Inline Feedbacks
View all comments