Archive für 7.1.2010

Bildschirm aufräumen

Bisher haben wir immer wieder unsere Zeichenfläche mit der Landschaft voll”gemalt”. Das sah schonmal ganz gut aus, jedoch fällt uns auf, dass in dem Falle das am Horizont hohe Berge zu sehen waren und danach wieder flache die hohen einfach “stehen” bleiben. Das liegt daran, dass wir den Bildschirm nicht aufräumen - also leeren - bevor wir neu zeichnen.
Wenn wir dies nun einfach so einbauen, werden wir schnell feststellen, dass dies zum Teil zum Flackern kommt. Das sieht sehr unschön aus. Um das zu verhindern gibt es einen recht einfachen Trick:
Backbuffer
Oder wie auch immer man es nennen wil. Man verwendet quasi zunächst eine unsichtbare Zeichenfläche.Diese wird geleert und das neue Bild dargestellt/berechnet. Anschließend wird der fertige Bildschirm sichtbar gemacht. Nun wird wieder im “verborgenen” gezeichnet. Das führt dazu dass wir nun 2 Datenbereiche haben in die wir unser Spiel zeichnen und sagen der PSP am Ende nur noch wo der fertige Bildschirm liegt.
Zuerst definieren wir einen “flip” parameter vom Typ bool der immer anzeigt welcher Puffer denn nun der ist, in den wir zeichnen. Dann brauchen wir noch eine Methode die uns immer die Adresse auf den aktuellen Zeichenpuffer liefert, diese Adresse werden wir dann auch an die Render-Methode übergeben. Zum Schluss muss nur noch der Mainthread angepasst werden, so dass die Darstellung zwischen den 2 Puffern gewechselt wird.
Doch nun erstmal zu den neuen Definitionen in der Homebrew Basisklasse:

class ClHomebrew {
….
protected:
/*
* in welchen Puffer soll gezeichnet werden ?
*/
bool pg_drawframe;
/*
* ermitteln des adresse des zeichen Puffers
*/
unsigned char* pgGetVramAddr();

Die Implementierung für diese Methode sieht dann so aus:

unsigned char *ClHomebrew::pgGetVramAddr(){
if (pg_drawframe)
return VRAM_TOP + FRAMESIZE;
else
return VRAM_TOP;
}

Zusätzlich müssen wir den neuen Zeichenpuffer auch beim setzen der Bildpunkte berücksichtigen. Darum sieht die Methode setPixel nun wie folgt aus:

void ClHomebrew::setPixel(int x, int y, int color){
//Zeiger auf das zu zeichnende Pixel berechnen
unsigned char* screen = pgGetVramAddr()+x*PIXEL_SIZE+y*LINE_SIZE*PIXEL_SIZE;
//Farbe setzen
u32* s;
s = (u32*)screen;
*s = color;
}

Im nächsten Schritt überarbeiten wir nun den Mainthread unserer Voxelklasse. Dort holen wir uns erstmal die Adresse des Zeichenpuffers und setzen dort alle Daten auf 0 (also schwarz). Danach wird die Voxellandschaft gezeichnet und dann wird der Puffer dargestellt. Am Schluss folgt das setzen des Flags, dass wir nun in den 2. Puffer zeichnen wollen. Ggfl. müssen wir folgende Includes in unsere Voxelklasse mit aufnehmen:<stdlib.h> und <pspdisplay.h>

void ClPspVoxel::mainthread(){
unsigned char* drawBuffer;
drawBuffer = pgGetVramAddr();
//leer räumen des Bildschirms
memset(drawBuffer, 0, FRAMESIZE);
//Zeichnen
render();
//nun teilen wir der PSP mit, welches unser Zeichenpuffer ist der nun gezeigt werden soll
sceDisplaySetFrameBuf(drawBuffer, LINE_SIZE, PSP_DISPLAY_PIXEL_FORMAT_8888, PSP_DISPLAY_SETBUF_IMMEDIATE );
//Flip des schalters welcher Puffer gefüllt wird
pg_drawframe = pg_drawframe?false:true;
//nach jedem Zeichnungsvorgang darauf warten dass die PSP das Bild
//komplett ausgegeben hat
sceDisplayWaitVblankStart();
}

|