Artigo
Usando Tiles para criar Mapas
A utilização de tiles otimiza o desenho de mapas. É possível criar mapas grandes a partir de imagens pequenas ou construir mapas diferetes usando uma mesma imagem.
Enviado por Denis Martins em 18/11/2009 0:00:00

tilemap

Por que usar Tiles?

Carregar grandes imagens pode deixar seu jogo bastante lento.
Através do uso de Tiles, podemos construir mapas grandes a apartir de imagens pequenas e montar mapas diferentes utilizando uma mesma imagem.



Para esse exemplo, vamos utilizar a seguinte imagem:
tileset

A Classe Tile

Um tile é representado por um retângulo. Cada tile possui um tipo e um atributo que define se ele pode ser atravessado, ou seja, se é possível se movimentar sobre ele.

public class Tile
    {
        private Rectangle retangulo;
        private int tipo;
        private bool andavel;
       
       
        public Rectangle Retangulo
        {
            get { return this.retangulo; }
            set { this.retangulo = value; }
        }

        public int Tipo
        {
            get { return this.tipo; }
            set { this.tipo = value; }
        }

        public bool Andavel
        {
            get { return this.andavel; }
            set { this.andavel = value; }
        }

       
        public Tile(int tipo, bool andavel, int x, int y, int larguraTile, int alturaTile)
        {
            this.Tipo = tipo;
            this.Andavel = andavel;
            this.retangulo = new Rectangle(x, y, larguraTile, alturaTile);
        }
       
        public bool Collide(Rectangle box)
        {
            return this.retangulo.Intersects(box);
        }
    }


Criando o Mapa

Um mapa de tiles pode ser representado por uma matriz:
0 0 0 0 0 0 0 0 0 0
0 1 1 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 0
0 1 1 0 1 1 0 1 1 0
0 1 1 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 0
0 1 1 0 1 1 0 1 1 0
0 1 1 1 1 1 1 1 1 0
0 1 1 1 1 1 1 1 1 0
0 0 0 0 0 0 0 0 0 0

public class Mapa
    {
        //imagem dos tiles
        private Texture2D imagemTiles;
        //conjunto de recortes da imagem dos tiles
        private Rectangle[] tileFrames;
        //matriz de tiles
        private Tile[,] tiles;
        //número de linhas da matriz de tiles
        private int linhas;
        //número de colunas da matriz de tiles
        private int colunas;
        //altura do tile
        private int tileAltura;
        //largura do tile
        private int tileLargura;
        //define o tipo de tile não andavel
        private const int TIPO_NAO_ANDAVEL = 0;

O mapa é formado por uma matriz de tiles. Por isso, devemos definir o número de linhas e colunas dessa matriz.
O mapa também possui uma imagem que representa o conjunto de tiles pelo qual ele é formado (tileset). Note que os tiles não possuem imagem, eles só definem a área da tela onde a imagem do mapa (tileset) será desenhada. Essa imagem é carregada através do método LoadContent.

public Mapa(int linhas, int colunas, int tileLargura, int tileAltura)
        {
            this.linhas = linhas;
            this.colunas = colunas;
            this.tileLargura = tileLargura;
            this.tileAltura = tileAltura;
            this.tiles = new Tile[linhas, colunas];
        }


        public void LoadContent(ContentManager content, string filename)
        {
            this.imagemTiles = content.Load<Texture2D>(filename);
        }


O atributo tileFrames representa um conjunto de recortes da imagem de tiles. Cada tipo de tile tem um recorte diferente.

//gera os recortes da imagem de tiles
        public void GerarTileFrames()
        {
            //quantidade de tiles na imagem
            int tileSprites = (this.imagemTiles.Width / this.tileLargura) *
                (this.imagemTiles.Height / this.tileAltura);

            this.tileFrames = new Rectangle[tileSprites];
            //indice do array de frames
            int indice = 0;
            //largura da imagem de tiles
            int width = this.imagemTiles.Width;
            //altura da imagem de tiles
            int height = this.imagemTiles.Height;

            for (int i = 0; i < width; i += this.tileLargura)
            {
                for (int j = 0; j < height; j += this.tileAltura)
                {
                    //cria os frames correspondentes a cada tipo de tile
                    this.tileFrames[indice] = new Rectangle(i, j, this.tileLargura, this.tileAltura);
                    indice++;
                }
            }
        }

Calculamos o número de tiles diferentes contidos na imagem. Percorremos a imagem criando os recortes correspondentes a cada tipo de tile.

Para carregar o mapa, passamos como parâmetro uma matriz de inteiros. Cada valor da matriz representa um tipo diferente de tile.

public void CarregarMapa(int[,] matriz)
        {
            /* testa se a matriz parâmetro tem
             * dimensões diferentes da matriz de tiles
             */
            if (matriz.GetLength(0) != this.tiles.GetLength(0) ||
                matriz.GetLength(1) != this.tiles.GetLength(1))
            {
                throw new Exception("Matriz não possui a mesma dimensão do Mapa");
            }
            //testa se a imagem dos tiles foi carregada
            if (this.imagemTiles == null)
            {
                throw new Exception("Imagem do mapa não foi carregada");
            }

            //gera os frames dos tiles
            this.GerarTileFrames();

            //posição x do tile
            int x = 0;
            //posição y do tile
            int y = 0;
            //tipo do tile que vai ser lido da matriz
            int tipo = -1;
            //indica se o tile pode ser atravessado
            bool andavel = true;

            for (int i = 0; i < this.linhas; i++)
            {
                for (int j = 0; j < this.colunas; j++)
                {
                    tipo = matriz[i, j];

                    if (tipo < 0 || tipo > this.tileFrames.Length)
                    {
                        throw new Exception("Tipo de tile inválido");
                    }
                    else
                    {
                        //testa se o tipo lido é não-andável
                        if (tipo == TIPO_NAO_ANDAVEL)
                            andavel = false;

                        //cria o tile na posição x y
                        this.tiles[i, j] = new Tile(tipo, andavel, x, y,
                            this.tileLargura, this.tileAltura);
                    }

                    x += this.tileLargura;
                }
                x = 0;
                y += this.tileAltura;
            }
        }

Definimos a posição x,y da tela onde cada tile será desenhado.
Para desenhar o mapa na tela, é só percorrer a matriz de tiles desenhando um tile de cada vez.

 //desenha os tiles na tela
        public void Draw(SpriteBatch batch)
        {
            for (int i = 0; i < this.linhas; i++)
            {
                for (int j = 0; j < this.colunas; j++)
                {
                    Rectangle destino = new Rectangle(
                        this.tiles[i, j].Retangulo.X,
                        this.tiles[i, j].Retangulo.Y,
                        this.tileLargura,
                        this.tileAltura);

                    int frame = this.tiles[i, j].Tipo;

                    batch.Draw(this.imagemTiles, destino, this.tileFrames[frame], Color.White);
                }
            }
        }
}


Usamos o método Draw() do SpriteBatch para definirmos como cada tile será desenhado na tela.
O primeiro parâmetro representa a imagem dos tiles e o segundo, a área onde a imagem será desenhada.
O terceiro parâmetro é o mais importante, porque define a área da imagem (recorte) que será usada na impressão. Usamos o frame correspondente ao tipo de tile que está sendo desenhado.

Usando o Mapa
Na Classe Game, adicione:
Mapa mapa;
        //estrutura do mapa
        int[,] matrizMapa =
        {
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
            {0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
            {0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
            {0, 1, 1, 0, 1, 1, 0, 1, 1, 0},
            {0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
            {0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
            {0, 1, 1, 0, 1, 1, 0, 1, 1, 0},
            {0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
            {0, 1, 1, 1, 1, 1, 1, 1, 1, 0},
            {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
        };

        //largura do tile
        const int LARGURA_TILE = 32;
        //altura do tile
        const int ALTURA_TILE = 32;

Definimos as dimensões dos tiles e criamos a matriz de construção do mapa.
protected override void Initialize()
        {
            //cria o mapa
            this.mapa = new Mapa(matrizMapa.GetLength(0),
                matrizMapa.GetLength(1), LARGURA_TILE, ALTURA_TILE);

            base.Initialize();
        }

        protected override void LoadContent()
        {
            spriteBatch = new SpriteBatch(GraphicsDevice);

            //carrega a imagem de tiles do mapa
            this.mapa.LoadContent(this.Content, "tiles");
            //carrega o mapa
            this.mapa.CarregarMapa(this.matrizMapa);
        }
       
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        protected override void Update(GameTime gameTime)
        {
           
            base.Update(gameTime);
        }
       
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.CornflowerBlue);

            this.spriteBatch.Begin();
            //desenha o mapa na tela
            this.mapa.Draw(this.spriteBatch);
            this.spriteBatch.End();
           
            base.Draw(gameTime);
        }

Arquivos Anexados
Download: TileGame.zip Download
Sobre o Autor

DenisMartins
Denis Martins
Não Definido

Clique para avaliar:

Comentários
" Denis, verifique seu cadastro aqui no portal e seu perfil no fórum. Se nada aparecer estranho, mande um e-mail para mim. jalf@sharpgames.net ok?"
Enviado por Jose Antonio Farias em 19/11/2009 8:49:43:
 
" Submeti esse artigo, mas meu nome não apareceu. Como sou novo aqui o que eu faço?"
Enviado por Denis Martins em 18/11/2009 17:08:56:
 

Adicione seu Comentário  Voltar
Translator
Logos do XBox 360, XNA e Games For Windows
Copyright 2010 por SharpgamesPolítica de Privacidade  |  Termos de Uso