Manejo del sistema de entrada de teclado


13

Nota: Tengo que sondear, en lugar de hacer devoluciones de llamada debido a limitaciones de API (SFML). También me disculpo por la falta de un título 'decente'.

Creo que tengo dos preguntas aquí; cómo registrar la entrada que estoy recibiendo y qué hacer con ella.

Manejo de entrada

Me refiero después del hecho de que haya registrado que se ha presionado la tecla 'A', por ejemplo, y cómo hacerlo desde allí.

He visto una variedad de todo el teclado, algo así como:

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

Pero esto parece ineficiente. Por ejemplo, no solo está acoplando la tecla 'A' al 'jugador que se mueve hacia la izquierda', sino que verifica cada tecla, 30-60 veces por segundo.

Luego probé otro sistema que solo buscaba las claves que quería.

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.

Sin embargo, el problema con esto es que ahora no puedo escribir un nombre, por ejemplo, sin tener que vincular cada clave.

Entonces, tengo el segundo problema, que realmente no puedo pensar en una buena solución para:

Enviando entrada

Ahora sé que se ha presionado la tecla A o que el jugador Izquierdo es verdadero. ¿Pero cómo voy desde aquí?

Pensé en simplemente marcar if(input->IsKeyDown(Key::PlayerLeft) { player.MoveLeft(); }
Esto acopla la entrada a las entidades, y me parece bastante desordenado. Prefiero que el jugador maneje su propio movimiento cuando se actualiza. Pensé que algún tipo de sistema de eventos podría funcionar, pero no sé cómo hacerlo. (Escuché señales y las ranuras eran buenas para este tipo de trabajo, pero aparentemente es muy lento y no puedo ver cómo encajaría).

Gracias.

Respuestas:


7

Lo que haría es usar el patrón de observador y tener una clase de entrada que mantenga una lista de devoluciones de llamada u objetos de manejo de entrada. Otros objetos pueden registrarse en el sistema de entrada para ser notificados cuando suceden ciertas cosas. Hay diferentes tipos de devoluciones de llamada que puede registrar, en función del tipo de eventos de entrada sobre los que los observadores desean recibir notificaciones. Esto podría ser tan bajo como 'la tecla x está abajo / arriba' o más específico del juego como 'ocurrió un evento de salto / disparo / movimiento'.

En general, los objetos del juego tienen un componente de manejo de entrada que es responsable de registrarse con la clase de entrada y proporcionar métodos para manejar los eventos que le interesan. Para el movimiento, tengo un componente de movimiento de entrada que tiene un método de movimiento (x, y). Se registra a sí mismo con el sistema de entrada y recibe notificaciones sobre eventos de movimiento (donde x e y serían -1 y 0 para indicar el movimiento hacia la izquierda), el componente de movimiento cambia sus coordenadas del objeto del juego principal en función de la dirección del movimiento y la velocidad del objeto.

No sé si esta es una buena solución, pero hasta ahora me funciona. En particular, me permite proporcionar diferentes tipos de sistema de entrada que se asignan a los mismos eventos del juego lógico, de modo que el botón x del juego y el espacio del teclado generan un evento de salto, por ejemplo (es un poco más complicado que eso, lo que permite al usuario reasignar la tecla mapeos).


Esto definitivamente me interesa; ¿El componente de entrada se registraría para ciertos eventos (algo como playerInput registra 'izquierda', 'derecha', etc.) o todos los componentes de entrada que están registrados recibirán todos los mensajes?
El pato comunista

Realmente depende de su requerimiento y de cómo le gusta implementarlo. En mi caso, los oyentes se registran para eventos específicos e implementan la interfaz de manejador de entrada apropiada. Hacer que todos los componentes de entrada reciban todos los mensajes debería funcionar, pero el formato del mensaje debe ser más genérico que el que tengo.
Firas Assaad

6

Creo que la discusión del OP está agrupando un montón de cosas, y que el problema puede simplificarse sustancialmente dividiéndolo un poco.

Mi sugerencia:

Mantenga su conjunto de estados de claves. ¿No hay una llamada API para obtener todo el estado del teclado a la vez? (La mayor parte de mi trabajo es en consolas, e incluso en Windows para un controlador conectado, obtienes todo el estado del controlador a la vez). Tus estados clave son un mapa "duro" en las teclas del teclado. Sin traducir es lo mejor, pero sin embargo, lo más fácil es sacarlo de las API.

Luego, mantenga unos pocos arreglos paralelos que indiquen si la clave subió en este marco, otro indicando que bajó y un tercero para indicar simplemente que la clave está "abajo". Quizás agregue un temporizador para que pueda realizar un seguimiento de cuánto tiempo ha estado la clave en su estado actual.

A continuación, cree un método para crear una asignación de teclas a acciones. Querrás hacer esto un par de veces. Una vez para las cosas de la interfaz de usuario (y tenga cuidado de consultar las asignaciones de Windows porque no puede asumir que el código de escaneo cuando presiona 'A' le dará una A en la computadora del usuario). Otro para cada "modo" en el juego. Estas son las asignaciones de códigos clave a acciones. Puede hacer esto de dos maneras, una sería mantener una enumeración que utiliza el sistema receptor, otra sería un puntero de función que indica la función que se llamará en un cambio de estado. Tal vez incluso un híbrido de los dos, dependiendo de sus objetivos y necesidades. Con estos "modos" puede empujarlos y desplegarlos en una pila de modo de controlador, con banderas que permiten que la entrada ignorada siga bajando por la pila o lo que sea.

Finalmente, maneje esas acciones clave de alguna manera. Para el movimiento es posible que desee hacer algo complicado, traduzca 'W' hacia abajo para que signifique "avanzar", pero no es necesario que sea una solución binaria; puede hacer que "W esté abajo" significa "aumentar la velocidad en X hasta un máximo de Y" y "W está arriba" para ser "disminuir la velocidad en Z hasta que llegue a cero". En términos generales, desea que su "interfaz de manejo del controlador en los sistemas de juego" sea bastante estrecha; haga todas las traducciones clave en un solo lugar y luego use los resultados de eso en cualquier otro lugar. Esto está en contraste con la comprobación directa para ver si la barra espaciadora se presiona en cualquier lugar al azar en el código, porque si está en algún lugar aleatorio, probablemente será golpeado en algún momento aleatorio cuando no lo desee, y simplemente no lo haga ' no quiero lidiar con eso ...

Realmente detesto el diseño de patrones y creo que los componentes aportan más costos de desarrollo que buenos, por eso no mencioné ninguno. Los patrones surgirán si están destinados a hacerlo, al igual que los componentes, pero establecerlo desde el principio solo complicará su vida.


¿No crees que es un poco sucio vincular eventos a marcos gráficos? Creo que eso debería estar separado.
Jo So

Quiero decir, entiendo que en casi todos los casos, el reproductor será más lento para ingresar botones que la tarjeta gráfica emitirá marcos, pero en realidad deberían ser independientes, y de hecho son para otros tipos de eventos, como los que se obtienen de la red .
Jo So

En efecto; No hay ninguna razón por la cual el sondeo del teclado (u otro dispositivo de entrada) no se pueda ejecutar a una velocidad diferente de la pantalla. De hecho, si sondea las entradas a 100Hz, puede proporcionar una experiencia de control de usuario realmente consistente, independientemente de la frecuencia de actualización del hardware de video. Tendrá que ser un poco más inteligente acerca de cómo maneja sus datos (con respecto al enganche, etc.), pero hará que cosas como los gestos sean mucho más fáciles de hacer consistentes.
dash-tom-bang

3

En SFML, tiene las teclas en el orden en que se presionaron con el tipo de evento KeyPressed. Usted procesa cada clave en un momento (getNextEvent ()).

Entonces, si la tecla presionada está en su mapa de Teclas-> Acción, ejecute la acción correspondiente, y si no es así, reenvíela a cualquier widget / material que pueda necesitarla.

Con respecto a tu segunda pregunta, te recomiendo que la mantengas así porque hace que sea más fácil modificar tu juego. Si usa señales o algo así, tendrá que hacer una ranura para "RunKeyDown" y otra para "JumpKeySlot". Pero, ¿qué sucede si en su juego, se realiza una acción especial cuando se presionan ambos?

Si desea decorelacionar la entrada y las entidades, puede hacer que su entrada sea un Estado (RunKey = true, FireKey = false, ...) y enviarla al jugador como si enviara cualquier otro evento a sus AI.


No estoy manejando eventos de teclado con sf :: Event, sino sf :: Input, porque es la detección en tiempo real IIRC. Traté de mantenerlo lo más agnóstico de API posible. : P (La pregunta, eso es)
El Pato Comunista

1
Pero creo que el punto que Calvin1602 está haciendo es que su declaración original en la pregunta "Tengo que sondear, en lugar de hacer devoluciones de llamada debido a limitaciones de API (SFML)" no es cierta; tiene eventos, por lo que puede emitir una devolución de llamada cuando maneja un evento.
Kylotan el

3

La solución correcta es a menudo una combinación de los dos métodos que discute:

Para la funcionalidad del juego con un conjunto predefinido de teclas, sondear cada cuadro es completamente apropiado y solo debe sondear las teclas que están realmente vinculadas a algo. En realidad, esto es análogo a cómo consultaría la entrada del controlador en un juego de consola, va a ser un sondeo completo, especialmente cuando se trata de dispositivos de entrada analógica. Durante el juego general, probablemente quieras ignorar los eventos de pulsación de teclas y solo usar el sondeo.

Cuando se trata de escribir entradas para cosas como nombres, debes cambiar el juego a un modo de manejo de entrada diferente. Por ejemplo, tan pronto como aparezca el mensaje "ingrese su nombre", puede modificar lo que hace su código de entrada. Puede escuchar los eventos de pulsación de teclas a través de alguna interfaz de devolución de llamada, o puede comenzar a sondear cada tecla si es necesario. Resulta que, por lo general, durante los eventos de mecanografía no te importa tanto el rendimiento de todos modos, por lo que las encuestas no serán tan malas.

Por lo tanto, desea utilizar un sondeo selectivo para la entrada del juego y el manejo de eventos para las entradas de escritura de nombres (o todo el sondeo del teclado si el manejo de eventos no está disponible).


Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.