Estoy tratando de entender el diseño de entidades basado en componentes.
Mi primer paso fue crear varios componentes que pudieran agregarse a un objeto. Para cada tipo de componente, tenía un administrador, que llamaría a la función de actualización de cada componente, pasando cosas como el estado del teclado, etc., según sea necesario.
Lo siguiente que hice fue eliminar el objeto y solo tener cada componente con un Id. Entonces, un objeto se define por componentes que tienen los mismos ID.
Ahora, estoy pensando que no necesito un administrador para todos mis componentes, por ejemplo, tengo un SizeComponent
, que solo tiene una Size
propiedad). Como resultado SizeComponent
, no tiene un método de actualización, y el método de actualización del administrador no hace nada.
Mi primer pensamiento fue tener una ObjectProperty
clase que los componentes pudieran consultar, en lugar de tenerlos como propiedades de los componentes. Entonces un objeto tendría un número de ObjectProperty
y ObjectComponent
. Los componentes tendrían una lógica de actualización que consulta las propiedades del objeto. El gerente administraría la llamada al método de actualización del componente.
Esto me parece una ingeniería excesiva, pero no creo que pueda deshacerme de los componentes, porque necesito una forma para que los gerentes sepan qué objetos necesitan qué lógica de componente ejecutar (de lo contrario, simplemente eliminaría el componente completamente e inserte su lógica de actualización en el administrador).
- Está presente (que tiene
ObjectProperty
,ObjectComponent
yComponentManager
clases) más de la ingeniería? - ¿Cuál sería una buena alternativa?
fuente
SizeComponent
exageración es excesivo, puede suponer que la mayoría de los objetos tienen un tamaño, es el renderizado, la inteligencia artificial y la física donde se usa el modelo de componentes; El tamaño siempre se comportará igual, por lo que puede compartir ese código.RenderingComponent
y aPhysicsComponent
. ¿Estoy pensando demasiado en la decisión de dónde colocar la propiedad? ¿Debo pegarlo en cualquiera de los dos y luego hacer que la otra consulta sea un objeto para el componente que tiene la propiedad necesaria?PhysicalStateInstance
(uno por objeto) junto con unGravityPhysicsShared
(uno por juego); Sin embargo, estoy tentado a decir que esto es aventurarse en los reinos de la euforia de los arquitectos, no se arme en un hoyo (exactamente lo que hice con mi primer sistema de componentes). BESO.Respuestas:
La respuesta simple a su primera pregunta es Sí, está superando la ingeniería del diseño. El '¿Hasta qué punto desgloso las cosas?' La pregunta es muy común cuando se da el siguiente paso y se elimina el objeto central (generalmente llamado Entidad).
Cuando está desglosando los objetos a un nivel tan detallado que tiene un tamaño propio, el diseño ha ido demasiado lejos. Un valor de datos por sí solo no es un componente. Es un tipo de datos incorporado y a menudo se le puede llamar exactamente como lo que comenzó a llamarlos, una propiedad. Una propiedad no es un componente, pero un componente contiene propiedades.
Entonces, aquí hay algunas pautas que intento y sigo al desarrollar en un sistema de componentes:
Entonces, con la guía de componentes que no son estructuras, SizeComponent se ha desglosado demasiado. Contiene solo datos y lo que define el tamaño de algo puede variar. Por ejemplo, en un componente de representación podría ser un escalar 1d o un vector 2 / 3d. En un componente de física podría ser el volumen delimitador del objeto. En un artículo de inventario podría ser la cantidad de espacio que ocupa en una cuadrícula 2D.
Intenta trazar una buena línea entre teoría y practicidad.
Espero que esto ayude.
fuente
Ya aceptó una respuesta, pero aquí está mi puñalada en un CBS. Descubrí que una
Component
clase genérica tiene algunas limitaciones, así que elegí un diseño descrito por Radical Entertainment en GDC 2009, que sugirió separar los componentes enAttributes
yBehaviors
. (" Teoría y práctica de la arquitectura de componentes de objetos de juego ", Marcin Chady)Explico mis decisiones de diseño en un documento de dos páginas. Solo publicaré el enlace, ya que es demasiado largo para pegarlo aquí. Actualmente solo cubre los componentes lógicos (no también los componentes de representación y física), pero debería darle una idea de lo que intenté hacer:
► http://www.pdf-archive.com/2012/01/08/entity-component-system/preview/page/1
Aquí hay un extracto del documento:
Editar: Y aquí hay un diagrama de relación que muestra cómo los componentes se comunican entre sí:
Un detalle de implementación: el nivel de entidad
EventManager
solo se crea si se usa. LaEntity
clase solo almacena un puntero a unEventManager
que se inicializa solo si algún componente lo solicita.Editar: En otra pregunta, le di una respuesta similar a esta. Puede encontrarlo aquí para una, quizás, mejor explicación del sistema:
► /gamedev//a/23759/6188
fuente
Realmente depende de las propiedades que necesita y dónde las necesita. La cantidad de memoria que tendrá y la potencia / tipo de procesamiento que usará. He visto e intento hacer lo siguiente:
Estos principios tienen sus limitaciones. Por supuesto, tendrá que llevar la geometría al renderizador, pero probablemente no querrá recuperarla desde allí. La geometría maestra se almacenará en el motor de física en el caso de que ocurran deformaciones allí y se sincronice con el renderizador (de vez en cuando, dependiendo de la distancia de los objetos). Entonces, de alguna manera, lo duplicará de todos modos.
No hay sistemas perfectos. Y algunos juegos estarán mejor con un sistema más simple, mientras que otros requerirán sincronizaciones más complejas entre sistemas.
Al principio, asegúrese de que se puede acceder a todas las propiedades de manera simple desde sus componentes para que pueda cambiar la forma en que almacena las propiedades de forma transparente una vez que comience a ajustar sus sistemas.
No hay vergüenza en copiar algunas propiedades. Si algunos componentes necesitan mantener una copia local, a veces es más eficiente copiar y sincronizar en lugar de acceder a un valor "externo"
Además, la sincronización no tiene que suceder en cada cuadro. Algunos componentes se pueden sincronizar con menos frecuencia que otros. El componente de representación suele ser un buen ejemplo. Los que no interactúan con los jugadores se pueden sincronizar con menos frecuencia, al igual que los que están lejos. Los que están lejos y fuera del campo de la cámara pueden sincronizarse incluso con menos frecuencia.
Cuando se trata de su componente de tamaño, probablemente podría agruparse dentro de su componente de posición:
En lugar del tamaño, incluso podría usar un modificador de tamaño, podría ser más útil.
En cuanto al almacenamiento de todas las propiedades en un sistema de almacenamiento de propiedades genérico ... No estoy seguro de que vaya en la dirección correcta ... Céntrese en las propiedades que son fundamentales para su juego y cree componentes que agrupen tantas propiedades relacionadas como sea posible. Siempre que abstraiga el acceso a estas propiedades correctamente (a través de captadores en los componentes que los necesitan, por ejemplo), debería poder moverlos, copiarlos y sincronizarlos más tarde, sin romper demasiadas lógicas.
fuente
Si los componentes se pueden agregar arbitrariamente a las entidades, entonces necesita una forma de consultar si un componente determinado existe en una entidad y obtener una referencia a él. Por lo tanto, puede iterar sobre una lista de objetos derivados de ObjectComponent hasta encontrar el que desea y devolverlo. Pero devolvería un objeto del tipo correcto.
En C ++ o C # esto generalmente significa que tendría un método de plantilla en la entidad como
T GetComponent<T>()
. Y una vez que tenga esa referencia, sabrá exactamente qué datos de miembro tiene, así que acceda directamente a ella.En algo como Lua o Python, no necesariamente tiene un tipo explícito de ese objeto, y probablemente tampoco le importe. Pero, de nuevo, puede acceder a la variable miembro y manejar cualquier excepción que surja al intentar acceder a algo que no está allí.
La consulta de propiedades de objetos suena explícitamente como la duplicación del trabajo que el lenguaje puede hacer por usted, ya sea en tiempo de compilación para los idiomas de tipo estático o en tiempo de ejecución para los de tipo dinámico.
fuente
La cuestión es que RenderingComponent usa posición, pero PhysicsComponent lo proporciona . Solo necesita una manera de decirle a cada componente de usuario qué proveedor usar. Idealmente de una manera agnóstica, de lo contrario habrá una dependencia.
No hay una regla común. Depende de la propiedad específica (ver arriba).
Crea un juego con una arquitectura fea pero basada en componentes y luego refactoriza.
fuente
PhysicsComponent
debería hacer entonces. Lo veo como la gestión de la simulación del objeto dentro de un entorno físico, lo que me lleva a esta confusión: no todas las cosas que deben representarse deberán simularse, por lo que me parece incorrecto agregarPhysicsComponent
cuando agregoRenderingComponent
porque contiene una posición queRenderingComponent
utiliza Pude verme fácilmente terminando con una red de componentes interconectados, lo que significa que todos / la mayoría deben agregarse a cada entidad.Su instinto le está diciendo que el tener el
ThingProperty
,ThingComponent
yThingManager
para cadaThing
tipo de un componente poco exagerado. Creo que esta bien.Pero, necesita alguna forma de realizar un seguimiento de los componentes relacionados en términos de qué sistemas los usan, a qué entidad pertenecen, etc.
TransformProperty
va a ser bastante común ¿Pero quién está a cargo de ello, el sistema de renderizado? El sistema de física? El sistema de sonido? ¿Por qué unTransform
componente incluso necesitaría actualizarse?La solución es eliminar cualquier tipo de código de sus propiedades fuera de getters, setters e inicializadores. Los componentes son datos, que los sistemas del juego utilizan para realizar diversas tareas como renderizado, IA, reproducción de sonido, movimiento, etc.
Lea sobre Artemis: http://piemaster.net/2011/07/entity-component-artemis/
Mire su código y verá que se basa en sistemas que declaran sus dependencias como listas de
ComponentTypes
. Escribe cada una de susSystem
clases y en el método constructor / init declara de qué tipos depende ese sistema.Durante la configuración de niveles o cualquier otra cosa, crea sus entidades y les agrega componentes. Luego, le dice a esa entidad que informe a Artemis, y Artemis luego determina en función de la composición de esa entidad qué sistemas estarían interesados en saber sobre esa entidad.
Luego, durante la fase de actualización de su ciclo, sus
System
correos electrónicos ahora tienen una lista de qué entidades actualizar. Ahora usted puede tener la granularidad de los componentes para que pueda idear sistemas locos, entidades construcción de unaModelComponent
,TransformComponent
,FliesLikeSupermanComponent
, ySocketInfoComponent
, y hacer algo extraño como marca un platillo volante que las moscas entre los clientes conectados a un juego de varios jugadores. De acuerdo, tal vez no sea eso, pero la idea es que mantiene las cosas desacopladas y flexibles.Artemis no es perfecto, y los ejemplos en el sitio son un poco básicos, pero la separación de código y datos es poderosa. También es bueno para tu caché si lo haces bien. Artemis probablemente no lo haga bien en ese frente, pero es bueno aprender de eso.
fuente