Die Präsentation wird geladen. Bitte warten

Die Präsentation wird geladen. Bitte warten

Tag 2 Look-up-Tabellen, Zufallszahlen, Listen, Speichermanagement und Dateiverwaltung 17.10.2007 Quelle: 3D-Spiele mit C++ und DirectX in 21 Tagen, Alexander.

Ähnliche Präsentationen


Präsentation zum Thema: "Tag 2 Look-up-Tabellen, Zufallszahlen, Listen, Speichermanagement und Dateiverwaltung 17.10.2007 Quelle: 3D-Spiele mit C++ und DirectX in 21 Tagen, Alexander."—  Präsentation transkript:

1 Tag 2 Look-up-Tabellen, Zufallszahlen, Listen, Speichermanagement und Dateiverwaltung 17.10.2007 Quelle: 3D-Spiele mit C++ und DirectX in 21 Tagen, Alexander Rudolph, Markt+Technik Verlag

2 2.1 Look-up-Tabelle Besonders kritisch für die Performance bei Computerspielen: Berechnungen für die grafische Darstellung und die Simulation von KI Daher unser Ziel: optimierter Programmcode Rechenzeit sparen kann eine Look-up-Tabelle Zeitaufwendige Berechnungen, die sich im Programmablauf häufig wiederholen, werden bereits zum Spielstart (oder Start einer bestimmten Sequenz/Szene) durchgeführt und in eine Tabelle geschrieben Vor dem Erstellen einer Look-up-Tabelle muss festgestellt werden, für welche Funktionen dies Sinn macht und welche Genauigkeit die Tabelle einmal haben wird Beispiele für Funktionen, aus denen eine Look-up-Tabelle bestehen könnte: Sinus, Kosinus, Arkuskosinus, Quadratwurzel...

3 Auszug Beispiel LookUp 1 #include #define SAFE_DELETE(p) {if(p) {delete (p); (p)=NULL;}} #define SAFE_DELETE_ARRAY(p) {if(p) {delete[] (p); (p)=NULL;}} float* SinLookUp = NULL; float* CosLookUp = NULL; float* ArcCosLookUp = NULL; float* WurzelLookUp = NULL; inline void Berechne_LookUpTabellen(void) { for(long winkel = 0; winkel < 360; winkel++) { SinLookUp[winkel] = sinf(winkel*3.141592654f/180.0f); CosLookUp[winkel] = cosf(winkel*3.141592654f/180.0f); } for(long i = 0; i < 2001; i++) { WurzelLookUp[i] = sqrtf(i/1000.0f); ArcCosLookUp[i] = 180.0f/3.141592654f*acosf((i-1000.0f)/1000.0f); } inline float ReturnSineValue(float winkel) { return(SinLookUp[(long)winkel]); }

4 int main(void) { SinLookUp = new float[360]; CosLookUp = new float[360]; ArcCosLookUp = new float[2001]; WurzelLookUp = new float[2001]; Berechne_LookUpTabellen(); cout << "sin(45 DEG) = " << ReturnSineValue(45.0f) << endl; SAFE_DELETE_ARRAY(SinLookUp) SAFE_DELETE_ARRAY(CosLookUp) SAFE_DELETE_ARRAY(WurzelLookUp) SAFE_DELETE_ARRAY(ArcCosLookUp) return 0; }

5 Klassische Funktion: Beim Funktionsaufruf springt der Compiler an die Stelle im Code, an der die gesuchte Funktion steht und führt sie dort aus. Sämtliche Parameter und die Rücksprungadresse müssen solang festgehalten werden. Inline-Funktion: Statt zur Funktion zu springen, ersetzt der Compiler den Funktionsaufruf im Code direkt durch den Funktionsinhalt. Vergrößert den Code, daher vor allem für kurze Funktionen geeignet. Kann dafür kleine Zeitersparnis bringen. Cast: (long)winkel wandelt die Variable vom Typ float in den Typ long um. Vorsicht geboten, C++ muss die Typenumwandlung auch wirklich durchführen können!

6 Klassen für Look-up-Tabellen Wünschenswert wäre eine Klasse für Look-up-Tabellen, die es erlaubt, dass ihre Genauigkeit flexibel bestimmt werden kann und die überwacht, dass nur zulässige Werte abgefragt werden Beispiel für Sinusfunktionen, im Beispielprogramm LookUp2 auch für Kosinus/Arkuskosinus/Quadratwurzel:

