Effektive Fehlersuche und Debugging in C-Script
Ein Tutorial von Timo Stark

 

 

 

 

Autor: Timo Stark (aka Triple-X, Hawkgames)
Datum: 18.08.2005
Letzte Aktualisierung: 18.08.2005

 

 

Inhaltsverzeichnis

Tipp: Durch Klicken auf einen Kapitelunterpunkt gelangen Sie sofort zur gewünschten Stelle. Über die Pfeilbuttons gelangen Sie wieder an den Anfang dieses Tutorials.

 

1. Einführung

Kapitel 1

Syntaktische Fehler

2. Einführung zu Syntaktischen Fehlern

2.1 Eine typische Compilerausgabe

2.2 Identifikationsnummern

2.2.1 Error ID(0), Syntax Error - - missing semicolon? [Code Zeile]

2.2.2 Error ID(0), Bad or missing parameters (too many assigns/unknown function)

2.2.3 Error ID(6), Script Error Last Line

2.2.4 Error ID(29), Keyword unknown [Keyword]

2.2.5 Error ID(31), Script error missing bracket

2.2.6 Error ID(63), Parameter unknown [Parameter]

2.2.7 Error ID(96), Syntax Error - nonexistent/empty function [functionname]

2.2.8 Error ID(137), Bitmap unknown [Bitmapname]

2.2.9 Error ID(171), Can't open [Dateiname]

 

Kapitel 2

Semantische Fehler

3. Einführung zu Semantischen Fehler

3.1 Breakpoint

3.1.1 Breakpoint_on

3.2 Watched

3.3 Überprüfung von Strings

3.3.1 Stringüberwachung per Text

3.3.2 Stringausgabe per Datei

3.3.3 Stringausgabe per Diag

3.3.4 Stringüberwachung per Draw_text

3.4 Variablenüberwachung

3.4.1 Variablenüberwachung per Panel

3.4.2 Variablenausgabe per Diag

3.5 Beep

3.6 Eigene Debug Systeme

Kapitel 3

Engine Fehler

4. Engine Fehler

4.1 Eine typische Engine Fehlermeldung

4.1.1 Crash in [function] : [script]

4.1.2 Invalid array index in [function] : [script]

4.1.3 <Actionname> Action not found

4.1.4 Error 1242: Cannot Open Video Device

4.1.5 Error: Too many Entity Types

4.1.6 Error: Not enough entities reserved ([num])

4.1.7 Malfunction W1301: [FILE]; Can't open file

4.1.8 acknex.exe hat einen Fehler verursacht und muss geschlossen werden

 

Kapitel 4

Debugging mit SED

5. Debugging mit SED

5.1 Überblick über die SED Debug Funktionen

5.2 Watches - Beobachten von Variablen & Strings

5.3 Breakpoints in SED

Kapitel 5

Sonstige Debugging Methoden

6. Das Engine-Debug Panel

7. Bugreport

 

1. Einführung 

Hallo

Obwohl der Bereich des Debuggings gerade in der Programmierung sehr wichtig ist, kennen sich leider nur wenige Programmierer damit aus, daher werde ich in diesem Tutorial auf Möglichkeiten des Debugging in C-Script und auf effektive Fehlersuche in 3DGamestudio eingehen. Zur effektiven Fehlersuche gehören unter anderem das beseitigen von Engine Fehlern (Empty Pointer, Invalid Array Index usw.), Syntax Fehlern und Logischen Fehlern. Solche Fehler können auf Dauer sehr nervtötend und demotivierend sein, allerdings muss man sich als Programmier darauf einstellen 80% seiner Zeit mit Fehlersuche zu verbringen.

Für dieses Tutorial sollte man etwas Script-Kenntnis mitbringen, da ich die Beispielscripte nur über Kommentare erklären werde. Falls es Probleme mit den Programmierbeispielen geben sollte, hilft meistens das Handbuch oder ein C-Script Tutorial wie z.B. das C-Script Tutorial für Anfänger.

Viel Spaß beim lesen, TripleX

 

2. Syntaktische Fehler  

Unter einem Syntaktischen Fehler versteht man einen Fehler in der Syntax des Scriptes. Der Fehler wird meist von dem C-Script Compiler ausgegeben. Einige typische Syntax Fehler sind z.B. das vergessen eines Semikolons nach einer Anweisung, oder auch ein Verschreibfehler. Syntaktische Fehler sind in der Regel sehr leicht zu beseitigen. Gerade als Anfänger muss man allerdings aufpassen, dass man bei vielen angezeigten Fehlern nicht demotiviert aufgibt sondern Fehler für Fehler beseitigt. Meistens sind die Ausgaben des Compilers nur Folgefehler. Wenn Sie zum Beispiel bei einer Variablendekleration statt var weapon_number, var weapo_number schreiben und im darauf folgenden Script oft auf die deklarierte Variable eingehen, werden Sie sehr viele Fehler bekommen obwohl Sie nur eine Zeile korrektieren müssen.

Grundsätzlich ist deshalb wie gesagt bei Syntaktischen Fehlern zu beachten, dass man Sie der Reinfolge nach abarbeitet. Das heißt Fehler für Fehler.

2.1 Eine typische Compilerausgabe 

Hier ist eine typische Compilerausgabe bei einem Syntaktischen Fehler:

Wichtig für einen Programmierer sind folgende Informationen:

