He estado interesado en el sistema de entidades basado en componentes por un tiempo, y leí innumerables artículos sobre él (los juegos Insomiac , el bastante estándar Evolve Your Hierarchy , T-Machine , Chronoclast ... solo por nombrar algunos).
Todos parecen tener una estructura en el exterior de algo como:
Entity e = Entity.Create();
e.AddComponent(RenderComponent, ...);
//do lots of stuff
e.GetComponent<PositionComponent>(...).SetPos(4, 5, 6);
Y si trae la idea de datos compartidos (este es el mejor diseño que he visto hasta ahora, en términos de no duplicar datos en todas partes)
e.GetProperty<string>("Name").Value = "blah";
Sí, esto es muy eficiente. Sin embargo, no es exactamente el más fácil de leer o escribir; se siente muy torpe y trabajando contra ti.
Personalmente me gustaría hacer algo como:
e.SetPosition(4, 5, 6);
e.Name = "Blah";
Aunque, por supuesto, la única forma de llegar a ese tipo de diseño es de vuelta en el tipo de jerarquía Entity-> NPC-> Enemy-> FlyingEnemy-> FlyingEnemyWithAHatOn que este diseño intenta evitar.
¿Alguien ha visto un diseño para este tipo de sistema de componentes que aún sea flexible, pero que mantenga un nivel de facilidad de uso? Y para el caso, ¿logra sortear el almacenamiento de datos (probablemente el problema más difícil) de una buena manera?
¿Qué diseños hay para un sistema de entidad basado en componentes que sea fácil de usar pero flexible?
fuente
Sugeriría algún tipo de clase de interfaz para sus objetos de entidad sería bueno. Podría hacer el manejo de errores con la verificación para asegurarse de que una entidad contenga un componente del valor apropiado también en una ubicación para que no tenga que hacerlo en todas partes donde acceda a los valores. Alternativamente, la mayoría de los diseños que he hecho con un sistema basado en componentes, trato directamente con los componentes, solicitando, por ejemplo, su componente posicional y luego accediendo / actualizando las propiedades de ese componente directamente.
Muy básico en un nivel alto, la clase abarca la entidad en cuestión y proporciona una interfaz fácil de usar para las partes componentes subyacentes de la siguiente manera:
fuente
En Python puede interceptar la parte 'setPosition' a
e.SetPosition(4,5,6)
través de declarar una__getattr__
función en Entity. Esta función puede recorrer en iteración los componentes y encontrar el método o propiedad apropiados y devolverlos, de modo que la llamada a la función o la asignación vayan al lugar correcto. Tal vez C # tenga un sistema similar, pero podría no ser posible debido a que está tipado estáticamente; presumiblemente no puede compilarse ae.setPosition
menos que e haya establecido la posición en la interfaz en alguna parte.También podría hacer que todas sus entidades implementen las interfaces relevantes para todos los componentes que pueda agregarles, con métodos de código auxiliar que generan una excepción. Luego, cuando llama a addComponent, simplemente redirige las funciones de la entidad para la interfaz de ese componente al componente. Aunque un poco complicado.
Pero quizás más fácil sería sobrecargar el operador [] en la clase de entidad para buscar los componentes unidos por la presencia de una propiedad válida y volver que, por lo que se puede asignar a este modo:
e["Name"] = "blah"
. Cada componente deberá implementar su propio GetPropertyByName y la Entidad llama a cada uno por turnos hasta que encuentre al componente responsable de la propiedad en cuestión.fuente
GetProperty
método ... Lo único negativo es la manipulación y comparación de cadenas. Pero ese es un punto discutible.Para ampliar la respuesta de Kylotan, si está utilizando C # 4.0, puede escribir estáticamente una variable para que sea dinámica .
Si hereda de System.Dynamic.DynamicObject, puede anular TryGetMember y TrySetMember (entre los muchos métodos virtuales) para interceptar nombres de componentes y devolver el componente solicitado.
Escribí un poco sobre los sistemas de entidades a lo largo de los años, pero supongo que no puedo competir con los gigantes. Algunas notas de ES (algo desactualizadas y no reflejan necesariamente mi comprensión actual de los sistemas de entidades, pero vale la pena leer, en mi humilde opinión).
fuente
object
parámetro out , pero todo esto se resolverá en tiempo de ejecución. Creo que es una forma glorificada de mantener una interfaz fluida sobre, por ejemplo, la entidad. TryGetComponent (compName, fuera del componente). Sin embargo, no estoy seguro de haber entendido la pregunta :)Veo mucho interés aquí en ES en C #. Echa un vistazo a mi puerto de la excelente implementación de ES Artemis:
https://github.com/thelinuxlich/artemis_CSharp
Además, un juego de ejemplo para que comiences cómo funciona (usando XNA 4): https://github.com/thelinuxlich/starwarrior_CSharp
¡Sugerencias son bienvenidas!
fuente
Decidí usar el excelente sistema de reflexión de C #. Lo basé en el sistema de componentes generales, más una mezcla de las respuestas aquí.
Los componentes se agregan muy fácilmente:
Y se puede consultar por nombre, tipo o (si son comunes, como Salud y Posición) por propiedades:
Y eliminado:
A los datos se accede nuevamente por propiedades:
Que se almacena en el lado del componente; menos gastos generales si no tiene HP:
Intellisense en VS ayuda a la gran cantidad de tipeo, pero en su mayor parte hay poco tipeo, lo que estaba buscando.
Detrás de escena, estoy almacenando
Dictionary<Type, Component>
yComponents
anulando elToString
método para verificar nombres. Mi diseño original usaba principalmente cadenas para nombres y cosas, pero se convirtió en un cerdo con el que trabajar (oh, mira, también tengo que lanzar en todas partes la falta de propiedades de fácil acceso).fuente