Você está na página 1de 10

[XNA] TileMap

3 Votes

Antes de comearmos a programar tilemaps vamos ver algumas definies.

Tiles:

Tiles so pequenos pedaos de imagens que servem para criar uma nova imagem
composta conhecida como layer, utilizadas para criar o cenrio de um jogo. Essa tcnica
chamada de TileMaps, no qual, atravs de uma imagem contendo diversos pedacinhos
de imagens (tiles), se pode criar um cenrio para um jogo. Exemplos clssicos do uso de
TileMaps, so em jogos de RPG (Role Playing Game). Os jogos de RPG geralmente
possuem cenrios extensos o qual se fosse feito com uma nica imagem ocuparia muito
espao de memria e naquela poca em que surgiram, as memrias eram bem limitadas.
Ento a tcnica de dividir o cenrio em tiles e recri-lo em tempo de processamento fez
essa tcnica ser muito utilizada at hoje em dia.

Como esta tcnica funciona?

A primeira etapa definir os tiles, ou seja, seu tamanho e suas imagens dentro dele.

A segunda parte montar o Tilemap (tambm conhecido como bricks), ou seja, definir
a estrutura que vai descrever aquele cenrio do nosso jogo.
E por fim em tempo de execuo nosso algoritmo deve substituir os nmeros da matriz
de tilemap pelas posies correspondentes do nosso tile. Esse cenrio conhecido como
layer.

Projeto:

Ento para fixarmos a parte terica vamos implementar um mapa bsico para um jogo
do Pacman, inicie um novo projeto e coloque o nome de Pacman.

Criado o projeto vamos ento criar uma classe com o nome Tile.cs que servir para
definirmos as propriedades dos tiles do jogo. Veja como vai ficar a classe.

01 using System;
02 using System.Collections.Generic;
03 using System.Linq;
04 using System.Text;
05 using Microsoft.Xna.Framework.Graphics;
06 using Microsoft.Xna.Framework;
07 namespace TileMap
08 {
09 /// <summary>
/// Controla o estado entre os tiles para ver se existe ou no
10
coliso
11 /// </summary>
12 public enum TileCollision
13 {
14 /// <summary>
15 /// Define que o tile passavel
16 /// </summary>
17 Passable,
18 /// <summary>
19 /// Define que o tile um slido impassavel, ou seja,
20 /// existe coliso caso tentem passar por este tile.
21 /// </summary>
22 Impassable
23 };
24
25 /// <summary>
26 /// Definio da classe tile
27 /// </summary>
28 public class Tile
29 {
30 public Texture2D texture;
31 public TileCollision collision;
32 public readonly Vector2 size;
33 /// <summary>
34 /// Construtor de um novo tile
35 /// </summary>
36 public Tile(Texture2D texture, TileCollision collision)
37 {
38 this.texture = texture;
39 this.collision = collision;
this.size = new Vector2(this.texture.Width,
40
this.texture.Height);
41 }
42 }
43 }

Essa classe define a imagem do tile, seu tamanho que conforme o tamanho da imagem
e se existe ou no coliso.

Agora clique com o boto direito e faa download das imagens abaixo.

Tiles:

block00.png

block01.png

block02.png

block03.png

Sprite:
ghost.png

Logo aps fazer o download das imagens as carregue em seu projeto conforme o
Solution Explorer abaixo.

Agora na nossa classe Game1.cs vamos carregar nossas imagens, mas para isso vamos
criar uma lista da nossa classe Tile e um Texture2D para carregar a imagem do
fantasmas (personagem do nosso jogo). Adicione ento as seguintes variveis:

1 // Texture a ser utilizada para o personagem do tutorial


2 Texture2D character;
3 // Lista de tiles para montar o cenrio
4 List<Tile> tiles;

E ento no mtodo LoadContent vamos carregar as imagens.

01 protected override void LoadContent()


02 {
03 spriteBatch = new SpriteBatch(GraphicsDevice);
04 // Criando a lista de tiles
05 tiles = new List<Tile>();
06 // Adicionando os tiles a lista
tiles.Add(new Tile(Content.Load<Texture2D>(@"Tiles\block00"),
07
TileCollision.Passable));
tiles.Add(new
0
Tile(Content.Load<Texture2D>(@"Tiles\block01"), TileCollision.Passab
8
le));
tiles.Add(new Tile(Content.Load<Texture2D>(@"Tiles\block02"),
09
TileCollision.Passable));
tiles.Add(new Tile(Content.Load<Texture2D>(@"Tiles\block03"),
10
TileCollision.Impassable));
11 // Carrega a textura do personagem
12 character = Content.Load<Texture2D>(@"Sprites\ghost");
13 }

Com nossas imagens carregadas precisamos agora criar a estrutura do nosso mapa. E
nosso mapa vai ser da seguinte forma:

