Manejo de entrada en diseño basado en componentes

12

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:

  1. 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.
  2. 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.
  3. 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.

Corazón triste
fuente
2
Por lo general (cuando la cámara sigue al jugador) no desea recibir información en la cámara, sino que hace que la cámara verifique la posición del jugador y la siga.
Luke B.
1
Realmente no importa conceptualmente si la cámara sigue al jugador, o "sí mismo". Sin embargo, no estoy seguro de cómo se implementaría su sugerencia en un diseño basado en componentes sin romper los principios de diseño.
Grieverheart
1
@Luke B .: Después de pensarlo un poco, veo que también puedes hacer la cámara como una clase separada, llevando un puntero a una entidad para seguir.
Grieverheart

Respuestas:

8

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.

Comunidad
fuente
Juego un poco mi pregunta y estaba a punto de responderla yo mismo. También llegué a la conclusión de que debería ser mejor desacoplar el manejo de entrada del sistema de la CE, así que es bueno ver una confirmación de esto. La forma en que pensé en hacer esto es mediante el uso de señales y la capacidad de asociar varias entidades a un tipo de evento. También decidí desacoplar la cámara, aunque esto no es realmente necesario y tenerla como entidad sería igualmente viable. Creo que cuando todavía eres un novato con ECs, realmente tienes que pensar cuáles son los beneficios de hacer que algo sea un componente o entidad.
Grieverheart
4

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.

Coyote
fuente
Coyote, gracias por tu respuesta. También he leído tu otra publicación aquí . Mi mayor preocupación no es cómo abstraer la entrada. Ya tengo una construcción de nivel inferior que maneja las pulsaciones de teclas y demás, y agregar un nivel más de indirección no sería difícil. Mi problema es manejar los eventos generados por, por ejemplo, su sistema de intención. Si entiendo correctamente, no tiene ningún componente de entrada en su método. ¿Cómo sabes qué entidades necesitan información y cómo la manejas? ¿Podría darnos algunos ejemplos más concretos?
Grieverheart
2

¿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?

Action jump = () =>
{
    entities["player"].Transform.Velocity.Y += 5;
};

Otro ejemplo, desde el OP:

Action moveRight = () =>
{
    foreach (var entity in entities.Tagged("player", "camera"))
        entity.Transform.Position.X += 5;
};
AlexFoxGill
fuente