Tuesday, February 27, 2007

[TUT] Tile Based Games, Maps bouwen

Tile Based Games, wat zijn dat? De meest bekende games, zoals Super Mario zijn Tile Based(TB) games. Wat houd het nou precies in? Het idee erachter is om met name de map op te delen in vierkantjes (de tiles). Want zonder dat je het echt door hebt, bestaan de meeste mappen uit herhalende patronen. Waarom zou je de hele map tekenen, terwijl je hetzelfde krijgt door het op te delen in aan elkaar passende delen? Misschien klinkt dit een beetje vaag, daarom een afbeelding om het misschien wat duidelijker te maken:

Zoals je ziet loopt alles naadloos over. Omdat Flash Object Georiënteerd Programmeren, in engels ook wel OOP, is dit ideaal. In dit artikel leer je met Flash ActionScript hoe je simpel mappen kan laten opbouwen, die makkelijk te veranderen zijn, en tips en tricks over de eigenschappen van een tile(bijvoorbeeld of je er op kan lopen of niet). Ik ga er al wel van uit dat je al een redelijke basis kennis hebt met ActionScript, want ik leg alleen de principes uit, en ga niet stap voor stap naar een resultaat toe werken.

De map

Eerst voor de gene die er nog nooit mee hebben gewerkt een korte uitleg over het opbouwen van een map uit een array. Deze vullen we met cijfers, elk cijfers staat voor een andere tile. De array is multidimensionaal, want het bevat rijen en kolommen. Een lege map met een muurtje aan de rand kan er bijvoorbeeld zo uit zien:

var map1:Array =
[["1", "1", "1", "1", "1"],
["1", "0", "0", "0", "1"],
["1", "0", "0", "0", "1"],
["1", "0", "0", "0", "1"],
["1", "1", "1", "1", "1"]];

Deze array willen we omzetten in een map op de stage. Hiervoor moet je eerst de movieclips van je tile's in de library zetten. Ikzelf gebruik altijd een linkage van 'tile0' voor de tile die bij het cijfer 0 hoort en 'tile1' voor degene die bij nummer 1 hoort. Dit maakt het zo makkelijk om het heel dynamisch te maken.


Waneer je dit hebt moet je de array doorspitten en voor elk nummer de juiste tile plaatsen. Dit doorspitten doe je met behulp van 2 for-loops. Een voor de rij, en daar binnen een voor de kolom.

for(var row:Number = 0; row < mapArray.length;row++){
for(var column:Number = 0; column < mapArray[0].length;column++){

}
}

Hij zal dus eerst de eerste rij afgaan, en daarbinnen alle kollommen, daarna gaat hij naar de 2e rij en zoekt daar weer door alle kollomen.
Binnen deze for-loop zorg je dan dat aan de hand van de waarde in de array de juiste tile op de juiste plaats word gezet. Het attachen gaat dan zo:

for(var row:Number = 0; row < mapArray.length;row++){
for(var column:Number = 0; column < mapArray[0].length;column++){

this.attachMovie("tile"+mapArray[row][column],"tile_"+row+"_"+column,this.getNextHighestDepth());

}
}

Voor de positie is het anders als je zou denken, dit komt namelijk doordat for-loops erg snel gaan. In eerste instantie zou je denken om gewoon de huidige rij te vermenigvuldigen met de hoogte van een tile. Dit is ook wel zo, het probleem is alleen dat je eerst de rij op moet slaan in de movieclip zelf, en dan via this.variabele hem op moet vragen. Doe je dit niet, worden de waarden pas toegekend wanneer de for-loop is afgelopen, en krijgen ze allemaal de positie van de laatste tile. Het idee van hoe het wel moet zie je hieronder:

this.attachMovie("tile"+arr[row][column], "tile_"+row+"_"+column, this.getNextHighestDepth());
this["tile_"+row+"_"+column].row = row;
this["tile_"+row+"_"+column].column = column;
this["tile_"+row+"_"+column]._x = this["tile_"+row+"_"+column].column*tileSize;
this["tile_"+row+"_"+column]._y = this["tile_"+row+"_"+column].row*tileSize;

Hoe dit stap voor stap gebeurt zie je hier:

Zoals je al ziet bij het attachen is het belangrijk dat je een bepaalde structuur hebt in de naamgeving van de MC's in de library en de geattachte MC's. Hieronder zal ik nog een simpel trucje bespreken om later, wanneer je bijvoorbeeld een poppetje toe gaat voegen, op de vragen welke tile er op een bepaalde positie is, en of hij een muur, of grond is.

Tile detectie

Hoe kan je makkelijk nagaan op welke positie welke tile zich bevind? Als je het goed hebt gedaan zal dit niet zo moeilijk zijn. wanneer je de naamgeving hebt gebruikt die ik hierboven heb aangeraden, is het zelfs simpel. De eerste tijd dat ik met TB games bezig was pushte ik alle coördinaten van de tile's in een array, en liet deze elke keer weer door spitten om te kijken binnen welke waarden de te checken waarde zat. Dit is natuurlijk vrij onhandig, en CPU vretend. Hier is een trucje voor:
Je deelt de x positie van het punt door de breedte van de tile's, en rond dit af naar beneden. Je weet dat de zoveelste tile in de rij is. Om na te gaan in welke rij hij zich bevind doe je natuurlijk hetzelfde alleen dan met de y waarde. Klink logisch toch? Mocht dat het niet zijn nog een voorbeeldje:

Je hebt tiles van 50 px breed. Welke tile bevind zich op positie (220,65)?
Kolom = Int(220/50);
Rij = Int(65/50);
Wanneer je dit weet, en je systematisch met je naamgeving om bent gegaan, kan je zo een simpele functie maken om de instancename van een tile op een bepaalde positie terug te geven:

function giveTile(x, y):MovieClip {
return this["tile_"+(Math.floor(x/tileSize))+"_"+(Math.floor(y/tileSize))];
}

Met deze wetenschap kan je tiles een bepaalde variabele meegeven, waaraan je af kunt lezen of deze tile een muur of grond is. Je zou dit bijvoorbeeld zo kunnen doen, bij het attachen:

this.attachMovie("tile"+arr[row][column], "tile_"+row+"_"+column, this.getNextHighestDepth());
this["tile_"+row+"_"+column].walkable = true;

Een nieuwe functie om te checken of een bepaalde positie loopbaar is is dan ook niet moeilijk meer:

function isWalkable(x, y):Boolean {
return this["tile_"+(Math.floor(x/tileSize))+"_"+(Math.floor(y/tileSize))].walkable;
}


Dit was het wat betreft het bouwen van maps. Zoals je waarschijnlijk al wel door hebt zijn TB games ideaal, aangezien je geen CPU vretende berekeningen nodig hebt om dingen te checken, en maps eenvoudig te veranderen zijn. Binnenkort zal ik in een nieuw artikel doorgaan op tilebased games, ik hoop je tot nu toe al een behoorlijke basis te hebben gegeven over de wereld van de TB games, waar je met minder meer kan krijgen!

1 comment:

Anonymous said...

Hey,

De tutorial is erg goed. Toegankelijk voor iedereen die een redelijke flashbasis heeft.

Zelf heb ik nog geen ervaring met tile-based games, maar dat zal ik eens ga proberen nu ik je artikel (tut) uit heb. Overigens wel in AS3.0, want daar gaat veel allemaal nog veel sneller ;-) kijk maar eens naar het verschil tussen particle classes in AS2.0 en 3.0. AS3 kan veel meer particles tegelijk aan:)

voor het script dat je hier gebruikt hoef je geloof ik nauwelijks iets om te zetten om er een AS3.0 script van te maken, dus ik ga er binnenkort lekker mee kloten;)
thanx =)

Ruben