<point1a+=1> In Diesen eckigen Klammern steht die Codezeile in der der Fehler auftritt.
weapon_starter.wd 496l Gibt die WDL Datei und die Zeile an in der der Fehler auftritt.
Error(63) Gibt die Fehler Identifikationsnummer an.
Parameter unknown point1a Da der Programmierer aber meist nur mit der Fehler ID wenig anfangen kann gibt der Compiler auch noch eine kurze Beschreibung aus.

Noch einmal kurz als Überblick:

< Fehlerhafter Code >
WDL Datei Zeile:0  Error(Identfikationsnummer des Fehlers): Fehlerbeschreibung

2.2 Identifikationsnummern  

Oft reicht es schon die Fehlerbeschreibung und die Zeile des Fehlerauftritts in der Compilerausgabe zu lesen um den Fehler zu beheben. Bei der oberen Fehlermeldung hab ich z.B. nach point einen Doppelpunkt vergessen (um einen goto Point zu definieren). Leider ist dies nicht immer möglich, deshalb werde ich im Folgenden auf die häufigsten Compiler Fehlermeldungen eingehen und typische Fehlerursachen und Lösungsmöglichkeiten erläutern.

2.2.1 Error ID(0), Syntax Error - - missing semicolon? [Code Zeile]  

Wie in der Fehlerbeschreibung schon angegeben verweist diese Meldung meist auf ein fehlendes Semikolon. Allerdings sollten Sie sich nicht darauf versteifen unbedingt einen Semikolon Fehler zu suchen. Unter Umständen könnte diese Fehlermeldung auch eine vergessene öffnende geschweifte Klammer hinter z.B. einem IF Abgleich bedeuten ( if(test) blub = 1; } ).

2.2.2 Error ID(0), Bad or missing parameters (too many assigns/unknown function)  

Diese Fehlermeldung bedeutet bei der Erweiterung mit "too many assigns" meistens, dass Sie in einer IF/WHILE Anweisung einen Operatorfehler gemacht haben. d.h. beim Vergleich z.B. nur ein "=" statt 2 "=". Bei dem Zusatz "unknown function" haben Sie vermutlich bei einem Funktionsaufruf zu viele oder zu wenige Funktionsparameter übergeben.

2.2.3 Error ID(6), Script Error Last Line  

Dieser Fehler bedeutet meistens, dass ein Semikolon nach einem Befehl vergessen wurde. In diesem Scriptbeispiel nach a+1. Unter Umständen kann er auch bedeuten, dass vor dem Ende einer WDL Datei vergessen wurde eine geschweifte Klammer zu schließen. ( } )

2.2.4 Error ID(29), Keyword unknown [Keyword]  

Diese Fehlermeldung bedeutet am Ende fast das gleiche wie PARAMETER UNKNOWN. Mit den Unterschied, dass sich diese Fehlermeldung meist auf Rechtschreibfehler in Keywords (Keywords sind z.B. var,string,on_esc,on_a-z, entity etc.) bezieht. In diesem Beispiel habe ich z.B. statt on_esc, on_ec geschrieben.

2.2.5 Error ID(31), Script error missing bracket 

Wie bei dieser Fehlerbeschreibung schon angegeben wird, fehlt eine geschweifte Klammer. Zur Fehlerlösung einfach bei der angegebenen Zeile nach fehlenden geschweiften Klammern suchen und wenn nötig einsetzten.

Zu beachten ist noch, dass diese Funktion sehr viele Folgefehler hervorruft. Diese beachten Sie am besten einfach nicht und folgen dem Grundsatz des Debuggings bei Syntaxfehlern: Fehler für Fehler.

2.2.6 Error ID(63), Parameter unknown [Parameter]  

Error 63 ist ein sehr häufig auftretender Fehler. Generell besagt er nur, dass im Script versucht wird auf ein C-Script Objekt (Sound/Variable/View/String usw.) zuzugreifen, das nicht definiert wurde. Zur Fehlerbeseitigung sollte man das entsprechende Parameter (in diesem Fall ammopack_snd) und das, falls vorhanden, definierte Objekt auf Rechtschreibfehler untersuchen. Möglicherweise wurde auch vergessen das notwendige Objekt zu definieren.

2.2.7 Error ID(96), Syntax Error - nonexistent/empty function [functionname] 

Diese Fehlermeldung kann mehrere Ursachen haben.

1) Von der, in der Fehlermeldung genannten Funktion, wurde ein Prototyp erstellt. Zu diesem Prototypen wurde allerdings keine Funktion gefunden.
2) Unter Umständen kann es auch sein, dass in der Funktion VOR der angegeben Funktion eine geschweifte Klammer nicht geschlossen, oder ein Semikolon vergessen wurde.
3) Es wird versucht auf eine Funktion zuzugreifen, die nicht vorhanden ist. Wie immer auf Rechtschreibfehler beim Funktionsaufruf und bei der Funktionsdefinition überprüfen.

2.2.8 Error ID(137), Bitmap unknown [Bitmapname]  

Die Ursache dieser Fehlermeldung ist, dass im Script (Zeilen und WDL Dateien sind wie immer angegeben) auf eine Bitmap zugegriffen werden soll, die nicht definiert wurde. Zur Lösung des Fehlers kontrollieren Sie, ob Sie das jeweilige Bitmap vielleicht definiert haben aber sich bei der Namensgebung verschrieben haben oder wenn nötig definieren Sie das Bitmap neu. ( bmap name <dateiname.dateiart>; )

