dilluns, 11 de maig del 2009

Cas d'estudi: Super Mario Bros - Part II: Estructura de joc i scroll

Be, açò es el que vaig fer el divendres, pero me vaig quedar sense temps de escriure.

Per a començar, he afegit algunes funcions a les APIs. Anem a repasar els canvis:
JDraw
- He afegit funcions per a carregar una font: JD_Font JD_LoadFont( char *file, int width, int height); carregarà una font a partir d'un bitmap i li especificarà que els caracters son de widthxheight. El bitmap que espera tindrà, en fila hitzontal, els caracters a partir del #32, que es el espai, en avant, seguint la codificació ASCII. Jo, en el meu bitmap, nomes he ficat els numeros (#48-#57) i les lletres majuscules (#65-#90), pero si es vol es poden ficar tots, seguint inclus més avant.
- Altra funció per a pintar text: void JD_DrawText( int x, int y, JD_Font *source, char *text); pintarà el text especificat en "text" en la posició (x,y) usant la font "source" que haurem carregat amb la funció d'abans. Més avant ho prepararé per a que les fonts puguen ser multicolor.
- Una funció que ens torna els FPS. Aquesta funció torna un text, ja que normalment vull els FPS per a pintarlos en pantalla.

JGame
- Normalment, en un bucle de joc, hi ha dos parts ben definides: El pintat i l'actualització interna. El pintat solem deixar que vaja el més rapid posible, així que es pinta a cada pas del bucle. No obstant, l'actualització de vegades no la volem el més rapid posible, per dos raons: La primera perque potser es massa rapid, i la segona perque el joc aniría a diferents velocitats depenent de la potencia de la màquina. Així que l'actualització lo normal es només executar-la cada x milisegons. Per a estalviar-se ficar la lògica de control del temps pel mig del bucle, ho he traslladat al mòdul JGame amb dos funcions:
- void JG_SetUpdateTicks(Uint32 milliseconds);. Amb aquesta funció fixem el nombre de milisegons que deu passar entre actualització i actualització.
- bool JG_ShouldUpdate();. En cada bucle podem consultar aquesta funció per a saber si ja toca actualitzar o no. Així, en compte de controlar tot el tema del temps, nomes tenim que fer un "if" d'aquesta funció. Ara després ho vorem en el codí del Super Mario Bros.


Ale, ara anem a començar per fi amb el Super Mario. Com el titol endica, vorem la estructura bàsica del joc i la càrrega i pintat del mapa.


Per a començar, anem a vore com es la funció "main" del joc. Repasaré un mínim conceptes de C, per a qui faça anys que no ho toca:


#include "jgame.h"
#include "jdraw.h"
#include "modulegame.h"

Comencem per inclure les capçaleres que ens faràn falta. Ací nomes s'inluou el que fa falta ací, no tot el que fa falta durant el joc.


int main( int argc, char* args[] ) {

JG_Init("Super Mario Bros");
JD_Init(256, 240, false);

Per a començar, inicialitzem JGame i JDraw. Com podeu vorem he ficat la resolució de la NES, per a que siga igual que l'original.


int gameState = 0;

La variable gameState mantindrà l'estat en que es troba el joc. Aquest estat es correspondrà amb els mòduls del joc, com per exemple: intro, menu, joc, sequència... i eixes coses.


while (gameState != -1) {
switch (gameState) {
case 0:
gameState = ModuleGame_Go();
break;
}
}

Aquest es el bucle principal. L'unic que fà es executar-se mentres el estat de joc no siga "-1", que voldrà dir que havem decidit eixir del joc. En la pròxima actualització canviarè aquestos numerets per constants, així es veu tot més clar.

En cas de que gameState no siga "-1" executarà el bucle, dins el qual el que tenim es un switch del propi gameState, que el que fa es que segons el estat executa un mòdul concret. Per ara nomes està el mòdul del propi joc, així que es simple.

Dins de cada mòdul hi ha un altre bucle que s'executarà fins que ocorrega el que siga per a eixir d'eixe mòdul, tornant a aquest bucle principal.


JG_Finalize();

return 0;
}

Quan s'haja decidit eixir del joc, es finlaitza JGame. I ja està, aquesta es la funció main.

Si vos haveu fixat, havem inclos un tal "modulegame.h" i, a més, estem cridant a una tal "ModuleGame_Go()", que no sabem per on para. Anem a crear tot aixó ara. Comencem per l'arxiu "modulegame.h":


#pragma once

int ModuleGame_Go();

Ale, ha sigut fàcil. Com a recordatori: els arxius de capçalera (.h) mostren la interficie externa d'un mòdul. Lo normal es que els ".h" vagen acompanyats d'un ".c" o ".cpp". Per a entendren's sería com si el ".h" continguera l'interficie d'un objecte i els ".cpp" contingueren l'implementació. Així, els demés moduls enllaçen amb el ".h", i nomes veuen el que havem declarat ahí dins. Després en el ".cpp" tenim que implementar el que havem declarat en el ".h", però també podem declarar més variables i funcions, que no es podràn vore desde fora del mòdul.

Per tant, com del mòdul de joc només ens interessa que es puga cridar al propi mòdul i au, nomes publiquem aquesta funció. Ara dins del ".cpp" implementarem aquesta funció i altres funcions satel·lit.

Una última cosa: "#pragma once" es una directiva de compilació que el que diu es que aquesta capçalera nomes s'ha d'incloure una vegada. Es equivalent al tipic "#IFNDEF _MODULEGAME_H_ ....". El cas es que en C, cada vagada que inclous una llibreria, esta va i se torna a incloure, provocant errors després de simbols duplicats. Amb aquestes directives el que fem es dir-li que, per moltes vegades que referenciem al arxiu, nomes s'incloga una.

Ara anem a vore l'implementació de "modulegame.cpp":


#include "modulegame.h"
#include "jgame.h"
#include "jdraw.h"
#include "jinput.h"
#include "jfile.h"

Lo primer es incloure les APIs que ens fan falta. A més, sempre incloguem el ".h" que anem a implementar.


JD_Surface *tiles;
JD_Font fuente;
Uint8 tileMap[256][15];
Uint8 blocMap[256][15];
int scrollX = 0;

Ara declarem les variables que necessitarem a nivell global en el mòdul. "tiles" mantindrà el bitmap de tiles; "fuente" mantindrà la font que usarem per a escriure text; "tileMap" mantindrà el array de tiles a pintar; "blocMap" mantidrà els tiles de bloc; per últim, "scrollX" mantindrà la posició horitzontal del mapa. Aquesta variable es tempòral, fins que tingam sprites i tal.

Ara ve la definició de les funcions. Les funcions, quan no s'han definit en el ".h", han de ser definides en el ordre correcte: Si una funció A crida a una funció B, la funció B ha de ser definida abans. Com aixó es un poc lio per a explicar, jo vaig a anar parlant de les funcions de més externa a més interna. Després, en el codi que vos podeu baixar, ja estàn ordenades correctament.


int ModuleGame_Go() {

Init();

while (!JG_Quitting()) {

ModuleGame_Draw();
ModuleGame_Update();
}

Finalize();

return ( JG_Quitting() ? -1 : 0 );
}

Aquesta es la funció principal, a la que se crida desde fora. En ella, el primer que es fa es cridar a una funció "Init()" que s'encarregarà d'inicialitzar tot el necessari. Després comença el bucle. Per ara, nomes controla si volem eixir de l'aplicació, pero més avant controlarà que eixim nomes del mòdul (per a tornar al menú, per exemple). Dins del bucle, es crida a una funció per a pintar l'escena i altra per a actualitzar-la. En quan eixim del bucle, cridem a una funció de finalització que allibere la memòria i tot això.

Per últim, tornem un valor, que serà el que adquirirà la variable gameState per aanar a altre mòdul o eixir de la aplicació. Ara mateixa nomes es pot eixir de la aplicació i au. En el return el que he ficat es un "if reduït" d'eixos: Si s'acompleix la condició que hi ha abans de l'interrogant, torna el que hi ha després de l'interrogant. Sinó, torna el que hi ha després dels dos punts.

Anem a vore les funcions a les que havem cridat:


void Init() {
LoadMap();
tiles = JD_LoadSurface("tiles.gif");
fuente = JD_LoadFont("fuente.gif", 8, 8);
JG_SetUpdateTicks(1);
}

La funció d'inicialització crida a altra funció que carregarà el mapa. Després carrega el bitmap de tiles i la font que usarem per a pintar text. Per últim, fixa el temps d'actualizació cada 1 milisegons.


void LoadMap() {
int fileSize;
char *buffer = JF_GetBufferFromResource("mapa1_1.map", &fileSize);
int filePos = 1;

int filenameSize = buffer[filePos++];
char *fileName = (char *)malloc(filenameSize+1);
for (int i=0;i fileName[i] = buffer[filePos+i];
}
fileName[filenameSize] = '\0';

filePos = filePos + filenameSize;

for (int y=0; y<15; y++) {
for (int x=0; x<256; x++) {
tileMap[x][y] = buffer[filePos++];
}
}

for (int y=0; y<15; y++) {
for (int x=0; x<256; x++) {
blocMap[x][y] = buffer[filePos++];
}
}

delete fileName;
delete buffer;
}

Aquesta es la funció que carrega el mapa desde l'arxiu. Aquesta funció la refaré més avant, ja que vull incloure en JFile funcions per a que llegir d'un arxiu siga més simple, sense buffers i chars (si no es vol, clar).

El primer que fa es obtindre un punter a un array de char amb el contingut de l'arxiu. Ara el processarem char a char (char = 8 bits sense signe). Primer fiquem el punter a 1 (passem del char[0], que es el de la versió). Llegim el tamany del nom del bitmap. Sabent el tamany del nom del bitmap, declarem un punter a array de char (que es el que s'usa per a les cadenes en C) del tamany
que havem llegit + 1 (ja que les cadenes en C acaven amb el caracter 0). Aleshores llegim cada char. Per últim, fiquem l'ultim a zero i ja tenim la cadena del nom del bitmap. Per ara no la use, pero més avant carregaré els tiles d'aquest arxiu, en compte de a pel com havem vist en la funció Init().

Després augmente el punter tantes posicions com caracters tenia el nom, i ja estic en posició de llegir l'informació del mapa de tiles i del mapa de blocs. Per últim allibere la memòria dels punters.


void Finalize() {
JD_FadeOut();

JD_FreeSurface(tiles);
JD_FreeSurface(fuente.surface);
}

Quan tot haja acavat farem un fade out, i alliberarem la memòria que tenen pillada els bitmaps de tiles i el bitmap de la font.


void ModuleGame_Draw() {
for (int y=0; y<15; y++) {
for (int x=0; x<17; x++) {
Uint8 tile = tileMap[x+(scrollX >> 4)][y] & 127;
JD_Blit((x << 4)-(scrollX & 15), y << 4, tiles, (tile & 15) << 4, (tile >> 4) << 4, 16, 16);
}
}

JD_DrawText(0, 0, &fuente, JD_GetFPS());
JD_Flip();
}

Aquesta es la funció que pinta el mapa a cada pas del bucle. Recorre tots els tiles en l'eix Y i 17 tiles en l'eix X (16 que en caben en pantalla + 1, com ja vaig explicar amb el Gauntlet). Per a cada un, mire en el array de tiles, sumant el scrollX per a saber el desplaçament en el mapa. A més, faig un AND de 127, amb lo qual lleve el bit 8 (que es el que m'especifica si el tile es animat) per a obtindre el número de tile correcte. Més avant comprovaré eixe bit per a saber si té animació o no. Per últim pinte el tile. Si algú no s'aclareix amb la lògica del scroll, que ho diga i fique un post especific.

Per a finalitzar, pinte els FPS per a vore que tal va, i faig el flip.


void ModuleGame_Update() {
if (JG_ShouldUpdate()) {
JI_Update();

if (JI_KeyPressed(SDLK_RIGHT)) {
scrollX++;
if (scrollX > 3776) scrollX = 3776;
}

if (JI_KeyPressed(SDLK_LEFT)) {
scrollX--;
if (scrollX < 0) scrollX = 0;
}

if (JI_KeyPressed(SDLK_ESCAPE)) {
JG_QuitSignal();
}
}
}

Aquesta es la funció d'actualització. Ara mateixa nomes comprove si s'ha d'actualitzar o no. En cas afirmatiu, actualizte JInput i comprove si s'ha pulsat dreta o esquerra, per a augmentar o disminuir el offset del scroll. També mire si s'ha pulsat ESC, per a enviar una senyal d'eixir de l'aplicació.


I açò es tot per ara. Per cert, també he actualitzat l'editor, perque era molt lento i xicotet, i se feia incómode usar-lo. Podeu descarregar el codi d'ací. Si hui me dona temps igual ja fique un inici als sprites. Comentaris, sugerencies, dubtes... seràn gràtament apreciats.

PD: Hui es el meu aniversari. Demostra interés per els JailGames per a felicitar-me ;-)

dijous, 7 de maig del 2009

API per a Jailgames

Vaig a presentar un poc la API que vaig a usar durant el cas d'estudi. Està composta de 6 moduls:

- JGame: S'encarrega de les coses genèriques.
- JDraw: S'encarrega de la part gràfica.
- JInput: S'encarrega dels dispositius d'entrada.
- JSound: S'encarrega del só i la música.
- JFile: S'encarrega de la càrrega de arxius encapsulats.
- JNnet: S'encarrega de la comunicació per red.

Per ara inclouré els 5 primers. jnet el deixe per a mes avant.

JGame

void JG_Init(char *title);
- Inicialitza tot el sistema. S'ha de cridar abans de res. El paràmetre que rep es el titol que tindrà l'aplicació en la barra de titol.

void JG_Finalize();
- Finalitza el sistema. S'ha de cridar al final de tot.

void JG_QuitSignal();
- Al cridar a aquesta funció marquem la senyal de que volem eixir de la aplicació. en realitat no fa res més que ficar un booleà a true. El criden algunes funcions internes per a avisar-nos de que ha ocorregut algún event d'eixida de l'aplicació. Si quan decidim que volem eixir de la aplicació l'usem, podrem gestionar-ho tot des del mateix lloc.

bool JG_Quitting();
- Aquesta funció ens tornarà true si la senyal anterior s'ha activat. Com ja he dit, podem activar-la nosaltres o potser s'ha activat internament, per exemple, quan pulsem l'aspa de tancar la finestra. Si no la tenim en compte i la ignorem, no es podrà tancar l'aplicació de les formes normals en que se tanca una aplicació.


JDraw

void JD_Init(int screenWidth, int screenHeight, bool doubleSize);
- Incialitza el sistema gràfic, amb la resolució indicada. Si doubleSize es true la finestra serà el doble de gràn. El mode de pantalla completa encara no està implementat. El ser una finestra, es pot ficar la resolució que es vullga.

void JD_ClearScreen(Uint32 color);
- Ompli la pantalla del color especificat (format de color: 0xRRGGBB)

JD_Surface *JD_LoadSurface(char *file, bool doColorKey = false);
- Carrega una imatge en una superficie. Les superficies son com les pantalles virtuals de antaño. Si s'especifica true per a doColorKey, els pixels de color 0xFF00FF seràn transparents. Més avant faré que el color transparent siga configurable.

void JD_Blit(int x, int y, JD_Surface *surface);
- Pinta la superficie "surface" en la posició (x,y) de la pantalla.

void JD_Blit(int x, int y, JD_Surface *surface, int sx, int sy, int sw, int sh);
- Pinta en la posició (x,y) de la pantalla, el rectangle (sx, sy, sw, sh) de la superficie "surface".

void JD_BlitToSurface(int x, int y, JD_Surface *surface, int sx, int sy, int sw, int sh, JD_Surface *dest);
- Igual que la anterior, pero en compte de pintar a pantalla, pinta sobre altra superficie "dest".

void JD_Flip();
- Fa el tipic flip per a que es mostre el que havem estat fent.

void JD_FreeSurface(JD_Surface *surface);
- Quan hajam acavat amb una superficie tenim que alliberar la memòria que havia reservat o es quedaràn "memory leaks".

Uint32 JD_GetPixel( JD_Surface *surface, int x, int y );
- Obtindre el pixel (x,y) de la superficie "surface".

void JD_PutPixel( JD_Surface *surface, int x, int y, Uint32 pixel );
- Escriure el pixel (x,y) del color "pixel".

void JD_FadeOut();
- Fa un fade a negre de forma síncrona. O siga, no es torna el control a la aplicació fins que no acava el fade.

void JD_FadeIn();
- Fa un fade desde negre. Recordeu pintar primer algo o no es farà res.


JInput
Per ara aquesta API la he deixada molt coixa. Més avant l'arreglaré bonica i amb soport per a ratolí i pad.

void JI_Update();
- Actualitza l'estat dels dispositius d'entrada. S'ha de cridar a cada pas del bucle de programa.

bool JI_KeyPressed(int key);
- Comprova si s'ha polsat determinada tecla.


JSound

bool JS_Init();
- Inicialitza el sistema de só.

void JS_LoadMusic(char *musicFilename);
- Carrega una cançò.

void JS_SetMusicVolume(int volume);
- Fixa el volum de la cançò (128 màxim)

void JS_PlayMusic(int loops);
- Fa que sone la música. Si loops = -1, sona infinitament. Si loops = 0 sona una vegada, si loops = 1 sona dos vegades, etc...

void JS_PauseMusic();
- Pausa la cançò.

void JS_FadeOutMusic();
- Para la cançò baixant progresivament el volum.

bool JS_MusicPlaying();
- Si la música està sonant, torna true.

JS_Sound *JS_LoadSound(char *soundFilename);
- Carrega un só.

void JS_SetSoundVolume(JS_Sound *sound, int volume);
- Fixa el volum del só.

void JS_PlaySound(JS_Sound *sound);
- Fa que sone el só.

void JS_FreeSound(JS_Sound *sound);
- Allibera la memòria ocupada per un só.

void JS_Finalize();
- Tanca el sistema de só.


JFile

void JF_SetResourceFile(char *p_resourceFileName);
- Fixa el arxiu paquet des del que se tenen que agafar els arxius. Per defecte es "data.jrf".

char *JF_GetBufferFromResource(char *resourcename, int *filesize);
- Obté un arxiu del arxiu paquet. Torna un punter a array de char. A més en el paràmetre d'entrada/eixida "filesize" escriu el tamany d'aquest array.


Exemple
Conforme anem avançant en el cas d'estudi anirem vegent-ho quasi tot, pero vaig a escriure un xicotet programeta d'exemple. Com podeu vore per la API, no estic usant orientació a objectes, i no la vaig a usar durant el cas d'estudi, per fer-ho tot el més similar als JailGames de antaño.

El que anem a fer es carregar un gràfic que ficarem de fondo, altre gràfic que serà un set de tiles, i en pintarem un en pantalla, un mp3 de música que deixarem sonant de fondo, i un WAV que sonarà quan pulsem la tecla espai.


