BlogBlogs.Com.Br

quarta-feira, 25 de junho de 2008

ActionScript 3.0 Game Development - Tiles

Ainda sobre desenvolvimentos de jogos usando AS 3.0 este post é um exemplo de como carregar um cenário com tiles. Esta técnica é muito usada em games plataforma ou RPGs com de visão isométrica, onde vários quadrados (tiles) se unem e formam uma imagem.

O lugar e qual peça deve ser encaixada para formar o cenário é representado por uma matriz de coordenadas. Pensando dessa forma uma fase de Super Mario Bros nada mais é que um enorme vetor multidimensional :P

Antes de codificar no flash é preciso um arquivo png com os tiles que formarão seu cenário. No Google imagens é possível encontrar esses arquivos rapidamente. Para este exemplo vou usar os tiles de um dos games de Zelda (Zelda tileSet).
Note as dimensões do bloco de tiles: 256 x 2272 pixels. Esse bloco é formado por tiles de 32 x 32 pixels. Sendo assim podemos mapear esta figura como uma matriz de 7 colunas por 70 linhas onde cada elemento é um tile (tendo a primeira coluna como 0 ).

O trabalho seria muito grande para montar um cenário escolhendo os tiles e anotando sua respectiva posição para gerar a matriz de referencia da cena. Por esta razão vamos usar um editor de mapas que alem de facilitar as coisas evita alguns erros.

Atualmente estou usando o Mappy que bem simples e gratuito ( http://www.tilemap.co.uk/ ).

1- No editor de mapas, click em “new map” e escolha 32 x 32 pixels para o tamanho de cada tile e 3 x 3 para o tamanho do mapa em si .
Após gerar seu mapa click em File>Import e escolha o tileset do zelda e crie seu mapa.
Escolha “Custom>Export Flash ActionScript”, para o valor de ajuste coloque “-1” pois o Mappy cria sempre um tile vazio no tileset.
O arquivo ActionScript gerado é um vetor multidimensional com os números dos tiles usados no mapa.

2- No flash crie um novo arquivo para ActionScript 3.0. E vamos aos códigos.

var loader:Loader;
var screenBitmapData:BitmapData;
var tilesBitmapData:BitmapData;
var screenBitmap:Bitmap;
var mapRows:int=3;
var mapCols:int=3;
var tileSize:int=32;
var mapWidth:int=mapRows*tileSize;
var mapHeight:int=mapCols*tileSize;

loader = responsável por carregar o tileset do zelda.
tilesBitmapData = armazena o tileset após ser carregado.
screenBitmapData = armazena os tiles montados.
screenBitmap = armazena os tiles montados para a exibição na tela.
mapRows e mapCols = tamanho a matriz .
mapWidth e mapHeight = tamanho do mapa.

var mapArray:Array = new Array(mapRows);
for (var i = 0; i < mapRows; i++) {
mapArray[i] = new Array(mapRows);
}

mapArray = ([
[416,417,418],
[424,425,426],
[432,433,434]
] );

Declaração de um array de duas dimensões (matriz 3 x 3). E atribuição da matriz gerada pelo Mappy ao vetor multidimensional.

tiles();
function tiles() {
loader = new Loader();
loader.contentLoaderInfo.addEventListener
(Event.COMPLETE,loadComplete);
loader.load(new URLRequest("zelda_tile.png"));
}

Instanciamos o objeto da classe Loader, e adicionamos um Listener que chamará a função loadComplete quando zelda_tile.png for carregado. O arquivo zelda_tile.png deve estar mesma pasta que o arquivo swf.

function loadComplete(e:Event):void {
tilesBitmapData=Bitmap(loader.content).bitmapData;
copyTiles();
}

Como não podemos manipular pixels pelo objeto loader , copiamos o tileset para um objeto da classe BitmapData. (Para passar o arquivo png para o objeto BitmapData temos que chamar loader.content como Bitmap e usar a propriedade bitmapData)

function copyTiles():void {

screenBitmapData=new BitmapData
(mapWidth,mapHeight,false,0x000000);
for (i=0; i < mapRows; i++) {

for (var j=0; j< mapCols; j++) {
var tileNum:int=int(mapArray[i][j]);
var destY:int=i*tileSize;
var destX:int=j*tileSize;
var sourceX:int=(tileNum % 8)*tileSize;
var sourceY:int=(int(tileNum/8))*tileSize;

screenBitmapData.copyPixels
(tilesBitmapData,newRectangle
(sourceX,sourceY,tileSize,tileSize),
new Point(destX,destY));
}

}
screenBitmap=new Bitmap(screenBitmapData);
addChild(screenBitmap);
screenBitmap.x=32;
screenBitmap.y=32;
}

O objeto screenBitmapData é instanciado nas dimensões de 96 x 96 pixels, sem transparência, e com fundo preto.

O loop for é usado para varrer o vetor e encontrar o número de cada tile e partir desde a referência de como montar o mapa.

destY e destX = guardam o ponto X e Y de onde um tile deve ser colocado. Por exemplo: O tile [0] [0] terá sua extremidade esquerda colocada nos pontos 0, 0.

sourceX e sourceY= guardam o ponto X e Y de um tile a ser usado. Exemplo : O elemento [0][0] é o tile número 416. Para X temos (416 mod 8) que é zero multiplicado pelo tamanho do tile. Desta forma encontramos em que coluna está o tile 416. ( O resto da divisão de 416 por 8 é zero) . Em Y temos 1664 (a parte inteira da divisão de 416 por 8 multiplicado pelo tamanho do tile).
Assim o elemento [0][0] é o tile 416 e esta no pixel 0 em X e o pixel 1664 em Y.

Os tiles são copiados um a um para o BitmapData passando para método copyPixels a origem e o destino dos pixels.

No final apenas instaciamos o Bitmap com o BitmapData já pronto e adicionamos no Display list
O resultado dessa experiencia está abaixo:

4 comentários:

Anônimo disse...

da um erro no newRectangle...
gostei muito.. mais tah dando esse erro...

4Ndr3 4nD3r1 disse...

qual erro?

Anônimo disse...

Aqui tb deu erro no newRectangle

screenBitmapData.copyPixels
(tilesBitmapData,newRectangle
(sourceX,sourceY,tileSize,tileSize)
,
new Point(destX,destY));

4Ndr3 4nD3r1 disse...

esse erro ocorre pois você deve estar usando um tile set diferente. Neste caso vc deve usar as dimensões corretas...
Não é possivel criar um tile de uma área que não existe no tile set.