2.2.9 Error ID(171), Can't open [Dateiname]  

Diese Fehlermeldung gibt nur an, dass eine im Script includierte/eingebundene Datei (welcher Dateiart auch immer) nicht gefunden werden kann. Falls Sie sicher sind, dass die Datei im Ordner vorhanden ist, überprüfen Sie am besten noch einmal die PATH Angaben und die Rechtschreibung.

 

3. Semantischer Fehler  

Ein Semantischer Fehler tritt auf, wenn das Script zwar startet (also kein Syntaktischer Fehler vorliegt), aber es nicht das macht was Sie wollen (z.B. wollen Sie dem Spieler einen Lebenspunkt hinzufügen, aber das Script gibt dem Gegner den Lebenspunkt). Diese Art von Fehler ist sehr viel schwerer aufzuspüren, auch wenn C-Script Ihnen eine Menge an Möglichkeiten zur Fehlerbeseitigung gibt.

3.1 Breakpoint  

Eine der besten Möglichkeiten zur Beseitigung von Semantischen Fehler ist der Breakpoint (übersetzt "Haltepunkt"). Mit der Hilfe eines Breakpoints können Sie das Script zu einer bestimmten Stelle anhalten und jede C-Script Anweisung (eine DLL können Sie mit breakpoints nicht Debuggen) nacheinander ausführen. Der breakpoint zeigt Ihnen die Rückgabewerte jedes Befehls an. Wozu ist das nützlich?

1) Mit Hilfe eines breakpoints können Sie zu einer bestimmten Stelle in einem Script den Wert von Variablen, Skills usw. überprüfen.
2) Durch den breakpoint ist es möglich zu testen, ob eine bestimmte Stelle im Script überhaupt aufgerufen wird.

Einen Breakpoint kann man ganz einfach über ein breakpoint; in der entsprechenden Zeile des Scriptes aufrufen. Ein Beispiel:

function test
{
temp = 32;
breakpoint; // Wenn diese Zeile ausgeführt wird, wird das Spiel angehalten und Sie können jede weitere Zeile der Funktion überprüfen
if(temp == 32) { temp = 1; }
}

Testen Sie den breakpoint; einmal. Wenn er aufgerufen wird, hören Sie ein "piepsen" und in der oberen linken Ecke des Engine Fensters erscheint eine Schrift die in etwa so aussieht:

Jetzt ist der Breakpoint-Modus aktiviert. Hier können Sie jetzt jede einzelne Anweisung der Funktion durchgehen -> (temp==32) ist die if Abfrage.

Wenn Sie nun [LEERTASTE] drücken erscheint auf der linken Seite (neben den Pfeil) eine Zahl. Diese Zahl gibt den Rückgabewert der ausgeführten Anweisung wieder (In diesem Beispiel wird 1.000 dort da stehen. Wenn eine IF Abfrage wahr ist gibt Sie 1 zurück, wenn falsch 0).

Zur nächsten Anweisung können Sie nun springen indem Sie wieder [LEERTASTE] drücken. Falls Sie den breakpoint beenden wollen, aber trotzdem spätere breakpoints NICHT ignorieren wollen drücken Sie [STRG] + [LEERTASTE]. Falls Sie den breakpoint beenden, und alle weiteren ignorieren wollen drücken Sie [ESC].

Auch wenn die Bedienung eines Breakpoints nicht leicht erscheint würde ich ihn doch oft nutzen. Mit seiner Hilfe kann man schnell Rückgabewerte und Objektwerte überprüfen.

Da es oft Probleme mit Rückgabewerten gibt ("warum gibt der breakpoint jetzt diese Zahl zurück") hier eine Liste von Rückgabewerten:

Scriptzeile Rückgabewert
   
if(wert1 OPERATOR wert2) 1 Falls wahr, 0 falls falsch
while(wert1 OPERATOR wert2) 1 Falls wahr, 0 falls falsch
Variable1 = Variable2; Neuer Wert von Variable1
Pointer1 = Pointer2; Wert des Pointers. Normalerweise nicht zu gebrauchen.
C-Script Anweisung (str_cmpi / str_cpy usw.) Siehe Handbuch -> Index -> Befehl -> Rückgabewert

Als Beispiel: Sie bekommen die Fehlermeldung "Invalid Array Index in .." bei folgendem Code:

var 2Dar[50];

function test(zeile,spalte)
{
   return(2Dar[zeile + spalte*10]); //Rechnungen mit 2D Arrays (hier 5 Spalten, 10 Zeilen)
}

Sie wollen jetzt das Ergebnis von zeile + spalte*10 überprüfen. Dies machen Sie mit Hilfe eines breakpoints so:

var 2Dar[50];

function test(zeile,spalte)
{
   breakpoint;
   temp = zeile + spalte*10;
   return(2Dar[zeile + spalte*10]); //Rechnungen mit 2D Arrays (hier 5 Spalten, 10 Zeilen)
}

Wenn Sie jetzt einmal nach dem breakpoint Aufruf [LEERTASTE] drücken sehen Sie das Ergebnis der Berechnung in der oberen linken Ecke. Diese Art von Ergebnisberechnung ist wegen dem 3. angegeben Rückgabewert in der Tabelle möglich (Variable1 = Variable2).

Breakpoints werden in der Release Version (Published) ignoriert.

