Vom Skript zum Spiel: Abhängigkeiten Revisited!

Wie ich bereits neulich erklärte, entstehen bei der Zusammenstellung von Gegenständen, Zaubern, Helden, Events, Karten, usw. bald Abhängigkeiten, weil eine Ressource auf andere zurückgreift. Mit großer künstlerischer Begabung habe ich dieses Gewirr in ein leicht verständliches Schaubild gefasst und dabei noch einige Pfeile vergessen:

Abhängigkeiten im RPG-Editor

Alles klar?

Wenn man jetzt eine Ressource löscht, die gewissermaßen Teil einer anderen Ressource ist, fällt dieses Kartenhaus in sich zusammen. Meine Lösung lautete, alle diese Abhängigkeiten in einem Verzeichnis festzuhalten, in dem das Programm vor dem Löschen einer Ressource nachschlagen konnte, welche Auswirkungen das haben würde – und dann den Benutzer mit diesen Auswirkungen zu konfrontieren: “Ressource wird von anderen verwendet. Soll sie bei Verwendung gelöscht oder gegen eine andere Ressource gleichen Typs ausgetauscht werden?”. So ähnlich.

Stellt sich heraus, dass das mehr Arbeit bedeutete als angenommen … viiiiel mehr Arbeit. Ich spreche jetzt vor allen Dingen von der Verwendung von Ressourcen innerhalb der Events. Diese Abhängigkeiten beim Löschen oder Ersetzen einer Ressource aufzulösen hätte einen enormen Aufwand bedeutet – hauptsächlich wegen der Art, wie ich die Event-Daten organisiere. Wer sich für die Problematik interessiert: In einem Spiel können durchaus Tausende von Events (Ereignissen) ausgelöst werden, die jedes für sich besonders in der leicht lesbaren Rohform, in der ich sie zum leichteren Bearbeiten im Event-Editor des Programms speichere, sehr groß werden können. Die alle in einem gemeinsamen Container und in einer einzigen Datei zu speichern, würde folglich eine sehr große Menge an Speicher belegen oder wenigstens sehr unhandlich. Normalerweise benötigt man sowieso nur die Events der gerade geöffneten Karte, nicht alle Events des gesamten Spiels gleichzeitig – also reicht es völlig aus, die Event-Daten pro Karte zu laden. Wenn ich nun eine Ressource löschen möchte, die über verschiedene Karten hinweg von Events benutzt wird, müsste ich für alle betreffenden Events ihre jeweiligen Daten pro Karte von der Festplatte laden. Nicht sehr schön. An dieser Stelle wurde mir klar, dass ich tun musste, was man immer tut, wenn das Rad schon erfunden wurde: hemmungslos kopieren! Naja, nicht ganz, man kommt einfach bei ähnlichen Problemen auch zu ähnlichen Lösungen.

Ich hatte eine Idee, die entweder elegant ist oder zu einem Dr.-Frankenstein-Monster führte, ich bin mir nicht ganz sicher. Aber dafür muss ich etwas ausholen! Programmiersprachen unterscheiden zwischen veränderbaren und unveränderbaren Datentypen, was einfach nur Folgendes bedeutet:

Unveränderbar:
a = 5
b = a
# Beide verweisen auf 5.
b += 30 # a verweist immer noch auf 5, und b verweist auf 35.
===> a ist nicht gleich b

Veränderbar:
a = ["Das", "ist", "eine", "Liste", "von", "Wörtern"]
b = a
# Beiden verweisen auf dieselbe Liste.
b.append("und von Satzteilen") # Obwohl ich nur b explizit erweitere, wird implizit auch a erweitert.
===> a ist gleich b

Zahlen sind also unveränderbar, das bedeutet, dass eine Variable nur eine Kopie des zugewiesenen Wertes ist. Wenn einer Variable der Wert einer anderen Variable zugewiesen wird, ist es zwar der gleiche Wert … aber nicht dasselbe Objekt. Wenn ich eine der beiden Variable verändere, bleibt die andere davon unberührt. Bei veränderlichen Datentypen wie Listen wird derselbe Wert zugewiesen, alle Variablen zeigen auf dasselbe Objekt.

Was hat das mit den Abhängigkeiten zu tun? Wenn wir a und b einmal gegen die Identifikationsnummern der Ressourcen tauschen, wird die Idee klarer: Wenn ich eine Ressource lösche und diese Änderung überall im gesamten Datenbestand reflektiert würde, müsste ich gar kein telefonbuchartiges Verzeichnis über die Abhängigkeiten mehr führen! Ich setze die Nummer einfach auf Null … und überall, wo die Nummer verwendet wurde, steht jetzt Null. Ganz automatisch, ohne meine Zutun, ohne dass das Programm dafür arbeiten müsste. Umgekehrt ginge es übrigens genauso, von Null auf die Identifikationsnummer zurück. Juhu! Nur, da gibt es ein kleines Problem … Nummern sind Zahlen, und Zahlen sind unveränderlich. Ich müsste also einen neuen Datentyp erstellen, der sich wie derjenige für Zahlen benimmt, und ihm zusätzlich die Veränderlichkeit beibringen. Frankensteins Monster! Es lebt!

Null als Identifikationsnummer bedeutet bei mir übrigens einfach nur eine Dummy-Ressource. So kann das Spiel niemals abstürzen, weil es eine Ressource nicht fände, denn die Dummy-Ressource existiert ja immer. Die Lösung gewinnt keinen Schönheitspreis, aber sie macht, was sie soll.

(Für die erfahreneren Programmierer: Ja, ich könnte auch direkt auf die Objekte verweisen, statt IDs zu verwenden, das sind nämlich Dictionarys und damit sowieso veränderlich. Könnte ich mir doch Arbeit sparen? Nein, denn manchmal ist es notwendig, tiefe Kopien von Daten anzulegen, und dabei würden aus den Referenzen eigenständige Objekte, diese “Verlinkung” würde aufgelöst. Das lässt sich verhindern, indem man einen neuen Datentyp vom Dictionary-Typ erben lässt und die __deepcopy__-Methode mit einer eigenen überschreibt … aber das wäre letztlich genau die gleiche Arbeit. Und das wären mir auch zuviele ineinander verschachtelte Dictionarys.)

This entry was posted in Programmieren and tagged , , , , , . Bookmark the permalink.

3 Responses to Vom Skript zum Spiel: Abhängigkeiten Revisited!

  1. maloney says:

    Heyhey dich gibts ja auch noch ;)

  2. Pingback: Mirror, Mirror on the Map … Spiegelbilder fürs 2D-Tileset | Animentor

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">