datenstrukturen look-up tabellen, zufallszahlen, listen, speichermanagement und dateiverwaltung
TRANSCRIPT
DatenstrukturenDatenstrukturen
Look-Up Tabellen, Look-Up Tabellen, Zufallszahlen, Listen, Zufallszahlen, Listen,
Speichermanagement und Speichermanagement und DateiverwaltungDateiverwaltung
Look-Up Tabellen: Look-Up Tabellen: Probleme bei SpielenProbleme bei Spielen
Spielszenen müssen sehr oft pro Spielszenen müssen sehr oft pro Sekunden berechnet werdenSekunden berechnet werden
Wenn es zu viele Berechnungen werden Wenn es zu viele Berechnungen werden fängt das Spiel an zu „ruckeln“fängt das Spiel an zu „ruckeln“
Problem ist nicht immer die Grafikkarte Problem ist nicht immer die Grafikkarte (bei 3D spielen)(bei 3D spielen)
Häufig ist KI schuld (z.B. bei Häufig ist KI schuld (z.B. bei Strategiespielen: viele Einheiten werden Strategiespielen: viele Einheiten werden von Computer bewegt und kommandiert)von Computer bewegt und kommandiert)
wiederkehrende Operationen werden immer neu berechnet
Lösung: Look Up TabelleLösung: Look Up Tabelle
Wikipedia:Wikipedia: In der In der InformatikInformatik ist eine ist eine Look-Up-TableLook-Up-Table eine eine
DatenstrukturDatenstruktur, meist ein (, meist ein (assoziativesassoziatives) ) ArrayArray, das , das komplizierte Laufzeitberechnungen durch einen komplizierte Laufzeitberechnungen durch einen einfachen Zugriff auf die Datenstruktur ersetzt. einfachen Zugriff auf die Datenstruktur ersetzt. Dies führt zu einem signifikanten Dies führt zu einem signifikanten Geschwindigkeitsgewinn, sofern die benötigten Geschwindigkeitsgewinn, sofern die benötigten Speicherzugriffe schneller sind als die normale Speicherzugriffe schneller sind als die normale Berechnung. Berechnung.
Alle wiederkehrenden Berechnungen Alle wiederkehrenden Berechnungen werden durch werden durch Look-Up TabellenLook-Up Tabellen vermiedenvermieden
Was macht eine Look-Up Was macht eine Look-Up Tabelle?Tabelle?
Vor Beginn des Spiels werden alle benötigten Vor Beginn des Spiels werden alle benötigten Werte vorberechnetWerte vorberechnet
Es gibt verschiedene Typen von Look-Up Es gibt verschiedene Typen von Look-Up Tabellen, so dass man sich vorher überlegen Tabellen, so dass man sich vorher überlegen muss, von welcher Art (sin, cos, Quadratwurzel) muss, von welcher Art (sin, cos, Quadratwurzel) diese sein muss und welche Genauigkeit diese diese sein muss und welche Genauigkeit diese haben musshaben muss
Es können nicht nur Funktionswerte, sondern Es können nicht nur Funktionswerte, sondern bsplw. auch Zufallszahlen in Look-Up Tabellen bsplw. auch Zufallszahlen in Look-Up Tabellen gespeichert werdengespeichert werden
Um flexibel mit Look Up Tabellen arbeiten zu Um flexibel mit Look Up Tabellen arbeiten zu können sollte man sie in Klassen einbettenkönnen sollte man sie in Klassen einbetten
Beispiel:Beispiel: #include <iostream.h>#include <iostream.h> #include <math.h>#include <math.h>
#define SAFE_DELETE(p) {if(p) {delete (p); (p)=NULL;}}#define SAFE_DELETE(p) {if(p) {delete (p); (p)=NULL;}} #define SAFE_DELETE_ARRAY(p) {if(p) {delete[] (p); #define SAFE_DELETE_ARRAY(p) {if(p) {delete[] (p);
(p)=NULL;}}(p)=NULL;}}
// bis hier hin nur „Vorgeplänkel“, Includen von iostream // bis hier hin nur „Vorgeplänkel“, Includen von iostream und math-Klasse und Einbinden von Makros von Heap-und math-Klasse und Einbinden von Makros von Heap-SpeicherSpeicher
//Beispiel: sinus-Tabelle//Beispiel: sinus-Tabelle
class CSinLookUpclass CSinLookUp {{ private:private: long long
AnzElements;AnzElements; float Genauigkeit;float Genauigkeit; float* Tabelle;float* Tabelle;
public:public: CSinLookUp(float Schrittweite = 1.0f) // CSinLookUp(float Schrittweite = 1.0f) // KonstruktorKonstruktor {{ Genauigkeit = 1.0f/Schrittweite;Genauigkeit = 1.0f/Schrittweite; AnzElements = (long)(360*Genauigkeit);AnzElements = (long)(360*Genauigkeit); Tabelle = new float[AnzElements];Tabelle = new float[AnzElements];
for(long winkel = 0; winkel < AnzElements; winkel++)for(long winkel = 0; winkel < AnzElements; winkel++) {{ // // Berechnung der Sinus Look-Up TabelleBerechnung der Sinus Look-Up Tabelle Tabelle[winkel] = sinf(winkel*Schrittweite*3.141592654f/180.0f);Tabelle[winkel] = sinf(winkel*Schrittweite*3.141592654f/180.0f); }} }} ~CSinLookUp() // ~CSinLookUp() // DestruktorDestruktor {{ SAFE_DELETE_ARRAY(Tabelle) // SAFE_DELETE_ARRAY(Tabelle) // Zerstören des ArraysZerstören des Arrays }} float ReturnSinValue(float winkel) // float ReturnSinValue(float winkel) // ZugriffsfunktionZugriffsfunktion {{ if(winkel < 0.0f)if(winkel < 0.0f) winkel += 360.0f;winkel += 360.0f; else if(winkel > 360.0f)else if(winkel > 360.0f) winkel -= 360.0f;winkel -= 360.0f;
return(Tabelle[(long)(winkel*Genauigkeit)]);return(Tabelle[(long)(winkel*Genauigkeit)]); }} };};
Aufruf in Methode mit:Aufruf in Methode mit: CSinLookUp* SinLookUp = new CSinLookUp(0.1f); // CSinLookUp* SinLookUp = new CSinLookUp(0.1f); //
Schrittweite Schrittweite //von 0.1//von 0.1
cout << "sin(45 DEG) = " << SinLookUp-cout << "sin(45 DEG) = " << SinLookUp->ReturnSinValue(45.0f) << endl;>ReturnSinValue(45.0f) << endl;
SAFE_DELETE(SinLookUp) // zerstören der TabelleSAFE_DELETE(SinLookUp) // zerstören der Tabelle
ZufallszahlenZufallszahlen
Werden in einem Spiel hauptsächlich für Werden in einem Spiel hauptsächlich für die Variation des Spielablaufes und des die Variation des Spielablaufes und des Gegnerverhaltens eingesetztGegnerverhaltens eingesetzt
In C++ gibt es eine Funktion für In C++ gibt es eine Funktion für ZufallszahlenZufallszahlen rand()-Funktion ist komfortabel für die rand()-Funktion ist komfortabel für die
Erzeugung von ZufallszahlenErzeugung von Zufallszahlen Am komfortabelsten ist es, eine Klasse Am komfortabelsten ist es, eine Klasse
alleine für Zufallszahlen anzulegen und die alleine für Zufallszahlen anzulegen und die Zahlen in einer Tabelle zu speichernZahlen in einer Tabelle zu speichern
Aufruf der Zufallszahlenmethode Aufruf der Zufallszahlenmethode und Initialisierung des und Initialisierung des
„Zufallsgenerators“„Zufallsgenerators“ int main(void)int main(void) {{ // Initialisierung des Zufallsgenerators// Initialisierung des Zufallsgenerators srand(time(0));srand(time(0)); // dann Aufruf der Zufallszahlenfunktion// dann Aufruf der Zufallszahlenfunktion cout << frnd(-5.0f,5.0f) << endl;cout << frnd(-5.0f,5.0f) << endl; }}
Erzeugung von Erzeugung von Zufallszahlen mit rand()-Zufallszahlen mit rand()-
FunktionenFunktionen Mit einfachen Inline-Funktion (hier float-Zufallszahl):Mit einfachen Inline-Funktion (hier float-Zufallszahl):
float tempFrnd;float tempFrnd;
inline float frnd(float low, float high)inline float frnd(float low, float high) {{ tempFrnd = low + ( high - low ) * ( tempFrnd = low + ( high - low ) * ( (long)rand()(long)rand() ) / ) /
RAND_MAX; RAND_MAX; // RAND_MAX enthält die größtmögliche Zufallszahl// RAND_MAX enthält die größtmögliche Zufallszahl if(tempFrnd < high)if(tempFrnd < high) return tempFrnd;return tempFrnd; elseelse return low;return low; }} // // weiteres Beispiel im Buch (extra Klasse für weiteres Beispiel im Buch (extra Klasse für
Zufallszahlen)Zufallszahlen)
Einfach verkettete ListenEinfach verkettete Listen Einheit zur Speicherung und Verwaltung Einheit zur Speicherung und Verwaltung
relevanter Datenrelevanter Daten Besteht aus mehreren Knoten (sofern Besteht aus mehreren Knoten (sofern
mehrere Daten vorhanden sind)mehrere Daten vorhanden sind) Jeder Knoten ist über einen Zeiger mit Jeder Knoten ist über einen Zeiger mit
dem nächsten Knoten verbundendem nächsten Knoten verbunden Jeder Knoten ist entweder eine Jeder Knoten ist entweder eine
Strukturvariable oder ein KlassenobjektStrukturvariable oder ein Klassenobjekt Erster Knoten = Head-KnotenErster Knoten = Head-Knoten Letzter Knoten = Tail-KnotenLetzter Knoten = Tail-Knoten
Wie sieht eine einfach Wie sieht eine einfach verkettete Liste aus?verkettete Liste aus?
Head Head Node Node Node Node Node Node Tail Tail
ProblemeProbleme
Probleme bei ListenProbleme bei Listen Sehr unflexibelSehr unflexibel Sehr langsamSehr langsam Will man auf ein Element zugreifen, muss Will man auf ein Element zugreifen, muss
die gesamte Liste durchlaufen werden um die gesamte Liste durchlaufen werden um das Element zu finden (es sei denn es das Element zu finden (es sei denn es befindet sich am Anfang)befindet sich am Anfang)
Die Reihenfolge der abgelegten Objekte Die Reihenfolge der abgelegten Objekte entspricht meistens nicht der Reihenfolge entspricht meistens nicht der Reihenfolge der im Spiel benötigten Objekteder im Spiel benötigten Objekte
SpeicherproblemeSpeicherprobleme
Jedes Erzeugen von Knoten kostet Jedes Erzeugen von Knoten kostet Speicherplatz (mit „new“ wird Speicherplatz Speicherplatz (mit „new“ wird Speicherplatz reserviert)reserviert)
„„new“ sucht nach einem Speicherblock mit new“ sucht nach einem Speicherblock mit geeigneter Größegeeigneter Größe
Nur wenn ein geeigneter Block gefunden wird Nur wenn ein geeigneter Block gefunden wird kann Objekt angelegt werdenkann Objekt angelegt werden
Sind alle gefundenen Speicherblöcke zu klein, Sind alle gefundenen Speicherblöcke zu klein, kann Objekt nicht erzeugt werdenkann Objekt nicht erzeugt werden
SpeicherfragmentieSpeicherfragmentierungrung
Arrays lösen diese Arrays lösen diese ProblemeProbleme
Arrays reservieren einen bestimmten Arrays reservieren einen bestimmten SpeicherplatzSpeicherplatz Kritik: unnötiger Speicherplatz wird Kritik: unnötiger Speicherplatz wird
verschenkt und Arrays sind zu unflexibelverschenkt und Arrays sind zu unflexibel Für Spieleprogrammierung gilt das nichtFür Spieleprogrammierung gilt das nicht
Beispiel:Beispiel: Spiel mit animierten AsteroidenSpiel mit animierten Asteroiden
Obergrenze der Anzahl an Asterioden wird durch Obergrenze der Anzahl an Asterioden wird durch vordefiniertes Array festgelegtvordefiniertes Array festgelegt
Kein Nachteil, weil sowieso nur eine gewisse Anzahl Kein Nachteil, weil sowieso nur eine gewisse Anzahl gerendert und animiert werden könnengerendert und animiert werden können
Zugriff auf Array geht viel schneller als Zugriff auf Zugriff auf Array geht viel schneller als Zugriff auf Element in verketteter ListeElement in verketteter Liste
Memory ManagerMemory Manager Es ist sinnvoll eine Klasse zu Es ist sinnvoll eine Klasse zu
entwerfen die die Speicherverwaltung entwerfen die die Speicherverwaltung in einem Spiel übernimmtin einem Spiel übernimmt Also alles in einer Memory Manager Also alles in einer Memory Manager
KlasseKlasse Klasse hat vordefiniertes Array mit Klasse hat vordefiniertes Array mit
dem gearbeitet werden kanndem gearbeitet werden kann Beispiel im BuchBeispiel im Buch
Programmbeispiele Programmbeispiele Tag2 Tag2 Memory Memory ManagerManager
DateiverwaltungDateiverwaltung
Dazu zählt etwa eine Speicherfunktion für Dazu zählt etwa eine Speicherfunktion für ein Spielein Spiel
Wichtige Struktur in Beispiel:Wichtige Struktur in Beispiel: _finddata_t _finddata_t Speichert alle Informationen Speichert alle Informationen
über die Dateiarbeitüber die Dateiarbeit Wichtige Funktionen sind:Wichtige Funktionen sind:
_findfirst( "Szenarien/*.*", &c_file )_findfirst( "Szenarien/*.*", &c_file ) Untersucht ob der Ordner überhaupt existiertUntersucht ob der Ordner überhaupt existiert
_findnext( hFile, &c_file )_findnext( hFile, &c_file ) Alle weiteren Dateien werden durchsuchtAlle weiteren Dateien werden durchsucht