3.1.1 breakpoint_on(Bedingung) 

Oft wollen Sie breakpoints erst zu bestimmten Bedingungen auslösen. Dies geht mit dem Befehl breakpoint_on(Bedingung); sehr einfach. Ein Beispiel:

var 2Dar[50];

function test(zeile,spalte)
{
   breakpoint_on(zeile > 10); //Falls es mehr als 10 Zeilen gibt breakpoint
   temp = zeile; //überprüfe den Wert von zeile
   temp = spalte;//überprüfe den Wert von spalte
   temp = zeile + spalte*10;
   return(2Dar[zeile + spalte*10]); //Rechnungen mit 2D Arrays (hier 5 Spalten, 10 Zeilen)
}

3.2 Watched  

3D Gamestudio bietet die Möglichkeit alle Aktionen eines Entitys zu beobachten. Watched ist ein Pointer kann also einfach über z.B. watched = my; einem Entity zugewiesen und mit Hilfe von watched = NULL; wieder gelöscht werden. Genau wie bei einem breakpoint; erscheinen nach dem setzten des watched Pointers in der oberen Linken Ecke Informationen über das jeweilige Entity.

Was am Anfang etwas unübersichtlich Aussieht, kann sich nach einigen besseren Blicken als sehr große Hilfe entpuppen. Im folgenden eine Liste (1) = 1. Zeile, 2) = 2. Zeile usw.) was die einzelnen Zahlen und Strings bedeuten.

1) Typ, Handle Nummer, Name
2) Position, Winkel, Skalierung
3) Ambient, albedo, alpha, rgb (Färbung), frame, next_frame, skin
4) Actions die von der Entitiy ausgeführt werden und die Event Funktionen.
5) Material
6) Untergrund unter der Entity, Untergrundoberflächennummer und die Textur.

Dies alles sind eine Menge nützlicher Informationen mit denen Sie viele Fehler herausfinden können. Noch ein Hinweis: Wenn Sie im Spiel [SHFIT]+[F11] drücken können Sie ein beliebiges Entity anklicken. Ab jetzt wird dieses Entity beobachtet.

3.3 Überprüfung von Strings  

Wenn Sie die Werte von Strings überprüfen wollen gibt es zwei gute Möglichkeiten.

1) Sie definieren einen Debug Text. Wenn Sie einen String überwachen bzw. überprüfen wollen, schreiben Sie einfach Debug_Txt.string = Der_zu_Ueberwachende_String; Und schon wird der String am Bildschirm angezeigt.
2) Des weiteren können Sie einen String noch in einer Datei ausgeben. Dazu öffnen Sie einfach eine Datei mit den file_ Befehle von 3D Gamestudio und schreiben per file_str_write den String hinein.
3) Mit Hilfe des Befehls diag(str); können Sie sehr schnell einen String in die Acklog.txt Datei schreiben.
4) Zu guter letzt können Sie noch Strings sehr schnell per draw_text darstellen.

3.3.1 Stringausgabe per Text  

Die String Ausgabe per Text hat den Vorteil, dass Sie den Inhalt des Strings sofort überprüfen können (auch per breakpoint; ). Zusätzlich ist es auch noch sehr einfach zu programmieren. Folgender Code reicht komplett aus:

font arial_font = "Arial",0,12;
text Debug_Txt { pos_X = 200; pos_y = 200; font = arial_font; flags = visible; red = 0; blue = 0; green = 0; }

Mit diesem Code definieren Sie den Text Debug_txt. Wenn Sie jetzt einen String anzeigen wollen schreiben Sie einfach in der betreffenden Codezeile:

breakpoint;
Debug_Txt.string = STRING_DER_ZU_ÜBERWACHEN_IST;

Wozu ist hier der breakpoint; da? Wenn Sie das Script ausführen und der breakpoint wird aufgerufen, können Sie den String Inhalt zu genau diesen Moment überprüfen. Wenn Sie viele Strings mit dieser Weise überwachen könnte es ohne breakpoint; passieren, dass ein ganz anderer String am Bildschirm angezeigt wird als Sie gerade überwachen wollen. Hört sich kompliziert an? Ist es nicht.. Ein Beispiel:

breakpoint;
Debug_Txt.string = STRING_DER_ZU_ÜBERWACHEN_IST;
Debug_Txt.string = STRING_DER_ZU_ÜBERWACHEN_IST2; /*Jetzt wird nur dieser 2. String angezeigt. Der erste String wird überschrieben. Mit Hilfe des breakpoints; Kann man sich beide Strings anschauen (nicht vergessen. Mit dem breakpoint können Sie Befehl für Befehl durchgehen). */

 

3.3.2 Stringausgabe per Datei  

Die Stringausgabe per Datei ist meiner Meinung nach nicht der Stringausgabe per Text vorzuziehen, trotzdem besitzt die Ausgabe per Datei einen großen Vorteil. Und zwar können Sie sich, was vor allem bei großen Strings (> 256 Characters) wichtig ist, den String im Nachhinein in Ruhe anschauen. Deshalb würde ich grade bei größeren Strings die Möglichkeit der Ausgabe in eine Text Datei nicht vergessen. Ein Scriptbeispiel:

var fhandle; //Dateihandle
fhandle = file_open_write("temp.txt"); //öffne Datei "temp.txt"
file_str_write(fhandle,STRING_DER_ZU_ÜBERWACHEN_IST);
file_close(fhandle); //Datei wieder schließen

