Zwei Tasten gleichzeitig abfragen, wie geht das?

Anfängerfragen zum Programmieren mit PureBasic.
OlderCoder
Beiträge: 109
Registriert: 18.03.2013 12:30
Wohnort: Bayerland
Kontaktdaten:

Zwei Tasten gleichzeitig abfragen, wie geht das?

Beitrag von OlderCoder »

Hallo miteinander,

ich möchte ein kleines Spiel programmieren, in dem man eine Spielfigur auf einem 2D-Spielfeld mittels der WASD-Tasten insgesamt in 8 Richtungen steuern kann.
Dazu muss ich in der Lage sein, zu erkennen, wenn zwei dieser Tasten gleichzeitig gedrückt werden, um mich diagonal bewegen zu können. Und genau daran scheitere ich bisher.
Eine einzelne Taste wäre einfach, bei zwei sehe ich keinen Weg.
Ich hab es mit AddKeyboardShortcut und #PB_Event_Menu/EventMenu() usw. versucht, aber auch mit GetAsyncKeyState_.
Ich muss dazu sagen, dass ich auch nach Jahren über den Anfängerstatus nicht hinauskomme, weil ich nur selten zum Programmieren komme. (Deshalb habe ich auch wieder dieses Forum gewählt.)
Mit GetAsyncKeyState habe ich generell immer wieder Probleme und muss froh sein, wenn es überhaupt mal bei einzelnen Tasten funktioniert.
Ich kann leider auch keinen eigenen Code anbieten, den man nur noch optimieren muss, weil ich bisher gar keine sinnvolle Methode gefunden habe, mit der ich dem Problem wirklich näher komme.

Ich würde mich freuen, wenn Ihr mir einen Weg zeigen könnt.

Gruß
OlderCoder
ST4242
Beiträge: 42
Registriert: 29.10.2011 16:54

Re: Zwei Tasten gleichzeitig abfragen, wie geht das?

Beitrag von ST4242 »

Hallo,
das ganze funktioniert nur im Screen Modus, denn Du für dein Spiel wahrscheinlich nutzen wirst.

Anbei ein kleines Beispiel (bitte nur mit Debugger ausführen)

Code: Alles auswählen

