Gutes Ressourcenmanagement zählt zu den wichtigsten Herausforderungen bei der Entwicklung eines Computerspiels. Bei der Darstellung von detailreichen und weitläufigen Spielewelten müssen kontinuierlich die hierfür benötigten Geometrie- und Textur-Daten geladen und die nicht mehr benötigten Daten aus dem Speicher entfernt werden (Streaming). Betrachten wir als Beispiel das Spiel Minecraft. Lediglich die Teile der Spielewelt, die sich im näheren Umfeld des Spielers befinden, werden auch wirklich in den Speicher geladen. Die Welt wird hierbei in sogenannte Chunks eingeteilt, wobei jeder Chunk aus 16 (Breite) mal 16 (Länge) mal 128 (Höhe) Blöcken besteht. Insgesamt werden 81 Chunks im Speicher gehalten, die in einem 9 mal 9 Quadrat um den Spieler herum gruppiert sind.
Betrachten wir als zweites Beispiel kurzlebige Spieleobjekte wie Waffenprojektile und Trümmerteile die noch dazu in großer Zahl auftreten. Der für diese Objekte benötigte Speicher muss sich möglichst schnell dynamisch allozieren (zuweisen) und wieder freigeben lassen. Kurzum, ohne eine vernünftige Speicherverwaltung sind wir völlig aufgeschmissen.
Posts mit dem Label Programmierung allg. werden angezeigt. Alle Posts anzeigen
Posts mit dem Label Programmierung allg. werden angezeigt. Alle Posts anzeigen
Verwendung von virtuellen Dateien
In den bisherigen OpenGL-Tutorials erfolgte das Einlesen der Mesh-Daten stets zeilenweise aus einer Textdatei heraus. Dieses Vorgehen ist alles andere als effizient und schnell – mit anderen Worten, wir sollten zukünftig einen anderen Weg einschlagen.
Labels:
Programmierung allg.
Thread-basierte (Kollisions)berechnungen – Worker-Threads, Events und Interlocked-Funktionen
Im heutigen Artikel werden wir uns mit dem Ablauf der im OpenGL-Programmbeispiel 12 durchgeführten thread-basierten Kollisionsberechnungen befassen. Sicher werden sich einige von Ihnen die Frage stellen, welche Vorteile wir von der Auslagerung der Kollisionsberechnungen zu erwarten haben oder ob wir die Berechnungen dadurch nicht unnötig verkomplizieren.
Um zu zeigen, wie viel Rechenleistung selbst einfachste Bounding-Sphären-Ausschlusstests bei einer zunehmenden Anzahl von potenziellen Kollisionspartnern beanspruchen, betrachten wir einige Zahlenbeispiele:
Ohne zusätzliche Optimierungen – wie beispielsweise durch die Sektorisierung der Spielewelt – können die Kollisionsberechnungen einen Prozessor schneller als man denkt in die Knie zwingen. Die Framerate bricht ein, das Spiel beginnt zu ruckeln und wird unspielbar. Entkoppelt man jedoch diese Berechnungen vom eigentlichen Hauptprogramm und führt sie in einem separaten Thread aus, dann bleibt die Framerate stabil.
Um zu zeigen, wie viel Rechenleistung selbst einfachste Bounding-Sphären-Ausschlusstests bei einer zunehmenden Anzahl von potenziellen Kollisionspartnern beanspruchen, betrachten wir einige Zahlenbeispiele:
- 2 Objekte: 1 mögliches Kollisionspaar
- 3 Objekte: 3 mögliche Kollisionspaare
- 4 Objekte: 6 mögliche Kollisionspaare
- 100 Objekte: 4950 mögliche Kollisionspaare
- n Objekte: n*(n-1)/2 mögliche Kollisionspaare
Ohne zusätzliche Optimierungen – wie beispielsweise durch die Sektorisierung der Spielewelt – können die Kollisionsberechnungen einen Prozessor schneller als man denkt in die Knie zwingen. Die Framerate bricht ein, das Spiel beginnt zu ruckeln und wird unspielbar. Entkoppelt man jedoch diese Berechnungen vom eigentlichen Hauptprogramm und führt sie in einem separaten Thread aus, dann bleibt die Framerate stabil.
Labels:
Physik,
Programmierung allg.
Multithreading in Echtzeit-Anwendungen – Einleitung
Im Zeitalter von Mehrkern-Prozessoren führt am Thema Multithreading kein Weg mehr vorbei. Selbstverständlich werden auch wir uns ausführlich mit diesem Thema befassen, wobei unser Fokus auf der Entwicklung von Echtzeit-Anwendungen liegen wird.
Labels:
Programmierung allg.
C/C++ Programmierung: Durchsuchen eines Programmordners und Erstellen einer Ordner-Übersicht
Im Artikel Durchsuchen eines Programmordners und Auswählen einer Datei haben wir uns damit befasst, wie sich zur Laufzeit eines Programms eine beliebige Datei innerhalb eines Ordners auswählen lässt, auf die dann zu einem späteren Zeitpunkt zugegriffen (öffnen und auslesen) werden kann.
In unserem konkreten Fall handelte es sich hierbei um Animationsposen sowie um 3D-Modelle samt Animations-Skelett. Die beiden hierfür verantwortlichen Funktionen
Select_NewModelAndSkeleton() sowie Select_NewAnimationPose() konnten uns jedoch zum damaligen Zeitpunkt keine Übersicht über die in den Ordnern gespeicherten Dateien liefern.
In unserem konkreten Fall handelte es sich hierbei um Animationsposen sowie um 3D-Modelle samt Animations-Skelett. Die beiden hierfür verantwortlichen Funktionen
Select_NewModelAndSkeleton() sowie Select_NewAnimationPose() konnten uns jedoch zum damaligen Zeitpunkt keine Übersicht über die in den Ordnern gespeicherten Dateien liefern.
Labels:
Programmierung allg.
C/C++ Programmierung: Durchsuchen eines Programmordners und Auswählen einer Datei
Im Animation Pose/Skeleton Modeller-Programmbeispiel ist es möglich, unterschiedliche 3D-Modelle samt Animations-Skelett sowie unterschiedliche Animationsposen zu laden. Bevor nun eine Datei geöffnet und ausgelesen werden kann, muss sie zunächst einmal vom Benutzer des Programms ausgewählt werden. Zuständig hierfür sind im besagten Programmbeispiel die beiden Funktionen Select_NewModelAndSkeleton() sowie Select_NewAnimationPose(), denen als Parameter der jeweilige Verzeichnispfad des zu durchsuchenden Programmordners zu übergeben ist. Da beide Funktionen vom Aufbau her völlig identisch sind, genügt es, wenn wir unser Augenmerk lediglich auf eine der Funktionen richten:
Labels:
Programmierung allg.
C/C++ Programmierung: Zufallszahlen
„Ohne Zufallszahlen geht in einem Computerspiel gar nichts!“
Mit der rand()-Funktion erfügt der C/C++-Programmierer über eine komfortable Möglichkeit für die Erzeugung von Zufallszahlen. Genaugenommen handelt es sich um so genannte Pseudo-Zufallszahlen, da diese auf mathematischem Wege berechnet werden, also nicht wirklich zufällig sind sondern nur so erscheinen. Die Illusion von „ein wenig mehr Zufall“ lässt sich erzeugen, wenn man den Zufallsgenerator vor dem ersten Aufruf der rand()-Funktion neu initialisiert. Hierfür dient die srand()-Funktion, der sie als Parameter beispielsweise die aktuelle Systemzeit übergeben können:
Mit der rand()-Funktion erfügt der C/C++-Programmierer über eine komfortable Möglichkeit für die Erzeugung von Zufallszahlen. Genaugenommen handelt es sich um so genannte Pseudo-Zufallszahlen, da diese auf mathematischem Wege berechnet werden, also nicht wirklich zufällig sind sondern nur so erscheinen. Die Illusion von „ein wenig mehr Zufall“ lässt sich erzeugen, wenn man den Zufallsgenerator vor dem ersten Aufruf der rand()-Funktion neu initialisiert. Hierfür dient die srand()-Funktion, der sie als Parameter beispielsweise die aktuelle Systemzeit übergeben können:
Labels:
Programmierung allg.
C/C++ Programmierung: Binäre Bäume (binary search trees)
Binäre Bäume gehören zu den hierarchischen Datenstrukturen. Da sich mit ihrer Hilfe große Datenmengen effizient handhaben lassen, finden sie in verschiedenen Bereichen der Spieleprogrammierung Verwendung. Hierzu zählt beispielsweise die Verwaltung von hierarchischen Kollisionsmodellen oder die Organisation von statischen Geometriedaten einer 3D-Szene.
Labels:
Programmierung allg.
C/C++ Programmierung: Verkettete Listen
Verkettete Listen gehören zu den so genannten dynamischen Datenstrukturen. Die einzelnen Elemente dieser Strukturen – in der Regel Instanzen einer Klasse – werden als Knoten bezeichnet. Ganz nach Bedarf können nun neue Knoten erzeugt oder nicht mehr benötigte Knoten gelöscht werden.
Labels:
Programmierung allg.
C/C++ Programmierung: Ein einfaches Log-File erzeugen
Irren ist menschlich – und fehlerfreien komplexen Programmcode zu schreiben ist nahezu unmöglich. Um den Zeitaufwand für die Fehlersuche und -korrektur zu minimieren, müssen Programmierfehler möglichst frühzeitig gefunden werden. In diesem Zusammenhang stellen Log-Files ein unverzichtbares Hilfsmittel dar, da sich mit ihrer Hilfe der Programmablauf je nach Bedarf mehr oder weniger genau protokollieren und analysieren lässt. Eine umfassende Analyse ist insbesondere während der Entwicklungsphase und den Testzyklen notwendig. Nach dem Release ist normalerweise eine weniger umfangreiche Protokollierung ausreichend, sofern ein Großteil der Fehler (hoffentlich!) im Vorfeld gefunden und behoben worden sind.
Labels:
Programmierung allg.
C/C++ Programmierung: Einen eigenen Memory-Manager entwickeln
Verkettete Listen sind eine schöne Sache. Sie zeugen von Eleganz, man kann mit ihnen angeben und sie sind langsam. Spaß beiseite, warum sind verkettete Listen langsam? Wenn man irgendeinen Knoten suchen muss, sei es, um ihn zu löschen oder um sonst etwas mit ihm zu machen, muss erst einmal die ganze Liste bis zum gesuchten Knoten durchlaufen werden. Erschwerend kommt hinzu, dass die Reihenfolge, in der die Game-Objekte in der Liste abgespeichert sind, normalerweise nicht der Reihenfolge entspricht, in der man auf diese Objekte Zugriff nehmen muss. Und wenn einmal nicht genügend Speicher vorhanden ist und die Erzeugung eines neuen Objekts fehlschlägt, bringt das bestenfalls den Spielablauf durcheinander.
Labels:
Programmierung allg.
C/C++ Programmierung: Entwicklung einer einfachen Windows-Rahmenanwendung (Game Shell)
Basis für ein unter Windows lauffähiges Spiel ist eine einfache Fensteranwendung. An dieser Stelle soll nun die Funktionsweise einer Windows-Anwendung kurz besprochen werden.
Labels:
Programmierung allg.
Abonnieren
Posts (Atom)