#include "jgame.h"
#include "jdraw.h"
#include "jinput.h"
#include "jsound.h"

Incloguem les unitats que anem a usar.


int main( int argc, char* args[] ) {
JG_Init("PROVA");
JD_Init(320, 200, false);
JS_Init();

Primer inicialitzem els sitemes. El general, el de gràfics i el de só.


JD_Surface *fondo = JD_LoadSurface("jailgames.gif");
JD_Surface *tiles = JD_LoadSurface("tiles.gif");

Carreguem els gràfics necessaris.


JS_LoadMusic("musica.mp3");
JS_Sound *bomba = JS_LoadSound("chainsaw.wav");
JS_SetSoundVolume(bomba, 4);

JS_PlayMusic(-1);

Carreguem la música, el só, i li fiquem volum al só (no se perqué s'escolta altissim, per això l'he ficat a 4). Per últim, fem que sone la música indefinidament.


JD_Blit(0, 0, fondo);
JD_Blit(0, 0, tiles, 16, 32, 16, 16);
JD_FadeIn();

Pintem el fondo i el tile, i aleshores fem el fadein. Si no haguerem pintat res, no podriem fer fade de res.


while (!JG_Quitting()) {

Entrem en el bucle de programa. S'executarà fins que arrive una senyal de tancar.


JD_Blit(0, 0, fondo);
JD_Blit(0, 0, tiles, 16, 32, 16, 16);
JD_Flip();

Pintem el fondo i el tile i fem flip, ara ja dins del bucle.


JI_Update();

if (JI_KeyPressed(SDLK_SPACE)) {
JS_PlaySound(bomba);
}

if (JI_KeyPressed(SDLK_ESCAPE)) {
JG_QuitSignal();
}

Actualitzem els dispositius d'entrada i aleshores comprovem si s'ha pulsat la tecla espai (si s'ha pulsat, fem sonar el só), i la tecla ESC (si s'ha pulsat enviem senyal d'eixir de la aplicació).


}
JS_FadeOutMusic();
JD_FadeOut();

Quan eixim del bucle fem un fadeout de la pantalla i de la música.


while( JS_MusicPlaying() ) { };

Aquest bucle farà que l'execució es pare fins que deixe de sonar la música.


JS_FreeSound(bomba);
JD_FreeSurface(tiles);
JD_FreeSurface(fondo);

Alliberem la memòria reservada per els gràfics i el só. La música la gestiona la unitat així que no ens preocupem.


JS_Finalize();
JG_Finalize();

return 0;
}

Finalitzem els sitemes. JDraw, per ara, no requereix finalització.

Ale, adjunte el códi font per si voleu començar a trastejar. El projecte es diu SuperMarioBros, perque es la plantilla que ja tenia preparada per al cas d'estudi.

També adjunte el programa ResPack, una xicoteta aplicació que vaig fer per a agafar tots els arxius de un directori i ficarlos en un JRF (Jailgames Resource File). Totes les funcions de la API lligen de un JRF, mai directament de disc, així que si voleu modificar algún arxiu tindreu que regenerar el JRF que adjunte, que realment conté els arxius que teniu al directori "recursos".

Joder, que post més llarg. Si algú te dubtes que escriga un comentari, que ja no vull allargar açò més.

dimecres, 6 de maig del 2009

Cas d'estudi: Super Mario Bros - Part I: L'editor

Be, per a començar he fet l'editor. No vaig a parlar de com està fet, ja que es Visual Basic .NET i no ens importa per al joc en sí. El que vaig a comentar es el format de mapa.

Cada mapa serà de 256x15 tiles, ja que es una restricció similar a la que tenia el joc de NES. Més avant crearé zones per al tema de entrar en les tuberies i tal, pero això ja es parlarà en el seu moment.

He dividit el mapa en dos parts: la part dels tiles que se van a pintar i la part del tipus de tile.

El array dels tiles a pintar es un array de bytes, cada un apunta a un tile que es traurà de un bitmap. Els 7 primers bits son els que ens diràn el tile a pintar. Per tant, estarém restringits a 128 posibles tiles. El bit de major ordre de cada tile ens dirà, si està a 1, que el tile té una animació.

Les animacions les he inclos perque, al treballar en 32 bits de color no tenim paleta, i resulta que el canvi de color de les monedes i els taulells amb interrogant es un canvi de paleta. El fingirem afegint al bitmap de tiles el tile en els tres diferents colors. Si un tile es animat, farà un cicle entre ell i els dos següents tiles en el bitmap de tiles.

El array de tipus de tile també es un array de bytes. Per ara he definit els següents tipus de tile:

- Buit: Es un tile atravesable, sense més.
- Solid: Es un tile que no es pot atravesar, sense més.
- Trencable: Es un tile que, quan sigues Super Mario podrás trencar de una cabotada.
- Solta moneda: Es un tile que al pegar-li cabotada et donarà una moneda.
- Solta monedes: Es un tile que al pegar-li cabotades et donarà fins a 12 monedes.
- Solta seta: Es un tile que al pegar-li cabotada soltarà una seta de Super Mario.
- Solta estrela: Es un tile que al pegar-li cabotada soltarà una estrela.
- Solta vida: Es un tile que al pegar-li cabotada soltarà una seta de vida.

Amb açò podem fer les típiques combinacions de interrogant que solta seta, taulell trencable, etc... E inclus buit que solta vida. De totes formes, tot aixó ho vorem més avant.

Així doncs, el format del mapa es el següent:

Bytes Contingut
====== =========
1 Versió del mapa
1 Nombre de caràcters de l'arxiu de tiles (x)
x Nom de l'arxiu de tiles
3840 Array de tiles a pintar
3840 Array de tiles de bloc


Podeu descarregar l'editor i el seu codi font. L'editor encara es un poc tosc, pero fa el seu treball. He adjuntat un GIF amb tiles i el GIF necesari per als blocs. A més he inclos un exemple de mapa, amb uns pocs tiles ficats.


El funcionament de l'editor es el següent: Primer tria el arxiu de tiles en la caixa de text de baix. Pots triar qualsevol arxiu d'on siga, pero per a que funcione ha d'estar en el mateix directori que l'executable. Una vegada carregat, feu click sobre un tile, feu click sobre un bloc i marqueu o desmarqueu el check d'animació; i aleshores ja podeu fer click en el mapa per a pintar el tile resultant.

En l'editor, els tipus de tile (blocs) es pintaràn com un marc sobre el tile a pintar. Supose que es prou intuitiu, pero si no ho es digau-m'ho i ho explique millor.

Si voleu modificar i/o compilar l'editor necessitareu el Visual Basic .NET 2008 Express, descarregable desde ca Microsoft. Per a editar i compilar en Linux no estic segur, ja que MonoDevelop crec que només admet C#. No obstant, per a executar el programa ja compilat desde Linux tindreu que sudoaptgetar l'ultima versió de Mono i, a més, una extensió o parxe per a executar Visual Basic. Mon, com crec que eres l'unic Linuxero que segueix activament el blog, si t'interessa m'ho dius i t'ho mire, que per casa ho tinc apuntat.

dimarts, 5 de maig del 2009

Reconsideracions

Joer, quant de temps sense escriure. Ja van dos setmanes liades. Apenes he pogut fer res de JailGames ni de res. De fet, apenes he pogut tastar encara la PSP.

De totes maneres, he estat reflexionant sobre estes últimes setmanes. Desde que vaig acavar amb el Arounders he anat botant de projecte en projecte, i cada vegada més lento. Crec que no estic centrat.

Primer vaig començar per intentar fer alguns JailGames. El Bryndiana es quedà parat pel curro que es fer les animacions. El Patman es quedà parat perque, després de reescriure-ho 2 o 3 vegades, seguia donant-me per cul la ordenació (la veritat es que va ser la época en que em trobava fatal, igual quan torne a mirar-ho resulta que es una peivada).

Vegent que no estava molt inspirat vaig decidir fer un cas d'estudi amb el Gauntlet. Pero, en primer lloc, no vaig pensar be el disseny, pel que tinc que tornar enrrere; i en segon lloc, me vaig ficar en el marrón del multijugador. No me pareix un gran exemple per a un primer cas d'estudi que vos espente a fer algo. Pitjor encara, ni tan sols estava adjuntant el codi font, com havia dit que faria.

Per tant, em feia falta reconsiderar les prioritats, el temps disponible, la utilitat del resultat i la forma de fer-ho.

Al final crec que el camí que volia seguir es el adequat. Fer un cas d'estudi amb el codi font que pugau seguir e inclus anar compilant desde casa. Pero me fa falta un projecte més simple i tipic, que a la vegada tinga alguns truquets interessants. I esta vegada en compte de decidir un joc arreu he mirat detingudament que no tinguera res que no puguera anar fent a la marxeta sense més.

El projecte que he triat es un clon del Super Mario Bros original. M'he preocupat de buscar primer si tenía facil trobar els gràfics i ja tinc recopilats tiles, sprites i demés gràfics.

Respecte a la música i sons, els ripejaré del emulador. De totes formes, no vaig a fer que la música vaja igual. En el Mario Bros la música va sonant, i quan s'està acavant el temps la música va més apresa, pero desde el punt on s'havia quedat. Aixó no ho faré.

Respecte a enemics, faré els que eixen en la primera fase i au. Si algú vol fer més com a exercici genial.

Respecte a la API, com ningú a proposat encara res, començare amb l'esborrany que tinc fet i aniré ampliant conforme faça falta. No obstant, encara m'agradaria que comentareu algo al respecte.

Respecte al codi, en cada post inclouré el codi fins a eixe moment i tot lo necessari per a compilar i jugar. També inclouré els arxius de projecte de Visual Studio 2008 per a que ho tingau fàcil. Nomes vos fa falta seguir els pasos del tutorial d'instalació d'SDL (excepte la part de OpenGL, que no fa falta). En Linux, per a instalar SDL tindreu que "sudoaptgetar" les llibreries de desenvolupament: libsdl1.2-dev libsdl-image1.2-dev i libsdl-mixer1.2-dev. Després, per a compilar, ja donaré instruccions al seu moment.

Demà comence, a vore si, donant totes les facilitats posibles, algú ho segueix.

Durant les pròximes 3 setmantes faré aixó i la expansió del Arounders, que la vaig deixar a meitant. Després me tiraré 2 setmanes de vacances en casa sol amb el nano, pel que tindré més temps per anar adelantant tot el que m'he anat deixant pel camí.

dijous, 30 d’abril del 2009

Pregunta a tots: API per a JailGames

M'agradaria que m'ajudareu a pensar una API xula per a fer jailgames, al estil de les que feiem per als jocs de pascal, tipo Grafix o DirectMon. Les APIs que fan falta són de video, de audio, de teclat/ratoli/pad i de red. Cregueu que faria falta definir algún grup de funcions més com a API?

Les de só i red son les mes clares que tinc. La de video es la que més m'interessa que me comenteu com la farieu, o qué cregueu que deuria incloure.

Tampoc es questió de matar-se i, per perea, no fer res. Prefereixc no escriure lo que per ara tinc pensat per a no influenciar-vos.

Es més, potser cregueu que no fa falta crear APIs per damunt de SDL, que ja es una api. En realitat jo ho he estat fent per a siga més facil fer certes coses i amb molt menys codi, millor per a començar i fer un joc ràpidament, pero perdent posibilitats.

Ale, espere respostes.

dimecres, 29 d’abril del 2009

Masters of Doom

He tingut poc de temps esta setmana. Lo poc que ho pogut fer ha sigut començar una classe per a poder escriure text (ho necesite per a escriure la IP del servidor, per a escriure els missatges de xat entre jugadors...), pensar una especie de consola que puga conectar a qualsevol joc i escriure comandos, i a passar el Gauntlet del motor OpenGL al motor per software que usa el Arounders. Açò últim, més que res perque no me fa falta acceleració hardware per a jocs d'aquest tipus, però a més perque me restringeix a textures de 256x256 com a maxim si vull que funcione en acceleradores antigues o cutretes (la típica acceleradora Intel que va integrada en moltes plaques, per exemple). Lo dels 256x256 es un fer la mà, perque no em permet ni tindre una pantalla de 320x200. Així que, com no me fa falta, pues torne a l'anterior, que també anava molt be, i au. Ja usaré OpenGL en altres projectes.

Per altra part, este cap de setmana he "devorat" el llibre "Masters Of Doom". Tracta dels inicis de ID Software, sobretot centrant-se en John Carmack i John Romero, desde que eren uns adolescents locs fins al Doom III, pasant per totes les alegries i problemes que tingueren. Es narrativa, res tècnic. M'ha agradat molt, el recomane a tot jailer que guste de fer jailgames. Si en Google busqueu "masters of doom pdf", desde el primer enllaç, que es de RapidShare, podeu descarregar-lo en PDF. De comprar-lo tindrà que ser a Amazon o similar, no l'he trobat en Fnac, El Corte Ingles i similars. Jo me l'he llegit desde el iPhone, que ja s'ha convertit en el meu lector de llibres i webs.

Lo següent que me vull llegir son el "Black Book" de Michael Abrash i el "Red Book" de OpenGL (i el "Fat Book" de Petete... jejeje... que xorrada ¬_¬')

Si no passa res, esta vesprada escric un post parlant del nou sistema de mapa+sprites.

dissabte, 25 d’abril del 2009

Cas d'estudi: Gauntlet - Reconsideracions

Moltes vegades, fent jocs, me passa que de repent me costa avançar. Hi ha algo que me molesta, que no quadra. El subconscient me diu que algo no va be. Es una putada, com en els somnis en que vols correr pero no pots, vas molt lento, com baix de l'aigua. Hi ha alguna cosa que no quadra i no se el qué.

Així que esta setmana he estat pensant en quins son els problemes que trobe amb el Gauntlet este. He trobat tres punts a considerar:
- La comunicació per red
- La gestió de events del joc
- La gestió de montonades de enemics

La comunicació per red
Aquest es el problema obvi, en el que estic treballant, per tant no es una sorpresa ni un problema. No obstant, al arrivar ací es quan s'han fet patents els demés problemes. Es, per tant, el encarregat de solventar-los, o al menys, de soportarlos.

La gestió de events del joc
Com ja he comentat, els events importants els ha de determinar el servidor. Quan juguem online, serà el servidor el que ens avisarà de dispars i col·lisions. Però quan juguem offline som nosaltres els encarregats. Com monte un sistema que puga compartir la major part del codi i, al mateix temps, soportar aquests dos sistemes? En un joc offline, conforme vas procesant els sprites trobes les colisions i, directament, actues en consequència. En red tindré que ignorar aquestes colisions i nomes mirar el que m'arriva del servidor.

Encara que una sol·lució podría ser fer-ho condicional i que segons el tipus de joc mirara les col·lisions d'un lloc o altre, me pareix molt poc elegant i escalable. Lo millor seria que el punt d'entrada d'aquestos events sempre fora el mateix, encara que el detonant de l'avis fora diferent.

Per tant, crec que el que tindré que fer es que el motor del client sempre comprova col·lisions i tal, pero en compte de actuar en consequència, tindrà que avisar al proxy, per dir-ho d'alguna forma, enviar-li un equivalent al missatge que el servidor ens envía per red. D'aquesta forma, el proxy, depenent de si estem conectats o no, executa els events que deu executar, i la resta del codi no s'entera d'aquestes diferències. He de repensar-ho un poc més. Estoy en ello.

La gestió de montonades de enemics
En els mapes del Gauntlet sol haver molts enemics, es un dels seus encants. Podrien ahver cents d'enemics en un mapa al mateix temps. Els meus calculs em diuen que molts més. No obstant, processar tants sprites serà molt costos. Es més, no es escalable, ja que depenent del nombre d'enemics el joc pot anar més o menys rapid.

Necessite una sol·lució escalable, que em permeta procesar els enemics d'una forma lineal. Es més, si el servidor s'encarrega de les decissions dels enemics, ha de trobar una forma d'enviar les ordres dels enemics sense saturar la red.

Hi ha una optimització clara: En Gauntlet nomes els enemics que estàn en pantalla es processen. Els demés es queden quetets, inclus els generadors de enemics no generen si no estem en pantalla.

Aprofitar aquesta ventaja no es fàcil amb el sistema d'sprites que tenía pensant. No veia la forma de no tindre que recorrer tots els sprites per a vore quins estaven en pantalla i processarlos.

La sol·lució seria clara si poguera tindre-l's en tiles, com el pepe, per exemple, que un ninotet esta en un tile o en altre. Pero aquestos sprites es mouen al pixel, a més, en un moment donat pot haver dos sprites tocant un tile.


En el que no havia caigut es que, al no poder solaparse els sprites, en realitat nomes hi ha un sprite en cada tile, encara que tinguen un offset de desviació del centre del tile. Encara que visualment estiguen tocant el mateix tile, en realitat nomes un està en cert tile, amb una certa deviació, l'altre està al tile contigu, amb certa desviació.


D'aquesta forma, en compte de tindre una llista d'sprites, cada tile serà un punter a una estructura amb informació del tile: imatge, si bloqueja o no... i el sprite que te dins, que tindrà una desviació en x i en y, per a pintar-se on toca, a part de l'informació que li faça falta.

Per tant, nomes tinc que recorrer els 12x12 tiles (en realitat tindré que procesar un tile més per cada vora, ja un sprite en un tile pot estar parcialment en el tile contigu) que estàn en pantalla i procesar els sprites que hi haja dins. El cost es pràcticament lineal o, al menys, el pitjor cas està controlat i no pot augmentar.

Aquest sistema nomes te una pega: Les bales son més xicotetes i, per tant, es poden solapar. Per tant, es tindràn que procesar apart. Alguna idea? Que penseu? molaria saber les vostres opinions al respecte.

dijous, 23 d’abril del 2009

Cas d'estudi: Gauntlet - Part Vb: Red

En aquest post vaig a parlar dels punts fonamentals a tindre en compte al desenvolupar una arquitectura de comunicació per red en temps real per a videojocs.

Consistencia
Tots els jugadors deuen vore el mateix: Els enemics han de estar al mateix lloc i fer el mateix, el mapa he de trobar-se en el mateix estat, els objectes deuen estar al mateix lloc, etc... Però, a més, ha d'haver un consens respecte a les accions importants, com que una bala toque a un enemic, o a un jugador.

Respecte al nostre joc en particular, hi ha una serie de punts que deuen mantindre una certa consistencia, i cada un requereix d'un grau major o menor de consistencia:
- La posició dels jugadors mentres es mouen: consistencia baixa
- La posició dels enemics mentres es mouen: consistencia baixa
- Els canvis de direcció dels jugadors: consistencia normal
- Els canvis de direcció dels enemics: consistencia alta
- La posició dels jugadors al disparar: consistencia alta
- La colisió entre una bala i un enemic o jugador: consitencia alta

Hi haurà més punts a tindre en compte, pero aquestos ja ens valen per a fer un anàlisis.

Si vos fixeu, he dit que la posició dels jugadors o els enemics requereixen consistencia baixa. Açò es així perque el fet que un jugador estiga uns pixels desviat en un comp que en altre no afecta seriament a la consistencia del joc. Imagineu la consistencia com la similitud entre la versió del joc que te el servidor i la que té el jugador. Hi ha accions, com la posició d'un jugador mentres corre, que no requereixen que siguen exactament iguals en els dos comps. No obstant, la posició des d'on eix una bala o la colisió d'una bala amb un enemic ha de ser exacta entre tots els comps.

Les accions que no requereixen una consistencia alta poden ser executades pel client sense esperar confirmació del servidor. Està clar, de totes formes, que no podem permetre que aquestes accions es desvien massa de la versió del servidor, pero per a això usarem mètodes de sincronia. En realitat no es tracta de trobar quines accions deuen ser consistents, ja que totes deurien ser consistents, sino les que podem cedir un poc de consistencia a canvi de que la jugabilitat millore.

Les accions que requereixen una consistencia alta deuràn fer-se (o al menys prendres la decisió) en el servidor, que propagarà aquesta decisió als demes comps. De totes formes, reduïrem aquestes accions a la mínima expresió.

Per exemple, al nostre joc, els enemics tindràn un comportament determinista, o siga, que no tenen cap component aleatoria al seu comportament. Bàsicament, un enemic calcula quin jugador te mes prop i va a per ell. Com la posició d'un jugador no te perque ser del tot consistent en tots els comps, en un comp un enemic podria triar a un jugador i el altre comp a altre jugador, trencant totalment la consistencia, ja que l'enemic duria rutes diferents en cada comp. Per tant, el que farem es que eixa decisió, la de triar a per qui va, es faça en el servidor, mentre que el moviment cap a eixe jugador es farà en el comp del client.


Sincronia

Havem parlat de la consistencia. La consistencia el que farà es mantindre nugades les coses que controla. Pero les coses que no controla al ditet estaràn un poc soltes i, si no es busca un mètode alternatiu de control, acavaràn desviant-se sistèmicament. Per a evitar que les accions poc consistents es degraden usarem mètodes de sincronia.

Els métodes de sincronia el que faràn es revisar que les accions poc consistents no s'estàn tornant massa inconsistents. Per exemple, que la posició del jugador en el comp del servidor no es massa diferent de la del comp del jugador.

Per a tal cosa enviarem junt a la informació de canvi de moviment de cada jugador, la posició d'eixe jugador en el servidor, de forma que els clients puguen corregir-la donat el cas.


Seguretat

Si el client te massa decisions al seu carrec pot fer trampes. Per exemple, si es el client el que envia la senyal de que la meua bala a tocat a un altre jugador, eixe client pot fer trampes inventant-se ordres imposibles en el joc, amb una versió hackejada del joc, per exemple. Pitjor encara, pot fer un xicotet programeta que envie paquets UDP al servidor amb eixe tipus de ordre, de forma que imposibilitaria als demes jugadors jugar la partida.

Per tant, es important que el client no tinga més responsabilitats que les seues propies decisions, i que aquestes responsabilitats estiguen restringides al que es possible fer.

Per tant, el client nomes enviarà ordres de cap on està caminant i si ha pulsat dispar. Es més, ni tan sols enviarà la seua posició, que podria ser un exploit al poder inventar-se una nova posició i teletransportarse. El jugador començarà en una posició, coneguda per tots, i després nomes transmetrà ordres de "ara camine cap a la dreta", "ara pare", "ara camine cap a l'esquerra", que permetràn a tots els comps recomposar la sequencia. Ací es on faràn falta els mecanismes de sincronia per a que cada comp no estiga interpretant l'escena d'una forma diferent, ja que la latencia pot fer que en uns comps el temps entre tecla polsada i tecla soltada pot ser diferent.


Escalabilitat

No vaig a mirar molt el tema de l'escalabilitat. L'unic pas a favor que he fet es triar una arquitectura client-servidor, i llevar la responsabilitat de ser servidor als clients per a deixar un servidor dedicat.

En jocs de molts jugadors, com els MMORPG, lo normal es que no hi haja un servidor, sino un cluster de servidors, que deuen sincronitzar-se entre sí. Aixó s'escapa del nostre objectiu.


En el proxim post parle de la classe que controla al jugador i de la classe proxy que manté la comunicació.

dimarts, 21 d’abril del 2009

Poc de temps

Ie, que estic viu. Este cap de setmana apenes he fet res. No, no he esta viciant-me like loc a la PSP, apenes he pogut probar-la i jugar un poc en la sobretaula. Ni tan sols he pogut fer alguna proba de demo.

He estat dissenyant el sistema de comunicació per red del Gauntlet. Vaig dir que aniria fent poc a poc, pero es una perea enorme, perque no es tan facil com anar afegint coses sino que al millorar el sistema has de reemprendre sol·lucions desde zero.

Així que vaig a escriure el codi de red tal i com ha de ser. Aixó planteja un estudi interessant de quí es responsabilitza de que, com optimitzar la comunicació i altres coses que no son tan obvies com pareixen. Dona per a un post sucoset, que estic escrivint, pero no tinc el temps suficient per a acavar-lo hui, així que demà, si tot va be, parlaré largo y tendido sobre el tema.

dijous, 16 d’abril del 2009

Brand New Joguet!

Be, hui no hi ha update del Gauntlet, ja que he dedicat els ratos lliures que tenía a... comprar una PSP! 8D

Ja feia temps que li tenia ganes, sobre tot per a jugar a emuladors, al doom, al quake... Ho vaig parlar amb JDes i me va asesorar un poc. El cas es que ahir vaig anar a ModCenter, que allí te actualitzen el firmware i tot... i el tio me diu que les PSP que tenen no son piratejables. Jo ja sabia que la PSP 3000 no es podia encara, pero allí vaig vore PSP 2000...

Al arrivar a casa vaig investigar i resulta que segons el firmware que duen no es pot encara, per a ser exactes l'ultim firmware piratejable es el 3.95. Es pot saber, aproximadament, quin firmware te la consola mirant la etiqueta de la caixa. Si la lletra es menor que G, es pot piratejar. La G potser si o poster no, i més avant no es pot. Pareix que Sony, desde nadal o per ahí segons me va dir el de ModCenter, nomes serveix versions G o superior.

La veritat es que no tenia ganes de calfarme el cap, així que ja m'havia fet a la idea de esperar i comprar la GP2x Wiz. Pero mira, este matí, quan he anat al OpenCor a comprar les rosquilletes he vist que tenien PSPs. Tenien una 3000, una 2000 G i una 2000 que venia amb el Lego Indiana Jones que no es veïa la versió (al ser un pack, du una caixa recobrint la caixa de la consola i el joc).

He tornat al curro i he investigat, i després de vore varios casos que deien que eixe pack duia una versió F, he anat i l'he comprat. Efectivament, era una F amb firmware 3.90.

A hora de dinar he anat a dur la PSP a ModCenter per a que li canviaren el firmware. La veritat es que esperava que tindria que deixar-la un dia, o inclus una setmana... pero nomes entrar el tio me diu "vaya, has encontrado una F", ha tret una bateria gorda i una memory stick, a apretat dos botons, m'ha fet el ticket (30€ de piratejar, que li anem a fer), i pis pas, ja estava apanyadeta. Pos millor.

Al tornar he provat el Lego Indiana... La puta! Tots els UMDs tarden esta eternitat en carregar?? m'ha recordat a la época del spectrum. Després he provat el Quake i el Rise Of The Triad, tot va de boles.

Externament la esperava mes boniqueta, respecte als acavats. La pantalla pareix que estiga encastrada, i a les vores de la pantalla la pintura pareix que estiga un poc com a gotes, com mal pintada... pero be, després de vore els acavats de un iPhone o iPod Touch... :-)

Seguidament he vist que es pot desenvolupar amb SDL... així que en uns dies faré els primers experiments, i algún port de jailgame caurà.

Be, content amb la compra. Realment la vull per jugar en la sobretaula al sillo amb Mariola, i no tindre que marginarme al estudi si vull jugar (la DS no dona pa molt).

Ale, demà seguim programant.

dimecres, 15 d’abril del 2009

Cas d'estudi: Gauntlet - Part Va: Red

Hui he estat preparant el codi de red. Per a la comunicació per red vaig a usar SDL_net (per als interessats, s'instala igual que SDL_image o SDL_mixer, segons explique al primer tutorial).

En principi he decidit que el servidor no siga més que un distribuidor d'ordres entre jugadors. O siga, si el jugador 1 comença a caminar cap a la dreta, envia un missatge al servidor dient-ho, i este redistribueix el missatge als demes jugadors, que mouràn al jugador convenientment. Aquesta aproximació es simplista i perillosa, pero es un començament.

En general, amb aquesta aproximació tot el proces que hi ha al joc ha de ser determinista. O siga, res de coses aleatòries, perque si cada comp decideix donar una aleatorietat diferent, les diferents versions en cada comp faran el joc injugable. En el cas de que s'introduïren elements aleatoris, deuria haver un "generador de nombres aleatoris en red" o similar, o sino, que siga el servidor el que genere aquesta aleatorietat. A més, problemes de sincronització, on un jugador pot estar uns pixels més lluny en un comp que en altre, poden dur a inconsistències. I es que fa falta un àrbitre que prenga les decisions importants, com per exemple si una bala a tocat a un personatge.

Tot açò ens du a que el servidor tindrà que estar "jugant" la partida també, tenint la seua versió al igual que els demes jugadors, i si hi ha algún event important, serà el servidor el que avisarà, mentre que els jugadors mai prendràn decisions sobre eixes coses. Per exemple, si una bala colisiona amb un personatge, el comp jugador no dirà ni mu. Serà el servidor el que, quan detecte aquesta situació, enviarà un missatge de colisió entre la bala i el personatge. Pot donar casos raros en que una bala que clarament ha tocat a un jugador en un comp, passe de llarg perque en el servidor no l'ha tocat, per diferencies de sincronia, pero al menys tots els comps tenen la mateixa versió dels fets i no es desincronitzen.

De totes formes, com ja he dit, al principi el servidor serà un distribuidor d'ordres i au. Per a joc en red local deuria ser suficient, amb latencies menors de 1ms (ara mateix el bucle de programa procesa ordres, com a molt, cada 10ms).

El que segurament implementaré prompte es el "paquet de sincronia". Durant el PFC vaig descobrir que les latencies havien baixat dràsticament desde que el vaig començar. Al final, fent que el servidor fora un redistribuidor i au funcionava be el 99'9% del temps. L'unic problema era si algún paquet UDP es perdia pel camí (lo qual ocorria una vegada de cada 10000 en Internet, i fent el burro amb coses descarregant i tal fa forçar la red. No ho vaig poder reproduïr en red local). Obviament, si se perd un paquet, encara que siga cada 10000, ja estem fotuts.

El que vaig fer es enviar un paquet de sincronia cada x segons. Aquest paquet contenia les dades actuals del jugador (x, y, acció actual...). Així em servia de ping, per a estar segur de que el comp i el servidor seguien vius, i per a restablir la sincronia en el cas de que s'haguera perdut (el que feia era, quan arrivaba un paquet de estos, forçar la posició i tal de cada jugador).

De totes formes, aquesta solució no arregla el fet de que el paquetet que es perda just siga el que diu que he disparat, amb lo qual ningú s'enteraria de que hi ha una bala per ahí. Crec que per a solventar-ho vaig fer que tot paquet enviat esperara una resposta. Ara mateix no recorde si al final també numerava els paquets, per a estar segur de que no es perdia ninguno. He de repasar-me el PFC.

No he acavat el codi, ni del servidor ni del jugador, així que este capitol encara no està tancat.

dimarts, 14 d’abril del 2009

Dia raro

Hui he tingut el dia raro. Ahir vaig pensar en fer un mini-framework usant SDL per a pasar els antics jailgames de 8 bits de color més facilment, pel tema de les manipulacions de paleta que feiem, pero després d'un rato ho he tirat a la basura perque el SDL es subnormal i quan li canvies la paleta, en compte de canviar els color i au, li pega per també intentar adecuar l'imatge als nous colors. Que desastre!

Be, temps perdut. Després he estat un rato rumiant de fer un fast game que tenia en ment, pero ho deixe per a altre dia.

Després he mirat de fer algo del cas d'estudi. El cap de setmana se me va ocorrer un jailgame que fer amb eixe motor, així que prepararé els sprites per a tal joc. Ja parlaré de ell. No importa, no tinc grafics ni ripejats ni fets per als sprites, així que he pintat dos boles rotjes en la pantalla i he fet unes proves de sprites. Tot molt be i com esperava, excepte que el que vaig comentar del mapa te un error:

Quan es calcula el punt mitg, després se li restava la meitat del espai visible. Pos be, encara que l'espai visible siga 192 pixels, en realitat a mi m'interessa un tile menys, sino el sprite s'en eix un tile per fora de la pantalla, perque el punt (0,0) del sprite es el que està en la coordenada 192, deixant la resta del sprite fora (pasa igual que amb el cursor, si vos fixeu en les vores de baix i de la dreta s'en eix). El que he fet es restar la meitat de l'espai visible menys un tile, o siga: 192-16 = 176 / 2 = 88. Res, per a que quede per al record.

Ah, i se m'oblidà dir-ho ahir. He afegit un tutorial més de SDL als tutorials del nostre wiki. Amb aquest tutorial ja tindreu un projecte buit per a començar un jailgame. Amb eixe projecte buit, per exemple, ja podrieu compilar el Arounders.

A vore si demà avance més...

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:

dijous, 9 d’abril del 2009

Cas d'estudi: Gauntlet - Part III: El editor

Després d'una nit en la que he pogut dormir unes quantes hores seguides, hui m'he alçat reconfortat i m'he ficat a la faena.

He començat l'editor de mapes. Per ara nomes pot ficar parets o llevar-les. Aniré completant-lo conforme el joc ho requereixca.


En principi, parlar del format del mapa. El mapa en sí es un simple array de 32x32 bytes, en el qual, ara mateix, nomes pots trobar zeros (buit) o unos (paret). Aquest es el mapa que m'interesa per a procesar colisions i tota la peixca interna.

No obstant, hi ha un segon mapa que es el que realment es pinta ¿perqué? Si vos fixeu en els tiles de les parets del Gauntlet no son tots iguals:


Com podeu vore, depenent de si un bloc de paret te un altre bloc a algún costat, es pinta de una forma o altra. Es més, els tiles del piso tampoc son iguals: Si tenen una paret prop potser tinguen sombra. Aixó ens dona un total de 16 posibilitats de paret (en la versió arcade en son alguna més al ficar més sombretes xules, pero per ara ho ignorarem) i 8 posibilitats per al piso (2 de les quals son iguals, pero també ho ignorarem)

Així pues, el que faig es, a partir del mapa de parets i buits, monte altre mapa en el que calcule, per a cada bloc, quin tile li toca mostrar. Aquest calcul el faig al principi de carregar i mostrar el mapa, i després vaig actualitzant nomes els tiles que canvien. En l'editor els tiles canvien molt a sovint, pero en el joc hi ha pocs moments en que canvien (obrir portes, que encara no les havem tractat, i obrir "parets trampa", que tampoc les tractem encara).

La veritat es que en principi no volia fer-ho per al editor, que no passa res si es veu mal, pero al final ho he fet tant per practicar-ho com perque sempre mola més vore-ho tot sucoset ;-D

Altra questió es el format dels mapes. Com segurament vaja canviant durant el desenvolupament, he fet que el primer byte del mapa indique la versió. Així, l'editor el primer que fa es llegir este byte i redirigir a la rutina de càrrega de la versió del mapa corresponent. Ho he fet així per no perdre mapes de versions anteriors. Ara no importa molt, pero si cap al final, amb uns quants mapes fets, he de canviar el format, pot ser un fer la mà refer els mapes.

Per ara, els dos següents bytes estàn a zero, pero representaràn el tileset i color de les parets i el piso. I després ja van 32x32 bytes que representen cada bloc del mapa (en format simple, el calcul de tiles que he comentat avans es torna a fer desde el joc, me pareix una opció més segura i, a més, el precalcul de tiles es ràpid i es fa nomes una vegada). Els bloc estàn guardats en format de lectura occidental, d'esquerra a dreta i dalt a baix. O siga: Y=0, totes les X, Y=1, totes les X...).

Be, en el pròxim capitol ja començarem a escriure codi del propi joc. Començarem per implementar el pintat del mapa, el scroll i el centrat del mapa en varios punts, com fa el Gauntlet per a mantindre a tots els personatges en pantalla. Començare sobre la base del Patman en el que respecta a estructura del joc, pintant, sò i teclat.

Volia anar ficant a disposició de tots el codi font conforme es va escrivint, i el subversion del Google Code es perfecte per a tal cosa (on està el Arounders), però necessite un nom per al projecte, i ficar-li Gauntlet no em pareix apropiat. ¿Algú me proporciona un nom per a aquest Gauntlet Jailer?

PD: Ah, i ficaré també els executables i codi dels programetes perifèrics que vaja fent, com l'editor de mapes, encara que estos sense control de codi font.

dimecres, 8 d’abril del 2009

Estic malet...

Aixó, que el dilluns ja em trobava fatal, ahir no vaig eixir del llit, i hui encara no estic massa be. Supose que no dormir res de bo (coses del nano) junt amb un constipat mal curat m'han deixat fet pols.

Crec que el tema de atascar-me amb el patman i tal també te que vore amb dur messos sense dormir com deu mana.

dilluns, 6 d’abril del 2009

Cas d'estudi: Gauntlet - Part II: Continguts

Vaig a parlar de qué trobarem en el joc. Està basat en el Gauntlet, però no te perquè estar tot lo del Gauntlet ni te perquè estar fet de la mateixa forma.

Mapa
El mapa basic estarà format per:

  • Buits: Obviament, per ací es per on es pot caminar sempre.

  • Parets: Les parets son tot el contrari als buits.

  • Portes: Les portes son com parets fins que colisionem amb una portant una clau.

  • Parets "borrables": Son com les parets, pero al xafar una "trampa" desapareixen. Podrà haver fins a tres parelles de trampes i grups de parets borrables.

  • Parets trencables: Son parets que després de pegar-li un parell de castanyes desapareixen.

  • Entrada: Nomes hi ha una i es on apareixen els herois al començar un mapa.

  • Eixides: Al xafarles pasarem de pantalla. No vaig a implementar les eixides a pantalles avançades.

  • Teletransports: Al xafarlos anirem al altre teletransport més proper (i que estiga en pantalla) que hi haja.

  • Trampes: Les trampes son taulells, que brillaràn o el que siga, que al xafarlos farán desapareixer les parets asignades a ells.



Enemics
Al mapa també podem trobar enemics i generadors d'enemics. Cada tipus d'enemic pot apareixer en tres variants diferents, sent cada variant mes forta que l'anterior. Hi haurà un generador per a cada tipus d'enemic i per a cada variant del mateix enemic.

  • Fantasma

  • Grunt

  • Dimoni

  • Hechicero

  • Llançadors



A més d'aquestos, també estará la mort, que no te diferents nivells ni te generador. No vaig a implementar el lladre.

Objectes
Per últim, podem trobar diferents coses que podem arreplegar:

  • Clau: Si havem agafat una clau, podrem obrir una porta al tocarla

  • Tresor: Ens donarà punts

  • Pocio Blava: Si després d'arreplegar-la la llançem farem pupa als malos. També podem disparar-li per a trencar-la, pero farà menys efecte.

  • Pocio Roja: Igual que la blava, pero no es pot trencar.

  • Menjar: Ens torna punts de vida al arreplegar-lo. Hi ha una variant destructible i altra indestructible.

  • Pocions Extra: Al arreplegar-les ens upgradaràn alguna de les següent qualitats: Armadura, Màgia, Poder de dispar, velocitat de dispar, velocitat al caminar i poder cos a cos. Per lo demés es comportaràn exàctament igual que una poció blava.




Tot açò ens dona un total de 56 posibles "tiles" a colocar en el mapa. Després, al joc, alguns es transformaràn en tiles que formaràn el mapa i altres en sprites. De fet, les parets es transformaràn en un tile més especific, per a fer la formeta segons cap on miren i tal, pero això ja ho vorem més avant.

A part, cada mapa tindrà, com a propietats, el tileset usat per a les parets, el tileset usat per al piso, el color de les parets i el color del piso: hi haurà diferents tilesets, i per a cada tileset se li podrà especificar una gama de colors.

Cada mapa estarà format per 32x32 tiles, dels quals es vorà una finestra de 12x12 tiles. Cada tile serà de 16x16 pixels.

Açò ja anirà canviant segons vaja trobant coses que falten o coses que me digau vosaltres. Supose que mes avant afegiré coses com el collar de invisibilitat, invencibilitat, o coses de eixes. Amb la informació que tenim ja podem fer un editor rapidet.

divendres, 3 d’abril del 2009

Cas d'estudi: Gauntlet - Part I: Introducció

Be, per a despejar-me un poc vaig a començar un cas d'estudi que desenvoluparé detalladament al llarg d'estos dies. He triat el Gauntlet, ja que desde l'altre dia que el Gamer ho va comentar m'entraren ganueles.

L'objectiu es desenvolupar el Gauntlet. No vaig a calfar-me el cap i vaig a usar els gràfics i la mecànica del joc original. Si més avant es vol fer un JailGame amb història i gràfics propis, que algú propose algo.

Crec que tots coneixeu el Gauntlet, així que no vaig a comentar la mecànica del joc. El que sí comente es que ha de poderse jugar offline, like el joc original, i online, a traves d'un servidor dedicat. En principi no em planteje que el servidor puga ser un dels jugadors.

Per a aconseguir aquesta dualitat desenvoluparé unes classes proxy que em premeten interactuar amb el joc independentment de que les ordres arriven des del teclat o desde la xarxa. Com a extra, desenvoluparé altra classe proxy que permeta que el joc jugue sol amb una serie d'ordres pregrabades, like el mode demo de les recreatives. Açò implica que tindré que fer una classe sniffer que grave les ordres entre el proxy i els elements del joc, encara que es probable que, en compte de crear una classe a propòsit, clave la lògica dins la clase proxy base, de la que hereten les demés.

El joc permetrà dur fins a 4 personatges. Vaig a implementar personatges, enemics, items, etc... segons aquesta documentació. De totes formes, tampoc es l'objectiu calcar exàctament el comportament del joc original.

Usaré els gràfics del Gauntlet 2 per a PC, que puc ficar-lo al DosBox per a traure, facilment, tiles i sprites.


Ale, crec que ja no hem queda res que introduïr. Vaig a passar més temps escrivint ací que escrivint codi, així que la visibilitat del projecte serà quasi total. Mentres dure el cas d'estudi hi haurà dies que escriga varies parts, i si se administrarme be no deuria haver dia sense post. Aixó si, recordar que el cap de setmana sempre estic liat i se me fa dificil escriure, així que no promet res.

dijous, 2 d’abril del 2009

Que li peten!!!

Ie, no comprenc com se m'està resistint la merda de codi este. La funció de reordenació nova te polls raros de nou. Com ja estic prou cremat, crec que me fa falta donar-me un descas de Patman.


Per tant, es queda aparcat uns dies i aprofite per a tirar avant el Bryndiana. Com vaig dir en un post anterior, ara seràn tres jocs, i vaig a començar pel primer. He pensat que siga tipus Wizards & Warriors III respecte a estructura del joc. De totes formes, encara no ho tinc clar del tot. Si algú te alguna proposta...

El que sí tinc clar es alguns conceptes. Pantalla amb scroll amb posibilitat de anar amunt, avall, esquerra i dreta, amb llocs on es pot entrar, amb tendes, amb diferents zones a les que no es pot accedir fins no aconseguir alguna cosa, amb prou enemics pel camí, no dificils de matar pero que si estiguen donant pel cul per a no avorrir...

Portes i tamany d'habitacions

Ahir no vaig poder escriure abans d'anar-me'n a casa, el blogger este no anava be, i en casa ja va ser impossible ficar-se cara al comp.

Vaig estar implementant els tamanys variables d'habitació, així com les portes.


Cada eix pot ser de 8 tiles, 6, 4 o 2, excepte l'altura, que sempre es de 4 tiles.


Encara que la lògica de les portes ja està pràcticament implementada, no es pot vore res perque encara no he fet gràfics per a elles, a part de tampoc tindre habitacions per les que caminar (l'habitació usada fins ara estava montada a mà desde el codi.

Respecte a les habitacions, funcionaràn de la següent forma: Cada habitació tindrà sis referències a portes, una per cada costat (est, oest, nord, sud, dalt i baix. Si està a -1 no hi ha porta, en altre cas durà a l'habitació amb el número indicat. A més, les portes est, oest, nord i sud tenen altura (0, 1, 2 o 3). Cada habitació també té un flag que especifica si el piso mata al tocarlo.
respecte als gràfics, cada habitació nomes te una referència a un "tileset".

Cada tileset especifica:
- El tile del piso
- Els tiles de les parets per a cada tamany de paret i si te porta o no
- Els tiles de les portes

Es un sistema que restringeix més la varietat de gràfics, pero es que quan més genèric tot més se perd uno fent mil coses. Si ho tinc més arreplegadet me centre més i no tarde una eternitat (o no l'acave). Per lo demés, també usarà menys memòria per a guardar els mapes.

Hui intentaré dedicar-me a fer grafics i l'editor.

dimecres, 1 d’abril del 2009

Patman movent caixes

El video que volia pujar ahir, gracies a les recomanacions del JailDesigner:



Al ser de 320x200 i redimensionarlo a 640x480 se veu estirat, pero be.

Per cert, el que es veu baix de Patman ho he deixat per a tindre una referència exacta de en quin tile està.

dimarts, 31 de març del 2009

Per fi!!!

Ale, ja està el motor basic acavat. Després d'un montó de proves no he trobat cap poll. Segur que acavarà eixint-ne algún, pero al menys la base es robusta i prou confiable.

Al final no hi ha forces ni resistències. El que si que pasa es que un objecte, quan espenta a altre, va la meitat de apresa. Si espenta a dos seguits, encara més lento. Queda prou be. De totes formes te un xicotet efecte raro, heretat del Batman de Jon Ritman (ja que ho he implementat like ho va fer ell): si espentes cap a fora de la pantalla tot es veu be, pero si espentes cap a dins se veu un xicotet bot entre els dos últims objectes de la cua. Es algo amb el que puc viure tranquilament. Tinc un video gravat amb el Fraps, pero al pujarlo me diu que no sap convertir-lo. ¿Algú me sap dir un format amb el que pujar les coses a YouTube i llocs similars?

Ara el sistema el que fa es el següent:
Quan un objecte es mou cap a un costat primer comprova si colisiona amb algú. Si no hi ha colisió es mou, i a més comprova si te algún objecte damunt d'ell. En cas afirmatiu els espenta en el mateix sentit en que s'ha menejat (no espera a vore si es mouen o no).

Si sí que hi ha colisió, no es mourà, i li pegarà una espenta al objecte o objectes contra els que ha colisionat, en el mateix sentit en que pretenia moures. Un objecte, al rebre una espenta, comprovarà si ha de respondre a una espenta per eixe costat. Si no ha de respondre, no farà res. Pero si sí que ha de respondre, li tornarà la "espenta de retorn" que tinga definida.

Normalment un objecte no te espenta de retorn, pero hi ha algúns que sí que la tenen. Per exemple, els malos tenen una espenta de retorn que el que fa es matar al espentador (en el cas que siga Patman); altres, al rebre una espenta per dalt, tornen una espenta cap a un costat, convertint-se en cintes transportadores; i els marcs de les portes tornen una espenta cap a la porta (al igual que pasava en el Batman), fent que siga més facil entrar en les portes. A més, si el objecte es movible intentarà moures en el sentit indicat, fent el mateix que abans.

A part, si el objecte te definit el flag de gravetat, tendirà a caure (espenta cap a baix) fins trobar altre objecte per baix, o el piso.

Quan s'espenta a un objecte, en realitat el que es fà es fixar un flag en el byte de espenta. Al haver 6 direccions, s'usen els bits 0-5. A més, el bit 6 es el flag de matar (el 7 no s'usa per ara).

Per a determinar per quins costats es sensible el objecte a que li espenten (per quins costat el objecte farà algo si l'espenten) cada objecte te un byte de màscara contra el que es compararà el byte de espenta amb un AND. Si se li ha espentat per un costat sensible, tornarà el byte de retorn, que es un byte amb la mateix estructura que el de espenta i determina per quins costats se li espenta al objecte que havía iniciat l'espenta (mmm, igual ha quedat molt lios).

Si un objecte es mou o no al ser espentat es defineix amb altre flag. Altres flags disponibles son: gravetat, desapareix, invisible, animat, orientable i actiu. Crec que son prou descriptius per si mateix, i crec que ja he parlat abans de quasi tots ells.

A part, cada objecte pot ser d'un tipus especific, o tindre una movilitat inherent, dels quals ja he parlat abans.

Demà intentarè implementar les portes, i a vore si puc releasar algo.

dilluns, 30 de març del 2009

Poc que dir

Pos eso, hui tinc poc que dir. No es que no haja fet res, pero es que estic en un moment de canvis en el codi i primer vull tindre clares les coses.

Joer, son quatre línies de codi mataes, pero lo que costaràn de parir... En realitat, el problema es intentar fer-ho tot bonic i genèric. Si haguera començat a escriure like loc, a part de que serien moltes més linies de codi ja estaria de sobra fet, pero crec que val la pena pensar ben pensades les coses per anar guanyant soltura en fer bones sol·lucions. Total, sent projectes personals m'ho puc permetre.

diumenge, 29 de març del 2009

Patman i més

Be, arriva el cap de setmana i es fa més dificil trobar temps. Be, com sempre, es té temps per al que se vol, perqué matinar si que he matinat per a vore la F1. Aixó si, la F1 es pot vore amb el xiquet en braços, pero programar no (snif...).

De totes formes, el desenvolupament del Patman no ha estat parat. El divendres li vaig pegar una bona ullada al sistema i vaig vore que no està tan mal. El poll causant de quasi tots els mals de cap ve de la forma en que procese les colisions.

Vaig a canviar el sistema un poc. Ara cada objecte, al moures en certa direcció comprovarà colisions amb tots els demés. Si troba una colisió amb un objecte directament no es mou, pero "l'espenta". Després, haja colisionat o no amb eixe objecte, pasa al següent, i així amb tots.

Espentar va a ser igual que amb el Batman de Jon Ritman. Quan un objecte espenta a altre, el que fa es encendre-li un flag on s'indica el sentit de l'espenta. Quan li toque a eixe objecte ser procesat, comprovarà els seus flags i intentará moures, fent el mateix que l'altre objecte. En principi, no vaig a tindre en compte forces, per simplificar el model (el Batman no te en compte forces, de totes formes).

Amb aquest sistema les espentes poden tindre una especie de retard, depenent de l'ordre de procesament dels objectes (but then again, al Batman li pasa lo mateix). Amb aquesta simplificació espere tindre un motor més estable sobre el que, si vull, fer ampliacions i tal.


En otro orden de cosas, he decidit que el Bryndiana siga una trilogia, en la qual cada joc serà d'un estil diferent:

Bryndiana Juan And The Fucking Machine (tentative title)
- To be announced
- Motivation: Serà tipus arcade de plataformes.
- Synopsis: Una extranya màquina esta fent coses rares a l'altra punta de la UPV. Bry, la fama del qual com a expert en aquest cap es mondialment reconeguda, serà requerit per a anar e investigar els aconteixements.

Bryndiana Juan And The Riot Of The Jail (tentative title)
- To be announced
- Motivation: Serà tipus Double Dragon, Golden Axe, i similar.
- Synopsis: La Jail ha sigut presa per Saucs i altres maleantes d'eixa calanya. Bry (amb l'ajuda opcional de Usufondo) tindrà que recuperarla a base de calbots.

Bryndiana Juan And The Quest For The Lost Comp (tentative title)
- In production
- Motivation: Serà el que tenía pensat, tipus Prince Of Persia.
- Synopsis: Falta una hora per obrir les Jails. Bry i Fondy veuen que s'ha estropeat un comp de la Jail y Bry ha d'anar a buscar-ne altre de reemplaç al magatzem, situat en les profunditats de les catacombes que s'extenen per baix de la UPV. Els ràcanos dels administradors de la UPV no li ficaràn les coses fàcils, pero Bry no deixarà la seua Jail sense un comp. Té una hora!

dijous, 26 de març del 2009

Agh! Plaga de polls!!

Buff, hui he estat implementant les espentes, he modificat la reordenació, les colisions i les forces. La conclusió es la següent:

Intentar resoldre un sistema determinista d'una forma poc formal ens du inevitablement al fracas.

Per moltes revisions que porte fetes, reescritures, captura de polls, etc... no paren de apareixer més. I estem parlant de nomes un centenar de línies de codi dispersades entre 2 classes i uns menys de 15 mètodes.

Quan no es que la reordenació no ha funcionat despres de moure un objecte al est d'altre objecte amb una z 1 unitat major, es que l'aplicació de la força de la gravetat sobre un objecte al mateix temps que altre l'està espentant cap amunt deixa un buit entre ells, o... be, farragosos polls.

Total, que vaig a agafar good old llapisera i paper i a planificar tots els casos posibles i les seues interaccions; i després vorem qué me toca afegir, esborrar o modificar.

Per a començar, probàblement vaig a tirar a la basura el mètode de reordenar nomes quan es mou un objecte i faré una reordenació total a cada bucle de proces. O com a molt nomes ordenaré quan s'haja menejat algo, pero tota la llista, no de forma selectiva com feia abans. Vaig a investigar quin es el millor algorisme per a ordenar llistes quasi ordenades.

Es més, vaig a plantejarme el sistema complet. Si no ho faig ara més tard perdre més temps solventant polls, o m'arrepentiré perque no podré fer alguna cosa, com va passar amb el "Patman i Estos". La premisa a seguir es:

Si tinc una sol·lució que m'aporta el 90% del que vull amb un 10% de l'esforç, decantar-se per eixa sol·lució.

En un principi volia encarar el desenvolupament del Patman desde el punt de vista de l'optimització, fent que les estructures de dades foren el més comprimit possible, etc... Més tard he vist que algunes coses, a part de no tindre sentit voler comprimirles tant, me complicaven molt la vida. Així que millor replantejar-ho tot. M'he donat compte que el codi del "Patman i Estos" es més simple i permitia moltes mes coses, lo qual es absurd.

Esta vesprada em dedicaré a pensar qué es el millor per al joc, la forma més simple de tindre el més prompte posible el joc funcionant. Vaig a intentar ser reduccionista i aplicar la navalla de Occam:

En igualtat de condicions, la sol·lució més senzilla es probablement la correcta

Mmm... deuria deixar totes estes frases per ací per a recordarles sempre. Hui ha sigut un dia amb molt de temps, pero també ha sigut un dia frustrant, perque despres de tot el treball vaig que el camí que estava agafant no es precissament un camí de roses. Intentarem ser optimistes i alegrarse per haver tingut un dia amb temps per a concentrarse i detectar prompte que el sistema era una merda per a corregir-ho.

Demà vos conte...

dimecres, 25 de març del 2009

Ordenant Objectes

Hui, per fí, he avançat prou amb el Patman. He implementat l'ordenació dels objectes i he fet una mínima interacció entre ells. El sistema de espentes encara no està funcionant, es lo següent que ve.

Vaig a parlar un poc més en profunditat del sistema d'ordenació del objectes. En un mapa isomètric els objectes en un moment donat poden estar més cap a dins o més cap a fora. Si estàn més cap a fora s'han de pintar després que els que estàn més cap a dins.

La forma que he decidit usar per a mantindre els objectes en l'ordre que toca es ficar-los en una llista enllaçada, així puc mourelos sense massa dificultat ni cost, movent punters. La llista està enllaçada desde el objecte més profund (que se pintarà primer) fins el mes extern (que es pintarà l'ultim).

Una aproximació hauria pogut ser reordenar la llista a cada pas del bucle, per si ha canviat algún objecte de posició. No obstant, he decidit que l'ordenació nomes es farà quan un objecte es moga. Es més, l'unic que es reordena es eixe objecte, que buscarà sobre quins objectes ha de permutar la seua posició.

Una optimització que he implementat sobre aquest sistema es el fet que no fa falta recorrer tota la llista de objectes, sino nomes el costat de la llista que convinga. Si el objecte va cap avall, cap al nord o cap a l'est, nomes pot pujar posicions en la llista (o siga, anar cap al fondo). Així, nomes he de comparar la seua posició amb els que te davant en la llista.


Pel contrari, si es mou cap a dalt, cap al sud o cap a l'oest, nomes pot baixar posicions en la llista, pel que nomes el compararé amb els que te darrere en la llista.


La comparació consistirà en: si he superat en posició al que tinc després, m'intercanvie amb ell i ho comprove amb el següent. Pararà en el moment en que trobe un objecte al que no pot superar.

Les colisions les comprove de la mateixa forma, ja que si em meneje cap al sud nomes puc colisionar amb un objecte que estiga més al sud que jo. Més baix en la llista, per tant, i només fa falta que mire eixe costat de la llista.

Està quedant super-sucosot (be, a mi m'ho pareix :-), es una llàstima no tindre molt de temps esta setmana. Com sempre, espere que demà millore la cosa. En quan tinga el sistema de espentes implementat releasaré una versió alpha amb una sola habitació amb objectes que espentar i tal, per que pugau tastar el saboret que tindrà i pugau provar, de paso, si trobeu algún poll.