He estado trabajando en algunos proyectos de pasatiempos en los últimos 3-4 años. Solo juegos simples en 2d y 3d. Pero últimamente he comenzado un proyecto más grande. Así que en los últimos meses he estado tratando de diseñar una clase de objeto de juego que pueda ser la base de todos mis objetos de juego. Entonces, después de muchas pruebas de probar y morir, recurrí a Google, que rápidamente me señaló algunos PDF y PowerPoints de GDC. Y ahora estoy tratando de entender los objetos de juego basados en componentes.
Entiendo que el motor crea un objeto de juego y luego une diferentes componentes que manejan cosas como la salud, la física, las redes y lo que sea que hagas que hagan. Pero lo que no entiendo es cómo el componente X sabe si Y ha cambiado el estado del objeto. ¿Cómo sabe el componente de física si el jugador está vivo, porque la salud está controlada por el componente de salud? ¿Y cómo juega HealthComponent la "animación de jugador muerto"?
Tenía la impresión de que era algo así (en el componente de salud):
if(Health < 0) {
AnimationComponent.PlayAnimation("played-died-animation")
}
Pero, de nuevo, ¿cómo sabe HealthComponent que el objeto del juego al que está conectado tiene un AnimationComponent adjunto? La única solución que veo aquí es
Verifique si hay un AnimationComponent adjunto o no (ya sea dentro del código del componente o en el lado del motor)
Tener componentes requiere otros componentes, pero eso parece luchar contra todo el diseño de componentes.
Escriba como, HealthWithAnimationComponent, HealthNoAnimationComponent, y así sucesivamente, lo que nuevamente parece luchar contra toda la idea del diseño de componentes.
Respuestas:
En todos sus ejemplos, hay un problema terrible. El componente de salud necesita saber acerca de cada tipo de componente que pueda necesitar responder a la muerte de la entidad. Por lo tanto, ninguno de sus escenarios es apropiado. Su entidad tiene un componente de salud. Tiene un componente de animación. Ni dependen ni conocen al otro. Se comunican a través de un sistema de mensajería.
Cuando el componente de salud detecta que la entidad ha "muerto", envía un mensaje "He muerto". Es responsabilidad del componente de animación responder a este mensaje reproduciendo la animación apropiada.
El componente de salud no envía el mensaje directamente al componente de animación. Tal vez lo transmite a todos los componentes de esa entidad, tal vez a todo el sistema; tal vez el componente de animación necesita informarle al sistema de mensajería que está interesado en los mensajes 'Morí'. Hay muchas formas de implementar el sistema de mensajería. Independientemente de cómo lo implemente, el punto es que el componente de salud y el componente de animación nunca necesitan saber o preocuparse si el otro está presente, y agregar nuevos componentes nunca requerirá modificar los existentes para enviarles los mensajes apropiados.
fuente
La forma en que Artemis resuelve el problema es no realizar el procesamiento dentro de los Componentes. Los componentes contienen solo los datos que necesitan. Los sistemas leen múltiples tipos de componentes y hacen cualquier procesamiento que sea necesario.
Entonces, en su caso, es posible que tenga un RenderSystem que lea el HealthComponent (y otros) y reproduzca las colas en las animaciones apropiadas. Separar los datos de las funciones de esta manera hace que sea más fácil mantener las dependencias adecuadamente administradas.
fuente
En su código, puede haber formas (las usé, posiblemente existen otras formas) para saber si el objeto cambió de estado:
Para esto utilicé: 1. Función HasComponent de GameObject, o 2. cuando adjuntas un componente puedes verificar las dependencias en alguna función de construcción, o 3. Si estoy seguro de que ese objeto tiene este componente, simplemente lo uso.
En algunos artículos que he leído, los componentes del sistema Ideal no dependen unos de otros, pero en la vida real no es así.
Es una mala idea escribir tales componentes. En mi aplicación creé el componente de salud más independiente. Ahora estoy pensando en algún patrón de Observador que notifique a los suscriptores acerca de algún evento específico (por ejemplo, "hit", "heal", etc.). Entonces AnimationComponent debe decidir por sí mismo cuándo reproducir animación.
Pero cuando leí un artículo sobre CBES me impresionó, así que estoy muy feliz cuando uso CBES y descubro nuevas posibilidades.
fuente
Es como Michael, dice Patrick Hughes y Blecki. La solución para evitar simplemente mover el problema es abandonar la ideología que causa el problema en primer lugar.
Es menos OOD y más como programación funcional. Cuando comencé a experimentar con el diseño basado en componentes, descubrí este problema en el futuro. Busqué en Google un poco más y encontré que la "Programación reactiva funcional" era la solución.
Ahora mis componentes no son más que una colección de variables y campos que describen su estado actual. Luego tengo un montón de clases de "Sistema" que actualizan todos los componentes que son relevantes para ellos. La parte reactiva se logra ejecutando los sistemas en un orden bien definido. Esto garantiza que cualquier sistema que sea el siguiente en la línea para realizar su procesamiento y actualización, y cualquier componente y entidad que tenga la intención de leer y actualizar, siempre está trabajando en datos actualizados.
Sin embargo, de alguna manera, aún podría afirmar que el problema se ha movido una vez más. Porque, ¿qué sucede si no es sencillo en qué orden deben ejecutar sus sistemas? ¿Qué pasa si hay relaciones cíclicas y es solo cuestión de tiempo antes de que estés mirando un desastre de declaraciones if-else y switch? Es una forma implícita de mensajería, ¿no? A primera vista, creo que es un pequeño riesgo. Por lo general, las cosas se procesan en orden. Algo así como: Entrada del jugador -> Posiciones de la entidad -> Detección de colisión -> Lógica del juego -> Representación -> Comenzar de nuevo. En ese caso, tendría un sistema para cada uno, proporcionaría a cada sistema un método update () y luego los ejecutaría en secuencia en su bucle de juego.
fuente