Combinazioni di tasti sull'input basato sul sondaggio


9

Quindi supponi di avere un sistema di input basato sul polling

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

Supponi di voler riconoscere da 3 a 8 combinazioni di tasti di lunghezza (come down, down-forward, forward, A per hado-ken)

Come sarebbe meglio creare un sistema di input di combinazioni di tasti generico (facilmente modificabile / programmabile) su input di polling ?

Risposte:


7

Fondamentalmente, non puoi . Le combinazioni di tasti richiedono un ordine e un ordine richiede eventi.

Quello che puoi fare è trasformare il polling in eventi confrontando gli stati chiave di ciascun frame e generando eventi post-hoc di keyup / keydown dalle differenze. Non è così affidabile perché perdi il timestamp e altri ordini all'interno del frame forniti dai sistemi di eventi "nativi", ma ti permetterà di rilevare e ordinare almeno una chiave per frame.

Una volta che hai quegli eventi ordinati, puoi usare una macchina a stati finiti o un altro strumento per abbinarli agli elenchi di spostamenti ed eseguire quello corretto.


È un po 'fuorviante dire che non puoi farlo senza eventi. Tecnicamente, gli eventi sono solo un modo per chiamare un metodo o un elenco di metodi quando viene soddisfatta una condizione. In tal senso, hai bisogno di eventi. Tuttavia, posso applicare la parola eventi (in modo improprio) a molte attività di programmazione che si adattano a quella descrizione. Ho aggiunto una risposta che risolve il problema nel modo in cui lo faccio di solito, che non utilizza ciò che la maggior parte dei programmatori tende a pensare come "eventi".
Olhovsky,

2
Il tuo codice fa esattamente quello che ti ho detto di fare senza eventi, ma ignora le avvertenze - che è che perdi l'ordinamento all'interno del frame. Quando si ottengono eventi, ad esempio dal ciclo di messaggi Win32, li si ottiene in un ordine con una granularità maggiore di un glob per frame. Quando calcoli delta te stesso, lo perdi. Per un gioco di combattimento in cui i giocatori possono inserire diverse parti di una combo in un singolo fotogramma, devi sapere se quelle erano in ordine o fuori servizio.

Giusto. A condizione che stiamo sondando a 60Hz, dubito che vogliamo distinguere tra macchine da stampa che sono a meno di 16 ms di distanza che il sondaggio a 60 Hz fornisce (supponendo che i nostri giocatori siano umani).
Olhovsky,

Noi facciamo. I giocatori esperti di giochi di combattimento possono inserire un comando di stick di hadouken / shoryuken in meno di tre fotogrammi, il che significa che dobbiamo distinguerli.

Quindi è necessario eseguire il polling più spesso di 60Hz.
Olhovsky,

3

Un modo è memorizzare gli stati di input attuali e precedenti e confrontarli ogni volta che si esegue il polling dell'input.

Per ogni tasto che può essere premuto, memorizza un oggetto che ha un timestamp dell'ultima volta in cui il tasto è passato da uno stato inattivo a uno in alto.

Aggiorna questi oggetti facendo questo ad ogni sondaggio:

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

Ora puoi abbinare le tue combo con il contenuto di keys.

Disponi un oggetto combo per ogni combo e chiama update () su ciascun oggetto combo in ogni sondaggio.

Il metodo update () di un oggetto combo corrisponderà al combo, verificando che in questo sondaggio siano soddisfatte tutte le condizioni necessarie per il combo. Vale a dire che tutti i timestamp dei tasti per la combo finora sono in ordine, e nessun altro tasto che spezzerebbe la combo è stato premuto in questo frame. Per ogni condizione soddisfatta, incrementare un contatore nell'oggetto combo alla condizione successiva da verificare. Quando tutte le condizioni sono soddisfatte in una combo, chiamare il metodo che la combo dovrebbe eseguire. Se some_key_has_been_pressed == truema il tasto che è la condizione successiva per la combo non è stato premuto, reimposta il contatore delle condizioni soddisfatte della combo su 0.

Quanto sopra è il mio metodo preferito, in quanto è semplice da implementare, facile da mantenere, efficiente e altamente modulare.

Tuttavia, per un altro buon metodo, controlla l' esempio della sequenza di input XNA , che è scritto in C #, e la logica è probabilmente trasferibile nella lingua che stai usando.


Nella seconda istruzione if if(current_input[key].down){, non vorresti anche controllare se la chiave era nello previous_inputstato? Come scritto, penso che aggiornerebbe continuamente una chiave in attesa down_timestamp.
webdevkit,

L'aggiornamento continuo del timestamp è il comportamento previsto. Ciò consente di confrontare up_timestamp e down_timestamp di qualsiasi chiave, per verificare per quanto tempo è stato tenuto. Se tieni continuamente premuto un tasto, non stai modificando up_timestamp, quindi (down_timestamp - up_timestamp) ti dà ancora la durata per cui è stato tenuto.
Olhovsky

grazie per il chiarimento. Stavo pensando il contrario, dove up_timestamp - down_timestamp è la durata di una chiave trattenuta.
webdevkit,

1

Crea una pila degli ultimi eventi chiave X, per ogni evento chiave aggiungi un oggetto con la chiave e l'ora della stampa / rilascio, quindi controlla se gli ultimi membri della pila corrispondono a un modello speciale e se quei tasti sono stati premuti abbastanza velocemente da conta come una combinazione. Rimuovere infine l'oggetto più vecchio dallo stack.


2
Una pila da cui puoi rimuovere l'oggetto in basso non è proprio una pila;)
Olhovsky

Un gruppo ordinato di entità dati, un elenco potrebbe essere il termine corretto.
aaaaaaaaaaaa

deque;) O (1) enq / deq ftw.
michael.bartnett,

3
Una sequenza ordinata in cui gli oggetti vengono posizionati su un'estremità e rimossi dall'altra è più propriamente chiamata coda .

1
Puoi creare un ring-buffer (BufferLength> = comando più lungo) e scrivere le chiavi su di esso (Position = Number MOD BufferLength). Nessuna aggiunta / rimozione sarà richiesta affatto
Kromster
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.