The Second Guest
Adventure-Treff
The Second Guest
(Anzeige)






Adventure-Treff
Lost Chronicles of Zerzura
utorial

LucasArts-GUI mit AGS

Dir hat die graphische Oberfläche von LucasArts-Spielen schon immer besser gefallen, als die von Sierra-Klassikern? Du findest es viel logischer, Verben, die ständig sichtbar auf dem Bildschirm stehen, direkt mit Spielelementen und Inventargegenständen zu kombinieren, als kryptische Mauszeiger-Symbole kognitiv verschiedenen Tätigkeiten zuordnen und gleichzeitig behalten zu müssen, wo Sierra das Menü versteckt hat, das die Tiefen deines Inventars offenbart? Und du hast kein Problem damit, dass der Preis für die intuitive Bedienung ein gewaltiger GUI-Bereich1 in der unteren Bildschirmhälfte ist, der das eigentliche Hintergrundbild zu einem schmalen Streifen abstrahiert? Dann ist dieses Tutorial genau das Richtige für dich, denn hier soll erklärt werden, wie mit AGS eine solche GUI realisiert wird.

Aller Anfang ist leicht

Zunächst brauchst du die kleine GUI-Demo (Monkey Island), die LucasFan ("Die neuen Abenteuer des Zak McKracken") netterweise zu Verfügung stellt. Darin sind alle wesentlichen Teile der GUI enthalten: Die Grafiken, der Mauszeiger und die Schriftarten im TTF Format, die es nirgendwo sonst gibt. Entpacke die Datei in das AGS-Verzeichnis und öffne das Projekt in AGS. Auf der Seite "GUIs" findest du unten den Button "Export GUIs", mit dem du die LucasArts GUI in das Projektverzeichnis als "la.gui" exportierst.
Auch den Mauszeiger musst du exportieren. Das geht im Sprite Manager, wo der Mauszeiger die Positionen 2-11 belegt. Diese Sprites exportierst du ebenfalls in das Monkey-Projektverzeichnis, z.B. als zeiger0.pcx bis zeiger9.pcx.
Jetzt startest du in AGS ein neues Projekt. Sagen wir, dein Adventure soll "Gitte und Irma" heißen, du nennst das Projekt also kurz "GUI". Das konvertierst du dann unter "Palette" zu einem Highcolor-Spiel, speicherst es ab und verlässt AGS wieder. In das neue Projektverzeichnis (man höre und staune - es heißt "gui") kopierst du dann aus LucasFans Demo-Projekt die Dateien agsfnt*.* (die Schriften), room1.crm (das Piratenschiff), la.gui (rate mal...) und die 10 Bilddateien des Mauszeigers. Jetzt öffnest du das neu angelegte Projekt (GUI) wieder und die Show kann beginnen.

Wie vor Jahr und Tag

