MonoGame — Renderizando Mapa Criado no Tiled
Neste artigo vou demonstrar como renderizar um mapa do Tiled utilizando um projeto para Android com MonoGame.
Este artigo não terá uma explicação aprofundada sobre o Tiled, irei explicar apenas o necessário para o entendimento do código que será desenvolvido.
Os arquivos utilizados podem ser baixados aqui:
https://mega.nz/#F!NMACUQRZ!VEZz2nPc4oGrGrM-t85XvA
O que é o Tiled!?
Tiled é o editor open source mais utilizado para criação de mapas de jogos 2D, com ele é possível criar de forma fácil e flexível mapas para serem utilizados em diversos tipos de jogos como: Platformer, RTS (Real-Time Strategy), TBS (Turn-Based Strategy), entre outros.
Para mais detalhes sobre o Tiled acesse a documentação em:
https://doc.mapeditor.org
Uma Observação e um Mini Tutorial
O projeto será desenvolvido utilizando o Visual Studio 2019, porém o instalador do MonoGame não tem os templates para está versão do VS (até o momento que o artigo foi escrito), para resolver este problema é necessário instalar os mesmos manualmente.
Para o correto funcionamento dos templates no Visual Studio 2019 baixe o arquivo com os templates modificados:
https://mega.nz/#F!NMACUQRZ!VEZz2nPc4oGrGrM-t85XvA
É extraia o conteúdo para a pasta:
%USERPROFILE%\Documents\Visual Studio 2019\Templates\ProjectTemplates\Visual C#\
Após realizar o processo de instalação dos templates, inicie o Visual Studio 2019 e clique no botão Create a new project, ao clicar no terceiro campo de ComboBox a opção Games-MonoGame deverá existir, como apresentado na imagem abaixo.
Show me the code !
Com o projeto do tipo MonoGame Android Project criado, na guia Solution Explorer navegue até a pasta Content e realize um duplo clique no arquivo Content.mgcb, assim que o MonoGame Pipeline Tool (MGPT) abrir, adicione o arquivo TiledBackgroundTileset.png ao Content, após finalizar a inclusão do arquivo, feche o MGPT:
Na guia Solution Explorer, clique com o botão direito na pasta Assets e adicione o arquivo TiledBackgroundMap.json.
O arquivo deve ser adicionado nesta pasta, pois desta forma será feito o deploy do mesmo junto com a aplicação, possibilitando acessa-ló.
Para utilizar o arquivo ele deve ser obtido na classe Activity1.cs e passado como parâmetro para a classe Game1.cs
Foi adicionando o parâmetro tiledBackgroundMapStream do tipo System.IO.Stream no construtor da classe Game1.cs que é passado na classe Activity1.cs
No método OnCreate o arquivo TiledBackgroundMap.json é obtido utilizando a propriedade Assets, que é do tipo Android.Content.Res.AssetManager, chamando o método Open que retorna um objeto do tipo System.IO.Stream que é armazenado na variável tiledBackgroundMapStream é passado para o construtor de Game1.cs
Com os arquivos de contents já preparados, vamos criar o código que ira utiliza-los, mas antes, vamos para uma breve explicação sobre o arquivo de projeto do Tiled.
No Tiled é possível exportar o projeto com a extensão .TMX, .JSON e .JS. A extensão padrão é o .TMX que é um arquivo estruturado no padrão XML.
Optei por utilizar o projeto do Tiled em .JSON, pois a estrutura é mais compacta e a manipulação mais simples.
Na imagem abaixo é apresentado a estrutura básica de um arquivo de projeto do Tiled.
OBS.: Para este artigo, algumas informações foram removidas da estrutura apresentada na imagem.
Foi destacado na imagem as informações que são necessárias para realizar a renderização de um mapa:
Map
- tileHeight: Altura do tile no mapa (em pixels)
- tileWidth: Largura do tile no mapa (em pixels)
- layers: Array com dados dos layers
- — data: Array contendo a ordem dos tile no layer
- — height: Altura do layer (em quantidade de tiles)
- — width: Largura do layer (em quantidade de tiles)
- tilesets: Array contendo os tilesets utilizados na construção dos layers
- — tileHeight: Altura do tile no tileset (em pixels)
- — tileWidth: Largura do tile no tileset (em pixels)
- — columns: Quantidade de colunas do tileset
A classe que representar está estrutura em C# ficou da seguinte forma:
Como a extensão do arquivo de mapa é do tipo .JSON, será necessário instalar o pacote Newtonsoft.Json no projeto para facilitar a deserialização/conversão do arquivo para a classe Map.cs:
Para instalar o pacote acesse o Package Manager Console em: Tools — NuGet Package Manager — Package Manager Console e execute o seguinte comando:
Install-Package Newtonsoft.Json -Version 12.0.3
Para realizar a renderização do mapa, será criado um componente do tipo Microsoft.Xna.Framework.DrawableGameComponent, desta forma a logica de manipulação fica separada possibilitando a reutilização.
Variáveis
- _tiledMapStreamReader — Armazena o stream do arquivo de projeto do Tiled convertido para System.IO.StreamReader para possibilitar a leitura das informações contidas no arquivo.
- _tilesetName — Armazena o nome do tileset que foi adicionado como conteúdo do tipo Microsoft.Xna.Framework.Graphics.Texture2D no MonoGame Pipeline Tools.
- _spriteBatch — Armazena a instância do tipo Microsoft.Xna.Framework.Graphics.SpriteBatch.
- _map — Armazena os dados deserializados/convertidos do projeto do Tiled.
- _tilesetTexture — Armazena o sprite do tileset utilizado na criação do mapa no Tiled.
As variáveis _tiledMapStreamReader e _tilesetName, recebem seus valores via parâmetro no construtor do componente.
Métodos
- LoadContent — É realizado a instanciação do SpriteBatch e atribuído para a variável _spriteBatch em seguida, a leitura do conteúdo do projeto do Tiled que foi armazenado na variável _tiledMapStreamReader, utilizando o método ReadToEnd que retorna uma string que é atribuída para a variável conteudoDoJson, em seguida o conteúdo é deserializado/convertido utilizando o método estático DeserializeObject<T> da classe Newtonsoft.Json.JsonConvert atribuindo o resultado para a variável _map, por fim o sprite do tileset é carregado e atribuído para a variável_tilesetTexture.
- Draw —Ocorre a iteração em cada layer do projeto do Tiled para identificar o que e como os dados devem ser renderizados. Como o mapa é construído com o conceito de Linhas e Colunas, a interpretação e renderização dos dados não é diferente.
No primeiro “for” é realizado a iteração nas linhas do layer e obtido os dados da linha que está sendo iterada utilizando os métodos de extensão Skip e Take do namespace System.Linq.
No segundo “for” é realizado a iteração nas colunas da linha, neste loop é identificado a posição do tile no mapa e qual o tile do tileset deverá ser desenhado. - ObtemTileDoTileset — O método que faz a coisa acontecer com 5 linhas de código, o ID do tile no tileset e a quantidade de colunas do tileset retornando um objeto do tipo Microsoft.Xna.Framework.Rectangle contendo a posição e o tamanho do tile…rsr
Explicando melhor o método ObtemTileDoTileset
O problema a ser resolvido neste método é:
- Descobrir qual tile do tileset será renderizado sabendo apenas o id, a quantidade de colunas do tileset e o tamanho do tile.
Toda imagem usada como tileset no Tiled, os seus tiles recebem um ID que sempre inicia em zero e incrementa em +1 da esquerda para direita, de cima para baixo, como pode ser observado na imagem abaixo os números em vermelho.
Duas informação são necessárias para renderizar o tile correto do tileset, são elas:
- Qual o índice da Linha e Coluna do tile no tileset (números em verde na imagem anterior)
- Qual a posição X, Y do tile no tileset (números em azul na imagem anterior)
Utilizando a formula “TileId / TotalDeColunasDoTileset” e arredondando o resultado para baixo, eu consigo obter o índice da linha do tile no tileset. E aplicando a formula “TileId-(IndiceDaLinhaDoTileNoTileset x TotalDeColunasDoTileset)” é obtido o índice da coluna.
Por fim é obtido a posição X multiplicando a largura pelo índice da coluna do tile e a posição Y multiplicando a altura pelo índice da linha do tile.
Exemplificando com as informações da imagem anterior:
Preciso saber a posição X, Y do tile de ID 17, sabendo que meu tileset tem 3 colunas e cada tile do tileset tem tamanho de 40 pixels para largura e altura.
Índice Linha = 17 / 3 = 5.66, arrendondando para baixo temos 5.
Índice Coluna = 17-(5 x 3) = 2X = 40 x 2 = 80
Y = 40 x 5 = 200
Com os cálculos malucos esclarecidos, vamos para a classe Game1.cs adicionar o código para utilizar o MapComponent.
Se tudo der certo e nada der errado, ao pressionar F5 o seguinte resultado será obtido.
Informação Adicional
Se você, caro leitor, abriu o arquivo TiledBackgroundMap.json no Tiled, deve ter reparado que o mapa que criei tem um tamanho total de 4000x4000 pixels. Em um tutorial futuro, vou ensinar como percorrer o mapa usando o conceito de visão de camera.
Repositório: https://github.com/RonildoSouza/MonoGameTiledMap
REFERÊNCIAS:
https://pt.wikipedia.org/wiki/Jogo_eletr%C3%B4nico_de_plataforma
https://pt.wikipedia.org/wiki/Estrat%C3%A9gia_em_tempo_real