Tastenkombinationen für umfragebasierte Eingaben

9

Angenommen, Sie haben ein Eingabesystem, das auf Abfragen basiert

void update()
{
    if( Keyboard[ 'A' ] )
        // A is down
}

Angenommen, Sie möchten Tastenkombinationen mit einer Länge von 3 bis 8 erkennen können (z. B. nach unten, nach unten, nach vorne, A für Hado-Ken).

Wie würden Sie am besten ein generisches (leicht modifizierbares / programmierbares) Tastenkombinations-Eingabesystem für abgefragte Eingaben erstellen ?

Bobobobo
quelle

Antworten:

7

Grundsätzlich kann man nicht . Schlüsselkombinationen erfordern eine Bestellung, und eine Bestellung erfordert Ereignisse.

Was Sie tun können, ist, Polling in Ereignisse umzuwandeln, indem Sie die Schlüsselzustände jedes Frames vergleichen und aus den Unterschieden Post-hoc-Keyup- / Keydown-Ereignisse generieren. Es ist nicht so zuverlässig, weil Sie die Zeitstempel und andere Intra-Frame-Reihenfolge verlieren, die "native" Ereignissysteme bieten, aber Sie können mindestens einen Schlüssel pro Frame erkennen und bestellen.

Sobald Sie diese geordneten Ereignisse haben, können Sie eine endliche Zustandsmaschine oder ein anderes Werkzeug verwenden, um sie mit Ihren Verschiebungslisten abzugleichen und das richtige auszuführen.


quelle
Es ist ein wenig irreführend zu sagen, dass man dies nicht ohne Ereignisse tun kann . Technisch gesehen sind Ereignisse nur eine Möglichkeit, eine Methode oder eine Liste von Methoden aufzurufen, wenn eine Bedingung erfüllt ist. In diesem Sinne brauchen Sie Ereignisse. Ich kann das Wort Ereignisse jedoch (nicht ordnungsgemäß) auf viele Programmieraufgaben anwenden, die dieser Beschreibung entsprechen. Ich habe eine Antwort hinzugefügt, die das Problem so löst, wie ich es normalerweise mache, und die nicht das verwendet, was die meisten Programmierer als "Ereignisse" betrachten.
Olhovsky
2
Ihr Code macht genau das, was ich gesagt habe, ohne Ereignisse, ignoriert jedoch die Einschränkungen - das heißt, Sie verlieren die Reihenfolge innerhalb des Frames. Wenn Sie Ereignisse beispielsweise aus der Win32-Nachrichtenschleife abrufen, erhalten Sie diese in einer Reihenfolge, die detaillierter ist als ein Glob pro Frame. Wenn Sie Deltas selbst berechnen, verlieren Sie das. Für ein Kampfspiel, bei dem Spieler möglicherweise mehrere Teile einer Combo in einem einzigen Frame eingeben, müssen Sie wissen, ob diese in Ordnung oder nicht in Ordnung waren.
Meinetwegen. Vorausgesetzt, wir wählen mit 60 Hz ab, bezweifle ich, dass wir zwischen Druckmaschinen unterscheiden möchten, die weniger als 16 ms voneinander entfernt sind, die 60 Hz-Umfragen bieten (vorausgesetzt, unsere Spieler sind Menschen).
Olhovsky
Wir tun es. Erfahrene Spieler von Kampfspielen können in weniger als drei Frames einen Hadouken / Shoryuken-Stick-Befehl eingeben, was bedeutet, dass wir zwischen ihnen unterscheiden müssen.
Dann müssen Sie öfter als 60 Hz abfragen.
Olhovsky
3

Eine Möglichkeit besteht darin, den aktuellen und den vorherigen Eingabezustand zu speichern und jedes Mal zu vergleichen, wenn Sie die Eingabe abfragen.

Speichern Sie für jede Taste, die gedrückt werden kann, ein Objekt mit einem Zeitstempel des letzten Umschaltens der Taste von einem Abwärtszustand in einen Aufwärtszustand.

Aktualisieren Sie diese Objekte, indem Sie dies bei jeder Umfrage tun:

void update(){
    some_key_has_been_pressed = false;
    foreach(key in keys){
        if(previous_input[key].Down && current_input[key].Up){
            keys[key].up_timestamp = current_time();
        }

        if(current_input[key].Down){
            keys[key].down_timestamp = current_time();
            some_key_has_been_pressed = true;
        }
    }
}

Jetzt können Sie Ihre Combos mit dem Inhalt von vergleichen keys.

Haben Sie ein Kombinationsobjekt für jede Kombination und rufen Sie bei jeder Abfrage update () für jedes Kombinationsobjekt auf.

Die update () -Methode eines Kombinationsobjekts stimmt mit der Kombination überein und prüft, ob bei dieser Abfrage alle erforderlichen Bedingungen für die Kombination erfüllt sind. Dh alle Tasten-Zeitstempel für die Combo sind bisher in Ordnung, und keine andere Taste, die die Combo brechen würde, wurde in diesem Frame gedrückt. Erhöhen Sie für jede erfüllte Bedingung einen Zähler im Kombinationsobjekt auf die nächste zu überprüfende Bedingung. Wenn alle Bedingungen in einer Kombination erfüllt sind, rufen Sie die Methode auf, die die Kombination ausführen soll. Wenn some_key_has_been_pressed == trueaber die Taste, die die nächste Bedingung für die Kombination ist, nicht gedrückt wurde, setzen Sie den Zähler für die erfüllte Bedingung der Kombination auf 0 zurück.

Das Obige ist meine bevorzugte Methode, da es einfach zu implementieren, leicht zu warten, effizient und hochmodular ist.

Schauen Sie sich für eine andere gute Methode das Beispiel der XNA-Eingabesequenz an , das in C # geschrieben ist, und die Logik ist wahrscheinlich auf die von Ihnen verwendete Sprache übertragbar.

Olhovsky
quelle
Möchten Sie in der zweiten if-Anweisung if(current_input[key].down){nicht auch überprüfen, ob der Schlüssel im previous_inputStatus vorhanden ist? Wie geschrieben, würde es die gehaltenen Schlüssel kontinuierlich aktualisieren down_timestamp.
Webdevkit
Das kontinuierliche Aktualisieren des Ausfallzeitstempels ist das beabsichtigte Verhalten. Auf diese Weise können Sie den up_timestamp und den down_timestamp eines beliebigen Schlüssels vergleichen, um zu überprüfen, wie lange er gehalten wurde. Wenn Sie eine Taste ständig gedrückt halten, ändern Sie den up_timestamp nicht. Daher gibt Ihnen (down_timestamp - up_timestamp) immer noch die Dauer, für die er gehalten wurde.
Olhovsky
Danke für die Klarstellung. Ich dachte das Gegenteil, wobei up_timestamp - down_timestamp die Dauer eines gehaltenen Schlüssels ist.
Webdevkit
1

Erstellen Sie einen Stapel der letzten X-Tastenereignisse. Fügen Sie für jedes Tastenereignis ein Objekt mit der Taste und dem Zeitpunkt der Druck- / Freigabe hinzu. Überprüfen Sie dann, ob die letzten Mitglieder des Stapels einem speziellen Muster entsprechen und ob diese Tasten schnell genug gedrückt wurden zählen als eine Kombination. Entfernen Sie schließlich das älteste Objekt vom Stapel.

aaaaaaaaaaaa
quelle
2
Ein Stapel, von dem Sie das untere Objekt entfernen können, ist nicht wirklich ein Stapel;)
Olhovsky
Eine geordnete Reihe von Datenentitäten, eine Liste könnte der richtige Begriff sein.
aaaaaaaaaaa
deque;) O (1) enq / deq ftw.
michael.bartnett
3
Eine geordnete Sequenz, in der Objekte an einem Ende platziert und vom anderen entfernt werden, wird am besten als Warteschlange bezeichnet .
1
Sie können einen Ringpuffer erstellen (BufferLength> = längster Befehl) und Schlüssel darauf schreiben (Position = Number MOD BufferLength). Es ist überhaupt kein Hinzufügen / Entfernen erforderlich
Kromster