7 Auszug Beispiel LookUp2 class CSinLookUp { private: long AnzElements; float Genauigkeit; float* Tabelle; public: CSinLookUp(float Schrittweite = 1.0f) { Genauigkeit = 1.0f/Schrittweite; AnzElements = (long)(360*Genauigkeit); Tabelle = new float[AnzElements]; for(long winkel = 0; winkel < AnzElements; winkel++) { // Berechnung der Sinus Look-Up Tabelle Tabelle[winkel] = sinf(winkel*Schrittweite*3.141592654f/180.0f); } ~CSinLookUp() { SAFE_DELETE_ARRAY(Tabelle) }

8 2.2 Zufallszahlen Zufallszahlen dienen vor allem dazu, den Spielablauf abwechslungsreich zu gestalten Zufallszahlengenerator muss zunächst initialisiert werden: Funktion srand() Als Argument dient Funktion GetTickCount() oder time() Srand(time(0)) startet den Generator auf Basis der Zeit in Millisekunden, die seit dem Systemstart vergangen ist Anschließend Zufallszahl generieren mit rand() bzw. long(rand())

9 Auszug Beispiel Zufallszahlen #include // Header für die Funktionen rand() und srand() #include float tempFrnd; inline float frnd(float low, float high) { tempFrnd = low + ( high - low ) * ( (long)rand() ) / RAND_MAX; if(tempFrnd < high) return tempFrnd; else return low; } int main(void) { // Initialisierung des Zufallsgenerators srand(time(0)); cout << frnd(-5.0f,5.0f) << endl;

10 Verknüpfen der Konzepte: Look-up-Tabelle mit Zufallszahlen füllen Auszug Beispiel ZufallszahlenKlasse class CLongRandomNumberList { private: long counter; // Diese Variable enthält das aktuelle Element der Zufallszahlen-Tabelle long AnzElements; long SmallestNumber; // kleinste Zufallszahl long HighestNumber; // größte Zufallszahl long* ZahlenListe; // Zeiger auf das Array ZahlenListe public: ~CLongRandomNumberList() { SAFE_DELETE_ARRAY(ZahlenListe) }

11 CLongRandomNumberList(long Anzahl, long s, long h) { srand(time(0)); // Initialisierung des Zufallsgenerators AnzElements = Anzahl; SmallestNumber = s; HighestNumber = h; ZahlenListe = new long[AnzElements]; // Erzeugung der Zufallszahlen: for(counter = 0; counter < AnzElements; counter++ ) ZahlenListe[counter] = lrnd(SmallestNumber, HighestNumber); counter = 0; //Variable wird auf das erste Element der Zufallszahlen-Tabelle gesetzt. } long NeueZufallsZahl() { if(counter < AnzElements-1) counter++; // counter wird auf das nächste Element der Tabelle gesetzt. else counter = 0; // falls counter schon auf letztem Element return ZahlenListe[counter]; }

12 2.3 Einfach verkettete Listen Struktur einer EvL: Head -> Node -> Node ->... -> Node -> Tail (-> NULL) Jedes Element der Liste besteht aus einem Objekt und einem Zeiger auf das jeweils nächste Objekt Das Objekt kann ein Klassenobjekt sein, aber auch eine mit struct erzeugte Strukturvariable Wichtige Operationen zum Verwalten einer verketteten Liste: - Initialisieren der Liste - neuen Knoten hinzufügen - einen Knoten löschen - die Liste durchlaufen (z.B. suchen) - die Liste löschen Beispiele für die Implementierung dieser Operationen im Programm SimpleLinkedList

13 2.4 Ein eigener Memory-Manager Verkettete Listen für Spieleprogrammierung nicht unbedingt optimal, da häufig die ganze Liste durchlaufen werden muss und die Spielobjekte selten in der Reihenfolge gebraucht werden, in der sie in der Liste liegen Alternative Array Nachteil: statische Größe, eventuell Speicherverschwendung Vorteil: schneller Zugriff auf einzelne Elemente, der Größe des Arrays sollten ohnehin Grenzen gesetzt sein (z. B. maximale Anzahl darstellbarer Objekte) Nicht mehr benötigten Speicherplatz unbedingt wieder freigeben, Speicherfragmentierung vermeiden! Speicherverwaltung bei 3D-Anwendungen besonders kritisch

14 Auszug Beispiel MemoryManager class CObject_Memory_Manager { private: CObject* pObject; long Element_Unbenutzt; long NoMore_Element; long* pReadingOrder; // Zeiger auf pReadingOrder-Array long* pUsedElements; // Zeiger auf pUsedElements-Array long ActiveElements; // Anzahl der aktiven Elemente long Size; // Arraygröße

15 In diesem Modell stehen drei Arrays im Mittelpunkt: -ein Array aus Objekten der Klasse CObject pObject = new CObject[Size]; Dort sollen die tatsächlichen Spielobjekte, in der Reihenfolge ihrer Erstellung, abgelegt werden. -ein Array aus long-Integern pReadingOrder = new long[Size+1]; Dieses Array hält die Reihenfolge fest, in der die Objekte – wie auch immer – verarbeitet werden sollen (pReadingOrder[0] enthält also die ID-Nummer des Objektes, das gerade die höchste Priorität besitzt). -ein weiteres Array aus long-Integern pUsedElements = new long[Size] Listet auf, welche Elemente des pObject-Arrays mit welcher ID belegt sind, oder enthält einen bestimmten Wert (Element_Unbenutzt), falls das Element noch frei ist.

16 Mithilfe dieser 3 Arrays wesentlich dynamischere Speicherverwaltung: -direktes Ansprechen eines Objektes im Speicher möglich, kein Durchlaufen der verketteten Liste mehr nötig -Objekte können entweder mit ihrer Index-Nummer angesprochen werden (Position 0, 1, 2 im Array pObjects...) oder nach ihrer Position in der ReadingOrder -effektiver für Spieleprogrammierung Beispielprogramm MemoryManager enthält Routinen zum Initialisieren der Objekte, Durchlaufen nach ReadingOrder, Löschen einer bestimmten Position in der Reading Order, Löschen einer bestimmten Index-Nr. und Löschen aller Objekte Destruktor der Klasse muss alle erstellen Arrays löschen!

17 2.5 Dateiverwaltung Auszug aus Beispiel Dateiverwaltung, Ziel: eine Datei finden und den Dateinamen einlesen #include struct _finddata_t c_file; long hFile; long pos = 0; const long MaxLength = 100; char TempStrategisches_ScenarioName[MaxLength]; char Strategisches_ScenarioName[2*MaxLength]; void Select_Spielvariante(long Strategische_ScenarioNr = 1 );

18 int main(void) { Select_Spielvariante(1); printf("%s\n", Strategisches_ScenarioName); Select_Spielvariante(2); printf("%s\n", Strategisches_ScenarioName); Select_Spielvariante(3); printf("%s\n", Strategisches_ScenarioName); return 0; } void Select_Spielvariante(long Strategische_ScenarioNr) { long SavedStrategicScenarioDateiNr = 0; memset(Strategisches_ScenarioName, '\0', 2*MaxLength); if( (hFile = _findfirst( "Szenarien/*.*", &c_file )) != -1) {

19 while( _findnext( hFile, &c_file ) == 0 ) { if( SavedStrategicScenarioDateiNr == Strategische_ScenarioNr) { memset(TempStrategisches_ScenarioName, '\0', MaxLength); for(pos = 0; pos < MaxLength; pos++) { if( c_file.name[pos] == '.') break; TempStrategisches_ScenarioName[pos] = c_file.name[pos]; } } // string der zu ladenen Datei zusammensetzen: strcpy(Strategisches_ScenarioName,"Szenarien/"); strcat(Strategisches_ScenarioName,TempStrategisches_ScenarioName); strcat(Strategisches_ScenarioName,".txt"); SavedStrategicScenarioDateiNr++; } _findclose(hFile); }

20 Strcpy(y,x)kopiert String x in String y Strcat(y,x)hängt String x an String y an Memset()sauberes Reinitialisieren eines Strings Struct _finddata_tenthält alle wichtigen Informationen für die Dateiarbeit _findfirst()testet, ob eine Datei oder ein Ordner existiert _findnext()wählt die nächste Datei im Ordner an _findclose() beendet die Interaktion mit dem Dateisystem


Herunterladen ppt "Tag 2 Look-up-Tabellen, Zufallszahlen, Listen, Speichermanagement und Dateiverwaltung 17.10.2007 Quelle: 3D-Spiele mit C++ und DirectX in 21 Tagen, Alexander."

Ähnliche Präsentationen


Google-Anzeigen