Gestione del sistema di input da tastiera


13

Nota: devo effettuare il polling, piuttosto che effettuare callback a causa delle limitazioni API (SFML). Mi scuso anche per la mancanza di un titolo "decente".

Penso di avere due domande qui; come registrare l'input che sto ricevendo e cosa farne.

Gestione dell'input

Sto parlando dopo il fatto che ti sei registrato che il tasto 'A' è stato premuto, ad esempio, e come farlo da lì.

Ho visto una serie di tutta la tastiera, qualcosa del tipo:

bool keyboard[256]; //And each input loop check the state of every key on the keyboard

Ma questo sembra inefficiente. Non solo stai accoppiando il tasto 'A' al 'giocatore che si sposta a sinistra', ad esempio, ma controlla ogni tasto, 30-60 volte al secondo.

Ho quindi provato un altro sistema che cercava le chiavi che voleva.

std::map< unsigned char, Key> keyMap; //Key stores the keycode, and whether it's been pressed. Then, I declare a load of const unsigned char called 'Quit' or 'PlayerLeft'.
input->BindKey(Keys::PlayerLeft, KeyCode::A); //so now you can check if PlayerLeft, rather than if A.

Tuttavia, il problema è che non posso ora digitare un nome, ad esempio, senza dover associare ogni singola chiave.

Quindi, ho il secondo problema, che non riesco davvero a pensare a una buona soluzione per:

Invio input

Ora so che è stato premuto il tasto A o che playerLeft è vero. Ma come posso andare da qui?