In Ihrem Savedir Ordner können Sie jetzt die Datei "temp.txt" mit dem entsprechenden Inhalt finden.

3.3.3 Stringausgabe per Diag  

Mit Hilfe von diag(string); können Sie schnell und einfach einen String in die Acklog.txt (beachten Sie bitte, dass diese Datei nur erschaffen wird, falls Sie die Engine mit der Startzeilenoption -diag starten) schreiben. Auch wenn der Befehl wirklich einfach ist hier ein Beispiel:

function MaterialSave(str)
{
   diag(str); //Schreibe den Parameter in die Acklog.txt
}

 

3.3.4 Stringausgabe per Draw_text  

Als letzte sinnvolle Möglichkeit der Stringausgabe zu Debugzwecken gibt es noch draw_text. Mit der Hilfe dieses Befehls können Sie einen String zu einer bestimmten Stelle am Bildschirm anzeigen. Bei draw_text müssen Sie allerdings noch beachten, dass draw_text den String nur für einen Frame auf den Bildschirm darstellt. Also lohnt es sich eine Funktion zu schreiben, die einen String dauerhaft auf dem Bildschirm darstellt:

function draw_debug(str,posx,posy,)
{
   while(1)
   {
      draw_text(str,posx,posy,vector(0,0,0)); //Zeichne den Text
      wait(1);
   }
}

Nun können Sie einen String einfach per draw_debug("der String",X Position,Y Position); auf den Bildschirm schreiben. Der Font mit dem der String dargestellt wird lässt sich übrigens mit Hilfe des Befehls draw_textmode verändern.

3.4 Überwachung von Variablen  

Es tritt oft der Fall auf, dass Sie bestimmte Variablen ständig auf den Bildschirm sehen möchten. Hierbei ist es ziemlich unsinnig jeden Frame den Variablenwert mit Hilfe eines breakpoints zu kontrollieren. Hier stellt 3D Gamestudio wesentlich nützlichere Werkzeuge.

1) Per Panel (digits) eine Variable dauerhaft sichtbar machen.
2) Genau wie Strings kann man auch Variablen leicht mit Hilfe des Befehls diag in die Acklog.txt schreiben.

Auch die Möglichkeit des Überwachens von Variablen mit draw_text und das schreiben von Variablen in Dateien sind möglich. Hierauf werde ich allerdings nicht mehr eingehen, da man hier eigentlich genau das gleiche Script wie bei Strings nutzen kann.

3.4.1 Variablenüberwachung per Panel  

Die sinnvollste Überwachungsmethode von Variablen sind sicher Panels. Mit Hilfe eines einfachen Panels können Sie sehr einfach den Wert einer Variable dauerhaft auf den Bildschirm darstellen. Ein Scriptbeispiel für solch ein Panel:

font arial_font = "Arial",0,12; //Der Font mit dem die Variable dargestellt wird

panel debug_pan
{
   pos_x = 100; pos_Y = 100;
   digits = 0,0,6.3,arial_font,1,ZU_ÜBERWACHENDE_VARIABLE;
   red = 0; blue = 0; green = 0; // schwarze Darstellfarbe
   flags = visible;
}

Wenn Sie mehr als eine Variable überwachen wollen, fügen Sie dem Panel einfach eine weitere digits Zeile hinzu.

3.4.2 Variablenausgabe per Diag  

Um eine Variable schnell in eine Datei auszugeben empfiehlt sich auch wieder Diag in Kombination mit str_for_num. Vom Prinzip her funktioniert es genauso wie bei den Strings daher sind wohl keine weiteren Erläuterungen notwendig. Vergessen Sie nicht Ihr Programm mit dem Start-Parameter "-diag" zu starten.

diag(str_for_num(NULL,VARIABLE_DIE_AUSGEGEBN_WERDEN_SOLL)); /*str_for_num gibt einen String Pointer zurück, falls im ersten Parameter NULL steht, daher ist dieser Code möglich*/

 

3.5 Beep  

Mit Hilfe des Befehls beep; kann man zu einer bestimmten Stelle im Script einen "Piepton" abspielen. Mit Hilfe von beep; lässt sich sehr schnell feststellen ob eine Funktion oder auch ein bestimmter If/While Block aufgerufen wird. Beispiel:

if(Bedingung)
{
   beep; //Bedingung ist wahr
   //weiterer code
}

 

3.6 Ein eigenes Debug System  

Als weitere Möglichkeit bietet sich noch die Programmierung eines eigenen Debug Systems an. Solch ein System ist dazu da, bestimmte Werte schnell und übersichtlich (also nicht in unübersichtlichen Text Dateien), meist in eine html Datei, auszugeben. Dieses Debug System sollte Ihnen nach Beendigung des Scriptes eine Datei erstellen in der alle Debug Ausgaben übersichtlich präsentiert werden. Ein Beispiel für eine Solche Ausgabe. Da solch ein Debug System sehr aufwändig zu programmieren ist, biete ich hier ein frei Downloadbares Debug System an.

 

4. Ingame Engine Fehler  

Ingame-Engine Fehler sind meistens folgen von Semantischen Fehler (durch die Beseitigung des Logischen Fehlers kann man auch meist die Engine Fehler beheben). Es gibt viele Gründe warum Sie auftreten und wie man Sie beheben kann. Grundsätzlich wird zwischen Fehlern und Warnungen unterschieden, wobei nach einem Fehler die Engine beendet werden muss. Hier ist es leider unmöglich eine generelle Hilfestellung zu geben, außer das man sich genau wie bei Syntaktischen Fehlermeldungen, die Fehlerbeschreibung sehr genau durchlesen sollte. Meist kann man so schon den Fehler erkennen.

4.1 Eine typische Engine Fehlermeldung  

Hier eine typische Engine Fehler und Warnmeldung:

   

Wichtig für den Spieleprogrammierer sind folgende Informationen:

1) Der Fehlerindex (Error E1513 bzw. Malfunction W1503). Geben Sie einfach diese Warnmeldung im Handbuch bei der Suchfunktion ein und Sie finden eine genauere Beschreibung des Fehlers.
2) Die Fehlerbeschreibung (crash in, Invalid array Index in)
3) Der Fehlerort ( in (function) main).
4) Die Codezeile in der der Fehler auftritt ( str_cat(@2,2); material_saved[1024] =2 ).

Im Handbuch finden Sie alle Fehler und Warnmeldungen mit entsprechender Erklärung. Da diese Erklärung aber nicht immer ausführlich genug ist, sind im Folgenden die häufigsten Fehler mit Lösungsmöglichkeiten aufgelistet.

4.1.1 Crash in [function] : [script]  

Die häufigste Fehlermeldung. Kann unter anderem folgende Ursachen haben:

1) Falsche Übergabe von Parametern an C-Script Funktionen (Variable statt String, Zu viele Parameter usw.)
2) Generell eine Ausführung von "falschen" C-Script Code (Teilung durch 0 usw.)

4.1.2 Invalid array index in [function] : [script]  

Diese Fehlermeldung tritt immer dann auf wenn in einem Array/Vektor versucht wird auf ein nicht vorhandenes Array Element zuzugreifen. Falls Sie z.B. einen Array mit 1024 Elementen erstellt haben, erhalten Sie diesen Fehler wenn Sie z.B. versuchen auf das 5000. Element zuzugreifen. Als Lösungsmöglichkeit vergrößern Sie wenn nötig den Array oder kontrollieren Sie ob Sie auch auf das richtige Array Element zugreifen (oft wird mit Variablen berechnet auf welches Array Element zugegriffen wird. Überprüfen sie diese Rechnung, wenn nötig über breakpoint).

4.1.3 <Actionname> Action not found  

Diese Warnung kann meist als harmlos betrachtet werden. Sie tritt auf falls Sie ein Level laden indem ein Entity mit einer nicht definierten Action vorhanden ist. Überprüfen Sie wie immer den Actionnamen auf Rechtschreibfehler, schreiben Sie falls nötig die Action oder löschen Sie die Action des Entitys mit WED.

4.1.4 Error 1242: Cannot Open Video Device  

Ihre 3D Grafikkarte besitzt nicht mehr genug Ressourcen. Starten Sie Ihren PC neu oder melden sich ab und wieder an. Dadurch wird der Grafikkartenspeicher geleert. Falls dies nichts hilft, liegt ein ernsteres Problem vor. Aktualisieren Sie Ihre Grafikkartentreiber und nehmen Sie eine Neuinstallation von DirectX 9.0c vor.

4.1.5 Error: Too many Entity Types  

Dieser Fehler tritt auf wenn Sie mehr als 1024 verschiedene Entity Typen in Ihrer Map haben. Entfernen Sie ggf. unnötige Entitys. Ab A6.4 ist dieser Fehler entfernt, also updaten Sie, falls möglich, Ihre Engine-Version. Zu beachten ist, dass wenn Sie LOD benutzen pro Model noch weitere 3 LOD-Modelle reserviert werden (_0 bis _3). Also bleiben dann nur noch 256 Entitiy Typen.

4.1.6 Error: Not enough entities reserved ([num])  

Dieser Fehler tritt auf, wenn mehr Entities in Ihrem Level vorhanden sind als per max_entities angegeben. Als Lösung setzten Sie max_entities einfach auf einen höheren Wert (VOR dem ersten level_load setzten!) oder erhöhen Sie den Nexus (WED -> Map Properties -> Nexus).

4.1.7 Malfunction W1301: [FILE]; Can't open file  

Falls dieser Fehler auftritt ist die angegebene Datei nicht in dem Spiel Verzeichnis, oder in einem der PATH Verzeichnissen vorhanden. Normalerweise tritt dieser Fehler beim Level laden (In der Map vorhanden Entities die nicht gefunden werden können) oder beim dynamischen laden von Ressourcen (ent_create,media_play usw. ) auf.

4.1.8 acknex.exe hat einen Fehler verursacht und muss geschlossen werden  

Einer der ernsten Fehler die auftreten können. Mögliche Ursachen sind meistens entweder ein Virus oder eine falsche DLL Anwendung (auf Memory Leaks aufpassen). Scannen Sie mit Hilfe von einem Virenscanner (z.B. Antivir) Ihren Computer ab und installieren Sie 3D Gamestudio neu.

 

5. Debugging mit SED  

