Combinaisons de touches sur une entrée basée sur un sondage

9

Supposons donc que vous disposez d'un système d'entrée basé sur l'interrogation

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

Supposons que vous souhaitiez pouvoir reconnaître des combinaisons de touches de 3 à 8 longueurs (comme vers le bas, vers le bas, vers l'avant, A pour hado-ken)

Quelle serait la meilleure façon de créer un système d'entrée de combinaison de touches générique (facilement modifiable / programmable) sur une entrée interrogée ?

bobobobo
la source

Réponses:

7

Fondamentalement, vous ne pouvez pas . Les combinaisons de touches nécessitent une commande et une commande nécessite des événements.

Ce que vous pouvez faire est de transformer l'interrogation en événements en comparant les états clés de chaque image et en générant des événements de keyup / keydown post-hoc à partir des différences. Ce n'est pas aussi fiable parce que vous perdez l'horodatage et les autres commandes intra-trame fournies par les systèmes d'événements "natifs", mais cela vous permettra de détecter et de commander au moins une clé par trame.

Une fois que vous avez ces événements ordonnés, vous pouvez utiliser une machine à états finis ou un autre outil pour les faire correspondre à vos listes de coups et exécuter le bon.


la source
Il est un peu trompeur de dire que vous ne pouvez pas le faire sans événements. Techniquement, les événements ne sont qu'un moyen d'appeler une méthode ou une liste de méthodes lorsqu'une condition est remplie. En ce sens, vous avez besoin d'événements. Cependant, je peux appliquer le mot événements (incorrectement) à de nombreuses tâches de programmation qui correspondent à cette description. J'ai ajouté une réponse qui résout le problème de la façon dont je le fais habituellement, qui n'utilise pas ce que la plupart des programmeurs ont tendance à considérer comme des «événements».
Olhovsky
2
Votre code fait exactement ce que j'ai dit de faire sans événements, mais ignore les mises en garde - à savoir que vous perdez l'ordre intra-trame. Lorsque vous obtenez par exemple des événements de la boucle de message Win32, vous les obtenez dans un ordre avec plus de granularité qu'un glob par trame. Lorsque vous calculez vous-même les deltas, vous perdez cela. Pour un jeu de combat où les joueurs peuvent entrer plusieurs parties d'un combo dans une seule image, vous devez savoir si celles-ci étaient en ordre ou non.
C'est suffisant. À condition que nous sondions à 60 Hz, je doute que nous voulions faire la distinction entre les presses qui sont à moins de 16 ms les unes des autres que le sondage à 60 Hz fournit (en supposant que nos joueurs sont des humains).
Olhovsky
Nous faisons. Les joueurs de jeux de combat qualifiés peuvent entrer une commande de bâton hadouken / shoryuken en moins de trois images, ce qui signifie que nous devons les distinguer.
Ensuite, vous devez interroger plus souvent que 60 Hz.
Olhovsky
3

Une façon consiste à stocker les états d'entrée actuels et précédents et à les comparer chaque fois que vous interrogez l'entrée.

Pour chaque touche qui peut être enfoncée, stockez un objet qui a un horodatage de la dernière fois que la touche est passée d'un état bas à un état haut.

Mettez à jour ces objets en procédant ainsi à chaque sondage:

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

Vous pouvez maintenant faire correspondre vos combinaisons avec le contenu de keys.

Ayez un objet combo pour chaque combo et appelez update () sur chaque objet combo à chaque sondage.

La méthode update () d'un objet combo correspondra au modèle du combo, vérifiant si toutes les conditions nécessaires pour le combo sont remplies lors de ce sondage. C'est-à-dire que tous les horodatages des touches pour le combo jusqu'à présent sont en ordre, et aucune autre touche qui briserait le combo n'a été appuyée sur cette image. Pour chaque condition remplie, incrémentez un compteur dans l'objet combo à la condition suivante à vérifier. Lorsque toutes les conditions sont remplies dans un combo, appelez la méthode que le combo doit effectuer. Si some_key_has_been_pressed == truela touche qui est la condition suivante pour le combo n'a pas été enfoncée, réinitialisez le compteur de conditions satisfaites du combo à 0.

Ce qui précède est ma méthode préférée, car elle est simple à mettre en œuvre, facile à entretenir, efficace et hautement modulaire.

Cependant, pour une autre bonne méthode, consultez l' exemple de séquence d'entrée XNA , qui est écrit en C #, et la logique est probablement transférable dans le langage que vous utilisez.

Olhovsky
la source
Dans la deuxième instruction if if(current_input[key].down){, ne voudriez-vous pas également vérifier si la clé était en place dans l' previous_inputétat? Tel qu'écrit, je pense que cela mettrait à jour en permanence les clés détenues down_timestamp.
webdevkit
La mise à jour continue de l'horodatage d'arrêt est le comportement souhaité. Cela vous permet de comparer les horodateurs up_timestamp et down_timestamp de n'importe quelle clé, pour vérifier la durée pendant laquelle elle a été conservée. Si vous maintenez une touche en continu, vous ne modifiez pas le timbre up_timestamp, donc (down_timestamp - up_timestamp) vous donne toujours la durée pendant laquelle elle a été maintenue.
Olhovsky
Merci pour la clarification. Je pensais le contraire, où up_timestamp - down_timestamp est la durée d'une touche maintenue.
webdevkit
1

Faites une pile des derniers événements de touche X, pour chaque événement de touche, ajoutez un objet avec la touche et l'heure de la pression / relâchement, puis vérifiez si les derniers membres de la pile correspondent à un modèle spécial et si ces touches ont été enfoncées assez rapidement pour compter comme une combinaison. Enfin, retirez l'objet le plus ancien de la pile.

aaaaaaaaaaaa
la source
2
Une pile dont vous pouvez retirer l'objet du bas n'est pas vraiment une pile;)
Olhovsky
Un groupe ordonné d'entités de données, une liste peut être le terme correct.
aaaaaaaaaaaa
deque;) O (1) enq / deq ftw.
michael.bartnett
3
Une séquence ordonnée dans laquelle des objets sont placés à une extrémité et retirés de l'autre s'appelle le plus correctement une file d'attente .
1
Vous pouvez créer un anneau-tampon (BufferLength> = commande la plus longue) et y écrire des clés (Position = Number MOD BufferLength). Aucun ajout / suppression ne sera nécessaire du tout
Kromster