Recientemente pregunté sobre cómo separar las entidades de su comportamiento y la respuesta principal vinculada a este artículo: http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/
El concepto final escrito aquí es el de: OBJETO COMO UNA AGREGACIÓN PURA.
Me pregunto cómo podría crear entidades de juego como agregación pura usando C #. Todavía no he entendido el concepto de cómo esto podría funcionar. (¿Quizás la entidad es una matriz de objetos que implementan una determinada interfaz o tipo base?)
Mi pensamiento actual todavía implica tener una clase concreta para cada tipo de entidad que luego implemente las interfaces relevantes (IMoveable, ICollectable, ISpeakable, etc.).
¿Cómo puedo crear una entidad puramente como una agregación sin tener ningún tipo concreto para esa entidad?
fuente
Respuestas:
El enfoque de "agregación pura" descrito por West en ese artículo vinculado evita por completo un objeto de "entidad". Hay componentes, que flotan en la memoria, pero están unidos solo por relaciones implícitas, si es que existen.
Una forma de hacerlo es el llamado enfoque externo . En un sistema de este tipo, los componentes están en manos de sistemas que los administran o los controlan (uso el término "administrar" aquí, pero no debe tomar esto como que estoy sugiriendo que tiene un montón de * clases de Gerentes para mantener tipos de componentes). Por ejemplo, su sistema de física puede aferrarse a un montón de cosas que representan cada cuerpo rígido en su mundo de simulación, y puede exponer esas cosas como componentes de física. Los componentes pueden ser los objetos reales manejados por el subsistema en cuestión, o pueden ser proxies para esos objetos, según sea necesario.
En dicho sistema, no es necesariamente necesario que una clase "Entidad" contenga una colección de referencias a los componentes que la componen; en su lugar, se genera una notificación sobre la creación o destrucción de una "entidad" y cada subsistema que maneja componentes examina la descripción de la entidad creada / destruida (que generalmente se carga a partir de algunos datos) y determina si un componente es necesario para ello.
Una de las ventajas de este enfoque es que obtienes una muy buena localidad de referencia para cada componente. Desafortunadamente, es un poco extraño, en general, y no es el sabor más amigable de las entidades basadas en componentes que he encontrado. A veces es realmente conveniente tener un objeto real que represente una entidad, incluso si ese objeto hace poco más que agregar referencias débiles a componentes que todavía están en otros subsistemas (si nada más, proporciona una manera fácil de enrutar mensajes entre componentes) .
Hay varias buenas maneras de implementar sistemas de objetos de juego orientados a componentes; realmente, realmente, realmente ayuda si tiene una idea sólida de los requisitos que desea de su sistema; puede ver lo que hacen los marcos populares como Unity como ejemplos. Sin establecer requisitos estrictos para usted, puede encontrarse con el problema de "diseñar" sin cesar el sistema sin construirlo realmente, intentando en vano lograr la implementación perfecta. Por alguna razón, he visto esto mucho con los sistemas de componentes.
fuente
La forma en que lo hace Unity es que todos los scripts (en su caso, el código del juego que es específico para un tipo de objeto de juego) derivan de una clase base
MonoBehaviour
, que en sí deriva de la clase más relevante para su casoComponent
. Nunca edita (y no tiene acceso al código de) la clase GameObject.El objeto del juego se encarga de contener todos esos
Component
s. También es aparentemente responsable de llamar a las funciones relevantes en ellos (es decirUpdate
). Unity utiliza la reflexión para determinar qué funciones llamar (consulte la sección "Funciones reemplazables" en esta página ), pero probablemente desee que sean virtuales.Entonces, uno de los mecanismos que usa en Unity es obtener componentes en su objeto de juego actual (o sus hijos) por tipo. Hay algunas propiedades auxiliares que envuelven algunas de las cosas comunes. Por ejemplo, si desea acceder al
Transform
componente del objeto del juego (para controlar la posición / rotación / escala del objeto del juego), normalmente tendría que hacer algo comothis.GetComponent<Transform>().position
eso, pero lo envuelven en unathis.transform.position
llamada de ayuda . Otro patrón común es acceder alRenderer
componente del objeto del juego actual . Entonces, si desea hacer algo como cambiar el material del objeto del juego actual, desde otro script puede hacer algo asíthis.renderer.material = someOtherMaterial
, y su objeto del juego se actualiza adecuadamente.Una de las formas en que esto funciona en Unity es que su editor está configurado de manera que pueda crear objetos de juego en su escena que tengan componentes ya conectados. En el caso de Unity, todos los objetos del juego tienen un
Transform
componente, pero también puede contener tipos integrados comoAudioListener
oRenderer
, que hacen lo que cabría esperar. O bien, puede agregar sus propios componentes que hagan lo que sea que desee que hagan. El editor también expone campos públicos / serializables en sus componentes para que no tenga que crear diferentes scripts si desea usar el mismo código básico, pero cambie algunos números mágicos.En general, es bastante hábil, y sugeriría descargar la versión gratuita de Unity y jugar con la forma en que su sistema de secuencias de comandos está configurado como una prueba bastante decente del concepto de lo que desea lograr.
fuente
Me gusta un enfoque más cercano a la visión de Adam desde el blog de t-machine. Los componentes deben ser solo datos, y los sistemas hacen todo el trabajo con ellos.
Vea un ejemplo de implementación de ES en C #: https://github.com/thelinuxlich/artemis_CSharp
Y un ejemplo de juego que lo usa (XNA 4): https://github.com/thelinuxlich/starwarrior_CSharp
fuente