Für alle A6 Benutzer gibt es mit SED noch eine weitere großartige Debug-Hilfe (Ein Hinweis: Falls die Debug-Hilfen im folgenden Kapitel bei Ihnen nicht funktionieren kopieren Sie die Datei Debug.dll aus Ihrem 3D Gamestudio Hauptverzeichnis, in Ihren Plugindir Ordner). Mit Hilfe von SED können Sie sich während der gesamten Laufzeit Ihres Programmes Variablen/Strings/Pointer usw. beobachten, deren Werte verändern und breakpoints setzten.

5.1 Überblick über die SED Debug Funktionen  

Im folgenden ein kleiner Überblick über alle möglichen SED Debug Funktionen:

Schauen wir uns zu erst einmal jeden Eintrag im Debug Fenster an:

Eintrag Funktion
   
Syntax Check Mit Hilfe dieser Funktion können Sie sich die Syntax - Fehlermeldungen beim letzten Engine Start anschauen.
Test Run Diese Funktion startet die Engine mit dem aktuellen Projekt.
Break Wenn Sie auf diesen Button drücken (nur aktiviert falls die Engine gestartet ist) können Sie Ihr Spiel pausieren.
Stop Debugging Beendet die Engine
Network Debug Diese Funktion ist zum Debugging eines Netzwerk Scriptes da. Hierauf werde ich in diesem Tutorial allerdings nicht näher eingehen.
Set current file to run Mit Hilfe dieses Buttons, setzt SED die aktuell selektierte WDL Datei als Start-WDL (Das heißt, wenn Sie das nächste Mal auf "Test Run" drücken, wird diese WDL Datei gestartet)
   
Step Over Falls ein breakpoint ausgelöst wurde, können Sie mit dieser Funktion zur nächsten Anweisung springen (genau wie [LEERTASTE] bei dem "normalen Scriptbreakpoint").
Goto current debug position Nach dem ausführen dieser Funktion springt SED zu der aktuellen Debug Position.
Toggle Breakpoint Mit Hilfe dieser Funktion können Sie breakpoints in SED setzten. Klicken Sie einfach mit der Maus auf die Zeile in der Sie den breakpoint setzten wollen und drücken Sie [F9]. Nun wird die Zeile rot, und die Engine wird an dieser Stelle einen breakpoint ausführen.
Ignore further breakpoints Ignoriert alle weiteren breakpoints. Gleiche Funktion wie [ESC] bei einem "normalen breakpoint".
   
Add Watch Fügt eine weitere Variable zur Beobachtung hinzu. Ich werde später genauer auf diese Möglichkeit eingehen.
Edit Watch Verändert den Wert der markierten Variable.
Delete Watch Löscht die markierte Variable aus der Beobachtungsliste.
Delete all Watches Löscht alle Variablen aus der Beobachtungsliste.
   
Execute Instruction Mit Hilfe dieser Funktion können Sie eine C-Script Anweisung ausführen. Funktioniert ähnlich wie execute(string); in C-Script
Reload FX Materials Diese Funktion lädt alle per effect_load geladene FX Materials neu.

Alle Buttons die im Bild nicht anklickbar (grau) sind, werden normal wenn die Engine gestartet wird.

5.2 Watches - Beobachten von Variablen & Strings  

Wie schon gesagt können Sie in SED mit Hilfe so genannter "Watches" ("watch" = beobachten/bewachen) Variablen und Strings beobachten uns sogar verändern. Wie macht man das nun genau? Nun als erstes drückt man auf [Debug -> Add watch]. Nun öffnet sich ein Eingabe Fenster. Hier geben Sie den Namen, der zu beobachtenden Variable (oder auch String) an. Ein Beispiel:

Bestätigen Sie die Eingabe per Klick on "OK". Nun sollte in der unteren Ecke von SED ein Fenster erscheinen (Falls nicht drücken Sie [F8] damit er aufpoppt). Wechsel Sie hier im Index Tab zu "Watch".

Jetzt starten Sie die Engine, und wie Sie sehen werden, ist der angegebene Wert (bei Value) immer genau der Wert der Variable.

Falls Sie sich wundern, dass es bei einer Variable einen X/Y/Z Wert gibt: Nur der X Wert ist entscheidend. Er repräsentatiert den Wert der Variable. Bei Vektoren haben natürlich alle 3 Werte eine Bedeutung.

Weitere Werte können Sie der Watch Liste einfach durch [Rechtsklick in das Watch Fenster -> Add Watch] hinzufügen. Durch [Rechtsklick auf eine bestimmte Variable -> Delete Watch] können Sie die Variable von der Watch Liste entfernen. Durch [Rechtsklick -> Edit] können Sie die/den Variable/String verändern. Also einen neuen Wert eingeben. Dies ist gerade zu Debug Zwecken extrem nützlich.

Vergessen Sie auch nie, dass Sie neben Variablen auch Strings in das Watch Fenster hinzufügen können. Nun wird im Value Fenster statt einer Zahl, der Inhalt des Strings angegeben.

5.3 Breakpoints in SED  

Vom Prinzip her funktionieren Breakpoints in SED genauso wie breakpoints per C-Script. Es gibt nur einige kleine Veränderungen.

1) Sie setzten die Breakpoints per [F9] in einer bestimmten Zeile.
2) Wenn der Breakpoint in der Engine ausgelöst wird, müssen Sie zu SED wechseln um die Debug Funktionen von SED nutzen zu können.

Wenn der breakpoint ausgelöst wurde, werden die Ergebnisse jeder Anweisung im "Output" Fenster (neben dem "Watch" Fenster) ausgegeben.

Zur nächsten Anweisung können Sie per [F4] springen. Um den breakpoint abzubrechen, und weitere breakpoints zu ignorieren, drücken Sie [Debug -> Ignore further breakpoints].

 

6. Das Engine Debug-Panel  

Als letzte Möglichkeit des Debuggens bietet 3D Gamestudio noch ein Debug Panel mit allen Statistiken über FPS, Speicherverbrauch und so weiter. Auch wenn dieses Debug Panel nicht unbedingt zum entfernen von Script-technischen Fehlern gedacht ist, kann es trotzdem bei der Optimierung von Scripten und Levels helfen.

Während des Spiels kann man das Debug Panel mit [F11] öffnen.

Generelle Werte
Millisekunden pro Frame für...
Anzahl an...
Memoryverbrauch (in Kilobytes)
Netzwerk
Bildschirmbreite (Pixel)
Frames pro Sekunde
Renderzeit für Level-Geometrie und Himmel
Entities
Nexus
Latency (in ms)
Kilopolygone (1000) pro Sekunde
Renderzeit für Entities
Sichtbare Level und Sky Entities
Level-Geometrie Videospeicherverbrauch
Datenrate Bytes pro Sekunde
Kameraposition (X)
Renderzeit für Partikel
Zahl der Partikel
Shadowmaps Videospeicherverbrauch
Spitzen-Datenrate Bytes pro Sekunde
Kameraposition (Y)
Renderzeit für Portale und Spiegel
Zahl der laufenden Media-Streams
Modelle/Sprites/Himmel Videospeicherverbrauch
Empfangene Bytes pro Sekunde, Reliable Modus
Kameraposition (Z)
Renderzeit für panels, texts and screen entities rendering
Zahl der laufenden Skript-Funktionen
Bmap/Font Videospeicherverbrauch
Empfangene Bytes pro Sekunde, Unreliable Modus
Kamerawinkel (pan)
Zeit für Bildwechsel und Monitorsynchronisations
Verfügbarer Videospeicher (eventuell ungenau)
Packets pro Sekunde unterdrückt
Kamerawinkel (tilt)
Zeit für Entity-Updates
Kamerawinkel (roll)
Zeit für Skriptausführung
Zeit für Physikausführung

 

Welche Werte z.B. bei Ms/Frame nicht übertroffen werden kann man nicht genau sagen. Allerdings erkennt man durch dieses Debug Panel sehr schnell durch welche Teile des Spiels man an meisten FPS verliert. Des Weiteren hier noch ein Zitat von JCL zum Thema Framerate.


The influence of entities on the frame rate should be not too hard to understand:

If an entity is not invisible, it can increase the rendering time. If it is not passable, it can increase the collision detection and physics execution time for other moving entites. And if it has an action, it will increase the overall script execution time. You can see all this effects in the [F11] panel. Even an entity that is not rendered because it's hidden behind the camera or a wall, can have a small impact on the rendering time because it's bounding box is clipped against the frustrum.

Übersetzt heißt das soviel wie:

Der Einfluss von Entities auf die Framerate sollte nicht zu schwer zu verstehen sein:

Wenn ein entitiy sichtbar ist, kann es die rendering Zeit erhöhen. Wenn es nicht passabel ist erhöht es die Kollisionserkennung und Physik Ausführungs-Zeit für andere, sich bewegende, Entities. Wenn es eine Aktion hat wird es die allgemeine Scriptausführungs Zeit erhöhen. Sie können all diese Effekte im [F11] panel sehen. Selbst wenn ein entity nicht gerendert wird, weil es hinter der Kamera oder hinter einer Wand ist, kann es einen geringen Einfluss auf die Rendering Zeit haben, weil seine Bounding Box gegen das Frustrum geclippt ist.

Mit Hilfe dieser Folgerungen sollten Sie die Millisekunden pro Frame angaben besser verstehen.

 

7. Bug Report 

Falls Sie sich einmal ganz sicher sind einen Bug in der Engine gefunden zu haben, beachten Sie bitte noch folgende Hinweise:

1) Kontrollieren Sie ob Ihr Bug schon berichtet wurde ( http://www.conitec.net/fixbugs.txt )
2) Ein Bug im Script-Compiler ist extrem unwahrscheinlich . Überprüfen Sie lieber noch einmal Ihr Script.
3) Eine ausführliche Fehlerbeschreibung ist entscheidend für einen guten Bug Report (wann tritt er auf, was passiert (nicht) usw.).
4) Falls möglich versuchen Sie den Fehler in einem einfachen Test Projekt zu reproduzieren. Falls Ihnen das möglich ist, fügen Sie Ihrem Post noch eine Anleitung hinzu, wie man den Fehler reproduzieren kann.
5) Bei Hardware Fehlern ist der Acklog noch wichtig. Ihn bekommen Sie indem Sie Ihr Script mit dem Parameter "-diag" starten (Hauptverzeichnis -> Acklog.txt)

Wenn sich alle User an diese Grundregeln halten können eventuelle Bugs wesentlich schneller gefunden und entfernt werden.

 

 


 

Ich hoffe, dass Sie durch dieses Tutorial gelernt haben mit Fehlern umzugehen. Bei Fragen über dieses Tutorial bitte bei Triple-X@hawkgames.de oder bei mir unter ICQ: 170911701 melden.

TripleX aka Timo Stark, 18.08.2005