- PSP Spieleentwicklung - http://psp.anmabagima.de -
Es brennt
Dieser Eintrag stammt von AnMaBaGiMa Am 30.11.2009 @ 21:46 In Allgemein | 1 Kommentar
Zielstellung
Nachdem wir nun eine grundlegende Struktur für das Erstellen einfacher Homebrews erstellt haben, wollen wir nun in großen Schritten von “Hallo Welt!” zu etwas spectakulärerem kommen: Ein Feuer auf der PSP.
Der Allgorythmus
Die Formel aus der das Feuer berechnet wird ist relativ einfach. Es gibt einschlägige Seiten (auch die meine ;o) ) die diesen näher beschreiben. Darum verweise ich hier auf die Beschreibung im [1] Netz. Anhand eines Windows-Programmes wird hier die Formel erläutert. Doch wir wollen das ganze ja in der PSP implementieren.
Das Feuerprogramm
Als erstes erstellen wir in unserem Projekt ein Klasse, welche von der allgemeinen Homebrewklasse abgeleitet wird und nennen diese ClPspFeuer. Bevor wir hier auf die PSP spezifika eingehen werden wir in dieser Klasse erstmal die Feuerroutine implementieren. Dazu definieren wir einen Puffer der die Hitzepunkte unseres Feuers aufnehmen kann und die Routine welche das Feuer in diesem Puffer berechnet.
#define FIRE_WIDTH 240
#define FIRE_HEIGHT 50
class ClPspFeuer : public ClHomebrew {
protected:
/*
* berechnen des Feuers
*/
void calculateFire();
private:
/*
* das Feuer in einer Größe 240×50
*/
unsigned char feuer[FIRE_WIDTH][FIRE_HEIGHT];
Die Implementierung der Feuerberechnung sieht dann wie folgt aus:
void ClPspFeuer::calculateFire(){
//zunächst in der letzten Zeile des Feuers ganz heiße Punkte dazu..
for (int n=0;n<25;n++){
//25 neue Punkte
int x = rand() % FIRE_WIDTH;
int heat = feuer[x][FIRE_HEIGHT-1];
heat += rand() % 16;
if (heat > 255) heat = 0;
feuer[x][FIRE_HEIGHT-1] = heat;
}
//dann von oben nach unten die neue Hitze berechnen
for (int y=0;y < FIRE_HEIGHT-1;y++){
for (int x=1;x < FIRE_WIDTH-1;x++){
int heat = feuer[x][y] + feuer[x][y+1] + feuer[x+1][y+1] + feuer[x-1][y+1];
heat >>= 2;
//abkühlen
if (heat > 0) heat- -;
feuer[x][y] = heat;
}
}
}
Feuer und Homebrew
Nun ist der “heiße” Teil erledigt. Nun müssen wir unsere Homebrew Klasse noch erweitern, so dass wir diese auch nutzen können. Wenn wir an die allgemeine Homebrewklasse zurückdenken sehen wir, dass wir folgende Methoden implementieren müssen:
init(): initialisieren der Basisklasse, “Hallo Feuer…” ausgeben und den Zufallsgenerator rand() initialisieren.
mainthread(): Hier wird das Feuer berechnet und gezeichnet
Wir brauchen ein paar neue Includes:
extern “C”{
#include <stdlib.h>
#include <pspdisplay.h>
}
bool ClPspFeuer::init(){
if (!ClHomebrew::init()) return false;
//in der Initialisierung einmal Hallo Feuer schreiben….
pspDebugScreenPrintf(”Hallo Feuer!”);
//Zufallsgenerator initialisieren
srand(1002345);
return true;
}
void ClPspFeuer::mainthread(){
//in der Hauptroutine das Feuer Berechnen und zeichnen
calculateFire();
//zeichne Feuer an festgelegter Position (top, left)
drawFire(120, 110);
//nach jedem Zeichnungsvorgang darauf warten dass die PSP das Bild
//komplett ausgegeben hat
sceDisplayWaitVblankStart();
}
Das PSP Display
Bevor wir uns der Methode zum zeichnen des Feuers auf den Bildschirm der PSP widmen nochmals ein wenig Theorie.
Die PSP hat einen Bildschirm mit 480×272 Bildpunkten. Jeder Bildpunkt hat - ohne weitere Eingriffe is das Bildpunktformat 8888. Das bedeutet 8 Bit für rot, grün, blau und den Alphakanal. Wobei der Alphakanal beim direkten setzen der Bildpunkte auf dem Bildschirm keinerlei Relevanz hat. Insgesammt ist jedes Pixel als 32Bit bzw. 4Byte “groß” (PixelSize).
Die Daten die die PSP im Display darstellt können theoretisch an jeder beliebigen Speicheraddresse stehen. Es muss dann nur über einen Befehl festgelegt werden wo dieser Berecih beginnt. Am Einfachsten ist es jedoch wenn man die “Werkseinstellung” nutzt. Hier beginnen die Displaydaten an der Addresse: 0×44000000 im sogenannten VRAM.
In diesem Speicherbereich ist der Bildschirm Zeilenweise abgelegt, wobei die Zeilenlänge immer einer Potenz von 2 entspricht, nicht aber der eigentlichen Bildschirmbreite. Eine Zeile ist hier also nicht 480 sondern 512 Punkte breit. Danach lässt sich jeder Punkt wie folgt Addressieren:
punkt_addresse = 0×44000000 + x*PixelSize + y*PixelSize*512;
Von dieser Theorie abgeleitet können wir nun die Zeichnungsroutinen implementieren.
/*
* für die Grafikausgabe definieren wir ein paar Konstanten
*/
// Den PSP Bildschirm der 480×272 Pixel misst
#define SCR_WIDTH 480
#define SCR_HEIGHT 272
// Die Pixelgröße in Bytes
#define PIXEL_SIZE 4
// Die Zeilenlänge - welche immer eine Potenz von 2 sein muss
#define LINE_SIZE 512
// Die größe des Grafikpuffers
#define FRAMESIZE (SCR_HEIGHT * LINESIZE * PIXEL_SIZE)
// Die Start-adresse des Grafikspeichers im VRAM
#define VRAM_TOP ((unsigned char *)0×44000000)
void ClPspFeuer::setPixel(int x, int y, int color){
//Zeiger auf das zu zeichnende Pixel berechnen
unsigned char* screen = VRAM_TOP+x*PIXEL_SIZE+y*LINE_SIZE*PIXEL_SIZE;
//Farbe setzen
*screen = color;
}
void ClPspFeuer::drawFire(int startX, int startY){
//jeden Feuerpunkt als Bildpunkt mit einer Farbe
//in Abhängigkeit von der Hitze setzen
for (int x=0;x<240;x++){
for (int y=0;y<50;y++){
setPixel(x+startX, y+startY, (int)feuer[x][y]);
}
}
}
Was noch fehlt
Zum Abschluss müssen nur noch ein paar “Kleinigkeiten” implementiert werden um die Homebrewklasse komplett zumachen. Zum einen soll dies eine Singleton-Klasse sein. Das heißt wir brauchen eine statisch Method die die Instanz der Klasse liefert. Im Anschluss daran ist die Klasse dann komplett. Das fehlt also noch:
bei der Definition:
private:
static ClPspFeuer* _instance;
bei der Implementierung:
ClPspFeuer* ClPspFeuer::_instance = 0;
ClPspFeuer *ClPspFeuer::getInstance(){
if (!_instance){
_instance = new ClPspFeuer();
}
return _instance;
}
Dieser Artikel wurde ausgedruckt ab PSP Spieleentwicklung: http://psp.anmabagima.de
URL zum Artikel: http://psp.anmabagima.de/es-brennt/
URLs in this post:
[1] Netz: http://www.anmabagima.de/DXCpp/ddrawV1.html
Klicken hier zum Drucken.