Este es un seguimiento de esta pregunta, que respondí, pero esta aborda un tema mucho más específico.
Esta respuesta me ayudó a entender Entity Systems incluso mejor que el artículo.
Leí el (sí, el) artículo sobre Entity Systems, y me dijo lo siguiente:
Las entidades son solo una identificación y una matriz de componentes (los artículos dicen que almacenar entidades en componentes no es una buena manera de hacer las cosas, pero no proporciona una alternativa).
Los componentes son datos que indican lo que se puede hacer con una determinada entidad.
Los sistemas son los "métodos", realizan la manipulación de datos en entidades.
Esto parece realmente práctico en muchas situaciones, pero la parte de que los componentes son solo clases de datos me está molestando. Por ejemplo, ¿cómo podría implementar mi clase Vector2D (posición) en un sistema de entidades?
La clase Vector2D contiene datos: coordenadas xey, pero también tiene métodos , que son cruciales para su utilidad y distinguen la clase de una matriz de dos elementos. Ejemplo métodos son: add()
, rotate(point, r, angle)
, substract()
, normalize()
, y todo otro estándar, métodos útiles, y absolutamente necesarios que las posiciones (que son instancias de la clase Vector2D) debería tener.
Si el componente fuera solo un titular de datos, ¡no podría tener estos métodos!
Una solución que probablemente podría aparecer sería implementarlos dentro de los sistemas, pero eso parece muy contrario a la intuición. Estos métodos son cosas que quiero realizar ahora , que estén completos y listos para usar. ¡No quiero esperar a MovementSystem
que lean un conjunto caro de mensajes que le indican que realice un cálculo sobre la posición de una entidad!
Y, el artículo establece muy claramente que solo los sistemas deberían tener alguna funcionalidad, y la única explicación para eso, que pude encontrar, fue "evitar la POO". En primer lugar, no entiendo por qué debería abstenerme de utilizar métodos en entidades y componentes. La sobrecarga de memoria es prácticamente la misma, y cuando se combina con sistemas, estos deberían ser muy fáciles de implementar y combinar de maneras interesantes. Los sistemas, por ejemplo, solo podían proporcionar lógica básica a entidades / componentes, que conocen la implementación por sí mismos. Si me preguntas, esto es básicamente tomar las cosas buenas de ES y OOP, algo que no se puede hacer de acuerdo con el autor del artículo, pero para mí parece una buena práctica.
Piensa en ello de esta manera; Hay muchos tipos diferentes de objetos dibujables en un juego. Plain Old imágenes, animaciones ( update()
, getCurrentFrame()
, etc.), combinaciones de estos tipos primitivos, y todos ellos podrían simplemente proporcionar un draw()
método para hacer que el sistema, que entonces no necesita preocuparse por cómo se implementa el sprite de una entidad, sólo se sobre la interfaz (dibujar) y la posición. Y luego, solo necesitaría un sistema de animación que llamara métodos específicos de animación que no tienen nada que ver con el renderizado.
Y solo otra cosa ... ¿Existe realmente una alternativa a las matrices cuando se trata de almacenar componentes? No veo otro lugar para almacenar componentes que no sean matrices dentro de una clase de entidad ...
Tal vez, este es un mejor enfoque: almacenar componentes como propiedades simples de entidades. Por ejemplo, un componente de posición estaría pegado entity.position
.
La única otra forma sería tener algún tipo de tabla de búsqueda extraña dentro de los sistemas, que haga referencia a diferentes entidades. Pero eso parece muy ineficiente y más complicado de desarrollar que simplemente almacenar componentes en la entidad.
Respuestas:
Creo que está totalmente bien tener métodos simples para acceder, actualizar o manipular los datos en los componentes. Creo que la funcionalidad que debe quedar fuera de los componentes es la funcionalidad lógica. Las funciones de utilidad están bien. Recuerde, el sistema de entidad-componente es solo una guía, no reglas estrictas que debe seguir. No salgas de tu camino para seguirlos. Si crees que tiene más sentido hacerlo de una manera, entonces hazlo de esa manera :)
EDITAR
Para aclarar, su objetivo no es evitar la POO . Eso sería bastante difícil en la mayoría de los idiomas comunes utilizados en estos días. Estás tratando de minimizar la herencia , que es un gran aspecto de la POO, pero no es obligatorio. Desea deshacerse de la herencia de Objeto-> Objeto móvil-> Criatura-> Bípedo-> Tipo humano.
Sin embargo, ¡está bien tener algo de herencia! Se trata de un lenguaje que está fuertemente influenciado por la herencia, es muy difícil no usarlo. Por ejemplo, puede tener una
Component
clase o interfaz que todos sus otros componentes extiendan o implementen. El mismo trato con tuSystem
clase. Esto hace las cosas mucho más fáciles. Le recomiendo que eche un vistazo al marco de Artemis . Es de código abierto y tiene algunos proyectos de ejemplo. Abre esas cosas y mira cómo funciona.Para Artemis, las entidades se almacenan en una matriz, simple. Sin embargo, sus componentes se almacenan en una matriz o matrices (separadas de las entidades). La matriz de nivel superior agrupa la matriz de nivel inferior por tipo de componente. Por lo tanto, cada tipo de componente tiene su propia matriz. La matriz de nivel inferior está indexada por ID de entidad. (Ahora no estoy seguro si lo haría de esa manera, pero así es como se hace aquí). Artemis reutiliza las ID de entidad, por lo que la ID de entidad máxima no se hace mayor que su número actual de entidades, pero aún puede tener matrices dispersas si el componente no es un componente de uso frecuente. De todos modos, no lo separaré demasiado. Este método para almacenar entidades y sus componentes parece funcionar. Creo que sería un buen primer paso para implementar su propio sistema.
Las entidades y componentes se almacenan en un administrador separado.
La estrategia que menciona, hacer que las entidades almacenen sus propios componentes (
entity.position
), es algo en contra del tema del componente de la entidad, pero es totalmente aceptable si cree que tiene más sentido.fuente
EntityManager
como se almacenan las cosas."Ese" artículo no es uno con el que estoy particularmente de acuerdo, por lo que creo que mi respuesta será algo crítica.
La idea no es garantizar que nada en su programa sea otra cosa que un ID de entidad, componente o sistema; es garantizar que los datos y el comportamiento de la entidad se creen a través de la composición de objetos en lugar de usar un árbol de herencia complejo o peor aún tratar de pon todas las funcionalidades posibles en un solo objeto. Para implementar estos componentes y sistemas, seguramente tendrá datos normales como vectores que, en la mayoría de los idiomas, se representan mejor como una clase.
Ignore el bit en el artículo que sugiere que esto no es OOP, es tan OOP como cualquier otro enfoque. Cuando la mayoría de los compiladores o los tiempos de ejecución del lenguaje implementan métodos de objeto, es básicamente como cualquier otra función, excepto que hay un argumento oculto llamado
this
oself
, que es un puntero a un lugar en la memoria donde se almacenan los datos de ese objeto. En un sistema basado en componentes, la ID de entidad se puede utilizar para encontrar dónde están los componentes relevantes (y, por lo tanto, los datos) para una entidad determinada. Por lo tanto, la ID de la entidad es equivalente a un puntero this / self, y los conceptos son básicamente la misma cosa, solo se reorganizaron un poco.Bueno. Los métodos son una forma efectiva de organizar su código. Lo importante que debe eliminarse de la idea de "evitar POO" es evitar el uso de la herencia en todas partes para ampliar la funcionalidad. En cambio, divida la funcionalidad en componentes que se puedan combinar para hacer lo mismo.
La idea de un sistema basado en componentes es que no tendrías clases separadas para estos, sino que tendrías una sola clase de Objeto / Entidad, y la imagen sería un Objeto / Entidad que tiene un ImageRenderer, las Animaciones serían un Objeto / Entidad que tiene un AnimationRenderer, etc. Los sistemas relevantes sabrían cómo representar estos componentes y, por lo tanto, no necesitaría ninguna clase base con un método Draw ().
Claro, pero esto no funciona bien con los componentes. Tienes 3 opciones:
Puede almacenar los componentes en el sistema. La matriz no es el problema, sino dónde almacena el componente.
fuente
Un vector son datos. Las funciones se parecen más a las funciones de utilidad: no son específicas de esa instancia de los datos, se pueden aplicar a todos los vectores de forma independiente. Una buena manera de pensarlo es: ¿pueden reescribirse estas funciones como métodos estáticos? Si es así, es solo utilidad.
fuente