Wintermute – Entwicklung einer InGame Debug-Console

Die Wintermute Engine liefert zwar ein Debug-Fenster mit, welches alle Variablen und Objekte anzeigt, es fehlen jedoch Möglichkeiten, komfortabel direkt zur Laufzeit Objekt und Spielzustände zu manipulieren.
Daher wollen wir eine InGame Console entwickeln, über welche wir über eine Kommandozeile Engine-Funktionen aufrufen und Objekte manipulieren können, z.B. Scenen wechseln, Items aufnehmen, Objekte ein- / ausschalten.

Das Window Objekt

Zuerst erstellen wir ein Fenster-Objekt (debugConsole.window), dieses teilen wir auf in folgende Bereiche:
– Log-Ausgabe (Static für Infotexte etc.)
– Kommandoliste (Static für Auflistung aller zur Verfügung stehenden Kommandos für die Kommandozeile)
– Kommandozeile (Editor)


wmeDebugConsoleWindow

Das Script

Jetzt hängen wir ein Script an das Fenster (debugConsole.script).
Hier fangen wir das Keypress-Event und parsen das eingegebene Kommando.
Die Return-Taste soll den eingegebenen Befehl parsen und ausführen, BILD-UP soll den vorherigen Befehl erneut in die Komandozeile schreiben, TAB oder ESC soll die Konsole schließen.

on "Keypress"
{
	// keypress on the console window triggers following actions
	// - parse and run command from input field
	// - cache the last command
	// - close window

	// Control of the input field
	var commandControl = this.GetControl("command");

	// VK_RETURN triggers: parse and run command from input field
	if(Keyboard.KeyCode==VK_RETURN)
	{
		// remember the current command and prev command
		var command = commandControl.Text;
		this.prevCommand = commandControl.Text;
		commandControl.Text = "";

		// get the logControl (= static text field)
		var logControl = this.GetControl("log");
		logControl.Text = "Log: ";

		// pars input command
		this.parse(command);

	}

	// VK_TAB and VK_ESCAPE: close this window
	if(Keyboard.KeyCode==VK_TAB || Keyboard.KeyCode==VK_ESCAPE)
	{
		this.Close();
	}

	// VK_PRIOR: write prev command in input field
	// Bild Up / Down - letztes Commando qwieder anzeigen
	if(Keyboard.KeyCode==VK_PRIOR || Keyboard.KeyCode == VK_PRIOR)
	{
		commandControl.Text = this.prevCommand;
	}

}

Zum Parsen des Befehls legen wir folgende Komandostruktur fest:
command [parameter] [parameter] […]
Trennzeichen zwischen Befehl und Parametern ist ein Leerzeichen.
In der parse-Methode wird nun der eingegebene String nach Leerzeichen gesplittet und das entstandene Array an die runCommand-Methode übergeben.

// parse
// parses the command from the input field
method parse(var command)
{
	// command structure: command [parameter] [parameter] [...]
	// that means: one command and many parameters, delimiter is a blank space

	// splitte commandName und Parameter
	var StrObject = new String(command);
	var splittedCommand = StrObject.Split(" ");

	// run command (see attached script: console\commands.script)
	this.runCommand(splittedCommand);

}

Zum Ausführen der Kommandos mittels runCommand-Methode binden wir ein weiteres Script ein (commands.script).

this.AttachScript("console\debugConsole\commands.script");

Das Ausführen der Kommandos

Die Kommandos werden im comandos.script definiert und ausgeführt.
Zuerst definieren wir die Kommandos und fügen sie zu einem Array hinzu.

// An array of all defined commands
var definedCommands = new Array();
definedCommands.Push("help");
definedCommands.Push("changeScene");
...
definedCommands.Push("exit");

Dieses Array können wir nun für die Anzeige der zur Verfügung stehenden Befehle durchlaufen.

// shows a list of all commands by activating the console window
this.showCommandList();

// showCommandList
// shows all in this.definedCommands stored commands on a static control
method showCommandList()
{
	var listControl = this.GetControl("commandList");
	var list = this.definedCommands;
	Game.LOG("command list length: " +list.Length);
	// excluding the help-command
	for(var i=1; i<list.Length; i=i+1) {
		Game.LOG("command number: " +i);
		listControl.Text = listControl.Text + "~n  "+list[i];
	}

}