OpenWindow(1,200,200,200,200,"Test")
InitSprite()
InitKeyboard()
OpenWindowedScreen(WindowID(1),0,0,200,200)
Repeat 
   ExamineKeyboard() 
  Erg1 = KeyboardPushed(#PB_Key_D)
  Erg2 = KeyboardPushed(#PB_Key_E) 
  
  If erg1
    If erg2=0
      Debug "D Gedrückt"
      Delay( 100)
    Else
      Debug "D und E gedrückt"
      Delay (100)
    EndIf
    
  Else
    If erg2
      Debug "E Gedrückt"
      Delay (100)
    EndIf
  EndIf
  
  
  
Until 1=0
Grüße
OlderCoder
Beiträge: 109
Registriert: 18.03.2013 12:30
Wohnort: Bayerland
Kontaktdaten:

Re: Zwei Tasten gleichzeitig abfragen, wie geht das?

Beitrag von OlderCoder »

Vielen Dank, ST4242.
Ich hab gleich mal Deinen Code in PB gestartet.
Es öffnet sich - beim ersten Versuch nach einiger Verzögerung - ein kleines Fenster, das "Keine Rückmeldung" anzeigt, auf keine Eingaben reagiert und nur über den Debugger zu schließen ist. Da stimmt also etwas nicht. Das Programm frisst auch CPU-Leistung, weil es, anders als WaitWindowEvent, das ich immer verwende, ständig nur mit der Schleife beschäftigt ist, nach meinem Verständnis. Aber das reicht nicht weit.

OpenWindowedScreen bin ich jetzt gar nicht gewohnt. (Bisher öffne ich einfach ein Fenster...). KeyboardPushed kenne ich nicht mal. Wissenslücken, die ich unbedingt schließen muss.
Lässt sich mit ähnlicher Vorgehensweise nicht auch #PB_Event_Menu verwenden (denn etwas ähnliches hatte ich versucht, auch mit GetAsyncKeyState_), und falls nicht, warum nicht?
OlderCoder
Beiträge: 109
Registriert: 18.03.2013 12:30
Wohnort: Bayerland
Kontaktdaten:

Re: Zwei Tasten gleichzeitig abfragen, wie geht das?

Beitrag von OlderCoder »

Ich hab mal vor der 7. Zeile ein
eventID = WaitWindowEvent(20)
eingefügt.
Interessanterweise hat sich das Fenster dadurch schwarz gefärbt, sich aber ansonsten wieder normal verhalten.
Die Ergebnisse im Debugger sind zwar grundsätzlich so, wie ich sie haben möchte, allerdings für meine Anwendung im Spiel nicht zuverlässig genug und in der Form auch unbrauchbar. Die Ergebnisse fließen praktisch daher, und auch wenn man immer beide Tasten drückt, werden sie immer wieder auch als einzelne erkannt. Und das wär im Spiel übel (eine eigene Version des uralten DOS-Daleks).
Es soll so sein, dass man die Tasten betätigt, und sich dann die Spielfigur genau einmal bewegt, bis man die nächsten Tasten drückt.
Man müsste daher dafür sorgen, dass es erst dann weitergeht, wenn die Tasten wieder losgelassen werden. Aber wie?
Edit: Ein Problem entsteht dadurch, dass beim Druck auf zwei Tasten eine Taste aber grundsätzlich vor der anderen betätigt wird, das aber nicht als Druck auf eine Taste gewertet werden darf.
Zuletzt geändert von OlderCoder am 03.07.2021 21:23, insgesamt 2-mal geändert.
GPI
Beiträge: 1511
Registriert: 29.08.2004 13:18
Kontaktdaten:

Re: Zwei Tasten gleichzeitig abfragen, wie geht das?

Beitrag von GPI »

ich würde ein

Code: Alles auswählen

repeat
  evetid = WindowEvent()
  if eventid = 0 ; kein event
     break
  endif
  ;eventhandlung, wie fenster geschlossen, menüs etc.
forever
einfügen. Deine Lösung mit Timeout 20 ist auch sehr lang - bei 60 bilder pro sekunde, hat ein Bild 1000/60 ms zeit = 16,666ms. Du wartest hier aber mit den Timeout länger. Das wird ruckelig. Die Lösung oben arbeitet alle Ereignisse ab, die da sind und gibt dann sofort weiter. Das ist in der Regel unter 1ms. Damit hast du für ein Flüssiges Spiel mehr Zeit die EIngaben auszuwerten und das Spielbrett zu zeichnen.

Ansonsten musst du halt eine Hilfsvariable setzen
etwa so

Code: Alles auswählen

;außerhalb
Xpressed=#false
;hauptschleife
if <keyXpressed> 
  if xpressed = #false
     xpressed = #true
    ;irgendwas machen, spielfigur bewegen etc.
  endif
else
    xpressed = #false
endif
CodeArchiv Rebirth: Deutsches Forum Github Hilfe ist immer gern gesehen!
ST4242
Beiträge: 42
Registriert: 29.10.2011 16:54

Re: Zwei Tasten gleichzeitig abfragen, wie geht das?

Beitrag von ST4242 »

Hallo,

bei meinen Code beispiel die Taste E oder D bzw. beide Drücken dann wird in der DEBUG ausgabe die texte ausgegeben.
Das hier keine Rückmeldung kommt ist normal, da ich keine Events abfrage, auch ist eine 100% Prozessorlast normal, da hier auch keine Pausen enthalten sind.

Dies war nur eine Demo wie es machbar ist.

Wenn Du ein Spiel machen möchtest und Sprites oder ähnliches verwenden willst, kommst du um die Screen befehle nicht drum rum.

Bitte so nochmal betrachten.

Danke
OlderCoder
Beiträge: 109
Registriert: 18.03.2013 12:30
Wohnort: Bayerland
Kontaktdaten:

Re: Zwei Tasten gleichzeitig abfragen, wie geht das?

Beitrag von OlderCoder »

Aber das Fenster hat auch keine Eingaben angenommen. Und "Keine Rückmeldung" ist für mich immer ein Warnsignal, dass etwas nicht funktioniert. Deshalb meine Reaktion darauf.
Sprites brauche ich nicht, obwohl man damit sicher tolle Sachen machen kann. Aber damit kenne ich mich sowieso nicht aus. Und das Spiel reagiert sowieso immer nur schrittweise auf die Bewegung des Spielers, der sich auch nur feldweise fortbewegen kann. Grundsätzlich eine ganz einfache Sache.

Ich hab das jetzt mal so gemacht:

Code: Alles auswählen

OpenWindow(1,200,200,200,200,"Test")
InitSprite()
InitKeyboard()
OpenWindowedScreen(WindowID(1),0,0,200,200)
Repeat 
  ExamineKeyboard() 
  eventID = WaitWindowEvent(20)
  Erg1 = KeyboardPushed(#PB_Key_D)
  Erg2 = KeyboardPushed(#PB_Key_E) 
  
  If Erg1
    Delay(200)
    Erg2 = KeyboardPushed(#PB_Key_E) 
    If Erg2
      Debug "D+E gedrückt"
    Else
      Debug "D gedrückt"
    EndIf  
  ElseIf Erg2
    Delay(200)
    Erg1 = KeyboardPushed(#PB_Key_D) 
    If Erg1
      Debug "D+E gedrückt"
    Else
      Debug "E gedrückt"
    EndIf  
  EndIf
  
  If eventID=#PB_Event_CloseWindow  
    End
  EndIf  
  
  If Erg1 
    Repeat : Until KeyboardReleased(#PB_Key_D)
  EndIf
  If Erg2
    Repeat : Until KeyboardReleased(#PB_Key_E)
  EndIf
  
ForEver
Schade, dass die Code-Ansicht keine Zeilennummern besitzt. So kann man nur schwer Bezug auf Programmstellen nehmen.
Wenn ich die unteren beiden If-Abfragen mit den KeyboardReleased-Befehlen auskommentiere, dann läuft es. Aber ich habe grundsätzlich das Problem, dass, auch wenn ich zwei Tasten drücke, immer wieder nur eine von beiden im Debug-Fenster angezeigt wird. Das kann ich auch mit einem Delay nicht verhindern. (Egal wie lang dieses ist, aber lange Werte verzögern das ganze sowieso nur unangenehm). Ich brauche aber eine 100% zuverlässige Auswertung meiner Eingaben - eine oder zwei Tasten - für das Spiel.
Die unteren beschriebenen Zeilen hatte ich eingefügt, damit das Programm wartet, bis ich die Tasten loslasse (um den schrittweisen Ablauf des Spiels sicherzustellen). Für mich sieht das richtig aus, aber das Programm hängt sich dort dann fest (...Keine Rückmeldung), was ich nicht verstehe. KeyboardReleased soll doch erkennen, wenn man eine vormals gedrückte Taste wieder loslässt. Und die Tasten lasse ich los, aber das Programm verbleibt in der Repeat-Until-Schleife. Warum?

Edit: Und falls es jemanden interessiert, wo die Reise mit meiner Spielidee ungefähr hingehen soll (allerdings grafisch etwas ansehnlicher und deutlich komfortabler), nach langem Suchen hab ich hier https://www.myabandonware.com/game/daleks-284/play-284 etwas gefunden.
(Edit: Dort hat der Spieler aber nur 4 Bewegungsrichtungen. Das gefällt mir nicht.)
Zwei Probleme, die zu lösen sind.
Zuletzt geändert von OlderCoder am 03.07.2021 23:49, insgesamt 1-mal geändert.
OlderCoder
Beiträge: 109
Registriert: 18.03.2013 12:30
Wohnort: Bayerland
Kontaktdaten:

Re: Zwei Tasten gleichzeitig abfragen, wie geht das?

Beitrag von OlderCoder »

Wenn ich bei den unteren Warte-Schleifen

Code: Alles auswählen

   If Erg1 
     Repeat : Until GetAsyncKeyState_(#VK_D)=0
   EndIf
   If Erg2 
     Repeat : Until GetAsyncKeyState_(#VK_E)=0
   EndIf
verwende, dann wartet das Programm ordentlich auf das Loslassen der Tasten. Allerdings habe ich dann plötzlich überhaupt keine Möglichkeit mehr, zwei Tasten gleichzeitig zu betätigen. Es wird immer nur entweder Taste D oder E erkannt. Das verstehe ich nicht.
OlderCoder
Beiträge: 109
Registriert: 18.03.2013 12:30
Wohnort: Bayerland
Kontaktdaten:

Re: Zwei Tasten gleichzeitig abfragen, wie geht das?

Beitrag von OlderCoder »

Ein weiterer Versuch von mir. Diesmal teilweise gründlich kommentiert, um meinen Gedankengang zu verdeutlichen. Eigentlich sollte das mit den einzelnen und doppelten Tasten jetzt funktionieren. Und das Warten auf das Loslassen auch. Eigentlich.
Komischerweise aber ändern sich die Variablen Erg1 und Erg2 jetzt aber im Debugtest nicht, bleiben immer auf 0. Warum?
Edit: Das scheint mit der untersten Repeat:Until-Zeile zusammenzuhängen. Die tut aber auch überhaupt nicht, was sie soll, wartet nicht ab. Auch das verstehe ich überhaupt nicht.
Entferne ich diese Zeile, dann habe ich wieder Reaktionen auf meine beiden Tasten, allerdings nie auf zwei Tasten gleichzeitig. Es ist wie verhext.

Ein bisschen verwirrend übrigens, dass der Code-Zeichensatz in der Vorschau ein anderer ist wie in der endgültigen Fassung. Die Kommentare sind plötzlich nicht mehr sauber untereinander. Ich hatte dann alles geradegerichtet, und danach hat es wieder nicht gepasst. Das muss man wissen.

Code: Alles auswählen

OpenWindow(1,200,200,200,200,"Test")
InitSprite()
InitKeyboard()
OpenWindowedScreen(WindowID(1),0,0,200,200)
Repeat 
  ExamineKeyboard() 
  eventID = WaitWindowEvent(20)
  Erg1 = KeyboardPushed(#PB_Key_D)                                             ; Prüfen auf D gedrückt
  Erg2 = KeyboardPushed(#PB_Key_E)                                             ; Prüfen auf E gedrückt
  
  ; ein Debug auf Erg1 und Erg2 ergibt hier immer 0, unabhängig von den gedrückten Tasten. Warum???
  
  If Erg1                                                                      ; wenn D gedrückt wurde  
   Repeat : Until GetAsyncKeyState_(#VK_D)=0 Or KeyboardPushed(#PB_Key_E)      ; warten, bis entweder D losgelassen oder E gedrückt worden ist
    If KeyboardPushed(#PB_Key_E)                                               ; wenn E zusätzlich gedrückt wurde 
      Debug "D+E gedrückt"                                                     ; dann Reaktion D+E gedrückt
    Else                                                                       ; wenn nur D wieder losgelassen
      Debug "D gedrückt"                                                       ; sofort Reaktion D gedrückt
    EndIf
  ElseIf Erg2                                                                  ; entsprechend wenn E gedrückt wurde 
    Repeat : Until GetAsyncKeyState_(#VK_E)=0 Or KeyboardPushed(#PB_Key_D)     ; warten, bis entweder E losgelassen oder D gedrückt worden ist 
    If KeyboardPushed(#PB_Key_D)                                               ; wenn D zusätzlich gedrückt wurde
      Debug "D+E gedrückt"                                                     ; dann Reaktion D+E gedrückt
    Else                                                                       ; wenn nur E wieder losgelassen 
      Debug "E gedrückt"                                                       ; sofort Reaktion E gedrückt     
    EndIf
  EndIf  
    
  If eventID=#PB_Event_CloseWindow                                             ; Reaktion auf Fenster schließen
    End
  EndIf  
  
  Repeat : Until GetAsyncKeyState_(#VK_D)=0 And  GetAsyncKeyState_(#VK_E)=0    ; Warten, bis garantiert beide Tasten losgelassen worden sind 
  
ForEver
OlderCoder
Beiträge: 109
Registriert: 18.03.2013 12:30
Wohnort: Bayerland
Kontaktdaten:

Re: Zwei Tasten gleichzeitig abfragen, wie geht das?

Beitrag von OlderCoder »

Ich hab jetzt die KeyboardPushed-Methode wieder gegen Events und KeyboardShortcuts ausgetauscht und ein normales Fenster genommen, das Prinzip aber beibehalten. Dann unten die Warteschleife entfernt und nach jeder Tastenreaktion noch ein WaitWindowEvent(20) angefügt, um zu verhindern, dass die zuletzt losgelassene Taste wieder als Eingabe gewertet wird. Vielleicht etwas gepfuscht gedacht, aber es funktioniert. Und zwar bisher sehr zuverlässig. Und das zählt. Auch wenn ich selbst etwas überrascht bin. :)

Code: Alles auswählen

OpenWindow(1,200,200,200,200,"Test")
AddKeyboardShortcut(1,#PB_Shortcut_D,6)      ; Shortcut 6 - Figur nach rechts
AddKeyboardShortcut(1,#PB_Shortcut_E,8)         ; Shortcut 8 - Figur nach oben

Repeat 
  eventID = WaitWindowEvent(20)
  If eventID=#PB_Event_Menu
    erg1=0                                              ; erst mal Tastenspeicher löschen
    erg2=0
    If  EventMenu()=6                                   ; D gedrückt?
      erg1=6                                            ; merken  
    EndIf  
    If  EventMenu()=8                                   ; E gedrückt?
      erg2=8                                            ; merken
    EndIf  
  
  If Erg1=6                                             ; wenn D gedrückt wurde  
    Repeat
      eventID = WaitWindowEvent(20)
    Until GetAsyncKeyState_(#VK_D)=0 Or EventMenu()=8   ; warten, bis entweder D losgelassen oder E gedrückt worden ist
    If EventMenu()=8                                    ; wenn E zusätzlich gedrückt wurde 
      Debug "D+E gedrückt"                              ; dann Reaktion D+E gedrückt
    Else                                                ; wenn nur D wieder losgelassen
      Debug "D gedrückt"                                ; sofort Reaktion D gedrückt
    EndIf
    eventID = WaitWindowEvent(20)                       ; Ereignis löschen, um zu verhindern, dass auf die 2. Taste erneut reagiert wird
  ElseIf Erg2=8                                         ; entsprechend wenn E gedrückt wurde 
    Repeat
      eventID = WaitWindowEvent(20)
    Until GetAsyncKeyState_(#VK_E)=0 Or EventMenu()=6   ; warten, bis entweder E losgelassen oder D gedrückt worden ist
    If EventMenu()=6                                    ; wenn E zusätzlich gedrückt wurde 
      Debug "D+E gedrückt"                              ; dann Reaktion D+E gedrückt
    Else                                                ; wenn nur E wieder losgelassen
      Debug "E gedrückt"                                ; sofort Reaktion E gedrückt
    EndIf
    eventID = WaitWindowEvent(20)                       ; Ereignis löschen, um zu verhindern, dass auf die 2. Taste erneut reagiert wird
  EndIf  
    
  ElseIf eventID=#PB_Event_CloseWindow                            
    End
  EndIf  
  
ForEver
Das sind jetzt aber nur 2 Tasten. Ob das dann bei 4 Tasten und 8 Richtungen immer noch funktioniert, werde ich sehen.
Das nur, falls es jemanden interessieren sollte.
Die Taste E ist ja eigentlich verkehrt, sollte ja die Taste W sein, ich hatte das so aus den Vorschlägen übernommen. Aber das ist ja im Prinzip völlig egal.

Gruß
OlderCoder
Antworten