Propriedades do mapa:

Matriz de nmeros inteiros de 28 x 25


Valor0 para block00
Valor1 para block01
Valor2 para block02
Valor3 para block03 e somente este bloco impassvel.

Abaixo ento de onde colocamos nossas variveis declare nosso mapa conforme o
cdigo abaixo.

01 /// <summary>
02
03 /// Este o brick ou seja nossa estrutura do mapa para nosso jogo
/// Os nmeros sero substituidos pelos tiles adcionados a nossa
04
lista
05 /// </summary>
06 int[,] map = {
07 {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},
08 {3,2,2,2,2,2,2,2,2,2,2,2,2,3,3,2,2,2,2,2,2,2,2,2,2,2,2,3},
09 {3,1,3,3,3,3,3,2,3,3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,3,3,1,3},
10 {3,2,3,3,3,3,3,2,3,3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,3,3,2,3},
11 {3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3},
12 {3,2,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,2,3},
13 {3,2,2,2,2,2,2,2,3,2,2,2,2,3,3,2,2,2,2,3,2,2,2,2,2,2,2,3},
14 {3,3,3,3,3,3,3,2,3,3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,3,3,3,3},
15 {0,0,0,0,0,0,3,2,3,2,2,2,2,2,2,2,2,2,2,3,2,3,0,0,0,0,0,0},
16 {0,3,3,3,3,3,3,2,3,2,3,3,3,0,0,3,3,3,2,3,2,3,3,3,3,3,3,0},
17 {0,0,0,0,0,0,0,2,2,2,3,0,0,0,0,0,0,3,2,2,2,0,0,0,0,0,0,0},
18 {0,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,0},
19 {0,0,0,0,0,0,3,2,3,2,2,2,2,2,2,2,2,2,2,3,2,3,0,0,0,0,0,0},
20 {3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3,3,2,3,2,3,3,3,3,3,3,3},
21 {3,2,2,2,2,2,2,2,2,2,2,2,2,3,3,2,2,2,2,2,2,2,2,2,2,2,2,3},
22 {3,2,3,3,3,3,3,2,3,3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,3,3,2,3},
23 {3,2,3,3,3,3,3,2,3,3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,3,3,2,3},
24 {3,2,2,2,2,2,3,2,2,2,2,2,2,0,0,2,2,2,2,2,2,3,2,2,2,2,2,3},
25 {3,3,3,3,3,2,3,2,3,2,3,3,3,3,3,3,3,3,2,3,2,3,2,3,3,3,3,3},
26 {3,3,3,3,3,2,3,2,3,2,3,3,3,3,3,3,3,3,2,3,2,3,2,3,3,3,3,3},
27 {3,2,2,2,2,2,2,2,3,2,2,2,2,3,3,2,2,2,2,3,2,2,2,2,2,2,2,3},
28 {3,2,3,3,3,3,3,2,3,3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,3,3,2,3},
29 {3,1,3,3,3,3,3,2,3,3,3,3,2,3,3,2,3,3,3,3,2,3,3,3,3,3,1,3},
30 {3,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,3},
31 {3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3},
32 };

NOTA: O mapa no precisa ser inicializado desta maneira, voc pode carregar sua
matriz atravs de um arquivo.

Antes de desenharmos nosso mapa e personagem vamos definir uma vriavel de


posio, velocidade e direo para nosso personagem. Veja abaixo como fica a
declarao das variveis

1 // Posio do personagem
2
3 Vector2 position = new Vector2(324.0f, 408.0f);
4 // Velocidade do personagem
5 float speed = 2.0f;
6 // Direo de movimento do personagem
7 Vector2 direction = new Vector2(0.0f, 0.0f);

Com todas essas variveis declaradas, agora podemos desenhar para ver o resultado e
depois trabalhar na movimentao e coliso do personagem. Veja o mtodo de desenho
como fica.

01 protected override void Draw(GameTime gameTime)


02 {
03 GraphicsDevice.Clear(Color.Black);
04 spriteBatch.Begin();
05 // Desenhando o mapa do jogo
06 for (int i = 0; i < map.GetLength(0); i++)
07 {
08 for (int j = 0; j < map.GetLength(1); j++)
09 {
10 spriteBatch.Draw(tiles[map[i, j]].texture,
new Vector2(j * tiles[map[i, j]].size.X, i *
11
tiles[map[i, j]].size.X),
12 Color.White);
13 }
14 }
15 // Desenhando o personagem
16 spriteBatch.Draw(character, position, Color.White);
17 spriteBatch.End();
18 base.Draw(gameTime);
19 }
Se voc executar o programa agora poder ver o mapa e o nosso personagem desenhado
na tela, mas sem movimento. Para criarmos seu movimento precisamos fazer como j
fizemos em diversos tutoriais.

