Estoy tratando de diseñar un sistema de entidad basado en componentes para fines de aprendizaje (y luego usarlo en algunos juegos) y tengo algunos problemas a la hora de actualizar los estados de la entidad.
No quiero tener un método update () dentro del Componente para evitar dependencias entre Componentes.
Lo que actualmente tengo en mente es que los componentes contienen datos y los componentes de actualización de sistemas.
Entonces, si tengo un juego 2D simple con algunas entidades (por ejemplo, jugador, enemigo1, enemigo2) que tienen componentes de Transformación, Movimiento, Estado, Animación y Representación, creo que debería tener:
- Un sistema de movimiento que mueve todos los componentes de movimiento y actualiza los componentes de estado
- Y un RenderSystem que actualiza los componentes de Animación (el componente de animación debe tener una animación (es decir, un conjunto de cuadros / texturas) para cada estado y actualizarlo significa seleccionar la animación correspondiente al estado actual (por ejemplo, saltar, mover_izquierda, etc.), y actualizar el índice del marco). Luego, RenderSystem actualiza los componentes de Render con la textura correspondiente al cuadro actual de la Animación de cada entidad y renderiza todo en la pantalla.
He visto algunas implementaciones como el marco de Artemis, pero no sé cómo resolver esta situación:
Digamos que mi juego tiene las siguientes entidades. Cada entidad tiene un conjunto de estados y una animación para cada estado:
- jugador: "inactivo", "moving_right", "saltando"
- enemigo1: "movimiento_abajo", "movimiento_abajo"
- enemy2: "moving_left", "moving_right"
¿Cuáles son los enfoques más aceptados para actualizar el estado actual de cada entidad? Lo único que se me ocurre es tener sistemas separados para cada grupo de entidades y componentes separados de Estado y Animación, por lo que tendría PlayerState, PlayerAnimation, Enemy1State, Enemy1Animation ... PlayerMovementSystem, PlayerRenderingSystem ... pero creo que esto es malo solución y rompe el propósito de tener un sistema basado en componentes.
Como puede ver, estoy bastante perdido aquí, así que agradecería cualquier ayuda.
EDITAR: Creo que la solución para hacer que esto funcione como tengo la intención es esta:
Hace que el componente de estado y el componente de animación sean lo suficientemente genéricos como para ser utilizados en todas las entidades. Los datos que contienen serán el modificador para cambiar cosas como qué animaciones se reproducen o qué estados están disponibles. - Byte56
Ahora, estoy tratando de descubrir cómo diseñar estos 2 componentes lo suficientemente genéricos como para poder reutilizarlos. ¿Podría ser una buena solución tener un UID para cada estado (por ejemplo, caminar, correr ...) y almacenar animaciones en un mapa en el AnimationComponent con este identificador?
fuente
statecomponent
yanimationcomponent
lo suficientemente genérico que debe utilizarse para todas las entidades. Los datos que contienen serán el modificador para cambiar cosas como qué animaciones se reproducen o qué estados están disponibles.Respuestas:
En mi humilde opinión, el
Movement
componente debe mantener el estado actual (Movement.state
) y elAnimation
componente debe observar los cambiosMovement.state
y actualizar su animación actual (Animation.animation
) en consecuencia, utilizando una simple búsqueda de id de estado a la animación (como se sugiere al final del OP). Obviamente, esto significaAnimation
dependerá deMovement
.Una estructura alternativa sería tener un
State
componente genérico , queAnimation
observa yMovement
modifica, que es básicamente el modelo-vista-controlador (estado-animación-movimiento en este caso).Otra alternativa sería hacer que la entidad envíe un evento a sus componentes cuando cambie su estado.
Animation
escucharía este evento y actualizaría su animación en consecuencia. Esto elimina la dependencia, aunque podría argumentar que la versión dependiente es un diseño más transparente.Buena suerte.
fuente
Movement
que el controlState
(no observar). Último caso: Sí,Movement
másentity.dispatchEvent(...);
o menos, y todos los demás componentes que escuchen ese tipo de evento lo recibirán. Por supuesto, el rendimiento es peor que las llamadas a métodos puros, pero no mucho. Puede agrupar objetos de eventos, por ejemplo. Por cierto, no tiene que usar la entidad como el "nodo de eventos", también podría usar un "bus de eventos" dedicado, dejando su clase de entidad completamente fuera de él.Sobre su problema, si el ESTADO solo se usa en animaciones, entonces ni siquiera necesita exponerlo a otros componentes. Si tiene más de un uso, debe exponerlo.
El sistema de Componentes / Subsistema que describe se siente más basado en la jerarquía que en los componentes. Después de todo, lo que usted describe como componentes son, de hecho, estructuras de datos. No significa que sea un mal sistema, solo que no creo que se ajuste demasiado bien al enfoque basado en componentes.
Como notó, las dependencias son un gran problema en los sistemas basados en componentes. Hay diferentes formas de lidiar con eso. Algunos requieren que cada componente declare sus dependencias y realice una verificación estricta. Otros consultan por componentes que implementan una interfaz específica. Aún otros pasan referencia a los componentes dependientes cuando instancian cada uno de ellos.
Independientemente del método que utilice, necesitará un GameObject de algún tipo para actuar como una colección de Componentes. Lo que proporciona GameObject puede variar mucho y puede simplificar sus dependencias entre componentes al impulsar algunos datos de uso frecuente al nivel de GameObject. Unity hace eso con la transformación, por ejemplo, obliga a todos los objetos del juego a tener uno.
En cuanto al problema que pides de diferentes estados / animación para diferentes objetos del juego, esto es lo que haría. Primero, no me gustaría demasiado en esta etapa de la implementación: solo implemente lo que necesita ahora para que funcione, luego agregue campanas y silbatos cuando los necesite.
Entonces, comenzaría con un componente 'State': PlayerStateComponent, Enemy1State, Enemy2State. El componente estatal se encargaría de cambiar el estado en el momento apropiado. El estado es algo que prácticamente todos sus objetos tendrán, por lo que puede residir en el GameObject.
Entonces, habría un AnimationCompoment. Esto tendría un diccionario de animaciones relacionadas con el estado. En update (), cambie la animación si el estado cambia.
Hay un gran artículo sobre el marco de construcción que no puedo encontrar. Decía que cuando no tienes experiencia en el dominio, debes elegir un problema y hacer la implementación más simple que resuelva el problema actual . Luego agrega otro problema / caso de uso y expande el marco a medida que avanza, para que crezca orgánicamente. Realmente me gusta ese enfoque, particularmente cuando trabajas con un nuevo concepto como lo estás haciendo.
La implementación que propuse es bastante ingenua, pero aquí hay algunas posibles mejoras a medida que agrega más casos de uso:
fuente
Además de la respuesta de ADB, puede usar http://en.wikipedia.org/wiki/Dependency_injection , que ayuda cuando necesita construir muchos componentes pasándolos como referencias a sus constructores. Obviamente, aún dependerán entre sí (si eso es necesario en su base de código), pero puede poner toda esa dependencia en un lugar donde las dependencias están configuradas y el resto de su código no necesita saber sobre la dependencia.
Este enfoque también funciona bien si usa interfaces porque cada clase de componente solo solicita lo que necesita o dónde debe registrarse y solo el marco de inyección de dependencia (o el lugar donde configura todo, generalmente la aplicación) sabe quién necesita qué .
Para sistemas simples que podría escapar sin usar DI o código limpio, sus clases de RenderingSystem suenan como si necesita llamarlas estáticamente o al menos tenerlas disponibles en cada componente, lo que las hace dependientes entre sí y difíciles de cambiar. Si está interesado en un enfoque más limpio, consulte los enlaces del enlace DI wiki arriba y lea sobre Clean Code: http://clean-code-developer.com/
fuente