![]() |
Andorra 2D - GUI Tutorial
Das Original befindet sich
![]() Andorra 2D GUI Teil 1 - Das GUI-System verwenden Einleitung ![]() Eigentlich alle professionellen Spiele verwenden nicht die Standardkomponenten, die Windows ihnen bietet, um das Benutzerinterface aufzubauen. Das GUI-System sollte schließlich dem entsprechenden Spiel angepasst sein - eine Mittelaltersimulation profitiert sicherlich nicht von kunterbunten Windows XP Schaltflächen. Außerdem ist es schwierig die Standardkomponenten hardwarebeschleunigt darzustellen. Damit auch Sie entsprechend stimmige, voll in Ihr Spiel integrierte, GUIs erstellen können, liefert Andorra 2D ein recht leistungsfähiges und für die meisten Fälle genügendes GUI-System mit. Dieses kann einfach über selbst geschriebene Komponenten erweitert werden und ist dazu noch voll Skinbar. Bitte beachten Sie: Ein solches GUI-System zu schreiben ist sehr zeitaufwendig - deshalb ist dessen Weiterentwicklung zugunsten der anderen Features von Andorra 2D momentan zurückgefahren. Wenn Sie selbst neue Komponenten entwickelt haben oder das System erweitern möchten, so schicken Sie mir einfach eine E-Mail. Vorbereitungen Zunächst müssen Sie sich eine entsprechene GUI im mitgelieferten GUI-Editor zusammenklicken, was ähnlich wie in Delphi funktioniert. Dabei sollten Sie jedes Formular einzeln als eine eigene GUI-Datei (*.axg, Andorra XML GUI) speichern. Skins können ebenso mit dem mitgelieferten Skineditor erstellt werden, was momentan allerdings noch recht kompliziert ist. Es bietet sich an, die mitgelieferte Skindatei "sunna.axs" einfach umzubauen. Außerdem benötigt eine Andorra GUI noch eine Mauszeigerdefinitionsdatei. Erstellen Sie hierzu mit dem Imagelisteditor eine Bilderliste mit den entsprechenden Mauszeigern. Erstellen Sie anschließend eine einfache XML Datei nach folgendem Beispiel, in dem Sie auf die einzelnen Einträge verweisen:
Delphi-Quellcode:
<?xml version="1.0" encoding="iso-8859-1"?>
<set> <images source="cursors.ail"/> <cursor name="default" src="default"> <hotspot x="0" y="0"/> </cursor> <cursor name="wait" src="wait"> <hotspot x="6" y="11"/> <anim start="0" stop="7" speed="25"/> </cursor> <cursor name="cross" src="cross"> <hotspot x="8" y="8"/> </cursor> </set> Integration Um nun die GUI in einem Programm zu verwenden, brauchen wir zunächst einige Units: In "AdGUI" befindet sich das Hauptgerüst von allen GUI-Komponenten, sowie die Implementierung der Mauszeiger und des GUI-Managers. Beachten Sie, dass Sie zum kompilieren eines Programms mit Andorra 2D GUI die Komponentensammlung "JVCL" installiert haben müssen, bzw. die benötigte Bibliothek "JvSimpleXML" und alle dazugehörigen Dateien im Suchpfad von Delphi eingetragen sind (unter Delphi in Tools->Optionen->Bibliothek - Win32). Außerdem muss die Unit "AdComponents" eingebunden werden, falls Sie auf die mitgelieferten Komponenten zurückgreifen. Mit dem Einbinden der Klasse werden die entsprechenden Komponenten im GUI-System registriert. Vergisst man das Einbinden, so kommt es beim Laden der GUI zu einer entsprechenden Fehlermeldung, da die angegebenen Komponenten nicht registriert sind und daher nicht gefunden werden können. Wir benötigen zunächst eine Instanz der Klasse "TAdGUI" - diese kümmert sich automatisch um Laden, Speichern, Skins und Mauszeiger.
Delphi-Quellcode:
Natürlich muss "AdGUI" am Ende wieder schön freigegeben werden.
AdGUI := TAdGUI.Create(AdDraw); //Erzeugen der Klasse
AdGUI.Skin.LoadFromFile('sunna.axs'); //Laden des Skins AdGUI.Cursors.LoadFromFile('cursors.xml'); //Laden der Cursor AdGUI.LoadFromFile('gui.axg'); //Laden der GUI Eigentlich sollten diese Schritte reichen um die GUI anzuzeigen. Die Betonung liegt dabei jedoch auf dem letzten Wort - man kann die Maus so fest drücken wie man möchte, es tut sich nichts. Woher auch? Schließlich müssen Maus- und Tastaturereignisse erst einmal an das GUI-System weitergeleitet werden. Dazu gibt es zwei Möglichkeiten. Zunächst erstmal die einfache, die auch in den meisten Fällen zunächst genügen sollte. Wir bedienen uns der Klasse "TAdGUIConnector" aus der Unit "AdGUIConnector". Dazu sind die folgenden Schritte nötig:
Delphi-Quellcode:
Und auch der GUI-Konnector sollte beim Beenden des Programms wieder schön freigegeben werden.
AdConnector := TAdGUIConnector.Create(AdGUI);
AdConnector.ConnectEventHandlers(self); Allerdings hat der GUI-Konnector (noch) ein paar Mankos: Man kann die Events nur mit einer Komponente der Klasse "TForm" verbinden - liegt das AdDraw zum Beispiel auf einem Panel, so müssen die Events manuell weitergegeben werden. Diese Methode ist jedoch nicht weiter schlimm und bringt sogar noch einen weiteren Vorteil mit sich: Man stelle sich ein Aufbauspiel vor, in dem sich frei verschiebbare Fenster auf dem Bildschirm befinden. Woher soll das Spiel nun wissen, ob wir auf das Fenster, oder auf das Spielfeld geklickt haben? Bei der manuellen Verknüpfung ist das kein Problem:
Delphi-Quellcode:
Es wird hierbei einfach die "Click"-Methode des GUI-Systems aufgerufen, die rekursiv die Clickmethoden jedes weiteren Elements aufruft. Wird festgestellt, dass der Klick tatsächlich auf einem Steuerelement gelandet ist, so gibt die "Click"-Methode den Wert "true" zurück.
procedure Form1Click(Sender:TObject)
var p:TPoint; begin GetCursorPos(p); //Aktuelle Mauszeigerkoordinaten auslesen p := ScreenToClient(p); //In Koordinaten relativ zum Formular umrechnen if not AdGUI.Click(p.X, p.Y) then //Ereignisbehandlung ausführen begin //Es wurde auf das Spielfeld geklickt end; end; Analog funktioniert das auch mit jedem weiteren Eventhandler. Auf Steuerelemente zugreifen Nun wäre es noch äußerst Praktisch, wenn wir auf die im Editor erstellten Elemente in unserem Programm irgendwie zugreifen könnten. Im Editor bekommen die Elemente unter der Eigenschaft "Name" einen Namen zugewiesen - wie in Delphi. Die Methode "FindComponent" von TAdGUI liefert uns unter Angabe des entsprechenden Namens das gesuchte Element zurück. Um auf komponentenspezifische Eigenschaften zuzugreifen, müssen wir das Ergebnis nur noch in die entsprechende Komponentenklasse "casten":
Delphi-Quellcode:
TAdButton(AdGUI.FindComponent('AdButton1')).Caption := 'Hallo!';
Steuerelementevents Unser GUI-System ist noch ein wenig leblos - auf Ereignisse wie Klicks reagieren wir noch überhaupt gar nicht. Dazu können wir einfach Prozeduren mit Ereignissen wie "OnClick" verbinden. Wir müssen dazu eine Prozedur erstellen, die genau die gleichen Parameter wie die Ereignisprozedur besitzt. Welche genau das sind, erfährt man in der Codevervollständigung. Beachten Sie, das die deklarierte Prozedur Bestandteil einer Klasse sein muss. Beispiel:
Delphi-Quellcode:
Übrigens funktioniert dies innerhalb der VCL genauso.
type
TForm1 = class(TForm) [...] private procedure AdButtonClick(Sender:TObject); public [...] end; [...] procedure TForm1.AdButtonClick(Sender:TObject); begin TAdButton(AdGUI.FindComponent('AdButton1')).Caption := 'Du hast mich geklickt!'; end; procedure TForm1.FormCreate(Sender:TObject); begin //Andorra 2D initialisieren [...] //Gui Laden [...] //Ereignisse zuweisen TAdButton(AdGUI.FindComponent('AdButton1')).OnClick := AdButtonClick; end; Eigene Komponenten Um eigene Komponenten zu erstellen, leiten Sie eine Klasse von "TAdComponent" ab, und registrieren Sie diese im "initialization" Abschnitt der Unit. Wie genau dies Funktioniert, schauen Sie sich gemäß dem Motto "Code sagt mehr als 1000 Worte" am besten in der Unit "AdComponents" ab. Um Ihre Eigenentwicklungen im Editor zu verwenden, müssen Sie die entsprechende Klasse auch dort einbinden. Code um eine geladene GUI zu erweitern Der Aufruf von "LoadFromFile" ersetzt normalerweise die komplette GUI - mit foldendem Code kann dies umgangen werden. Beachten Sie, dass Sie zusätzlich die Unit "JvSimpleXML" einbinden müssen.
Delphi-Quellcode:
Fazit
procedure ExpandGUI(afile: string);
var elems:TJvSimpleXMLElems; xml:TJvSimpleXML; j:integer; cref:TPersistentClass; function SearchFirst(elem:TJvSimpleXMLElem):TJvSimpleXMLElems; var i:integer; begin result := nil; for i := 0 to elem.Items.Count - 1 do begin if elem.Items[i].Name <> 'TAdGUI' then begin result := elem.Items; exit; end else begin result := SearchFirst(elem.Items[i]); if result <> nil then begin exit; end; end; end; end; begin xml := TJvSimpleXML.Create(nil); xml.LoadFromFile(afile); elems := SearchFirst(xml.Root); if elems <> nil then begin for j := 0 to elems.Count - 1 do begin cref := GetClass(elems.Item[j].Name); if cref <> nil then begin with TAdComponent(TAdComponentClass(cref).Create(AdGUI)) do begin LoadFromXML(elems.Item[j]); end; end; end; end; xml.Free; end; Diese Informationen sollten Ihnen helfen sich mit dem GUI-System außeinander zu setzen. Am wichtigsten ist es, sich die Eigenschaften der Komponenten zu betrachten. Scheuen Sie auch nicht davor in die Implementierung einer Komponente zu blicken - dann lösen sich aufkommende Fragen meist von selbst. In Delphi aber auch in Lazarus ist dies schließlich ganz einfach: Wenn Sie mit gedrückter STRG-Taste auf einen Bezeichner klicken, so springen sie automatisch zu dessen Deklaration. Copyright und Lizenz (c) by Andreas Stöckel November 2007 Der Inhalt dieses Tutorials steht unter der ![]() |
Re: Andorra 2D - GUI Tutorial
Zitat:
Eher mit gedrückter Strg-Taste ;) Ansonsten thx fürs Tut (habs noch nicht durchgelesen, bis auf den letzten Absatz^^). Edit: die anderen Fehler in dem Absatz hast du ja schon behoben ("á" statt "ß" etc), aber "Lösen" (vorher "Läsen") stimmt immernoch nicht ganz :) |
Re: Andorra 2D - GUI Tutorial
Ist ja gut ;-). Ich habe jetzt nochmal einige Fehler raus genommen.
Ich hoffe jemand kann etwas mit dem Tutorial anfangen ;-) Edit: Vorher war es "Lüsen" und nicht "Läsen" |
Re: Andorra 2D - GUI Tutorial
Habe die GUI auch schon ohne Anleitung benutzt, muss aber einfach loswerden, dass ich es super finde wie du dich um Andorra kümmerst, Support machst, Anleitungen schreibst, etc... Weiter so! :-D
|
Re: Andorra 2D - GUI Tutorial
Wenn Andreas was anfängt, dann hat das Hand und Fuß. Respekt! :thumb:
|
Re: Andorra 2D - GUI Tutorial
Mh.. Also, ich habe das jetzt versucht, und irgentwie stürtzt er immer ab.
So siehts bei mir aus:
Delphi-Quellcode:
Bei allen drei LoadFromFile-Routinen stürtzt er ab: "Access violation at address 00460F7C in module 'Andorratest.exe'. Read of address 000000."
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, AdDraws, AdClasses, AdTypes, AdSprites, AdPNG, AdPerformanceCounter, AdComponents, AdGUI; [...] var AdGui: TAdGUI; [...} AdGUI := TAdGUI.Create(AdDraw); AdGUI.Skin.LoadFromFile('sunna.axs'); AdGUI.Cursors.LoadFromFile('cursor.xml'); AdGUI.LoadFromFile('hauptmenu.axg'); Hat da jemand eine Idee? Ich kann es mir gerade nicht erklären (die AdPNG und AdComponents sind ja da). Danke schonmal, Roman |
Re: Andorra 2D - GUI Tutorial
hmmh, also ich kann da keine Fehler finden, bzw. ich bin mir sicher, dass da nicht der Fehler ist. Zeig doch am Besten deine gesamte Initialisierungsprozedur.
Gruß Looper Edit: Ist denn schon AdDraw erstellt worden? |
Re: Andorra 2D - GUI Tutorial
Außerdem wäre es noch hilfreich, wenn du uns deine Delphiversion sowie die Andorra-Version verraten würdest, die du benutzt :D
|
Re: Andorra 2D - GUI Tutorial
Danke für eure Hilfe, aber irgentwie hat sich das Problem von selbst gelöst, ich hab nur Neugestartet und die neuste Version von Anddorra installiert.. :gruebel:
Aber ich habe jetzt ein anderes Problem. Und zwar funktioniert das GUI perfekt, bis ich ein Sprite erzeuge. Dann kann ich nichts mehr anklicken im GUI. Was mache ich falsch? |
Re: Andorra 2D - GUI Tutorial
Ohne Quellcode wird das für uns eine lustige Ratestunde ;)
|
Re: Andorra 2D - GUI Tutorial
Achso, ja, fast vergessen ;)
Ist alles etwas durcheinander und nicht durchkommentiert, weil es nur ein Test ist. Aber so schwer sollte der Quelltext nicht sein ;)
Delphi-Quellcode:
Konkret tritt das Problem auf, wenn TSpieler erzeugt wird (wird vom GUI aufgerufen).
unit uSpiel;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, AdDraws, AdClasses, AdTypes, AdSprites, AdPNG, AdPerformanceCounter, AdComponents, AdGUI, AdGUIConnector; type TBild = class(TImageSpriteEx) private protected procedure DoMove(TimeGap: double); override; public shake: boolean; procedure pict(pict: string); constructor Create(AParent:TSprite); override; end; type TSpieler = class private protected public name: string; StatsPic: tBild; StatsPos: tPoint; Spielernummer: integer; constructor create(pName: string); destructor frei; end; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); procedure FormDestroy(Sender: TObject); procedure Idle(Sender:TObject; var Done:boolean); private { Private declarations } public procedure HauptSpielerHinzu(Sender: TObject); procedure HauptSpielerEntf(Sender: TObject); procedure HauptSpielerDone(Sender: TObject); procedure AddPlayerDone(Sender: TObject); end; var Form1: TForm1; AdDraw: TAdDraw; AdSpriteEngine: TSpriteEngine; AdImageList: TAdImageList; AdPerCounter: TAdPerformanceCounter; AdGui: TAdGUI; AdConnector: TAdGUIConnector; Spieler: Array[0..9] of tSpieler; AnzSpieler: integer; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin AdDraw := TAdDraw.Create(self); AdDraw.DllName := 'AndorraOGl.dll'; AdSpriteEngine := TSpriteEngine.Create(nil); AdSpriteEngine.Surface := AdDraw; AdImageList := TAdImageList.Create(AdDraw); AdPerCounter := TAdPerformanceCounter.Create; AnzSpieler := 0; AdGUI := TAdGUI.Create(AdDraw); if AdDraw.Initialize then begin Application.OnIdle := Idle; AdGUI.Skin.LoadFromFile('sunna.axs'); AdGUI.Cursors.LoadFromFile('cursors.xml'); AdGUI.LoadFromFile('hauptmenu.axg'); AdConnector := TAdGUIConnector.Create(AdGUI); AdConnector.ConnectEventHandlers(AdDraw.Window); TAdButton(AdGUI.FindComponent('SpielerHinzu')).OnClick := HauptSpielerHinzu; AdImageList.Restore; end else begin ShowMessage('Grafikenginefehler.'); halt; end; end; procedure TForm1.HauptSpielerHinzu(Sender: TObject); begin AdGUI.LoadFromFile('Spielerhinzu.axg'); TAdButton(AdGUI.FindComponent('done')).OnClick := AddPlayerDone; end; procedure TForm1.HauptSpielerEntf(Sender: TObject); begin end; procedure TForm1.HauptSpielerDone(Sender: TObject); begin end; procedure TForm1.AddPlayerDone(Sender: TObject); begin inc(AnzSpieler); Spieler[AnzSpieler] := tSpieler.create(TAdEdit(AdGUI.FindComponent('name')).text); AdGUI.LoadFromFile('Hauptmenu.axg'); end; procedure TForm1.Idle(Sender: TObject; var Done: boolean); begin if AdDraw.CanDraw then begin AdPerCounter.Calculate; AdDraw.ClearSurface(clBlack); AdDraw.BeginScene; AdSpriteEngine.Move(AdPerCounter.TimeGap / 1000); AdSpriteEngine.Draw; AdSpriteEngine.Dead; AdGUI.Update(AdPerCounter.TimeGap / 1000); AdDraw.EndScene; AdDraw.Flip; end; Done := false; end; procedure TForm1.FormDestroy(Sender: TObject); begin AdDraw.Free; end; constructor TBild.Create(AParent:TSprite); begin inherited; end; procedure TBild.Pict(pict: string); begin if AdImageList.Find(pict) = nil then begin with AdImageList.Add(pict) do begin Texture.LoadGraphicFromFile(pict, true, clFuchsia); end; AdImageList.Restore; end; self.SetImage(AdImageList.Find(pict)); end; procedure TBild.DoMove(TimeGap: double); begin inherited; end; constructor TSpieler.create(pName: string); begin name := pName; Spielernummer := AnzSpieler; StatsPic := tBild.create(AdSpriteEngine); StatsPic.pict('pic/stats.png'); StatsPic.Y := 1; StatsPic.X := (StatsPic.Width)*(Spielernummer) - StatsPic.Width; if StatsPic.X = 0 then StatsPic.X := 1; end; destructor TSpieler.frei; begin end; end. |
Re: Andorra 2D - GUI Tutorial
Weiß niemand Rat? Das komische ist, dass ich andere Sprites erzeugen kann, ohne dass das Menü nicht mehr bneuztbar ist - es muss also an tBild liegen, ich sehe jedoch gar keinen Unterschied zum anderen Sprite..
Edit: Stimmt nicht ganz. tBild kann ich auch ohne Probleme erzeugen, das Problem tritt nur auf, wenn tSpieler es erzeugt. :gruebel: Edit2: Stimmt auch nicht ganz. Wenn ich tSpieler manuell erzeuge (mit einer eigenen Variabele), dann geht auch noch alles. Edit3: Nun, es liegt scheinbar gar nicht an dem Sprite :oops: Diese Zeile:
Delphi-Quellcode:
macht das GUI unbrauchbar. Muss ich das irgentwie anders laden (Hauptmenu.axg wurde ja vorher schonmal geladen)?
AdGUI.LoadFromFile('menu/Hauptmenu.axg');
|
Re: Andorra 2D - GUI Tutorial
Aloha,
ich habe gleich mehrere Probleme mit der GUI :( Erstens mal kann ich den AdGUIConnector nicht verwenden, da er in der Zeile
Delphi-Quellcode:
meckert: "[Fehler] Unit1.pas(43): Inkompatible Typen: 'TAdWindowFramework' und 'TForm1'"...
AdConnector.ConnectEventHandlers(self);
Wieso? Und, bissl blöder, weil man das nicht so leicht umgehen kann wie das AdConnector-Problem (da gibts ja ne Alternative): Sobald ich
Delphi-Quellcode:
aufrufe, kriege ich eine Exception: "Im Projekt Project2.exe ist eine Exception der Klasse ENoValidCompressor aufgetreten. Meldung: 'No compressor not found to load the compressed bitmap data.'. Prozeß wurde angehalten. Mit Einzelne Anweisung oder Start fortsetzen."
AdGUI.Skin.LoadFromFile('sunna.axs'); //Laden des Skins
Was für ein Kompressor fehlt da? Habe den mitgelieferten Sunna-Skin unverändert übernommen. Er springt dann übrigens in die AdBitmap, Zeile 307 (procedure TAdBitmap.ReadRawData(AStream: TStream);). Könnte irgendeiner der beiden Fehler damit zusammenhängen, dass ich partout die JVCL im Moment nicht installiert bekomme? |
Re: Andorra 2D - GUI Tutorial
Liste der Anhänge anzeigen (Anzahl: 1)
Okay, die Exception ist weg, man musste noch die TAdPNG einbinden :)
Aber noch ein anderes Problem: Wieso sieht die GUI bei mir so hässlich aus? Der scheint das ja irgendwie nicht richtig zu zeichnen (siehe Anhang). |
Re: Andorra 2D - GUI Tutorial
Probiere mal die neuste CVS-Version aus. Kann sein, dass das Problem dort gelöst ist. Mit meiner neuen Grafikkarte hatte ich auch so ähnliche Grafikfehler, ich habe sie jedoch weg bekommen. Du kannst aber auch noch ein paar Tage warten, dann wird es eine Bugfixversion geben.
|
Re: Andorra 2D - GUI Tutorial
OK, jetzt funktioniert es tatsächlich, danke! :)
|
Re: Andorra 2D - GUI Tutorial
Ich bins schon wieder :mrgreen:
Und zwar lade ich jetzt meine GUI (Hauptmenü) und setze danach die OnClick-Events einiger Buttons. Das funktioniert auch prima, die entsprechenden Funktionen werden dann nach einem Klick aufgerufen. Später will ich aber eine neue GUI laden+anzeigen (Pause-Menü) und weise den neuen Elementen wiederrum andere OnClick-Events zu - diese werden dann aber nicht mehr ausgeführt! Sprich er ignoriert bei der später geladenen GUI einfach die Events. Was ist da los? Das ganze tritt immer dann auf, wenn ich durch neu-laden die alte GUI überschreiben und durch eine neue ersetzen will. |
Alle Zeitangaben in WEZ +1. Es ist jetzt 02:32 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz