Voy a hablar de un poco de experiencia, pasando de un diseño OO rígido a un diseño de Sistema de Componentes de Entidad (ECS).
Hace un tiempo, era como tú , tenía un montón de diferentes tipos de cosas que tenían propiedades similares y construí varios objetos e intenté usar la herencia para resolverlo. Una persona muy inteligente me dijo que no hiciera eso y, en cambio, usara Entity-Component-System.
Ahora, ECS es un gran concepto, y es difícil acertar. Se necesita mucho trabajo para construir entidades, componentes y sistemas de manera adecuada. Sin embargo, antes de que podamos hacer eso, necesitamos definir los términos.
- Entidad : esta es la cosa , el jugador, el animal, el NPC, lo que sea . Es algo que necesita componentes unidos a él.
- Componente : este es el atributo o propiedad , como un "Nombre" o "Edad", o "Padres", en su caso.
- Sistema : esta es la lógica detrás de un componente o un comportamiento . Por lo general, construye un sistema por componente, pero eso no siempre es posible. Además, a veces los sistemas necesitan influir en otros sistemas.
Entonces, aquí es donde iría con esto:
En primer lugar, crea una ID
para tus personajes. An int
, Guid
lo que quieras. Esta es la "Entidad".
En segundo lugar, comience a pensar en los diferentes comportamientos que tiene. Cosas como el "árbol genealógico", es un comportamiento. En lugar de modelar eso como atributos en la entidad, cree un sistema que contenga toda esa información . El sistema puede decidir qué hacer con él.
Del mismo modo, queremos construir un sistema para "¿Está vivo o muerto el personaje?" Este es uno de los sistemas más importantes en su diseño, ya que influye en todos los demás. Algunos sistemas pueden eliminar los caracteres "muertos" (como el sistema "sprite"), otros sistemas pueden reorganizar internamente las cosas para soportar mejor el nuevo estado.
Construirá un sistema "Sprite" o "Drawing" o "Rendering", por ejemplo. Este sistema tendrá la responsabilidad de determinar con qué sprite debe mostrarse el personaje y cómo mostrarlo. Luego, cuando un personaje muere, retíralo.
Además, un sistema de "IA" que puede decirle a un personaje qué hacer, a dónde ir, etc. Esto debería interactuar con muchos de los otros sistemas y tomar decisiones basadas en ellos. Una vez más, los personajes muertos probablemente se puedan eliminar de este sistema, ya que en realidad ya no están haciendo nada.
Su sistema "Nombre" y el sistema "Árbol genealógico" probablemente deberían mantener al personaje (vivo o muerto) en la memoria. Este sistema necesita recuperar esa información, independientemente del estado del personaje. (Jim sigue siendo Jim, incluso después de enterrarlo).
Esto también le brinda el beneficio de cambiar cuando un sistema reacciona de manera más eficiente: el sistema tiene su propio temporizador. Algunos sistemas necesitan dispararse rápidamente, otros no. Aquí es donde comenzamos a entrar en lo que hace que un juego funcione de manera eficiente. No necesitamos volver a calcular el clima cada milisegundo, probablemente podamos hacerlo cada 5 o más.
También le da más influencia creativa: puede construir un sistema "Pathfinder" que puede manejar el cálculo de una ruta de A a B, y puede actualizarse según sea necesario, permitiendo que el sistema de Movimiento diga "¿Dónde necesito? ¿siguiente?" Ahora podemos separar completamente estas preocupaciones y razonar sobre ellas de manera más efectiva. El movimiento no necesita encontrar el camino, solo necesita llevarte allí.
Querrá exponer algunas partes de un sistema al exterior. En su Pathfinder
sistema probablemente querrá un Vector2 NextPosition(int entity)
. De esta manera, puede mantener esos elementos en matrices o listas estrechamente controladas. Puede usar struct
tipos más pequeños, que pueden ayudarlo a mantener los componentes en bloques de memoria contiguos más pequeños, lo que puede hacer que las actualizaciones del sistema sean mucho más rápidas. (Especialmente si las influencias externas en un sistema son mínimas, ahora solo necesita preocuparse por su estado interno, como por ejemplo Name
).
Pero, y no puedo enfatizar esto lo suficiente, ahora un Entity
es solo un ID
, incluyendo mosaicos, objetos, etc. Si una entidad no pertenece a un sistema, entonces el sistema no lo rastreará. Esto significa que podemos crear nuestros objetos "Árbol", almacenarlos en los sistemas Sprite
y Movement
(los árboles no se moverán, pero tienen un componente de "Posición") y mantenerlos fuera de los otros sistemas. Ya no necesitamos una lista especial para los árboles, ya que renderizar un árbol no es diferente a un personaje, aparte del papel. (Lo que el Sprite
sistema puede controlar, o el Paperdoll
sistema puede controlar). Ahora NextPosition
puede reescribirse ligeramente: Vector2? NextPosition(int entity)
y puede devolver una null
posición para entidades que no le importan. También aplicamos esto a nuestro NameSystem.GetName(int entity)
, vuelve null
para árboles y rocas.
Voy a terminar esto, pero la idea aquí es darle algunos antecedentes sobre ECS, y cómo realmente puede aprovecharlo para obtener un mejor diseño en su juego. Puede aumentar el rendimiento, desacoplar elementos no relacionados y mantener las cosas de una manera más organizada. (Esto también se combina bien con lenguajes / configuraciones funcionales, como F # y LINQ, que recomiendo consultar F # si aún no lo ha hecho, se combina muy bien con C # cuando los usa en conjunto).