Nur den zuletztgedrückten Button benutzen

Für allgemeine Fragen zur Programmierung mit PureBasic.
wutschel
Beiträge: 14
Registriert: 04.01.2005 12:50
Wohnort: Nettetal

Nur den zuletztgedrückten Button benutzen

Beitrag von wutschel »

Hi All!
Also, ich will folgendes haben, aber ich bekomme es nicht hin:
Ich will das bei einer Bewegungsabfrage in meinem Spiel nur die als letzte gedrückte Taste angenommen wird. Das heißt ich habe (in 2D) einen Menschen den man mit A,S,D,W bewegen kann. Doch wenn man S und D gleichzeitig drückt bewegt er sich nach unten links. Das soll er aber nicht, sondern er soll sich dahin bewegen, wo man zuletzt gedrückt hat. Zum Beispiel wenn man ihn nach unten bewegt (also zuerst S drückt) und dann nach rechts bewegt (also D drückt) soll er sich nur nach rechts bewegen und nicht nach unten links. Wenn man aber zuerst D drückt und dann S soll er sich nach unten bewegen.
Ich dachte das es ganz einfach ist, aber ich habs einfach nicht hinbekommen, also pls help me!
thx und cYa
Mfg euer wutschel ^^
--------------------------
Benutzeravatar
Ynnus
Beiträge: 855
Registriert: 29.08.2004 01:37
Kontaktdaten:

Beitrag von Ynnus »

Ich hab mir dazu eine Art Stapel-Array angelegt, für für alle 4 Richtungen, (von 0 - 3) und dann jede gedrückte Taste, die vorher noch nicht gedrückt wurde, an oberste Stelle (0) geschrieben. Alle anderen rücken weiter runter. Wird eine Taste losgelassen, wird diese aus dem Stack-Array gelöscht, nachfolgende werden aufgerückt, damit keine Lücken entstehen. Dann kann man einfach die Richtung, welche an Stelle 0 im Array steht, als derzeitige Richtung nehmen. Wird die Taste losgelassen, rückt die Taste von Array-Stelle 1 an 0 und wird abgearbeitet. Es ist also möglich, alle 4 Tasten (oder natürlich auch weniger) zu drücken, er geht in die zuletzt gedrückte Richtung.
Leider hab ich kein Code-Beispiel parat, da ich das in C++ mit OOP gemacht habe und das etwas verwirrent aussehen würde, jetzt hier Klassen und Methoden rauszupflücken.
wutschel
Beiträge: 14
Registriert: 04.01.2005 12:50
Wohnort: Nettetal

Beitrag von wutschel »

gute idee! dankeschön, ich werde mal sehen was ich machen kann
Mfg euer wutschel ^^
--------------------------
Benutzeravatar
ZeHa
Beiträge: 4760
Registriert: 15.09.2004 23:57
Wohnort: Friedrichshafen
Kontaktdaten:

Beitrag von ZeHa »

Ich habs bei Gloomy Nights damals anders gemacht. Ich frage die Tasten ab und gebe je nachdem dann die Richtung an.

Beispiel:

Code: Alles auswählen

