ZFX
ZFX Neu
Home
Community
Neueste Posts
Chat
FAQ
IOTW
Tutorials
Bücher
zfxCON
ZFXCE
Mathlib
ASSIMP
NES
Wir über uns
Impressum
Regeln
Suchen
Mitgliederliste
Membername:
Passwort:
Besucher:
4370141
Jetzt (Chat):
12 (0)
Mitglieder:
5239
Themen:
24223
Nachrichten:
234554
Neuestes Mitglied:
-insane-
"In-Game GUI" von Stefan Zerbst (Stefan Zerbst)


Hi,
da die IOTW Schlange im Moment nicht ganz so voll ist zeige ich mal woran ich nebenher bastle wenn ich die Zeit finde. Es handelt sich hierbei um eine halbwegs flexible In-Game GUI. Gerendert wird sie über eine eigene 3D-Engine welche letzten Endes intern Direct3D Sprites dafür verwendet. WinAPI oder Windows Messages werden nicht verwendet, daher ist die GUI natürlich auch im Fullscreen verwendbar.

Was man hier im Bild sehen kann ist eine 3D Szene in der ein Milkshape Modell gerendert wird. Danach folgt die GUI, welche hier spasseshalber mal einen Alphawert von 0.8 hat und damit partiell transparent ist. Als GUI Elemente sieht man hier Fenster, Child-Fenster, eine Menüleiste, Buttons und natürlich einen Toolbar. Alle Elemente sind voll funktionsfähig und vom Design her an das Windows aussehen angenähert. Allerdings kann man das Design sehr flexibel verändern.

Für Buttons kann man neben dem 3D Stil auch ein Image verwenden welches ein normales Bild und eine Pushed-Version des Buttons zeigt. Der Toolbar verwendet ebenfalls ein beliebiges Bild, bei dem aber alle Elemente die gleiche Größe und Höhe haben und quadratisch sein müssen. Separatoren kann man in die Menüs und die Toolbars einfügen. Man kann hier auch schön sehen, dass ein Fenster beliebig viele Toolsbars untereinander haben kann. Jeder Toolbar kann dabei eine beliebige Höhe haben, das entsprechendeBild wird dabei skaliert was nicht unbedingt so gut aussieht. Man sollte das Bild also schon in der Größe erstellen wie es nachher auch im Toolbar verwendet wird.

Flexibel steuerbar ist das Design über die folgende Struktur:

Code:
struct SDesign
    {
        spg::IColor        cFocusBorder;
        spg::IColor        cWindow;
        spg::IColor        cToolbar;
        spg::IColor        cToolbarSeparator;
        spg::IColor        cMenubar;
        spg::IColor        cMenubarSeparator;
        spg::IColor        cMenuHighlight;
        spg::IColor        cTitlebar;
        spg::IColor        cButtons;
        spg::IColor        cFontInactive;
        spg::IColor        cBorderColor;
        spg::IColor        cBorderColorInactive;
        spg::IColor        cFonts[FONT_MAX];    // see FONTTYPE
        spg::SFont        Fonts[FONT_MAX];    // see FONTTYPE
        unsigned long    TitlebarHeight;
        unsigned long    BorderThickness;
    };

Insbesondere kann man also die Farben einzelner GUI-Controls unabhängig voneinander einstellen. Der Alphawert wird dabei als Alphawert der jeweiligen Farbe genommen, so dass man auch die Transparenz der einzelnen Control-Typen unabhängig voneinander einstellen kann. Auch die Fonts und Farben der Fonts (hier als Array) sind entsprechend unabhängig.

Beim Handling der GUI macht die Engine natürlich vieles intern automatisch, wie beispielsweise das Z-Ordering der Controls, das Verschieben der Fenster beim Mouse-Drag,Öffnen der Menüs beim Mausklick, usw.

Das Handling der Nachrichten funktioniert über eigene Nachrichten-Strukturen und eine eigene Message-Queue der Engine die man entsprechend abfragen muss um auf Events wie beispielsweise einen Toolbar-Button-Klick zu reagieren.

Die Usability stand beim Design im Vordergrund, und so lassen sich alle Elemente recht einfach erzeugen. So erzeugt man einen Toolbar:
Code:
spgg::IGuiToolbar *pToolbar = m_pGuiDevice->CreateGuiToolbar( std::string(\"toolbar.bmp\"), 32 );
    m_pGuiWindow->AppendToolbar( pToolbar );
    pToolbar->AppendButton( 0 );
    pToolbar->AppendButton( 1 );
    pToolbar->AppendButton( 2 );
    pToolbar->AppendSeparator();
    pToolbar->AppendButton( 3 );

Jedem Button gibt man einfach eine ID mit, die ihn nachher identifiziert. Position und Größe der Buttons ergibt sich aus dem Parameter der CreateGuiToolbar() Methode bzw. der Reihenfolge des Hinzufügens.

Ein Menü für ein Fenster kann man beispielsweise wie folgt erzeugen:
Code:
spgg::IGuiMenu *pMenu = m_pGuiDevice->CreateGuiMenu( \"1st Menu\" );
    pMenu->AppendItem( 11, std::string(\"Menu 1.1\") );
    pMenu->AppendSeparator();
    pMenu->AppendItem( 12, std::string(\"Menu 1.2\") );
    pMenu->AppendSeparator();
    pMenu->AppendItem( 13, std::string(\"Menu 1.3\") );
    pMenu->AppendSeparator();
    pMenu->AppendItem( 14, std::string(\"Menu 1.4\") );
    m_pGuiWindow->AppendMenu( pMenu );

Was bisher noch verbesserungsbedürftig ist, das ist das Anordnen der Controls im Parent-Control. Bisher kann man die Elemente initial auch außerhalb des Clientbereichs positionieren. Beim Mouse-Drag wird allerdings schon kontrolliert, dassder User das Element nicht außerhalb des sichtbaren Bereichs zerrt falls es dragable ist. Die Controls sind auch nicht ab- oder andockbar, das erscheint mir für eine In-Game GUI zu viel Aufwand. Natürlich fehlen auch noch Conrols die man gut gebrauchen kann und die werde ich im Laufe der Zeit, so ich diese denn haben sollte, hinzufügen:
- Combobox (auch im Toolbar)
- Tree-Control
- Split-Window
- Text-Box
- Scrollbar
- Label :P

So, dann erstmal genug von mir, ich rede eh schon wieder zu viel :)






Von D-Coder am 20.08.2005, 08:32:52 Uhr
Schaut sehr nützlich aus.
Zum einen mal eine komponente die sonst kaum beachtung findet und zum anderen ist es auch schön mal wieder zu hören was du so treibst.

Ist die dein GUI system komplett unabhängig oder hängen da noch andere komponenten mit drin (z.b. eine bestimmte 3d engine o.ä) die man dann auch benutzen muss ?

Von @uzingLG am 20.08.2005, 08:34:01 Uhr
Tolle Sache :) Können die Fenster auch Schließen-Buttons rechts oben (oder woanders) haben, so wie Windows- oder X-Fenster?

Von Stefan Zerbst am 20.08.2005, 08:50:09 Uhr
Hi,

so ganz unabhängig ist das System nicht, weil es Komponente einer Engine ist. Die Engine selbst ist API-unabhängig und die GUI-Komponente wiederum benutzt natürlich nur das Render-Interface der Engine. Ist also damit auch API-unabhängig aber eben in der Engine verwurzelt.

Des weiteren hängt die Input-Komponente der Engine auch mit drin. Diese ist so designed, dass man von einer Basisklasse ableiten und virtuelle Funktionen die den Empfang eines Events verarbeiten sollen überschreiben muss. Das basiert auf einem Design von Seraph von dem ich geborgt habe. Das GuiDevice erhält also von dem Input-Device automaisch benachrichigungen wenn sich die Maus bewegt usw. Daraufhin wird dann automatish alles ausgeführt was getan werden muss, beispielsweise das Rendern von 3D Effekten der Menüköpfe wenn die Maus darüberschwebt oder das Verschieben von Fenstern wenn die Maus zieht.

Man sieht also, dass die GUI-Komponente zwei andere Komponenten der Engine nutzt und daher nicht stand-alone verwendet werden kann.

@Fenster-Buttons
Bisher gibt es noch keine Schließen-Buttons oder Minimieren/Maximieren in den Fenstern. Ich wollte eventuell noch die Möglichkeit einbauen, eine gestückelte Grafik für die Titelleiste eines Fensters zu verwenden, so dass man quasi auch hier beliebige Grafiken oder Texturen für den Kopf des Fensters verwenden kann. Hier beisst sich das dann allerdings mit dem Default 3D-Design welches ohne Texturen gerendert wird und ich weis snoch nicht genau, wie ich da am besten Buttons in der Titelleiste mit behandle. Ob die dan auch in den Grafiken enthalten sein müssten, was wieder zu Skalierungs-Problemen führen könnte usw.

Aber das kommt sicherlich irgendwann :)

Von Renderstate am 21.08.2005, 21:11:40 Uhr
Zitat:
da die IOTW Schlange im Moment nicht ganz so voll ist zeige ich mal woran ich nebenher bastle wenn ich die Zeit finde.

Was kann es schlimmeres geben als keine Zeit zum coden zu finden :-o !

mal ne weltenbewegende Frage bezüglich der Funktionsbenennungen, wieso hast du "AppendButton/AppendItem" statt "AddButton/AddItem" gewählt ;-) ?




Von Stefan Zerbst am 21.08.2005, 22:41:50 Uhr
Hi,

na ganz einfach deshalb weil die Reihenfolge des Hinzufügens entscheidend ist, jedenfalls bei den Menüs und den Toolbars. Und "Add" im Sinne von Hinzufügen ist dann doch etwas anderes als "Append" im Sinne von Anhängen.

Letzteres trifft es besser, weil es aussagt, dass die neuen Buttons bzw. Items hinten angehängt werden ;)

Ciao,
Stefan

Von Kimmi am 22.08.2005, 09:29:15 Uhr
Hi Stefan,

sieht gut aus :-). Kann man die Ingame-GUI über einen Renderadapter auch an andere Engines anbinden?

MfG Kimmi

Von Stefan Zerbst am 22.08.2005, 12:38:54 Uhr
Hi Kimmi,

nein das GuiDevice ist fest in die Engine integriert, da diese dazu gedacht ist als Einheit verwendet zu werden. Absolute OS Unabhängigkeit oder Austauschbarkeit der Implementierungen sind zwar mit ein wenig Verrenkung möglich, aber dies ist kein Designziel wie beispielsweise bei der ZFXCE ;)

Die GUI verwendet direkt die Interfaces des RenderDevice für Texturen und für "Pens", welche D3D-Sprites sind.

Und da viel beim Input auch automatisiert läuft muss das InputDevice ja auch mit angebunden sein ;)

Ciao,
Stefan

Von 6SidedDice am 22.08.2005, 18:27:37 Uhr
Komisch, ich arbeite auch momentan, für mein spiel, an der gui ;). Allerdings wird sie nicht so Windows nah. Eine frage die mich brennend interessiert ist, wie kümmerst du dich um die Positionen von Widgets? Plazierst du sie von hand mit koordinaten? Ich überlege ob die mühe sich lohnen würde eine Art Layout manager zu implementieren, obwohl ich eher zu dem Sizer ansatz von wxWidgets greifen würdest. Wie hast du vor das zu handhaben?
Auch würde mich interessieren, wie du einzelne Events abfragst. Ich habe das, momentan, mit Signals & Slots geregelt, was mir auch ziemlich gut gefällt. So hat ein Button einen Klick signal und dort kann ich dann eine funktion meiner view, die den button enthält, einfach eine OnClickButton1 funktion zuweisen.


Von Stefan Zerbst am 22.08.2005, 21:02:35 Uhr
Hi,

>> die Positionen von Widgets
>> Layout manager [...] Sizer von wxWidgets

Bisher verwende ich nur sture Koordinaten und die sogar momentan noch relativ zur oberen Ecke eines Controls. Also noch nicht mal relativ zur Client-Area was unschön ist wenn man Toolbars oder einen Meubar drin hat. Aber das korrigiere ich noch. Einen Sizer von wxWidgets hatte ich auch schon angedacht, aber ich muss zugeben, dass ich mir noch nicht angesehen habe wie der funktioniert. Und da kam wieder die Zeit ins Spiel. Letzten Endes soll das ja mehr oder weniger eine In-Game GUI sein, die man notfalls auch für Tools vergewaltigen kann. Aber so exzessive Oberfläche soll man damit gar nicht machen :D auch wen es natürlich möglich wäre :D

>> wie du Events abfragst

Also bisher werden alle relevanten Events, was sehr wenige sind wie beispielsweise TB_BUTTON mit der ID usw. in eine eigene Queue des GuiDevice gesteckt. So was wie Slots, was bei mir Abos heisst, gibt es nur Seitens der InputDevices. Bei der GUI muss man sich die Messages aber wie bei Windows selbst abholen da mit die Abo Geschichte hier zu übertrieben schien.

Ciao,
Stefan

Von Peter Müller am 23.08.2005, 23:55:56 Uhr
Sieht gut aus. Ist der Cursor teil der Engine oder der GUI und kann man diesen auch veraendern (um z.B. einen animierten einzusetzen)?

Von Stefan Zerbst am 24.08.2005, 12:42:17 Uhr
Hi,

der Cursor ist ein Objekt der GUI und man kann beliebig viele Bilder als Cursor laden und zwischen denen nach Bedarf umschalten.

Ciao,
Stefan

Von Wassja am 29.08.2005, 19:53:41 Uhr
Ich hab mir die Mühe gemacht und versucht sowas für DirectDraw zu programmieren. Für mein 2D Spiel im Mittelalter und davor. War total viel Arbeit. Hab das wahrscheinlich nicht ganz richtig gemacht.

Von Löwe am 12.09.2005, 01:45:55 Uhr
@Wassja
Muss nicht sein. Niemand hat gesagt es sei einfach und in 2 minuten fertig :D
Buttons und "Rahmen" sind (imho) eigentlich recht einfach. über das event-handling sollte man sich schon ein paar gedanken machen. am aufwendigsten sind imho texteingabefelder, scrollbalken und tree-views (ich nenn sie mal so)

geht zumindest mir jedesmal (bei jeder eigenen gui) so. was auch ein grund sein könnte, das die meisten nicht fertig werden

mfg
Löwe

Von Tactive am 14.09.2005, 09:51:21 Uhr
@Stefan:
Nicht schlecht, auch wenn ich da wohl aus Faulheit eher eine fertige Library für nehmen würde (z.B. CeGUI). ;)
Was mich aber interessiert ist wie Du den Zeichensatz darstellst. Benutzt Du dafür eine Library (freetype) oder renderst Du den selber ?? Oder sind das einfache Bitmap-Fonts ??

Von Stefan Zerbst am 14.09.2005, 15:16:29 Uhr
@Tactive
Ich verwende DrawText() meiner Engine ;)

Intern wird in der D3D Implementierung dazu einfach der D3DXFont verwendet, der seinerseits sehr eng mit der WinAPI und CreateFont kooperiert.

Ciao,
Stefan

Von malu am 25.10.2005, 09:15:42 Uhr
Sieht gut aus. Vor allem das die Fenster usw. durchsichtig sein können finde ich schön.
Kannst du darüber vielleicht mal ein Tutorial schreiben?
Bitte...