Zuerst sind ein paar Vorbereitungen notwendig. Auf der Seite mit den "General Settings" muss LucasArts-gerecht "Don't automatically lose inventory" (das passiert auf Grund der etwas komplexeren Funktionsweise scriptgesteuert) und "Always display text as speech" angekreuzt werden. Die Einstellung bei "When interface disabled" lautet "GUIs unchanged". Auch bei "Game name" darf dein persönlicher Wunschname eingegeben werden.
Zuerst kümmerst du dich um die "Views". Die von AGS voreingestellte View 1 kann vorerst so bleiben. Willst du unbedingt Guybrush Threepwood aus LucasFans Demo benutzen, muss du diesen Bild für Bild aus der GUI-Demo exportieren und dann in deinem Projekt wieder einfügen (am besten die Standardfigur einfach ersetzen). View 2 muss die Spielfigur sein, wie sie spricht. Da solche Grafiken bei AGS nicht mitgeliefert werden muss du entweder komplett einen eigenen Charakter verwenden, Guybrush Threepwood importieren (wie gerade beschrieben) oder einfach jeweils das erste Sprite der gehenden Animation verwenden. In letztem Fall hat jeder der vier notwendigen Loops genau ein Sprite, dafür bewegt der Spieler beim Reden den Mund nicht. Zu Testzwecken reicht das aber aus.
Die Datei la.gui wird unter "GUIs" mit "Import GUIs..." importiert und erste graphische Eindrücke derselbigen sollten im AGS-Fenster sichtbar werden. Jetzt befinden sich zumindest schon mal die Grafiken der Benutzeroberfläche im Spiel.
Zu den Mauszeigern. Zuerst musst du die eben aus LucasFans Projekt exportierten Bilder in deinem Projekt importieren. Am besten überschreibst du die Sprites 2-11 damit2. Als View 3 / Loop 0 werden jetzt die 10 Bilder des Mauszeigers eingefügt, um die Animation zu definieren. Jetzt geht es auf die Seite "Cursors". Allen Mauszeigern bis auf #7 wird jetzt als Bild das erste Cursor-Bild (Sprite 2) zugewiesen und der Hotspot wird genau in die Mitte auf Position {7, 7} gesetzt. Den Cursorn #0-#6 wird dann die Animation von View 3 zugewiesen. Das Kontrollkästchen "Standard Cursor Mode" darf nur bei #0-#3 angekreuzt sein. Der Cursor #7 bekommt als Bild das kleine transparente Sprite #37 aus der GUI.
Füge jetzt ein paar Inventargegenstände hinzu. Die Grafiken sind mit der GUI importiert worden. Wichtig ist, dass Inventar-Icons eine Größe von 34x20 Pixeln haben. Wenn du "Player starts with this inventory Item" bei allen Gegenständen anwählst, dann kannst du testen, ob das scrollen des Inventars auch funktioniert. Dafür musst du mindestens 9 Gegenstände hinzufügen.
Auf der Seite "Fonts" kannst du jetzt die LucasArts-Schriftarten importieren ("Import font..."). Font 0 ist schon die richtige Schriftart. Font 1 wird mit agsfnt1.ttf überschrieben und Font 2 mit agsfnt2.ttf. Als Schriftgröße wird jeweils 10 eingegeben3. Achtung: Diese Schriftarten funktionieren nur mit Spielen der Auflösung 320x200 und auch nur in diesem Modus. Also kann man Spiele, in denen sie verwendet werden, nicht im 640x400-Modus spielen.
Eine Anmerkung zu Font 0: Das ist die Schriftart, die in der Statuszeile verwendet wird wo Dinge stehen wie "Benutze Ast mit Zweiköpfiges Eichhörnchen". Diese Schrift beherrscht normalerweise nicht die Umlaute der deutschen Sprache. Deshalb darf nicht, was in dieser Zeile steht (zum Beispiel die Namen von Objekten), Umlaute enthalten. Stattdessen werden andere Symbole benutzt. Hier die Tabelle mit den Zeichen, die stattdessen verwendet werden müssen.

Ä =
Ö = #
Ü =
ä = |
ö = ~
ü = $
ß =

Offensichtlich können weder die Buchstaben Ä, Ö und ß noch die Sonderzeichen #, |, ~ und $ verwendet werden. Mit dem SCI Studio kann die Schrift noch angepasst werden, wenn die fehlenden Zeichen nötig sind.
Bei "Characters" muss jetzt noch die "Talking View" auf 2 gestellt und die Zahlen hinter "Animation speed" und "Talking colour" auf 5 und 15 geändert werden. Um LucasArts-Spielen möglichst nahe zu kommen wird noch das Häkchen vor "No Interaction" gesetzt, damit man sich nicht selbst anklicken kann.

Gitte und Irma

Jetzt kommt die eigentliche GUI ins Spiel (im wahrsten Sinne des Wortes sogar). Mit dem Button "Edit script..." auf der "GUI"-Seite soll dieser jetzt Leben eingehaucht werden.
Angst vor Scripts? Schreibhemmungen beim Betrachten des Scripteditors? Dann solltest du jetzt dieses Fenster schließen, schreiend in den Wald rennen und mit spitzen Zweigen "Ich werde nie ein Spiel wie LucasArts machen" in den Waldboden ritzen. Ansonsten schlage ich vor, das gesamte Script, was jetzt im Editor erschienen ist (wird bei einem Klick auf die GUI ausgeführt), zu löschen, weil es zum Einen nicht ganz richtig ist und du es zum anderen ja auch verstehen sollt (ach, das hast du nicht gewusst? Jetzt ist zu spät!).
Zuerst muss jetzt abgefragt werden, ob auch tatsächlich die LucasArts GUI im Bild ist. Das geschieht mit

if (interface == 0) {

Jetzt müssen abhängig vom gedrückten Button in der GUI verschiedene Einstellungen gemacht werden:

if (button == 0) {       // gib
  SetCursorMode(2);
  SetGlobalInt(299,2);
}
if (button == 1) {       // oeffne
  SetCursorMode(8);
  SetGlobalInt(299,3);
}
if (button == 2) {       // schau
  SetCursorMode(1);
  SetGlobalInt(299,8);
}
if (button == 3) {       // benutze
  SetCursorMode(2);
  SetGlobalInt(299,7);
}
if (button == 4) {       // schliesse
  SetCursorMode(8);
  SetGlobalInt(299,1);
}
if (button == 5) {       // druecke
  SetCursorMode(8);
  SetGlobalInt(299,4);
}
if (button == 6) {       // nimm
  SetCursorMode(5);
  SetGlobalInt(299,6);
}
if (button == 7) {       // rede
  SetCursorMode(3);
  SetGlobalInt(299,9);
}
if (button == 8) {       // ziehe
  SetCursorMode(8);
  SetGlobalInt(299,5);
}

So wird in der globalen Variable 299 und dem CursorMode gespeichert, welche Art von Aktion der Spieler gerade angewählt hat. Die folgende Tabelle gibt eine Übersicht über die Einstellungen:

           CursorMode    / GobalInt 299
Gib        2 (Interact)    2
Öffne      8 (User 1)      3
Schau      1 (Look at)     8
Benutze    2 (Interact)    7
Schließe   8 (User 1)      1
Drücke     8 (User 1)      4
Nimm       5 (Pick up)     6
Rede       3 (Talk to)     9
Ziehe      8 (User 1)      5

Der Code für die beiden Pfeile, mit denen man das Inventar durchblättern kann, muss jetzt noch dazukommen. Er sieht so aus:

  if ((button == 10) & (game.top_inv_item < game.num_inv_items - 7))
    game.top_inv_item = game.top_inv_item + 4;
  if ((button == 9) & (game.top_inv_item > 0))
    game.top_inv_item = game.top_inv_item - 4;
}

Er sollte eigentlich relativ selbsterklärend sein. game.top_inv_item und game.num_inv_items sind globale Variablen. Erstere gibt den Index des ersten angezeigten Inventargegenstandes an und letztere die Anzahl der aktuellen Inventargegenstände.
Das GUI-Script ist damit erst mal fertig und es kann getrost geschlossen werden. Speichern nicht vergessen!

Globalisierung

Damit ist das eigentliche GUI Script schon fertig. Jetzt muss aber noch das globale Spielscript an das LucasArts'sche Spielprinzip angeglichen werden. Dazu öffnest du es mit Strg+G und fügst zuerst folgenden Code in die game_start-Funktion ein:

SetGlobalInt(299,8);
game.items_per_line=4;
game.num_inv_displayed=8;
game.text_speed=7;

Damit werden die Inventar-Einstellungen und die globalen Integer vorbereitet und die Textgeschwindigkeit wird eingestellt. In die repeatedly_execute-Funktion fügst du jetzt die folgenden Zeilen ein:

// Die Variablen werden initialisiert
string buffer, text;
int cur_mode, mode;
mode = GetGlobalInt(299);
cur_mode = GetCursorMode();
StrCopy (text, "");
 
// Je nach Cursor-Modus wird jetzt ein Verb in die Statuszeile geschrieben
if (cur_mode == MODE_WALK) StrCat(text,"Gehe zu ");
else if (cur_mode == MODE_LOOK) StrCat (text,"Schau an ");
else if (cur_mode == MODE_USE) {
  if (mode == 7) StrCat(text,"Benutze ");
  if (mode == 2) StrCat(text,"Gib ");
}
else if (cur_mode == MODE_TALK) StrCat(text,"Rede mit ");
else if (cur_mode == 5) StrCat(text,"Nimm ");
else if (cur_mode == 4) {
  if (mode == 7) {
    StrCat(text, "Benutze ");
    GetInvName (player.activeinv, buffer);
    StrCat(text, buffer);
    StrCat(text, " mit ");
  }
  if (mode == 2) {
    StrCat(text, "Gib ");
    GetInvName (player.activeinv, buffer);
    StrCat(text, buffer);
    StrCat(text, " an ");
  }
}
else if (cur_mode == 8) {
  if (GetGlobalInt(299) == 1) StrCat(text, "Schliesse ");
  if (GetGlobalInt(299) == 2) {
    StrCat(text,"Gib ");
    StrCat(text," mit ");
  }
  if (GetGlobalInt(299) == 3) StrCat(text, "#ffne ");
  if (GetGlobalInt(299) == 4) StrCat(text, "Dr$cke ");
  if (GetGlobalInt(299) == 5) StrCat(text, "Ziehe ");
}
 
// Das Objekt unter dem Cursor finden und an die Statuszeile anhängen
GetLocationName(mouse.x, mouse.y, buffer);
StrCat(text, buffer);
SetLabelText (0, 12, text);

An dieser Stelle wird als die Statuszeile am oberen Rand der GUI zusammengesetzt und angezeigt. Deshalb steht das Script auch in der repeatedly_execute-Funktion, weil die Statuszeile ja ständig aktualisiert werden muss.
Auch die on_mouse_click-Funktion muss etwas erweitert werden. Sie sieht dann insgesamt so aus:

if (IsGamePaused() == 1) {} // Wenn das Spiel pausiert ist, dann passiert nix
else if (button == LEFT) {
  ProcessClick(mouse.x, mouse.y, GetCursorMode() );
  SetCursorMode(MODE_WALK);
}
else if (button==RIGHT) {
  if (GetLocationType(mouse.x, mouse.y) == 2) {
    SetGlobalInt(299,9);
    ProcessClick(mouse.x, mouse.y, MODE_TALK);
    SetGlobalInt(299,0);
  }
  else {
    FaceLocation(GetPlayerCharacter(), mouse.x, mouse.y);
    ProcessClick(mouse.x, mouse.y, MODE_LOOK);
    SetCursorMode(MODE_WALK);
  }
}

Bei einer Pause gibt es also keine Reaktion, ansonsten wird zwischen linker Maustaste und rechter Maustaste (bei Person reden, sonst schauen) unterschieden. Zu guter Letzt benötigt das Spiel noch eine unhandled_event-Funktion, die folgendermaßen aussehen sollte:

function unhandled_event (int what, int type) {
  if (what == 1) {
    if (type == 1) Display("Ich sehe nichts Besonderes.");
    else if (type == 2) Display("Das kann ich nicht benutzen.");
    else if (type == 3) Display("Diese Sachen passen nicht zusammen.");
    else if (type == 4) Display("Es antwortet nicht.");
    else if (type == 7) Display("Ich kann das nicht nehmen.");
    else Display("Nein.");
  }
  if (what == 2) {
    if (type == 0) Display("Ich sehe nichts Besonderes.");
    else if (type == 1) Display("Das kann ich nicht benutzen.");
    else if (type == 2) Display("Damit kann ich nicht reden.");
    else if (type == 3) Display("Diese Sachen passen nicht zusammen.");
    else if (type == 5) Display("Ich kann das nicht nehmen.");
    else Display("Nein.");
  }
  if (what == 3) {
    if (type == 0) Display("Hübsch, hübsch.");
    else if (type == 1) Display("Das lasse ich lieber.");
    else if (type == 2) Display("Keine Antwort. Hmm...");
    else if (type == 3) Display("Das kann ich so nicht benutzen.");
    else if (type == 5) Display("Wie bitte? Nehmen?");
    else Display("Nein.");
  }
  if (what == 4) {
    if (type == 1) Display("Da ist nichts.");
    else if (type == 2) Display("Da ist nichts.");
    else if (type == 3) Display("Nein, wirklich nicht.");
    else Display("Die Luft ist kein sehr angenehmer Gesprächspartner.");
  }
  if (what == 5) {
    if (type == 0) Display("Was für ein Prachtstück.");
    else if (type == 1) Display("Jetzt nicht.");
    else if (type == 2) Display("Damit reden??? Freak!");
    else if (type == 3) Display("Die beiden Dinge passen nicht zusammen.");
    else Display("Nein.");
  }
}

In dieser Funktion werden alle Klicks im Spiel verwertet, die keine bestimmte Reaktion hervorrufen. Du kannst natürlich deine eigenen Sätze einfügen. Die Bedeutung jeder einzelnen Kombination der Variablen what und type findest du in der AGS-Hilfe unter "Text script event reference".

Was nicht passt wird passend gemacht

Jetzt geht es an die Umsetzung. Die GUI ist jetzt voll funktionstüchtig und muss nur noch im Spiel umgesetzt wird. Um das ein bisschen zu testen, kannst du jetzt im "Room Editor / Settings" den Raum room1.crm von LucasFan öffnen. Du solltest die Startkoordinaten des Charakters dann auf {120,110} stellen, damit er im begehbaren Bereich startet. Wenn du eigene Räume hinzufügst, ist wichtig, dass nur die oberen 144 Pixel mit Grafik belegt sind, der Rest muss für die GUI schwarz bleiben. Sowohl der begehbare Bereich als auch zwei Hotspots für die Kanone und die Insel sind in LucasFans Raum bereits fertig.
Mit Strg+I (oder einem Klick auf den i-Button) kommst du in den Interaction-Editor für den Raum. Hier kannst du zunächst mit Klick auf die rechte Maustaste den Eintrag "Player enters Screen (before Fadein)" löschen, da dieser nichts wichtiges enthält. Auch der andere Eintrag hält nur ein wegkommentiertes Script, dass du auch problemlos löschen kannst. Um zu sehen, was das Script machst, kannst du aber auch in jeder Zeile die zwei ‚//' entfernen. Über den OK-Button verlässt du dann wieder das Fenster.
Wähle im "Room Editor / Hotspots" unter Hotspots die Kanone aus (#2). Mit einem Klick auf "Interaction..." öffnest du den Interaction Editor für die Kanone, wo es einige Dinge zu beachten gibt: Statt mehrere verschiedene Interaktionen einzustellen, reicht es bei der LucasArts-GUI "Any click on hotspot" zu benutzen, hinter dem das folgende Script steht:

int mode = GetGlobalInt(299);
if (mode == 1) Display("Kanone schließen?");              // schließe
if (mode == 2) Display("Was soll ich ihr geben?");        // gib
if (mode == 3) Display("Ich will sie nicht öffnen.");     // öffne
if (mode == 4) Display("Ich will sie nicht drücken.");    // drücke
if (mode == 5) Display("Ich will sie nicht ziehen.");     // ziehe
if (mode == 6) Display("Ich will sie nicht nehmen.");     // nimm
if (mode == 7) Display("Ich will sie nicht benutzen.");   // benutze
if (mode == 8) {
  FaceLocation (EGO,295,99);                    // schaue
  Display("Else-Bedingung.");
}
if (mode == 9) Display("Mit der Kanone reden?");          // rede
SetGlobalInt(299,0);

Theoretisch muss diese Konstruktion bei jedem Hotspot, Objekt, und Charakter angewendet werden. Es gibt aber auch Möglichkeiten, diese abzukürzen. Falls bei etwas nur die drei Interaktionen "Look at hotspot", "Pick up hotspot" und/oder "Talk to hotspot" (bzw. object/character) wichtig sind, dann können diese Interaktionen auch klassisch angewendet werden. Man könnte zum Beispiel über "Talk to character" einen Dialog starten, ohne die ganzen eben genannten if-Abfragen einzubauen. Versucht man dann, etwas anderes mit dem Charakter zu machen (nehmen/benutzen...) werden die Meldungen ausgegeben, die in der unhandeled_event-Funktion definiert wurden.
Eine weitere Besonderheit gibt es, wenn man Inventargegenstände mit etwas anderem benutzen oder etwas an einen anderen Charakter geben will. Beides funktioniert über die Interaktion "Use inventory on...". Beispiel: Klicke im Interaction Editor der Kanone auf "Use inventory on hotspot" und trage bei "Data value for this action" Die Inventar-Nummer des Messers ein, zum Beispiel 3. Wenn man also das Messer mit der Kanone benutzt oder es der Kanone gibt wird folgendes Script ausgeführt:

int mode = GetGlobalInt(299);
if (mode == 2) Display("Ich kann das Messer der Kanone nicht geben.");
if (mode == 7) {
  LoseInventory(3);
  Display("Ich habe das Messer mit der Kanone benutzt und es dabei verloren.");
}
SetGlobalInt(299,0);

Auch hier muss also zwischen "Benutze … mit …" und "Gib … an …" unterschieden werden.
Damit wären die Grundlagen der LucasArts GUI erklärt und du solltest in der Lage sein, weiter damit zu arbeiten. Sicher gibt es noch viele Verbesserungsmöglichkeiten und Dinge, die man einbauen kann, um noch näher an das klassische LucasArts Look and Feel zu kommen, aber diese Möglichkeiten sollen vorerst nur durch deine eigene Phantasie begrenzt sein...

Noch Fragen?

Fragen oder Anregungen zu diesem Tutorial können in diesem Thread unseres Forums gepostet werden.

Fußnoten

1 GUI, die; = Graphical User Interface (Grafische Benutzeroberfläche)
2 Es gilt grundsätzlich, dass man kein Sprite im Sprite Manager löschen sollte (auch wenn man es nicht braucht). Stattdessen sollten nur unwichtige Sprites überschrieben werden, wenn man eigene ins Spiel einfügt.
3 Auf Grund eines Bugs in AGS muss nach dem Import der Schriftarten AGS neu gestartet werden, damit das Programm die neuen Schriften "schluckt". Eventuell werden die *.ttf-Dateien beim Import auch zerstört, sodass sie neu in das Spielverzeichnis kopiert werden müssen.

Jan 'DasJan' Schneider


 

 
Lost Chronicles of Zerzura

(Anzeige)


DosBox-Demo
der Woche


The Big Red Adventure
(Dynabyte)

Download (5 MB)
Lösung

Es findet im Moment keine Umfrage statt.

Amazon.de Partnerschaft

(c) 2000 - 2012 by Adventure-Treff
Alle Bilder, Sounds, Dateien und weitere Inhalte dürfen nicht ohne vorherige
Genehmigung benutzt werden. Aber wenn ihr nett fragt, bekommt ihr sie bestimmt!