01 protected override void Update(GameTime gameTime)


02 {
03 // Recebe o estado do teclado
04 KeyboardState keyboardState = Keyboard.GetState();
05 // Verifica se o jogador pressionou seta para cima
06 if (keyboardState.IsKeyDown(Keys.Up))
07 {
08 this.direction.Y = -1;
09 }
10 // Verifica se o jogador pressionou seta para baixo
11 if (keyboardState.IsKeyDown(Keys.Down))
12 {
13 this.direction.Y = 1;
14 }
15 // Verifica se o jogador pressionou seta para direita
16 if (keyboardState.IsKeyDown(Keys.Right))
17 {
18 this.direction.X = 1;
19 }
20 // Verifica se o jogador pressionou seta para esqueda
21 if (keyboardState.IsKeyDown(Keys.Left))
22 {
23 this.direction.X = -1;
24 }
25 // Atualiza a posio do personagem
26 this.position += this.direction * this.speed;
27 base.Update(gameTime);
28 }

Legal, se executar o nosso joguinho agora ver poder movimentar o fantasma, porm
ele esta atravessando a parede e andando em diagonal, ou seja, precisamos corrigir isso.
Para corrigirmos isso vamos ter que verificar se existe coliso do personagem com o tile
que ele esta no momento. Ento vamos criar um mtodo para verificar isso. Abaixo do
mtodo de desenho, digite o seguinte cdigo:

01 /// <summary>
02 /// Mtodo que detecta coliso
03 /// </summary>
04 /// <param>posio y do tile</param>
05 /// <param>posio x do tile</param>
06 /// <returns></returns>
07 public bool CheckTileCollision(int x, int y)
08 {
09 if (tiles[map[y, x]].collision != TileCollision.Impassable)
10 {
11 return false;
12 }
13 return true;
14 }

Agora temos um mtodo que a partir de um ponto X e Y de tile ele verifica se o tile no
passvel. Vamos ento utilizar esse mtodo aps tentarmos movimentar nosso
personagem no mtodo Update(). Digite o seguinte cdigo antes de atualizarmos a
posio do personagem:

01 protected override void Update(GameTime gameTime)


02 {
03 .
04 .
05 .
06 .
07 // Define a nova posio do personagem
08 Vector2 newPosition = this.position;
09 newPosition.X += this.direction.X * speed;
10 newPosition.Y += this.direction.Y * speed;
// Define os tiles vizinhos do personagem conforme a nova
11
posio
12 int left = (int)(newPosition.X / 24);
13 int top = (int)(newPosition.Y / 24);
14 int right = (int)((newPosition.X + (character.Width - 1)) / 24);
int bottom = (int)((newPosition.Y + (character.Height - 1)) /
15
24);
// Verifica coliso do personagem com todos os tiles vizinhos
16
definidos ao deslocar-se
if (this.direction.Y == -1) // Se o personagem esta indo para
17
Cima
18 {
if (CheckTileCollision(left, top) ||
19
CheckTileCollision(right, top))
20 {
21 this.direction.Y = 0;
22 }
23 }
else if (this.direction.Y == 1) // Se o personagem esta indo para
24
baixo
25 {
if (CheckTileCollision(left, bottom) ||
26
CheckTileCollision(right, bottom))
27 {
28 this.direction.Y = 0;
29 }
30 }
if (this.direction.X == 1) // Se o personagem esta indo para
31
direita
32 {
if (CheckTileCollision(right, top) ||
33
CheckTileCollision(right, bottom))
34 {
35 this.direction.X = 0;
36 }
37 }
else if (this.direction.X == -1) // Se o personagem esta indo
38
para esquerda
39 {
if (CheckTileCollision(left, top) ||
40
CheckTileCollision(left, bottom))
41 {
42 this.direction.X = 0;
43 }
44 }
45 // Verifica se o personagem passou a tela pela esquerda
46 if (this.position.X < 24.0)
47 {
48 this.position.X = 624.0f;
49 }
50 // Verifica se o personagem passou a tela pela direita
51 if (this.position.X > 624.0)
52 {
53 this.position.X = 24.0f;
54 }
55 // Atualiza a posio do personagem
56 this.position += this.direction * this.speed;
57 base.Update(gameTime);
58 }

O que fizemos ento criamos uma varivel que recebe a nova posio que o personagem
vai receber, ento verificamos esta posio dentro do nosso mapa de tiles se ela ocupa
algum espao que existe tiles impassveis com a funo CheckTileCollision(int x, int y)

Se voc acompanhou corretamente todos os passos e no digitou nada errado e resultado


para ser um fantasma se movimentando na tela colidindo com o cenrio, conforme
imagem abaixo.
Para fazer download do cdigo completo clique aqui.

Abraos a todos e espero que tenham gostado. At a prxima.