Fehler treten während der Spieleentwicklung in unterschiedlichen Formen auf. Eine Spielfigur fällt durch den Boden, ein Questziel aktualisiert sich nicht oder das Spiel beendet sich unerwartet. Debugging bezeichnet die systematische Suche nach der Ursache solcher Probleme und deren anschließende Korrektur.
Was bedeutet Debugging?
Beim Debugging untersuchst du, warum sich ein Programm anders verhält als vorgesehen. Ausgangspunkt ist ein beobachteter Fehler. Anschließend versuchst du festzustellen, welche Abläufe, Daten oder Einstellungen daran beteiligt sind.
Nicht jeder Fehler liegt unmittelbar im Programmcode. Falsche Werte, fehlerhafte Konfigurationen, nicht erwartete Daten oder das Zusammenspiel verschiedener Systeme verursachen ebenfalls Probleme.
Der sichtbare Fehler ist zunächst nur ein Symptom. Fällt deine Spielfigur beispielsweise durch eine Plattform, kann die Ursache bei der Plattform liegen. Möglich sind aber auch Probleme mit der Kollisionsabfrage, der Position der Spielfigur oder einer Funktion, die deren Position verändert.
Vom Symptom zur Ursache
Angenommen, dein Spieler besitzt einen Schlüssel und soll damit eine Tür öffnen. Bei der Interaktion mit der Tür stürzt das Spiel ab. Daraus lässt sich zunächst nur ableiten, dass der Absturz während dieses Ablaufs auftritt. Die Türfunktion muss nicht zwangsläufig die Ursache sein.
Das Spiel könnte vorher das Inventar prüfen und dabei auf einen ungültigen Wert stoßen. Ebenso könnte eine Information über den Schlüssel fehlen oder eine nachfolgende Funktion mit Daten arbeiten, die sie nicht erwartet.
Beim Debugging versuchst du deshalb, den Ablauf bis zum Fehler zu rekonstruieren. Stack Traces und Logs können dafür technische Informationen liefern.
Ein Stack Trace zeigt die Abfolge der aufgerufenen Funktionen zum Zeitpunkt eines Fehlers. Er kann den Bereich eingrenzen, in dem du suchen solltest. Daraus folgt jedoch nicht automatisch, dass die zuletzt aufgeführte Funktion auch die Ursache des Problems enthält.
Ein Log protokolliert Ereignisse des Programms. Darin kann beispielsweise stehen, dass dein Spieler mit der Tür interagiert, anschließend das Inventar geprüft und danach eine weitere Funktion aufgerufen wurde. Auch ein Log liefert zunächst Hinweise und keine automatische Diagnose.
Einen Fehler reproduzieren
Für die Untersuchung ist es hilfreich, einen Fehler gezielt erneut auslösen zu können. Dieser Vorgang wird als Reproduzieren eines Fehlers bezeichnet.
Stürzt dein Spiel bei jeder Interaktion mit derselben Tür ab, kannst du die Bedingungen vergleichsweise einfach nachstellen. Tritt das Problem nur gelegentlich auf, musst du genauer untersuchen, welche Bedingungen bei den einzelnen Abstürzen übereinstimmen.
Du kannst beispielsweise prüfen, ob der Fehler nur mit einem bestimmten Gegenstand im Inventar, nach dem Laden eines Spielstands, in einem bestimmten Level oder auf einer bestimmten Plattform auftritt.
Aus der allgemeinen Beobachtung „Das Spiel stürzt manchmal ab“ kann so eine präzisere Beschreibung entstehen: „Der Absturz tritt nach dem Laden dieses Spielstands auf, wenn der Spieler ohne gültigen Schlüssel mit dieser Tür interagiert.“
Informationen sammeln
Bevor du eine mögliche Ursache veränderst, kannst du zunächst den Zustand dokumentieren, in dem der Fehler auftritt. Dazu gehören die ausgeführte Aktion, der verwendete Spielstand, die betroffene Spielversion und vorhandene Fehlermeldungen.
Bei einem reproduzierbaren Fehler kannst du anschließend einzelne Bedingungen verändern. Tritt das Problem beispielsweise nur mit einem bestimmten Spielstand auf, vergleichst du diesen mit einem funktionierenden Spielstand. Erscheint es ausschließlich in einer neueren Version, vergleichst du die unterschiedlichen Entwicklungsstände.
Dadurch grenzt du mögliche Ursachen schrittweise ein.
Hypothesen überprüfen
Aus den gesammelten Informationen leitest du mögliche Erklärungen ab. Im Beispiel mit der Tür könnte eine Hypothese lauten, dass die Inventarabfrage einen ungültigen Wert zurückgibt.
Diese Vermutung überprüfst du gezielt. Bestätigt sie sich nicht, untersuchst du eine andere mögliche Ursache.
Mehrere Änderungen gleichzeitig erschweren diese Überprüfung. Funktioniert das Spiel danach wieder, lässt sich nicht eindeutig feststellen, welche Änderung dafür verantwortlich war. Einzelne, nachvollziehbare Änderungen erleichtern dagegen die Zuordnung von Ursache und Wirkung.
Versionsverwaltung bei der Fehlersuche
Ein Fehler fällt nicht immer unmittelbar nach seiner Entstehung auf. Zwischen einem funktionierenden und einem fehlerhaften Entwicklungsstand können zahlreiche Änderungen liegen.
Eine Versionsverwaltung zeichnet Veränderungen an Projektdateien auf. Dadurch kannst du nachvollziehen, welche Dateien zwischen verschiedenen Entwicklungsständen bearbeitet wurden.
Funktioniert beispielsweise Build 120, während Build 121 beim Öffnen einer Tür abstürzt, kannst du die Änderungen zwischen beiden Versionen untersuchen. Die Versionsverwaltung beweist dabei nicht, welche Änderung den Fehler verursacht hat. Sie zeigt dir jedoch, welche Änderungen als Untersuchungsbereich infrage kommen.
Was ist ein Build?
Ein Build ist eine aus dem Entwicklungsprojekt erzeugte, ausführbare Version des Spiels. Während der Entwicklung entstehen häufig zahlreiche interne Builds, die ausschließlich für Tests vorgesehen sind.
Build-Nummern helfen dabei, unterschiedliche Entwicklungsstände auseinanderzuhalten. Meldet ein Tester einen Fehler in Build 121, lässt sich dadurch feststellen, welchen Stand des Projekts er verwendet hat.
Das ist besonders wichtig, wenn sich der Programmcode bereits weiterentwickelt hat. Ein Fehlerbericht ohne Angabe der getesteten Version kann sich auf einen Entwicklungsstand beziehen, der inzwischen verändert wurde.
Debugging im Team
In größeren Projekten bearbeiten mehrere Personen gleichzeitig Programmcode, Level, Benutzeroberflächen und andere Bestandteile des Spiels. Dadurch kann ein beobachteter Fehler mit Änderungen zusammenhängen, die du nicht selbst vorgenommen hast.
Eine Versionsverwaltung dokumentiert, welche Änderungen in das Projekt eingeflossen sind. Bei einem Code Review kann eine Änderung zusätzlich von anderen Teammitgliedern überprüft werden.
Ein Code Review ist keine Garantie für fehlerfreien Programmcode. Es bietet eine weitere Möglichkeit, Änderungen auf logische Fehler, nicht berücksichtigte Situationen oder unerwünschte Auswirkungen auf andere Systeme zu untersuchen.
Nicht jeder Fehler verursacht einen Absturz
Ein Crash bezeichnet die unerwartete Beendigung eines Programms. Viele Spielfehler führen allerdings nicht zu einem Crash.
Ein Gegner kann durch eine Wand laufen, ein Questziel bleibt unverändert oder ein Gegenstand verschwindet aus dem Inventar. Das Spiel läuft in diesen Fällen weiter, verhält sich aber nicht wie vorgesehen.
Solche Fehler werden häufig als Bugs bezeichnet. Bei ihnen steht nicht zwangsläufig eine automatische Fehlermeldung zur Verfügung. Du musst deshalb untersuchen, an welcher Stelle das tatsächliche Verhalten vom erwarteten Verhalten abweicht.
Ursache und sichtbarer Fehler können weit auseinanderliegen
Je mehr Systeme miteinander interagieren, desto schwieriger kann die Zuordnung werden. Bleibt beispielsweise ein Gegner plötzlich stehen, liegt die Ursache nicht zwangsläufig in seiner Bewegungssteuerung.
Möglicherweise fehlt sein Ziel. Eine Animation befindet sich in einem falschen Zustand. Eine Kollision blockiert den Weg. Eine andere Spiellogik hat den Gegner deaktiviert oder ein benötigter Wert enthält unerwartete Daten.
Deshalb solltest du beim Debugging zwischen dem Ort, an dem du den Fehler bemerkst, und dem Ort seiner Ursache unterscheiden.
Die Korrektur überprüfen
Verschwindet ein Fehler nach einer Änderung, folgt die Kontrolle. Dabei prüfst du zunächst, ob sich das ursprüngliche Problem unter denselben Bedingungen noch reproduzieren lässt.
Zusätzlich solltest du verwandte Funktionen testen. Verwendet beispielsweise die Tür dieselbe Inventarabfrage wie eine verschlossene Truhe, kann eine Änderung an dieser Abfrage beide Systeme beeinflussen.
Ein behobener Fehler kann andernfalls einen neuen Fehler an einer anderen Stelle verursachen. Solche unerwünschten Auswirkungen einer Änderung werden als Regressionen bezeichnet.
Debugging als Kreislauf
Der grundlegende Ablauf lässt sich als Kreislauf darstellen: Fehler beobachten → Fehler reproduzieren → Informationen sammeln → mögliche Ursachen eingrenzen → Hypothese überprüfen → Änderung vornehmen → erneut testen.
Bestätigt sich eine Vermutung nicht, kehrst du zur Untersuchung zurück. Auch nach einer erfolgreichen Korrektur folgt ein weiterer Test, um die Änderung zu überprüfen.
Debugging ist damit weniger eine einzelne Tätigkeit als ein wiederholbarer Untersuchungsprozess. Welche Werkzeuge dafür sinnvoll sind, hängt vom jeweiligen Fehler und vom Entwicklungsumfeld ab.
Fehlerberichte aus Testversionen untersuchen
Nicht jeder Fehler tritt während deiner eigenen Tests auf. Bei größeren Testgruppen können Probleme auf anderen Rechnern, Betriebssystemen oder Hardwarekonfigurationen erscheinen.
Fehlerberichte sollten deshalb möglichst genau beschreiben, welche Version getestet wurde, welche Schritte zum Problem führten und welches Verhalten erwartet wurde. Technische Informationen wie Logs und Stack Traces können diese Beschreibung ergänzen.
Für die automatisierte Erfassung solcher Informationen existieren unterschiedliche Systeme. Eines davon ist Sentry. Die Plattform kann Fehler- und Absturzinformationen aus Anwendungen sammeln und für die weitere Untersuchung bereitstellen. Die gesammelten Daten ersetzen die Analyse des Fehlers nicht; sie dienen als zusätzliche Grundlage für das Debugging.
Weitere Informationen zu den Funktionen des Systems findest du auf der Website von Sentry.
Weitere Artikel auf Games und Lyrik:
