Sé que esta pregunta se ha hecho varias veces, pero todavía no estoy seguro de cómo implementar el manejo de entrada en un motor basado en componentes.
El diseño basado en componentes que utilicé se basó en la serie de blogs de T = Machine y en Artemis en el que las entidades son solo identificadores.
Hay tres ideas principales que tengo para implementar el manejo de entradas:
- El componente de entrada contendrá eventos que le interesen. El sistema de entrada traducirá los eventos de tecla y mouse a eventos del juego y recorrerá las entidades con el componente de entrada y, si están interesados en el evento, el sistema de entrada tomará las medidas apropiadas. Esta acción estaría codificada en el sistema de entrada.
- Sin componente de entrada. Debería registrar entidades con eventos específicos en el sistema de entrada. El sistema de entrada enviaría mensajes (con la identificación de la entidad y el tipo de evento) a otros sistemas para que estos puedan tomar las medidas apropiadas. O como en el primer caso, las acciones estarían codificadas en el sistema de entrada.
- Similar al primer método, pero en lugar de codificar la acción al sistema de entrada, el componente contendría un mapa de eventos a funciones (es decir
std::map<std::function>
) que sería llamado por el sistema de entrada. Esto tiene el efecto adicional de poder acoplar el mismo evento a diferentes acciones.
¿Recomendaría alguno de los métodos anteriores o tiene alguna sugerencia que me ayude a implementar un sistema de manejo de entrada flexible? Además, todavía no estoy familiarizado con los subprocesos múltiples, pero cualquier sugerencia que haga que la implementación sea amigable con los hilos también es bienvenida.
Nota: Un requisito adicional que me gustaría que cumpliera la implementación es que podría pasar la misma entrada a muchas entidades, como por ejemplo mover una entidad de cámara y el reproductor al mismo tiempo.
fuente
Respuestas:
Creo que, al igual que mi respuesta con respecto a los materiales en un sistema de componentes , se encuentra con un problema en el que está tratando de meter todo en un "componente". No necesita hacer esto y al hacerlo, probablemente esté creando una interfaz realmente engorrosa al tratar de encajar un montón de clavijas cuadradas en agujeros redondos.
Parece que ya tiene un sistema que maneja la adquisición de entrada del jugador. Optaría por un enfoque que luego traduzca esa entrada en acciones ("avanzar" o "retroceder") o eventos y enviarlos a las partes interesadas. En el pasado, he no permitidos componentes de registrar a sí mismos para estos eventos, prefiriendo un enfoque en el que el sistema de nivel superior seleccionado explícitamente la "entidad controlada." Pero podría funcionar de otra manera si lo prefiere, especialmente si va a reutilizar los mismos mensajes para tomar acciones que no fueron estimuladas directamente por la entrada.
Sin embargo, no necesariamente sugeriría implementar el comportamiento de seguimiento de la cámara haciendo que tanto la entidad de la cámara como la entidad del jugador respondan al mensaje de "avance" (etcétera). Esto crea una conexión extremadamente rígida entre los dos objetos que probablemente no se sentirá bien para el jugador, y también hace que sea un poco más difícil manejar cosas como hacer que la cámara orbite al jugador cuando el jugador gira hacia la izquierda o hacia la derecha: tienes una entidad respondiendo a "rotar a la izquierda" asumiendo que está esclavizado al jugador, pero eso significa que no puede responder correctamente si alguna vez no fue esclavizado ... a menos que introduzca ese concepto como un estado que puede verificar. Y si va a hacer eso, también puede implementar un sistema adecuado para esclavizar dos objetos físicos juntos, completar con ajustes de elasticidad apropiados, etc.
Con respecto a los subprocesos múltiples, realmente no veo la necesidad de emplearlo aquí, ya que probablemente causaría más complicaciones de lo que vale, y está lidiando con un problema inherentemente en serie, por lo que solo necesitará involucrar una gran cantidad de hilos primitivas de sincronización.
fuente
Mi experiencia puede estar sesgada, pero en proyectos multiplataforma los dispositivos de entrada no están expuestos directamente al sistema de la entidad.
Los dispositivos de entrada son manejados por un sistema de nivel inferior que recibe los eventos de las teclas, botones, eje, mouse, superficies táctiles, acelerómetros ...
Estos eventos se envían a través de una capa de generadores de intención dependientes del contexto.
Cada generador registra los cambios de estado de los componentes, entidades y sistemas que son relevantes para sus funciones.
Estos generadores luego envían mensajes / intenciones para enrutar al sistema de intención donde las entidades tienen un componente o directamente a los componentes correctos.
De esta manera, simplemente puede confiar en que "siempre" tenga la misma entrada, es decir, JUMP_INTENT (1), JUMP_INTENT (0), AIM_INTENT (1) ...
Y "todo" el trabajo de entrada dependiente de la plataforma sucia permanece fuera de su sistema de entidad.
En cuanto a la cámara, si desea moverla alrededor del reproductor, puede registrar su propio componente de intención y escuchar las intenciones que enviará.
De lo contrario, si sigue al jugador, nunca debería escuchar las entradas destinadas al jugador. Debe escuchar los cambios de estado emitidos por el jugador (ENTITY_MOVED (transformar)) ... y moverse en consecuencia. Si utiliza un sistema de física, incluso puede conectar la cámara al reproductor utilizando una de las diferentes articulaciones.
fuente
¿Qué beneficio aporta un InputComponent? Seguramente es prerrogativa del comando de entrada decidir en qué entidades está realizando una acción. El ejemplo clásico es el de hacer saltar al jugador. En lugar de tener un InputComponent en cada entidad que escucha los eventos "Jump", ¿por qué no hacer que el comando jump busque la entidad marcada como "player" y realice la lógica necesaria?
Otro ejemplo, desde el OP:
fuente