If KeyboardPushed(#PB_Key_Up)
  richtung=#hoch
EndIf

If KeyboardPushed(#PB_Key_Down)
  richtung=#runter
Endif

If KeyboardPushed(#PB_Key_Left)
  richtung=#links
EndIf

If KeyboardPushed(#PB_Key_Right)
  richtung=#rechts
Endif

;---

Select Richtung
Case #hoch
  playerY=playerY-1
Case #runter
  playerY=playerY+1
Case #links
  playerX=playerX-1
Case #rechts
  playerX=playerX+1
EndSelect
Das funktioniert eigentlich recht gut, der einzige Nachteil ist nun, daß die horizontalen Tasten Priorität haben (da diese ja später erst abgefragt werden, somit könnte ja die Richtung "hoch" mit "links" überschrieben werden), aber das hängt dann letztendlich von Deinem Spiel ab, ob das stören würde. In unserem Fall tat es das nicht :mrgreen:

Natürlich mußt Du noch einbauen, daß wenn keine Taste gedrückt wird, daß die Figur dann stehen bleibt. Dies hab ich damals mit einer walk-Variable gemacht, die bei jedem Schleifendurchlauf auf Null gesetzt wird, und dann eben wenn eine Taste gedrückt wird (also bei den oberen Zeilen) auf 1 gesetzt wird. Dann kannst Du den unteren Teil in einen "If walk" Block einbauen.
Benutzeravatar
Ynnus
Beiträge: 855
Registriert: 29.08.2004 01:37
Kontaktdaten:

Beitrag von Ynnus »

@ ZeHa: Dein System ist eigentlich das Standard-System für sowas, nur leider funktioniert es nicht einwandfrei, wenn man horizontale und vertikale Tasten zusammen drückt und dann die zuletzt gedrückte haben will.
So nach diesem normalen System hatte ich es früher auch alles laufen, bis ich dann auf dieses Stack-System gekommen bin. Damit klappts dann einwandfrei.
wutschel
Beiträge: 14
Registriert: 04.01.2005 12:50
Wohnort: Nettetal

Beitrag von wutschel »

ok ich habs mal versucht un folgendes hinbekommen:

Code: Alles auswählen

InitSprite()
InitKeyboard()
OpenScreen(800,600,32,"")
Dim Richtungen.s(4)
NewList LetzteTasteSpeichern.s()


Richtungen(0) = "Left"
Richtungen(1) = "Down"
Richtungen(2) = "Right"
Richtungen(3) = "Up"
Richtungen(4) = "None" 

InsertElement(LetzteTasteSpeichern())
LetzteTasteSpeichern() = Richtungen(4) 
Repeat
ExamineKeyboard()
If KeyboardPushed(#PB_KEY_A) 
 If PressedA = 0
  PressedA = 1
  InsertElement(LetzteTasteSpeichern())
  LetzteTasteSpeichern() = Richtungen(0)
 EndIf
EndIf
If KeyboardReleased(#PB_KEY_A)
 PressedA = 0
 If LetzteTasteSpeichern() = "Left"
  DeleteElement(LetzteTasteSpeichern())
 EndIf
EndIf

If KeyboardPushed(#PB_KEY_S) 
 If PressedS = 0
  PressedS = 1
  InsertElement(LetzteTasteSpeichern())
  LetzteTasteSpeichern() = Richtungen(1)
 EndIf
EndIf
If KeyboardReleased(#PB_KEY_S)
 PressedS = 0
 If LetzteTasteSpeichern() = "Down"
  DeleteElement(LetzteTasteSpeichern())
 EndIf
EndIf

If KeyboardPushed(#PB_KEY_D) 
 If PressedD = 0
  PressedD = 1
  InsertElement(LetzteTasteSpeichern())
  LetzteTasteSpeichern() = Richtungen(2)
 EndIf
EndIf
If KeyboardReleased(#PB_KEY_D)
 PressedD = 0
 If LetzteTasteSpeichern() = "Right"
  DeleteElement(LetzteTasteSpeichern())
 EndIf
EndIf

If KeyboardPushed(#PB_KEY_W) 
 If PressedW = 0
  PressedW = 1
  InsertElement(LetzteTasteSpeichern())
  LetzteTasteSpeichern() = Richtungen(3)
 EndIf
EndIf
If KeyboardReleased(#PB_KEY_W)
 PressedW = 0
 If LetzteTasteSpeichern() = "Up"
  DeleteElement(LetzteTasteSpeichern())
 EndIf
EndIf


If KeyboardPushed(#PB_KEY_A) = 0 And KeyboardPushed(#PB_KEY_S) = 0 And KeyboardPushed(#PB_KEY_D) = 0 And KeyboardPushed(#PB_KEY_W) = 0
 InsertElement(LetzteTasteSpeichern())
 LetzteTasteSpeichern() = Richtungen(4)
EndIf

SelectElement(LetzteTasteSpeichern(),0)

If LetzteTasteSpeichern() = "Left"
 Debug "A gedrückt"
ElseIf LetzteTasteSpeichern() = "Down"
 Debug "S gedrückt"
ElseIf LetzteTasteSpeichern() = "Right"
 Debug "D gedrückt"
ElseIf LetzteTasteSpeichern() = "Up"
 Debug "W gedrückt"
ElseIf LetzteTasteSpeichern() = "None"
 Debug "Nix gedrückt"
EndIf

Until KeyboardPushed(#PB_KEY_ESCAPE)
Funktioniert soweit ich weis.
Na dann cYa!
Mfg euer wutschel ^^
--------------------------
Benutzeravatar
Ynnus
Beiträge: 855
Registriert: 29.08.2004 01:37
Kontaktdaten:

Beitrag von Ynnus »

Richtungen(0) = "Left"
Richtungen(1) = "Down"
Richtungen(2) = "Right"
Richtungen(3) = "Up"
Richtungen(4) = "None"
Hier würd ich noch optimieren und keine Strings nutzen. Das braucht nicht nur mehr Speicher sondern ist auch Fehleranfälliger.
Ich würd da Konstanten einführen und diese den Richtungen entsprechend zuweisen.
Also etwa:
#DIRECTION_DOWN = 1
#DIRECTION_RIGHT = 2
usw...

Und dann noch folgendes: "Richtungen(3)"
Wenn du weißt, dass es das Dritte Glied des Arrays, also "UP" ist, wieso dann nicht gleich "UP" schreiben bzw. durch eine Konstante DIRECTION_UP ersetzen? Weniger Rumgerechne, nur eine Konstante (kann ja auch eine Byte sein, die dann wenig Speicher frisst). ;)

Und eine Linked List würd ich für die Sache auch nicht nehmen. Ist langsamer als ein Array und du weißt ja, dass es maximal 4 Tasten gleichzeitig, also 4 Array-Elemente sein können. Jedes 1 byte (welches die Taste an dieser Stelle im Stack angibt) macht gerade mal 4 byte. Mit Linked Lists braucht schon allein der pointer zum vorherigen und nächsten Element sowie der zu den aktuellen Daten jeweils 4 byte. Also 12 byte pro Element = 48 Byte bei allen Tasten gedrückt. Dann noch mit Strings anstelle von Konstanten, macht nochmal ca. 20 byte zusätzlich für das Array.
Also etwas optimieren solltest du da noch. Immerhin ist die Tastenroutine auch etwas, was permanent nebenher läuft und gut gehen muss.
wutschel
Beiträge: 14
Registriert: 04.01.2005 12:50
Wohnort: Nettetal

Beitrag von wutschel »

ok, kann ich noch machen, aber machen die 68Byte den unterschied? da viele PC's schon 512 MB Ram oder 1024MB Ram haben sind doch 68Byte nichts dagegen. es war ja auch nurmal schnell ausprobiert, und in meinem projekt funktioniert es einwandfrei!
Mfg euer wutschel ^^
--------------------------
Benutzeravatar
NicTheQuick
Ein Admin
Beiträge: 8808
Registriert: 29.08.2004 20:20
Computerausstattung: Ryzen 7 5800X, 64 GB DDR4-3200
Ubuntu 24.04.2 LTS
GeForce RTX 3080 Ti
Wohnort: Saarbrücken

Beitrag von NicTheQuick »

Ich hab das ganze mal etwas allgemeiner und dynamischer gestaltet.

Vielleicht kanns ja so auch jemand gebrauchen. Die entscheidenden Stellen hab ich mit Kommentaren versehen.

Code: Alles auswählen

NewList LastKeys.l()
; Hier die Anzahl der Tasten angeben, die in der Routine verwalten werden sollen
#MaxKeys = 6

Procedure GetKeys()
  Protected *Key.LONG, LastKey.l, a.l
  Static *Mem
  
  If *Mem = 0 : *Mem = AllocateMemory(#MaxKeys * 4) : EndIf
  
  ExamineKeyboard()
  
  If CountList(LastKeys()) = 0 : AddElement(LastKeys()) : LastKeys() = -1 : EndIf
  
  ;Lösch alle Tasten in der Liste, die nicht gedrückt sind
  ForEach LastKeys()
    If KeyboardPushed(LastKeys()) = 0 And LastKeys() <> -1
      DeleteElement(LastKeys())
    EndIf
  Next
  
  If FirstElement(LastKeys())
    LastKey = LastKeys()
  EndIf
  
  ;Setze alle Tasten im Array auf Null, die nicht gedrückt sind
  a = 0
  *Key = ?UsedKeys
  While *Key\l <> -1
    If KeyboardPushed(*Key\l) = 0
      PokeL(*Mem + a, 0)
    Else
      If PeekL(*Mem + a) = 0
        InsertElement(LastKeys())
        LastKeys() = *Key\l
        LastKey = *Key\l
      EndIf
      PokeL(*Mem + a, 1)
    EndIf
    *Key + 4
    a + 4
  Wend
  
  ProcedureReturn LastKey
  
  DataSection
    ; Hier alle Tasten angeben, die von der Routine verwalten werden sollen
    UsedKeys:
      Data.l #PB_Key_Up, #PB_Key_Down, #PB_Key_Left, #PB_Key_Right, #PB_Key_Space, #PB_Key_Escape
      Data.l -1
  EndDataSection
EndProcedure

If InitSprite() = 0 : End : EndIf
If OpenWindow(0, 0, 0, 800, 600, #PB_Window_ScreenCentered | #PB_Window_SystemMenu, "Test") = 0 : End : EndIf
If OpenWindowedScreen(WindowID(0), 0, 0, 800, 600,0, 0, 0) = 0 : End : EndIf

x = 400
y = 300
c = $FF0000

Repeat
  ClearScreen(0, 0, 0)
  
  StartDrawing(ScreenOutput())
    Box(x, y, 10, 10, c)
  StopDrawing()
  
  FlipBuffers()
  
  Select GetKeys()
    Case #PB_Key_Up
      y - 1
    Case #PB_Key_Down
      y + 1
    Case #PB_Key_Left
      x - 1
    Case #PB_Key_Right
      x + 1
    Case #PB_Key_Space
      c ! $FF00FF
    Case #PB_Key_Escape
      Break
  EndSelect
ForEver

CloseScreen()
CloseWindow(0)
///Edit:
Die paar Bytes, die eine LinkedList mehr braucht, sind wirklich egal. Und dann beweise mir mal noch, dass Arrays schneller als LinkedLists sind. Arrays sind nur dann schneller, wenn man zufällig auf irgendwelche Elemente zugreifen will. Wenn man allerdings alle Elemente der Reihe nach durchgeht, können LinkedLists auch mal schneller als Arrays sein.
Benutzeravatar
Ynnus
Beiträge: 855
Registriert: 29.08.2004 01:37
Kontaktdaten:

Beitrag von Ynnus »

Ein Array ist in sofern schneller, da die Daten an einem Block im Speicher sind und man sie per Index sehr leicht "anspringen" kann. Eine LinkedList hingegen verweist ja immer auf die Speicherstelle des nächsten bzw. vorherigen Gliedes und auch auf die Daten des derzeitigen Elements. Man muss also wesentlich mehr Speicherstellen anspringen als mit einem Array wo man nur eine Stelle hat und dann +x macht, um nachfolgende Elemente anzusprechen.
Und soweit ich mich recht erinnere, wird der RAM immer etwas großzügiger um die aktuelle Speicherstelle abgelesen. Bei Arrays hätte das den Vorteil, dass man eventuell direkt das ganze Array einließt und verwenden kann. Bei LinkedLists können die Daten ja sonstwo sein, nur nicht in der Nähe des vorherigen Elements. Man kann sie zwar per Pointer flott anspringen, aber auch da muss erstmal die entsprechende Reihe im Arbeitsspeicher gefunden werden, welche per Array bekannt sein müsste, da die Daten ja hintereinander sortiert liegen. Ich kann mir also nicht vorstellen, wei eine LL jemals schneller sein sollte, als ein Array.

Was die paar Byte betrifft, mag ja im Arbeitsspeicher wenig Platz nehmen im Vergleich zu den 1024 MB, es werden aber mehr Bytes "jongliert" pro Frame als mit Array und Konstanten anstelle von Strigns. Wenn er jedesmal einen 6 byte großen String umschreibt, je nach derzeitiger Taste, sind es schon 6 byte die er schreibt. Bei Konstanten nur ein Byte. Nur ein Sechstel der Menge an Daten wird geschrieben. Wenn man das überall vernachlässigt, kann sich das summieren.
Nicht, dass es in diesem Beispiel ein besonderes Vergehen wäre, nur man muss eben überall möglichst optimal programmieren, so denk ich mir das zumindest.
Antworten