Kombinacje klawiszy w danych wejściowych opartych na ankiecie

9

Załóżmy, że masz system wprowadzania oparty na odpytywaniu

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

Powiedz, że chcesz być w stanie rozpoznać kombinacje klawiszy o długości od 3 do 8 (np. Dół, dół-przód, przód, A dla hado-ken)

Jak najlepiej stworzyć ogólny (łatwy do modyfikacji / programowalny) system wprowadzania kombinacji klawiszy na zapytaniu wejściowym?

Bobobobo
źródło

Odpowiedzi:

7

Zasadniczo nie możesz . Kombinacje klawiszy wymagają zamówienia, a zamówienie wymaga zdarzeń.

To, co możesz zrobić, to przekształcić odpytywanie w zdarzenia, porównując stany kluczy w każdej ramce i generując post-hoc zdarzenia z kluczowania / keydown z różnic. Nie jest tak niezawodny, ponieważ tracisz znaczniki czasu i inne porządkowanie wewnątrz ramki, które zapewniają „natywne” systemy zdarzeń, ale pozwoli ci wykryć i zamówić co najmniej jeden klucz na ramkę.

Po uzyskaniu uporządkowanych zdarzeń można użyć maszyny stanów skończonych lub innego narzędzia, aby dopasować je do list ruchów i wykonać prawidłowe.


źródło
Trochę mylące jest stwierdzenie, że nie da się tego zrobić bez wydarzeń. Z technicznego punktu widzenia zdarzenia są tylko sposobem na wywołanie metody lub listy metod, gdy spełniony jest pewien warunek. W tym sensie potrzebujesz wydarzeń. Mogę jednak zastosować słowa zdarzenia (nieprawidłowo) do wielu zadań programistycznych, które pasują do tego opisu. Dodałem odpowiedź, która rozwiązuje problem w sposób, w jaki zwykle to robię, co nie wykorzystuje tego, co większość programistów nazywa „zdarzeniami”.
Olhovsky
2
Twój kod działa dokładnie tak, jak powiedziałem, bez zdarzeń, ale ignoruje zastrzeżenia - to znaczy, że tracisz porządkowanie w ramce. Gdy otrzymujesz zdarzenia np. Z pętli komunikatów Win32, otrzymujesz je w kolejności o większej szczegółowości niż glob na ramkę. Kiedy sam obliczasz delty, tracisz to. W przypadku gry walki, w której gracze mogą wprowadzać kilka części kombinacji w jednej ramce, musisz wiedzieć, czy były one w porządku, czy nie.
Słusznie. Pod warunkiem, że odpytujemy w częstotliwości 60 Hz, wątpię, abyśmy chcieli rozróżnić prasy, które są krótsze niż 16 ms od siebie, co zapewnia odpytywanie 60 Hz (zakładając, że naszymi graczami są ludzie).
Olhovsky
My robimy. Zręcznie walczący gracze mogą wprowadzić polecenie kija hadouken / shoryuken w mniej niż trzech klatkach, co oznacza, że ​​musimy je rozróżnić.
Następnie musisz sondować częściej niż 60 Hz.
Olhovsky
3

Jednym ze sposobów jest przechowywanie obecnych i poprzednich stanów wejściowych i porównywanie ich za każdym razem, gdy sondujesz dane wejściowe.

Dla każdego klawisza, który można nacisnąć, przechowuj obiekt, który ma znacznik czasu ostatniej zmiany tego klawisza ze stanu obniżonego na wyższy.

Zaktualizuj te obiekty, wykonując to przy każdej ankiecie:

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;
        }
    }
}

Teraz możesz dopasować wzór do swoich kombinacji z zawartością keys.

Mają obiekt kombi dla każdej kombinacji i wywołują aktualizację () na każdym obiekcie kombinacji przy każdej ankiecie.

Metoda update () obiektu combo dopasuje wzór do zestawu, sprawdzając, czy podczas tej ankiety spełnione są wszystkie niezbędne warunki dla zestawu. Oznacza to, że dotychczasowe znaczniki czasowe klawiszy dla kombinacji są w porządku i żaden inny klawisz, który złamałby kombinację nie został wciśnięty w tej ramce. Dla każdego spełnionego warunku zwiększ licznik w obiekcie kombi do następnego warunku do sprawdzenia. Gdy wszystkie warunki zostaną spełnione w kombinacji, wywołaj metodę, którą kombinacja powinna wykonać. Jeśli some_key_has_been_pressed == truenie został naciśnięty klawisz będący kolejnym warunkiem dla kombinacji, zresetuj licznik spełnionego warunku dla kombinacji na 0.

Powyższe jest moją preferowaną metodą, ponieważ jest łatwe do wdrożenia, łatwe w utrzymaniu, wydajne i wysoce modułowe.

Jednak w przypadku innej dobrej metody sprawdź próbkę sekwencji wejściowej XNA , która jest napisana w języku C #, a logika jest prawdopodobnie możliwa do przeniesienia do używanego języka.

Olhovsky
źródło
Czy w drugiej instrukcji if if(current_input[key].down){nie chciałbyś również sprawdzić, czy klucz był w previous_inputstanie? Jak napisano, myślę, że ciągle aktualizuje trzymany klucz down_timestamp.
webdevkit,
Ciągłe aktualizowanie znacznika czasu przestoju jest zamierzonym zachowaniem. Pozwala to na porównanie up_timestamp i down_timestamp dowolnego klucza, aby sprawdzić, jak długo był trzymany. Jeśli ciągle trzymasz klucz, nie zmieniasz znacznika czasu, więc (znacznik czasu - znacznik czasu) nadal podaje czas, przez który był trzymany.
Olhovsky,
Dziękuję za wyjaśnienie. Myślałem przeciwnie, gdzie up_timestamp - down_timestamp to czas trwania przytrzymanego klawisza.
webdevkit,
1

Zrób stos ostatnich X zdarzeń kluczowych, dla każdego zdarzenia kluczowego dodaj obiekt z kluczem i czasem naciśnięcia / zwolnienia, a następnie sprawdź, czy ostatnie elementy stosu pasują do specjalnego wzorca i czy te klawisze zostały naciśnięte wystarczająco szybko, aby liczą się jako kombinacja. Na koniec usuń najstarszy obiekt ze stosu.

aaaaaaaaaaaa
źródło
2
Stos, z którego można usunąć dolny obiekt, nie jest tak naprawdę stosem;)
Olhovsky
Uporządkowana wiązka jednostek danych, lista może być poprawnym terminem.
aaaaaaaaaaaa
deque;) O (1) enq / deq ftw.
michael.bartnett
3
Uporządkowana sekwencja, w której obiekty są umieszczane na jednym końcu i usuwane z drugiego, najlepiej jest nazywana kolejką .
1
Możesz utworzyć bufor pierścieniowy (BufferLength> = najdłuższe polecenie) i zapisać do niego klucze (Position = Number MOD BufferLength). W ogóle nie będzie wymagane dodawanie / usuwanie
Kromster