dissabte, 11 d’abril del 2009

Cas d'estudi: Gauntlet - Part IV: El mapa

Per fí anem a escriure codi del joc!

Comencem pel mapa. Per a gestionar el mapa he creat una clase LevelProcesor que s'encarregarà de carregar i pintar el mapa, així com de respondre als personatges que hi ha en cert tile.

El primer que vaig a fer es que es pinte el mapa que havem creat amb l'editor. Per a tal cosa, he ficat el arxiu del mapa dins un arxiu de recursos com en l'Arounders. Per a obtindre'l faig una cridada a GetBufferFromResource, de la API de lectura/escritura que tinc, que me tornarà un punter a un array de char. Eixe array el procese ficant el seu contingut dins de l'array que manté el mapa de blocs:

void LevelProcesor::LoadLevel(int level) {
int tamany = 0;
char *buffer = GetBufferFromResource("prova.lev", &tamany);
int contador = 3;

for (int y=0; y<32; y++) {
for (int x=0; x<32; x++) {
mapBlock[y][x] = buffer[contador];
contador++;
}
}
UpdateMapVis();
}

Com podeu vore, fique el contador a 3, per a botar-me els primers 3 bytes, que contenen el nombre de versió, i els dos bytes de tilesSet i color, que per ara no mire. També cride al mètode que actualitza el mapa visible.

A partir d'ara, per a parlar del mapa intern diré el mapBlock, mentre que per a parlar del mapa de tiles visibles diré el mapVis.

void LevelProcesor::UpdateMapVis() {
int tile = 0;

for (int y=0; y<32; y++) {
for (int x=0; x<32; x++) {

if (mapBlock[y][x] == 0) {
tile = 16;
if (x - 1 >= 0 && mapBlock[y][x-1] == 1) tile += 1;
if (x - 1 >= 0 && y + 1 <= 31 && mapBlock[y+1][x-1] == 1) tile += 2;
if (y + 1 <= 31 && mapBlock[y+1][x] == 1) tile += 4;
} else if (mapBlock[y][x] == 1) {
tile = 0;
if (y - 1 >= 0 && mapBlock[y-1][x] == 1) tile += 1;
if (x + 1 <= 31 && mapBlock[y][x+1] == 1) tile += 2;
if (y + 1 <= 31 && mapBlock[y+1][x] == 1) tile += 4;
if (x - 1 >= 0 && mapBlock[y][x-1] == 1) tile += 8;
}
mapVis[y][x] = tile;
}
}
}

El calcul de quin tile li toca a una paret es prou simple. El que ens importa es si té alguna paret a algún costat en horitzontal o en diagonal, així que asigne potències de dos a cada costat i el que faig es sumarles segons quines parets trobe. Aixó ens dona 16 combinacions, que ficaré en el bitmap de tiles en ordre:


Amb el piso es similar, però com el que canvia es si té alguna paret als costats en que fan sombra, nomes m'importen els costat de l'esquerra, baix, i esquerra-baix en diagonal, donant-me un total de 8 combinacions que també ficaré en ordre en el mapa de tiles:


Mmm, me pareix que algú vol un biberó. Ara vinc...


Ale seguim. Per a pintar el mapa, bàsicament faré un bucle pintant 13 tiles a partir d'un offset:


for (int j=0; j<=12; j++) {
for (int i=0; i<=12; i++) {
tiles->Draw((i<<4)-(offsetX%16),
4+(j<<4)-(offsetY%16),
16,
16,
(mapVis[j+(offsetY>>4)][i+(offsetX>>4)]%16)<<4,
(mapVis[j+(offsetY>>4)][i+(offsetX>>4)]>>4)<<4,
16,
16,
0,
0,
255,
actualColorSchemes[mapVis[j+(offsetY>>4)][i+(offsetX>>4)]>>4].red,
actualColorSchemes[mapVis[j+(offsetY>>4)][i+(offsetX>>4)]>>4].green,
actualColorSchemes[mapVis[j+(offsetY>>4)][i+(offsetX>>4)]>>4].blue
);
}
}

Per recordar-ho: els 4 primers paràmetres son la X, Y, ample i alt del tile en el destí, els 4 següents el mateix pero en l'origen. Els següents son l'angle de rotació, si fa espill i el canal alfa.

Els tres últims paràmetres son el RGB amb que anem a tintar el tile. En el bmp he deixat els tiles en blanc i negre, així els puc aplicar un tint i tinc més varietat de tiles canviant-los el color. Per ara he deixat 8 colors: roig, verd, blau, groc, taronga, morat, gris oscur i gris clar.


Tornant al pintat del mapa, no ens interesa pasar-li uns offsets i au. El que ens interessa es pasar-li entre una i quatre coordenades (x,y) (una per jugador) i que la pantalla es centre en eixes coordenades.

Per a aconseguir-ho, buscarem la x major, la x menor la y major i la y menor. El punt central serà (per a la X, la Y es igual):

migX = ((maxX-minX)/2)+minX

Al punt mig, restant-li la meitat dels pixels que ocupa la pantalla visible (32 tiles * 16 pixels = 192 pixels / 2 = 96 pixels) tindrem el punt des d'on començar a pintar. Abans de pintar, restringirem que eixe punt estiga dins dels límits (si la X calculada es menor que zero, la ficarem a zero, etc...).

Una aclaració, per si algú ja no recorda el tema dels scrolls. La pantalla visible son 12 tiles, pero en pinte un més perque durant la transició de un tile a altre, en pantalla hi ha 11 tiles sencers, i el primer i últim a meitant. Per tant, eixe tile 13.

Per últim, ens queda retallar eixe tile, que es pinta sencer, pero nomes en volem un trocet. Si el scroll fora a pantalla sencera no hi hauria problema, ja que la part de tiles que no volem estaria fora de la pantalla, pero la pantalla visible es de 192x192.



Aixó ens donarà 4 pixels per dalt i per baix a retallar, i 16 pixels per la dreta (per l'esquerra estem a ras de pantalla). Per a cobrir estos trocets no desitjats pinte damunt d'ells un poligon negre simple (un per cada costat).


Be, el següent es ficar els personatges en marxa. Vaig a rippejar els sprites i a la feina. Com a resenya, dir que m'ha costat mes del doble escriure el post que la feina que explique :-P. Ale, un shot, que quasi se m'oblida:

4 comentaris:

  1. llegit. Tinc pendent comentar les 2 entrades i un mail. Ah, i acabar un jailgame este estiu. Llevat del jailgame, lo altre espere aclarir-ho en tindre 2 minuts de tranquilitat a casa.

    Escrito desde mi aipod en una wifi del carrer :D

    Pd. Que mono guillem! :)

    ResponElimina
  2. Gonna instalar el SDL eixe, supose que he deseguir el tutorial de la wikijailer ...

    ResponElimina
  3. Ale, a descarregarse el Win32 SDK que son 1,2 GB, ho deixe fent feina i demà vorem que ha fet.

    Al final m'eixirà a compte instalar el Visual Studio i au, que crec que el tenia per no se on que li'l vaig pillar per al meu germà.

    ResponElimina
  4. Estic en ànsia viva per saber més :D

    ResponElimina