Nur den zuletztgedrückten Button benutzen
Nur den zuletztgedrückten Button benutzen
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
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 ^^
--------------------------
--------------------------
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.
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.
Ich habs bei Gloomy Nights damals anders gemacht. Ich frage die Tasten ab und gebe je nachdem dann die Richtung an.
Beispiel:
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
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.
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

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.
@ 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.
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.
ok ich habs mal versucht un folgendes hinbekommen:
Funktioniert soweit ich weis.
Na dann cYa!
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)
Na dann cYa!
Mfg euer wutschel ^^
--------------------------
--------------------------
Hier würd ich noch optimieren und keine Strings nutzen. Das braucht nicht nur mehr Speicher sondern ist auch Fehleranfälliger.Richtungen(0) = "Left"
Richtungen(1) = "Down"
Richtungen(2) = "Right"
Richtungen(3) = "Up"
Richtungen(4) = "None"
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.
- 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
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.
///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.
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)
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.
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.
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.