Im debugConsole.script wird bereits die Methode runCommand zum Ausführen des eingegebenen und geparsten Befehls aufgerufen.
Diese wird nun implementiert. Übergeben wird ein Array bestehend aus einer Befehlbezeichnung und n Befehlsparametern.
Alle Befehle werden als Events implementiert. Mit der WME-Objektmethode CanHandleEvent prüfen wir zunächst, ob das Window-Objekt den entspr. EventHandler hat für den Befehl, d.h ob wir das Event definiert und implementiert haben.
Ist das der Fall, hängen wir mit ApplyEvent(Eventname) das „Command“-Event an das Fenster.

// runCommand
// gets the array of the splitted command string with command and parameters
// if the command is defined and has an implemented event the command will be run
method runCommand(var splittedCommand)
{
	var logControl = this.GetControl("log");
	var commandName = splittedCommand[0];

	// if an event is implemented apply the command as event
	if(this.CanHandleEvent(commandName))
	{
		logControl.Text = logControl.Text + "~nTry to run command: "+commandName;
		this.commandParameter = splittedCommand;
		this.ApplyEvent(commandName);

	} else {
		logControl.Text = logControl.Text + "~nUnknown command: "+commandName;
	}
}

Definieren und Implementieren der Command-Events

Nun fehlt noch die eigentliche Implementierung der Befehle als Events.
Damit CanHandleEvent nicht fehlschlägt, definieren wir nun die Events für alle Befehle.

on "changeScene"
{
	...
}

...

on "exit"
{
	...
}

Jetzt fehlt noch die Implementierung des eigentlichen Befehls, d.h. der Aufruf der benötigten WME-Script-Methoden mit den entspr. Parametern, damit der eingegebene Befehl das tut, was er soll, z.B. eine Scene wechseln.
Die Methode CheckParameter prüft, ob die richtige Anzahl an Parametern mit dem Befehl übergeben wurde.

on "changeScene"
{
	if(checkParameter(this.commandParameter,1))
	{
		var logControl = this.GetControl("log");
		logControl.Text = logControl.Text + "~nrun command Game.ChangeScene().";

		var Parameter = this.commandParameter;
		Game.ChangeScene(""+Parameter[1]+"");

		Sleep(1000);
		if(Scene.Filename == Parameter[1]) {
			logControl.Text = logControl.Text + "~nScene changed.";
		}

	}
}

Die Methode CheckParameter:

// checkParameter
// returns true if the number of parameters are correct
// otherwise returns false
method checkParameter(var params, var requiredNumber)
{
	var logControl = this.GetControl("log");
	for(var i=1; i<=requiredNumber; i=i+1) {
		if(params[i]=="" || params[i] == null) {
			logControl.Text = logControl.Text + "~nWrong number of parameter. Number of required parameter: "+requiredNumber;
			return false;
		}
	}
	this.logControl = logControl;
	return true;
}

Die Grundfunktionalität unserer Konsole ist nun implementiert. Jetzt fehlt noch der Aufruf des Konsolenfensters.

Aufruf der DebugConsole

Der Aufruf soll per TAB-Taste aus dem Spiel heraus erfolgen. Dazu gehen wir in das Game-Script (scripts/game.script bei Verwendung des Standardtemplates) bzw. GameEvent-Script (core/game/control/[controlname]/gameEvent.script bei Verwendung des Oblique-WME-Core) und fügen unter dem „keypress“-Event folgende Zeilen hinzu.

on "Keypress"
{
	...
	// Debug Functions
	if(Game.DebugMode == true) {

		// InGame Debugg Console
		if(Keyboard.KeyCode == VK_TAB ) {
			var WinDebugConsole = Game.LoadWindow("console\debugConsole\debugConsole.window");
			WinDebugConsole.GoExclusive();
		}
	}

Starten wir jetzt das Spiel im Debug-Modus und drücken TAB öffnet sich schließlich die neue Konsole.

wmeDebugConsole

Um ein kompiliertes Spiel im Debug-Modus zu starten, legen wir im Verzeichnis des Spiels eine wme.ini Datei an und schalten den DebugModus mit folgender Zeile ein:

[Debug]
DebugMode=1

Jetzt brauchen wir nur noch die von uns benötigten Befehle für die Konsole im command.script zu implementieren. Begonnen hatten wir mit dem Wechsel von Scenen. Auf der folgenden Seite wollen wir weitere nützliche Funktionen wie Wechsel des Actors, An-/Ausschalten von Objekten, etc implementieren.

Ähnliche Beiträge

Schreibe einen Kommentar

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