Ho pensato di controllare if(input->IsKeyDown(Key::PlayerLeft) { player.MoveLeft(); }
Questo accoppia in modo significativo l'input alle entità e lo trovo piuttosto disordinato. Preferirei che il giocatore gestisse il proprio movimento quando viene aggiornato. Pensavo che un qualche tipo di sistema di eventi potesse funzionare, ma non so come seguirlo. (Ho sentito i segnali e le slot erano buone per questo tipo di lavoro, ma apparentemente è molto lento e non riesco a vedere come si adatterebbe).

Grazie.

Risposte:


7

Quello che farei è usare il modello di osservatore e avere una classe di input che mantiene un elenco di callback o oggetti di gestione dell'input. Altri oggetti possono registrarsi con il sistema di input per essere avvisati quando accadono determinate cose. Esistono diversi tipi di richiamate che è possibile registrare, in base al tipo di eventi di input di cui gli osservatori desiderano essere informati. Potrebbe trattarsi di un livello basso come "tasto x è giù / su" o più specifico del gioco come "si è verificato un evento di salto / tiro / movimento".

Generalmente gli oggetti di gioco hanno un componente di gestione dell'input che è responsabile della registrazione stessa con la classe di input e fornisce metodi per gestire gli eventi a cui è interessato. Per il movimento, ho un componente del motore di input che ha un metodo move (x, y). Si registra con il sistema di input e viene informato sugli eventi di movimento (dove xey sarebbero -1 e 0 per indicare lo spostamento a sinistra), il componente mover cambia quindi le coordinate dell'oggetto di gioco principale in base alla direzione del movimento e alla velocità dell'oggetto.

Non so se questa sia una buona soluzione, ma finora funziona per me. In particolare, mi consente di fornire diversi tipi di sistema di input che si associano agli stessi eventi di gioco logici in modo che il pulsante gamepad x e lo spazio della tastiera generino entrambi un evento di salto, ad esempio (è un po 'più complicato di così, consentendo all'utente di riassegnare la chiave mappature).


Questo sicuramente mi interessa; il componente di input si registrerebbe per determinati eventi (qualcosa come playerInput registra 'left', 'right', ecc.) o ogni componente di input registrato riceverà tutti i messaggi?
Il comunista Duck il

Dipende davvero dalle tue esigenze e da come ti piace implementarlo. Nel mio caso, gli ascoltatori si registrano per eventi specifici e implementano l'interfaccia del gestore di input appropriata. Avere ogni componente di input che riceve tutti i messaggi dovrebbe funzionare, ma il formato del messaggio deve essere più generico di quello che ho.
Firas Assaad,

6

Penso che la discussione del PO stia mettendo insieme un sacco di cose e che il problema possa essere sostanzialmente semplificato spezzandolo un po '.

Il mio consiglio:

Mantieni la tua matrice di stati delle chiavi. Non c'è una chiamata API per ottenere l'intero stato della tastiera contemporaneamente? (La maggior parte del mio lavoro è su console e anche su Windows per un controller collegato ottieni l'intero stato del controller in una sola volta.) I tuoi stati dei tasti sono una mappa "dura" sui tasti della tastiera. Non tradotto è il migliore, ma comunque lo si ottiene più facilmente dalle API.

Quindi mantieni alcune matrici parallele che indicano se la chiave è salita in questo frame, un'altra indica che è scesa e una terza per indicare semplicemente che la chiave è "giù". Magari lanciare un timer in modo da poter tenere traccia di quanto tempo la chiave è stata nel suo stato attuale.

Quindi creare un metodo per creare una mappatura delle chiavi per le azioni. Ti consigliamo di farlo un paio di volte. Una volta per roba dell'interfaccia utente (e fai attenzione a interrogare i mapping di Windows perché non puoi assumere lo scancode quando premi "A" per dare una A sul computer dell'utente). Un altro per ogni "modalità" di gioco. Questi sono i mapping dei codici chiave alle azioni. Puoi farlo in un paio di modi, uno sarebbe mantenere un enum che viene utilizzato dal sistema di ricezione, un altro sarebbe un puntatore a funzione che indica la funzione da chiamare su un cambio di stato. Forse anche un ibrido dei due, a seconda dei tuoi obiettivi e delle tue esigenze. Con queste "modalità" è possibile spingerle e inserirle in uno stack in modalità controller, con flag che consentono agli input ignorati di continuare a scendere nello stack o altro.

Infine, gestisci le azioni chiave in qualche modo. Per il movimento potresti voler fare qualcosa di complicato, tradurre 'W' in basso per significare "andare avanti", ma non deve essere una soluzione binaria; puoi avere "W è giù" significa "aumenta la velocità di X fino a un massimo di Y" e "W è su" per essere "diminuisci la velocità di Z fino a raggiungere zero". In generale, vuoi che la tua "interfaccia di gestione dei controller nei sistemi di gioco" sia piuttosto stretta; fai tutte le traduzioni chiave in un unico posto e poi usa i risultati di tutto il resto. Ciò è in contrasto con il controllo diretto per vedere se la barra spaziatrice viene premuta in qualsiasi punto casuale nel codice, perché se si trova in un punto casuale verrà probabilmente colpito in un momento casuale quando non lo vuoi, e semplicemente non non voglio occuparmene ...

Detesto davvero la progettazione di modelli e penso che i componenti comportino più costi di sviluppo che benefici, quindi è per questo che non ho menzionato nessuno dei due. I modelli emergeranno se sono destinati a farlo, così come i componenti, ma partire per fare fin dall'inizio complica solo la tua vita.


Non pensi che sia un po 'sporco legare gli eventi ai frame grafici? Penso che dovrebbe essere separato.
Jo So,

Voglio dire, capisco che in quasi tutti i casi il lettore sarà più lento nell'input dei pulsanti rispetto alla scheda grafica emetterà frame, ma in realtà dovrebbero essere indipendenti, e in effetti sono per altri tipi di eventi, come quelli raccolti presi dalla rete .
Jo So,

Infatti; non c'è motivo per cui il polling della tastiera (o di un altro dispositivo di input) non possa essere eseguito a una velocità diversa dal display. In effetti, se si esegue il polling degli input a 100Hz, è possibile fornire un'esperienza di controllo utente davvero coerente indipendentemente dalla frequenza di aggiornamento dell'hardware video. Dovrai essere un po 'più intelligente su come gestisci i tuoi dati (per quanto riguarda il latching ecc.) Ma renderà le cose come i gesti molto più facili da rendere coerenti.
dash-tom-bang,

3

In SFML, hai i tasti nell'ordine in cui sono stati premuti con il tipo di evento KeyPressed. Ogni chiave viene elaborata in un attimo (getNextEvent ()).

Quindi, se il tasto premuto si trova nella mappa Chiave-> Azione, esegui l'azione corrispondente e, in caso contrario, inoltralo a qualsiasi widget / oggetto che potrebbe averne bisogno.

Per quanto riguarda la tua seconda domanda, ti consiglio di tenerlo così perché semplifica la modifica del tuo gameplay. Se usi segnali o simili, dovrai creare uno slot per "RunKeyDown" e un altro per "JumpKeySlot". Ma cosa succede se nel tuo gioco viene eseguita un'azione speciale quando vengono premuti entrambi?

Se si desidera decorrelare input ed entità, è possibile rendere uno input uno stato (RunKey = true, FireKey = false, ...) e inviarlo al giocatore come se si inviasse qualsiasi altro evento ai propri AI.


Non sto gestendo eventi da tastiera con sf :: Event, piuttosto sf :: Input, perché è il rilevamento in tempo reale IIRC. Ho cercato di mantenerlo il più agnostico possibile nelle API. : P (La domanda, cioè)
L'anatra comunista il

1
Ma penso che il punto che Calvin1602 sta facendo sia che la tua affermazione originale nella domanda "Devo fare il polling, piuttosto che fare callback a causa delle limitazioni API (SFML)" non è vera; hai eventi, quindi puoi emettere una richiamata quando gestisci un evento.
Kylotan,

3

La soluzione corretta è spesso una combinazione dei due metodi discussi:

Per la funzionalità di gioco con un set predefinito di chiavi, il polling di ogni fotogramma è completamente appropriato e dovresti eseguire il polling solo per i tasti che sono effettivamente associati a qualcosa. Questo è in realtà analogo al modo in cui interrogheresti l'input del controller in un gioco per console, sarà completamente polling soprattutto quando si tratta di dispositivi di input analogici. Durante il gameplay generale probabilmente vorrai ignorare gli eventi di pressione dei tasti e usare semplicemente il polling.

Quando si tratta di digitare input per cose come i nomi, è necessario passare a una modalità di gestione degli input diversa. Ad esempio, non appena viene visualizzata la richiesta "inserisci il tuo nome", puoi modificare ciò che fa il codice di input. Può ascoltare gli eventi di pressione dei tasti tramite qualche interfaccia di richiamata, oppure è possibile avviare il polling per ciascun tasto, se necessario. Si scopre che in genere durante gli eventi di digitazione non ti importa molto delle prestazioni, quindi il polling non sarà poi così male.

Pertanto, si desidera utilizzare il polling selettivo per l'input di gioco e la gestione degli eventi per gli input di digitazione dei nomi (o tutto il polling della tastiera se la gestione degli eventi non è disponibile).


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.