Combinaciones de teclas en entradas basadas en encuestas

9

Supongamos que tiene un sistema de entrada basado en encuestas

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

Digamos que quiere poder reconocer combinaciones de teclas de 3 a 8 longitudes (como abajo, abajo, adelante, adelante, A para hado-ken)

¿Cómo crearía mejor un sistema de entrada de combinación de teclas genérico (fácilmente modificable / programable) en la entrada encuestada ?

bobobobo
fuente

Respuestas:

7

Básicamente, no puedes . Las combinaciones de teclas requieren un pedido, y un pedido requiere eventos.

Lo que puede hacer es convertir el sondeo en eventos comparando los estados clave de cada cuadro y generando eventos de activación / desactivación de teclas post-hoc a partir de las diferencias. No es tan confiable porque pierde la marca de tiempo y otros pedidos dentro del cuadro que proporcionan los sistemas de eventos "nativos", pero le permitirá detectar y ordenar al menos una clave por cuadro.

Una vez que tenga esos eventos ordenados, puede usar una máquina de estados finitos u otra herramienta para compararlos con sus listas de movimientos y ejecutar la correcta.


fuente
Es un poco engañoso decir que no se puede hacer esto sin eventos. Técnicamente, los eventos son solo una forma de llamar a un método o una lista de métodos cuando se cumple alguna condición. En ese sentido, necesitas eventos. Sin embargo, puedo aplicar la palabra eventos (incorrectamente) a muchas tareas de programación que se ajustan a esa descripción. Agregué una respuesta que resuelve el problema de la manera en que lo hago habitualmente, que no utiliza lo que la mayoría de los programadores tienden a pensar como "eventos".
Olhovsky
2
Su código hace exactamente lo que dije que hiciera sin eventos, pero ignora las advertencias, que es que pierde el orden dentro del marco. Cuando obtiene eventos de, por ejemplo, el bucle de mensajes de Win32, los obtiene en un orden con más granularidad que un globo por cuadro. Cuando calcula usted mismo los deltas, lo pierde. Para un juego de lucha en el que los jugadores pueden ingresar varias partes de un combo en un solo cuadro, debes saber si estaban dentro o fuera de orden.
Lo suficientemente justo. Siempre que realicemos encuestas a 60 Hz, dudo que deseemos distinguir entre las prensas que están a menos de 16 ms de distancia que proporciona el sondeo de 60 Hz (suponiendo que nuestros jugadores sean humanos).
Olhovsky
Hacemos. Los jugadores expertos en juegos de lucha pueden ingresar un comando de palo hadouken / shoryuken en menos de tres cuadros, lo que significa que debemos distinguir entre ellos.
Entonces necesita sondear con más frecuencia que 60Hz.
Olhovsky
3

Una forma es almacenar los estados de entrada actuales y anteriores, y compararlos cada vez que sondea la entrada.

Para cada tecla que se puede presionar, almacene un objeto que tenga una marca de tiempo de la última vez que la tecla cambió de un estado inactivo a un estado activo.

Actualice estos objetos haciendo esto en cada encuesta:

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

Ahora puedes hacer coincidir tus combos con el contenido de keys.

Tenga un objeto combinado para cada combinación y llame a update () en cada objeto combinado en cada encuesta.

El método update () de un objeto combo coincidirá con el combo, verificando si se cumplen todas las condiciones necesarias para el combo en esta encuesta. Es decir, todas las marcas de tiempo de las teclas para el combo hasta ahora están en orden, y ninguna otra tecla que rompa el combo se ha presionado este marco. Para cada condición cumplida, incremente un contador en el objeto combinado a la siguiente condición para verificar. Cuando se cumplen todas las condiciones en un combo, llame al método que debe realizar el combo. Si some_key_has_been_pressed == trueno se ha presionado la tecla que es la siguiente condición para el combo, restablezca el contador de condición satisfecha del combo a 0.

Lo anterior es mi método preferido, ya que es simple de implementar, fácil de mantener, eficiente y altamente modular.

Sin embargo, para otro buen método, consulte la muestra de secuencia de entrada XNA , que está escrita en C #, y es probable que la lógica sea transferible al lenguaje que está utilizando.

Olhovsky
fuente
En la segunda declaración if if(current_input[key].down){, ¿no querrías verificar si la clave estaba activa en el previous_inputestado? Tal como está escrito, creo que actualizaría continuamente una clave retenida down_timestamp.
webdevkit
La actualización continua de la marca de tiempo de inactividad es el comportamiento previsto. Esto le permite comparar el up_timestamp y down_timestamp de cualquier clave, para verificar cuánto tiempo se mantuvo. Si mantiene presionada una tecla de forma continua, no está cambiando la marca de tiempo superior, por lo que (marca de tiempo inferior - marca de tiempo superior) aún le proporciona la duración durante la que se mantuvo.
Olhovsky
gracias por la aclaración. Estaba pensando lo contrario, donde up_timestamp - down_timestamp es la duración de una tecla retenida.
webdevkit
1

Haga una pila de los últimos eventos clave X, para cada evento clave agregue un objeto con la tecla y la hora de la pulsación / liberación, luego verifique si los últimos miembros de la pila coinciden con un patrón especial y si esas teclas se presionaron lo suficientemente rápido como para contar como una combinación Finalmente, retire el objeto más antiguo de la pila.

aaaaaaaaaaaa
fuente
2
Una pila de la que puedes quitar el objeto inferior no es realmente una pila;)
Olhovsky
Un grupo ordenado de entidades de datos, una lista podría ser el término correcto.
aaaaaaaaaaaa
deque;) O (1) enq / deq ftw.
michael.bartnett 05 de
3
Una secuencia ordenada en la que los objetos se colocan en un extremo y se eliminan del otro se denomina más correctamente cola .
1
Puede hacer un anillo de búfer (BufferLength> = comando más largo) y escribirle claves (Posición = Número MOD BufferLength). No será necesario agregar / eliminar en absoluto
Kromster