GATE Games

Mit gategames existiert nun also die erste GATE Framework basierte Demo-App für Windows, X11 # und Android, die per OpenGL ein bisschen Grafik auf die Displays zeichnet.

Damit ist ein nicht unwesentliches Teilziel des GATE-Projektes erreicht, wenn gleich UIs kein Primärfeature darstellen.

Wie auch immer, meiner Motivation tat diese Demo recht gut.


Neben der Herausforderung den kleinsten gemeinsamen Nenner zwischen OpenGL 1.1 (Windows) und EGL (X11 Linux und Android) zu finden, und das Android-App Modell mit einer klassischen int main() basierten Anwendung zu kombinieren, erforderte gategames eine weitere Anpassung, die ich bisher unterschätzt bzw. wenig bedacht hatte: Keyboard und/oder Maus-basierte Steuerung.

Wer nutzt heute eine Maus?

Das Problem liegt an den Programmiermodellen “meiner Zeit”, also vor 20 Jahren, wo es genau zwei Gruppen von Benutzer-Ereignisse gab:

  • on_key_something(): Wenn jemand etwas über die Tastatur eingibt
  • on_mouse_something(): Wenn jemand mit der PC-Maus hantiert.

Und weil wir heute alle eine kleine Tastatur und eine Maus an unsere Smartphones anschließen, bevor wir sie nutzen, kann das ja auch so bleiben …….. ODER ?

Nun, während sich einige Frameworks genau einem Zweck widmen, können sie entweder für Desktop-Anwendungen so weitermachen wie bisher und wer für Smartphones codet, arbeitet nur noch mit on_touch_something(). Ähm … leider nein, denn auch das reicht nicht. Moderne Apps werden teilweise auch mit Sensoren gesteuert, denn jedes Smartphone kann einigermaßen genau sagen, wie es gerade gehalten wird und schon haben wir eine weitere Input-Quelle … also on_sensor_somthing()

Keycodes und Pointer

Schon bei Tastaturen ergibt sich bei stark interaktiven grafischen Programmen (auch gerne “Spiele” genannt) die Frage, will man Texte tippen, oder Elemente per Tasten fernsteuern.
Bei Texten hilft uns oft das OS, in dem es einen Tastenanschlag (key-down + key-up) in ein einzelnes Ereignis umwandelt und uns ein generiertes UTF-Zeichen liefert. Und wer eine Taste länger drückt, bringt sein OS dazu dem Programm vorzugaukeln, man hätte die Taste ganz schnell mehrmals gedrückt.

Wenn man aber Spiele spielt, startet das Drücken einer Taste eine Aktion und das Loslassen beendet diese in der Regel … und genau das ist eine ganz andere Logik.

Mäuse und Touch-Eingaben lassen sich oft als “Pointer-Events” zusammenfassen. Man hat also mit einem “Zeigegerät” auf dem Bildschirm etwas berührt oder den Zeiger bewegt. Bei der Logik von Spielen ist es aber intuitiver, wenn Mäuse bei Bewegungen ohne Tastendruck auch eine Aktion auslösen, wie z.B. die Bildkamera im virtuellen 3D Raum bewegen, während ein Touch-Gerät nur nach einer Berührung (on_pointer_down()) weitere Bewegungen wahrnehmen kann.

Lösen kann man das, in dem man bei jedem on_pointer_something() wieder einen Pointer-Typ übergibt.
Nachdem man ja an Android-Tabletts sehr wohl eine Maus anschließen kann, sollte man auch auf dieser Plattform auf Mause-Eingaben vorbereitet sein.

Implementierung

Unter Windows ist das Sache sehr old-style artig gelöst: WM_KEYDOWN und WM_KEYUP sagen uns den VK-Code (Virtual Key Code), der vom GATE-Framework in generische Codes übersetzt wird. Mit GetKeyState(VK_CONTROL/MENU/SHIFT) & 0x8000 lässt sich auch noch herausfinden, ob parallel zur Taste eine andere Steuertaste gedrückt wurde, was in Spielen gerne für alternative Bewegungen genutzt wird. WM_LBUTTONDOWN/UP, WM_RBUTTONDOWN/UP, WM_MBUTTONDOWN/UP lassen uns 3 Mausbuttons verwalten und WM_MOUSEMOVE meldet Bewegungen.

In X11 holt man sich mit XSelectInput() über die Event-Masken KeyPressMask, KeyReleaseMask, ButtonPressMask, ButtonReleaseMask, PointerMotionMask die nötigen Ereignisse via XNextEvent() ab. Hier braucht man noch ein XLookupKeysym() um einen Tastencode in ein einheitliches “Symbol” umwandeln zu können. Auch das wird am Ende vom GATE-Framework übersetzt.

Android nutzt in seinem NDK-Gluecode-Beispiel eine ALooper Nachrichtenschleife, die sich AInputEvents abholt und dann nach Typen sortiert bearbeitet. AINPUT_EVENT_TYPE_KEY bildet dabei die Tastatur ab und AINPUT_EVENT_TYPE_MOTION verarbeitet Touch und Mause Ereignisse. Man kann hier immer noch zwischen AMOTION_EVENT_ACTION_DOWN/UP und AMOTION_EVENT_ACTION_POINTER_DOWN/UP unterscheiden und so den Finger von der Maus oder einem Stylus unterscheiden.

Fazit

Nicht ganz so trivial ist die Umwandlung der nativen Keyboard-Tasten in generische System-Übergreifende Key-Codes, weil jedes System das ein bisschen anders macht. Damit meine ich Dinge wie:

  • Nutzen Shift + A und nur A jetzt einen Tastencode mit oder ohne Shift-Flag, oder wird Klein-a und Groß-A dafür benutzt.
  • Welche Tastekombinationen lassen sich nutzen und welche sind reserviert. Weil ein Ctrl + C eigenlich ein Kopieren auslösen sollte, doch eine Linux-Konsole reagiert da meist ganz anders darauf.
  • Und ob man jetzt eine Tastenwiederholung hat oder braucht, wenn eine Taste länger gedrückt wird, regeln die APIs ebenfalls abweichend.

Wie auch immer, alle diese Erkenntnisse und Codeänderungen machen es mir nun möglich, sowohl auf grafischer Ebene kleine Spielereien auf alle Plattformen zu bringen und gleichzeitig lassen sich auch CLI Programme mit den neuen “vereinheitlichten” Codes steuern.

In diesem Sinne: Mission accomplished! Und ab geht’s zum nächsten Thema…


Meine Dokus über:
 
Weitere externe Links zu:
Alle extern verlinkten Webseiten stehen nicht in Zusammenhang mit opengate.at.
Für deren Inhalt wird keine Haftung übernommen.



Wenn sich eine triviale Erkenntnis mit Dummheit in der Interpretation paart, dann gibt es in der Regel Kollateralschäden in der Anwendung.
frei zitiert nach A. Van der Bellen
... also dann paaren wir mal eine komplexe Erkenntnis mit Klugheit in der Interpretation!