GATE Games
« | 11 Jul 2021 | »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 eingibton_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 Maus-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 AInputEvent
s abholt und dann nach Typen
sortiert bearbeitet. AINPUT_EVENT_TYPE_KEY
bildet dabei die Tastatur ab
und AINPUT_EVENT_TYPE_MOTION
verarbeitet Touch und Maus 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 nurA
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
eigentlich 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…