Wintermute – Multilanguage Support

In diesem Artikel wollen wir beleuchten, welche Möglichkeiten wir haben, bequem mehrere Sprachen in unserem Wintermute-Spiel zu unterstützen. 

Dabei kommen uns zwei Features der Engine zu gute, die Unterstützung von String-Tab-Dateien und das Paketsystem, d.h. das Aufteilen der Spieldateien in mehrere Pakete.

Einleitung

Da uns die Wintermute-Engine die Möglichkeit gibt, String-Tab-Dateien benutzen zu können und zudem ein einfach zu verwendendes Paketsystem mitliefert, ist es relativ einfach, ein Spiel mehrsprachig zu machen. Wir wollen uns  2 Varianten näher anschauen :

  1. Mehrsprachigkeit über Packages
  2. Sprachumschalter per In-Game Optionsmenü

1. Mehrsprachigkeit über Packages

1.1 Vorbereitung

In der ersten Variante wollen wir die Mehrsprachigkeit und das Anbieten der einzelnen Sprachen komplett über das Paketsystem und der Priorisierung der Pakete verwirklichen. Das heißt, wir liefern ein Standardsprachpaket mit dem Spiel mit, weitere Sprachpakete können dann z.B. heruntergeladen oder im Rahmen einer Lokalisierung als Sprachpaket in eine eigenständige „Landes“-Distribution des Spiels eingebunden werden. Wir wollen das nun am Beispiel zweier Sprachpakete beleuchten.

Zunächst erstellen wir ein neues WME Projekt. Als Template können wir das 2D Demo Projekt verwenden.

wme-multilanguage-0

1.2 Pakete anlegen und priorisieren

Als nächstes legen wir 2 Pakete an, z.B. en und de. Das werden unsere Sprachpakete. In die Pakete legen wir jeweils eine string.tab Datei. Wir können diese aus dem data-Ordner kopieren. Damit wir dann eine Übersetzung sehen müssen wir noch einige Konstanten im deutschen Sprachfile übersetzen.

wme-multilanguage-1.1

 

Nun geben wir den Paketen eine Priorität. Höhere Prioritäten überschreiben niedrige Prioritäten.
Als Standardsprache wollen wie englisch verwenden. Wir setzen also die Priorität des Paketes en auf 0. Dem Paket de geben wir die Priorität 1. Nun wählen wir in den Startup Settings unter String table die Datei aus dem Standardsprachpaket aus.

wme-multilanguage-2

Was bedeutet das jetzt? Standardmäßig wird die String-Table aus dem Paket en geladen. Ist nun ein höher priorisiertes Sprachpaket vorhanden, wird die String-Table aus diesem Paket geladen. Wenn wir jetzt unser Projekt kompilieren, finden wir folgende kompilierte Paketdateien im Ausgabeordner:

wme-multilanguage-3

Wenn wir jetzt das Spiel starten, sehen wir den Settings-Dialog in deutscher Sprache – da das höher priorisierte de-Paket vorhanden ist.

wme-multilanguage-4

Wenn wir jetzt das Paket de aus dem Ordner löschen, wird das niedriger priorisierte Standardsprachpaket en verwendet.

wme-multilanguage-6

wme-multilanguage-5

 

Interessant wird die Sache, wenn wir nicht nur Text lokalisieren, sondern auch Grafiken und Sprache. Denn wir können in die Sprachpakete alle Dateien hineinlegen, die wir wollen. Höher priorisierte Pakete mit deutscher Sprachausgabe überschreiben dann z.B. das Standardsprachpaket mit englischer Sprachausgabe.

Jetzt könnten wir auf der Webseite unser Spiel in der Standardsprache und ganz einfach die zusätzlichen Sprachpakete der Lokalisierungen zum Download anbieten. Fertig.

Hinweis:
Ein Nachteil dieser Variante ist, dass die Sprachen nicht über ein Optionsmenü  gewechselt werden können. Um das zu ändern, könnte man aber ein eigenes Setup-Programm / Launcher schreiben, welches die mitgelieferten Sprachen auflistet  und nach Auswahl das entsprechende Sprachpaket  aus einem Unterordner in den Spielordner kopiert.
Wie man die Sprache per In-Game-Optionsmenü wechseln kann, schauen wir uns in Variante 2 an.

 

2. Sprachumschalter per In-Game Optionsmenü

2.1 Vorbereitung

Im Gegensatz zur ersten Variante, für die keine einzige Zeile Code geschrieben werden musste, erfordert ein In-Game-Sprachumschalter einen anderen Ansatz. Hier bedienen wir uns der Möglichkeit, per Skript eine String-Table laden zu können.

Zunächst erstellen wir wieder ein neues Projekt auf Basis des 2D Demo Templates.

Dieses mal benötigen wir nur ein Package für unsere Sprachfiles. In dem Paket legen wir jeweils einen Ordner für die zur Verfügung stehende Sprache an. Dorthinein kopieren wir dann jeweils die entsprechend übersetzte string.tab-Datei und setzen eine Default-StringTable.

wme-multilanguage-7

2.2 Erstellen der Menüs

Jetzt benötigen wir ein In-Game Optionsmenü. Dafür legen wir ein neues Windowobjekt options an.

wme-multilanguage-8

Dieses können wir nun mittels WME-Tool WindowEdit gestalten. Zum Umschalten erstellen wir uns 2 Grafiken für RadioButton – eins für an und eins für aus. Die Grafiken tauschen wir dann entsprechend im Skript.

wme-multilanguage-9

Jetzt benötigen wir noch einen Button im Hauptmenü (mainmenu.window), der unser  Optionsmenü aufruft.

wme-multilanguage-10

Das war es an dieser Stelle schon. Jetzt müssen wir skripten.

2.3 Scripting

Als erstes legen wir eine globale Variable lang in der Datei scripts/base.inc an.

global lang;

Als nächstes wollen wir, dass unser Optionsmenü geöffnet wird, wenn man im Hauptmenü auf den Button drückt. Dazu passen wir das Skript mainmenu.script an. Unsern Button haben wir options genannt, wir benötigen nun also eine Event-Funktion, die das neue Fenster öffnet.

on "options"
{
  var WinOptions = Game.LoadWindow("interface\system\options.window");
  WinOptions.Center();
  WinOptions.GoSystemExclusive();
}

Wenn wir jetzt das Spiel starten, das Hauptmenü mit ESC aufrufen und den Button klicken, öffnet sich unser neues Menü.

wme-multilanguage-11

Jetzt schauen wir uns das Skript für das Otionsmenü an: options.script
Initial schauen wir, welche Sprache in der globalen Variable gesetzt ist und initialisieren die RadioButtons.

this.initRadioBtn();

method initRadioBtn()
{
  var radioBtnEn = this.GetControl("btnEn");
  var radioBtnDe = this.GetControl("btnDe");
  if(lang == "de") {
    radioBtnEn.SetImage("sprites\system\RadioBtn0.png");
    radioBtnDe.SetImage("sprites\system\RadioBtn1.png");
  } else {
    // default en
    radioBtnEn.SetImage("sprites\system\RadioBtn1.png");
    radioBtnDe.SetImage("sprites\system\RadioBtn0.png");
  }
}

Wenn wir jetzt auf per RadioButton die Sprache wechseln, tauschen wir die Grafiken, setzen die globale variable und schreiben den Wert in die Registry, damit die Spracheinstellung beim nächsten Start noch wirksam ist.

// Auf Sprache en wechseln
on "btnEn"
{
  lang = "en";
  var radioBtnEn = this.GetControl("btnEn");
  var radioBtnDe = this.GetControl("btnDe");
  radioBtnEn.SetImage("sprites\system\RadioBtn1.png");
  radioBtnDe.SetImage("sprites\system\RadioBtn0.png");

  Game.RegWriteString("language", lang);
  Game.setLanguage(lang);
}

// Auf Sprache de wechseln
on "btnDe"
{
  lang = "de";
  var radioBtnEn = this.GetControl("btnEn");
  var radioBtnDe = this.GetControl("btnDe");
  radioBtnEn.SetImage("sprites\system\RadioBtn0.png");
  radioBtnDe.SetImage("sprites\system\RadioBtn1.png");

  Game.RegWriteString("language", lang);
  Game.setLanguage(lang);
}

Wir haben in den Events zum Sprachwechsel eine Methode setLanguage des Game-Objekts aufgerufen. Diese müssen wir als nächstes implementieren. Dazu wechseln wir ins Skript scripts/game.script und fügen die Methode hinzu. In der Methode laden wir die String-Table aus dem ausgewählten Sprachordner. Die Inventory-Items müssen ebenfalls neu geladen werden, damit die Sprache auch dort wirksam wird.

method setLanguage(lang)
{
  Game.LoadStringTable(lang+ "\String.tab", true);
  Game.LoadItems("items/items.items");
}

Der 2. Parameter der Methode LoadStringTable gibt an, ob die vorher geladenen Strings ersetzt (true) oder ergänzt (false) werden sollen.

Jetzt fehlt noch eine Sache, das initiale Setzen der Sprache beim Spielstart. Dazu erweitern wir erneut das game.script, in dem wir die Sprache aus der Registry laden und setzen. Als 2. Parameter der Methode RegReadString geben wir an, welche Sprache gesetzt werden soll, wenn noch kein Registry-Eintrag existiert.

lang = Game.RegReadString("language", "en");
this.initLanguage();

method initLanguage()
{
  Game.LoadStringTable(lang+ "\String.tab", true);
  Game.LoadItems("items/items.items");
}

Jetzt haben wir alles implementiert, was wir für das Umschalten der Sprachen brauchen. Jetzt fehlt nur noch die Lokalisierung selbst.

Hinweis:
Die verwendeten Methoden setzen einen Neustart des Spiels voraus, damit die Änderung der Sprache wirksam wird.

 

3. Die Lokalisierung

Für beide Varianten fehlt nun noch die Lokalisierung an sich. Dazu muss einiges beachtet werden.

3.1 String-Table

Schauen wir uns zunächst einmal die mitgelieferte StringTable an. Da finden wir zunächst Einträge SYSENG0001 bis SYSENG0xxx. Diese Konstanten sind die Übersetzungen für den WME-Setup Dialog. Diese erweitern wir jetzt um einige Konstanten aus dem Menü und dem Spiel, um die Wirksamkeit unseres Sprachschalters zu erkkennen.


; -------------------------------------
; Game GUI
; -------------------------------------
MENU_RESUME Resume game
MENU_LOAD Load game
MENU_SAVE Save game
MENU_OPTIONS Options
MENU_QUIT Quit game
OPTIONS_HEADLINE Select language
OPTIONS_HINT The game must be restarted for the changes to take effect

; -------------------------------------
; IN-Game
; -------------------------------------
DESK A heavy wooden desk.
CLOSET closet
WINDOW window
HOLE hole
FAN fan
BOOK WME book
DRAWER drawer
OPEN_DRAWER open drawer
DOOR door
ITEM_MONEY Money


; -------------------------------------
; Game GUI
; -------------------------------------
MENU_RESUME Spiel fortsetzen
MENU_LOAD Spiel laden
MENU_SAVE Spiel Speichern
MENU_OPTIONS Optionen
MENU_QUIT Spiel beenden
OPTIONS_HEADLINE Sprachauswahl
OPTIONS_HINT Das Spiel muss neu gestartet werden, damit die Änderungen wirksam werden.

; -------------------------------------
; IN-Game
; -------------------------------------
DESK Ein schwerer Holzschreibtisch
CLOSET Schrank
WINDOW Fenster
HOLE Loch
FAN Ventilator
BOOK WME Buch
DRAWER Schublade
OPEN_DRAWER geöffnete Schublade
DOOR Tür
ITEM_MONEY Geld

Hinweis: Bei Verwendung von UTF-8 ist darauf zu achten, dass man die Datei mit BOM speichert. Andernfalls kann es zu Problemen bei der Darstellung von Umlauten kommen.

3.2 Verwendung der Konstanten aus den StringTables im Spiel

Um einen String im Spiel aus der StringTable holen, nutzen wir folgendes Schema:

/TAB_CONSTANTE/Standardtext oder Platzhalter

Das funktioniert direkt im Code für Funktionen zur Textausgabe, als auch in den Text- und Caption-Eigenschaften der einzelnen Objekte. Hier ein paar Beispiele.

Textausgabe im Spiel:

Game.ExpandString("/TEXT001/Textausgabe");
actor.Talk("/ACTOR001/Das wird übersetzt.");

Texte in Window-Objekten (Editor):

wme-multilanguage-12

Texte in Window-Objekten (Code):

...
STATIC
{
  NAME="headline"
  CAPTION=""

  FONT="fonts\outline_white.font"
  TEXT="/OPTIONS_HEADLINE/Language"
...

Bei Items:

ITEM
{
  CURSOR_COMBINED = TRUE
  CAPTION = "/ITEM_MONEY/Money"
  NAME = "money"
...

Im Scene Editor:

wme-multilanguage-13

3.3 Voice-Files ausgeben

Um automatisch ein Voice-File zur String-Tab Konstante abzuspielen, müssen wir die Audiodatei so benennen, wie die zugehörige Konstante.
Beispiel:

ACTOR0001 Well, here we are.

wme-multilanguage-14

Und wir müssen der Engine beim Spielstart mitteilen, wo die Speech-Files liegen.

Game.AddSpeechDir(lang);

Mit der Methode AddSpeechDir können mehrere Verzeichnisse angegeben werden, mit RemoveSpeechDir lassen sich diese wieder entfernen. Ebenso kann man mittels LoadStringTable mehrere String-Table Dateien laden. Mit diesen beiden Möglichkeiten kann man die Sprachfiles besser ordnen (z.B. nach Actor, nach Scenes, etc.)

 

 

Ähnliche Beiträge

Schreibe einen Kommentar

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