Nota: estoy programando esto en Javascript, pero en su mayor parte debería ser independiente del lenguaje.
Estoy pensando en convertir mi motor a uno basado en ECS.
Tengo la idea básica ( nota: esto está mal, mira mi respuesta ):
Las entidades son objetos del juego.
Los componentes son bits de funcionalidad ( reactToInput()
) o estado ( position
) que pueden "pegarse" a las entidades.
Los sistemas tienen una lista de entidades que administran y actualizan.
Pero, no estoy muy seguro de obtener la implementación y algunos detalles ...
Pregunta: ¿ puede un sistema operar en diferentes tipos de entidades? Por lo general, doy el ejemplo de una clase llamada Scene
en mi motor, y ahora servirá para este propósito también. Una escena es un contenedor de todos los objetos que pueden renderizarse, actualizarse, afectar el renderizado (luces) y tal vez, en el futuro, incluso los 2DSoundEmitter
objetos. Tiene una interfaz de alto nivel para que el usuario no tenga que preocuparse por el tipo de objeto que está scene.add()
usando y todo ese tipo de cosas.
Me doy cuenta de que Scene
podría ser un sistema. Toma entidades, las almacena y luego puede llamar a sus métodos de actualización, y tal vez incluso hacer algunos cambios de estado. Pero hay un problema: como describí anteriormente, ¡ Scene
se pueden alimentar diferentes tipos de objetos! ¿Qué debo hacer en una situación en la que una escena tiene tanto objetos renderizables ("dibujables") como luces en ella? ¿Debo hacer que verifique las entidades antes de interactuar? O, debería resolverlo en un nivel aún más bajo: hacer un LightSource
componente que se pueda agregar a cualquier objeto, y la luz sería solo una entidad con LightSource
y Position
componentes. ¿Es eso aceptable?
Además, ¿es una buena práctica seguir usando la herencia convencional y las clases tradicionales? Por ejemplo, ¡simplemente no puedo entender cuál sería mi Renderer
ser! No es un sistema, ya que su única función es capturar una cámara y una escena, renderizar todo y aplicar efectos (como sombras). También gestiona el contexto, el ancho y la altura del juego, hace traducciones ... ¡Pero todavía no es un sistema!
Editar: ¿ podría vincular los recursos que encontró en el ECS? Tengo problemas para encontrar buenos.
Respuestas:
Déjame ver si al tratar de entender como un desarrollador web / UI JS, puedo ser de ayuda. Además, no vaya demasiado lejos en el agnosticismo lingüístico. Vale la pena estudiar muchos patrones establecidos en otros idiomas, pero se pueden aplicar de manera muy diferente en JS debido a su flexibilidad o realmente no son necesarios debido a la naturaleza maleable del lenguaje. Podría aprovechar algunas oportunidades si escribe su código pensando que JS tiene el mismo conjunto de límites que un lenguaje más clásico orientado a OOP.
En primer lugar, en el factor "no usar OOP", recuerde que los objetos de JavaScript son como plastilina en comparación con otros lenguajes y que realmente debe hacer todo lo posible para construir una pesadilla de esquema de herencia en cascada ya que JS no es clase de composición y composición es mucho más natural. Si está implementando algún tipo de sistema tonto de clase o prototipo en su JS, considere deshacerse de él. En JS usamos cierres, prototipos y pasamos funciones como dulces. Es asqueroso, sucio e incorrecto, pero también poderoso, conciso y así nos gusta.
En realidad, los enfoques pesados de la herencia se explican como un antipatrón en los Patrones de diseño y, por una buena razón, como cualquiera que haya caminado por más de 15 niveles de clase o estructuras similares a la clase para tratar de descubrir dónde diablos la versión reventada de un método venía de puedo decirte.
No sé por qué a tantos programadores les encanta hacer esto (especialmente los chicos de Java que escriben JavaScript por alguna razón), pero es horrible, ilegible y completamente imposible de mantener cuando se usa en exceso. La herencia está bien aquí y allá, pero no es realmente necesaria en JS. En los lenguajes donde es un atajo más atractivo, realmente debería reservarse para preocupaciones de arquitectura más abstracta en lugar de esquemas de modelado literales como frankensteining una implementación zombie a través de una cadena de herencia que incluía un BunnyRabbit porque funcionó. Eso no es buena reutilización de código. Es una pesadilla de mantenimiento.
Como JS dev Entity / Component / System, los motores basados me parecen un sistema / patrón para desacoplar problemas de diseño y luego componer objetos para su implementación en un nivel altamente granular. En otras palabras, un juego de niños en un lenguaje como JavaScript. Pero déjame ver si estoy asimilando esto correctamente primero.
Entidad: lo específico que está diseñando. Estamos hablando más en la dirección de los nombres propios (pero no en realidad, por supuesto). No 'Scene', sino 'IntroAreaLevelOne'. IntroAreaLevelOne podría estar dentro de un cuadro sceneEntity de algún tipo, pero nos estamos centrando en algo específico que varía de otras cosas relacionadas. En el código, una entidad es realmente solo un nombre (o ID) vinculado a un montón de cosas que necesita haber implementado o establecido (los componentes) para ser útil.
Componentes: tipos de cosas que una entidad necesita. Estos son sustantivos generales. Como caminar, animación. Dentro de WalkingAnimation podemos ser más específicos, como "Shambling" (buena opción para zombies y monstruos de plantas), o "ChickenWalker" (ideal para los tipos de robot ed-209ish de articulación inversa). Nota: No estoy seguro de cómo eso podría desacoplarse de la representación de un modelo 3D como ese, por lo que tal vez sea un ejemplo de mierda, pero soy más un JS profesional que un desarrollador de juegos experimentado. En JS, pondría el mecanismo de mapeo en el mismo cuadro con los componentes. Es probable que los componentes por derecho propio sean ligeros en lógica y más en una hoja de ruta que le indique a sus sistemas qué implementar si los sistemas son necesarios (en mi intento de ECS, algunos componentes son solo colecciones de conjuntos de propiedades). Una vez que se establece un componente, '
Sistemas: el verdadero programa de carne está aquí. Los sistemas de inteligencia artificial se construyen y se vinculan, se logra el renderizado, se establecen secuencias de animaciones, etc. Estoy recortando y dejando estos principalmente a la imaginación, pero en el ejemplo System.AI toma un montón de propiedades y escupe una función que se usa para agregar controladores de eventos al objeto que finalmente se usa en la implementación. La clave de System.AI es que cubre varios tipos de componentes. Podría ordenar todas las cosas de IA con un componente, pero hacerlo es malinterpretar el punto de hacer que las cosas sean granulares.
Tenga en cuenta los objetivos: queremos facilitar la conexión de algún tipo de interfaz GUI para que los no diseñadores puedan modificar fácilmente diferentes tipos de cosas maximizando y combinando componentes dentro de un paradigma que tenga sentido para ellos, y queremos alejarnos de esquemas de código arbitrario populares que son mucho más fáciles de escribir que modificar o mantener.
Entonces, en JS, tal vez algo como esto. Los desarrolladores de juegos, por favor, dime si me equivoqué terriblemente:
Ahora, cada vez que necesite un NPC, puede construir con
npcBuilders.<npcName>();
Una GUI podría conectarse a los objetos npcEntities y componentes y permitir a los diseñadores ajustar entidades antiguas o crear nuevas entidades simplemente mezclando y combinando componentes (aunque no hay ningún mecanismo para componentes no predeterminados, pero se pueden agregar componentes especiales sobre la marcha). código siempre que haya un componente definido para él.
fuente
He leído sobre Entity Systems en los artículos que las personas amables proporcionaron en los comentarios, pero aún tenía algunas dudas, así que hice otra pregunta .
En primer lugar, mis definiciones estaban equivocadas. Las entidades y los componentes son simplemente titulares de datos tontos, mientras que los sistemas proporcionan toda la funcionalidad.
Aprendí lo suficiente para cubrir la mayor parte de mi pregunta aquí, así que la responderé.
La
Scene
clase de la que estaba hablando no debería ser un sistema. Sin embargo, debería ser un administrador central que pueda contener todas las entidades, facilitar mensajes y tal vez incluso administrar sistemas. También puede funcionar como una especie de fábrica para entidades, y he decidido usarlo así. Se puede tomar cualquier tipo de entidad, pero entonces tiene que alimentar a esa entidad a un sistema apropiado (que, por razones de rendimiento, no debe realizar ningún tipo de controles, a menos que estén a nivel de bits).No debería usar ningún OOP al implementar un ES, sugiere Adam, pero no encuentro ninguna razón para no tener objetos con métodos para entidades y componentes, no solo titulares de datos tontos.
El
Renderer
simplemente puede implementarse como un sistema. Mantendría una lista de objetos dibujables y llamaría aldraw()
método de su componente de representación cada 16 ms.fuente
Introducción a la gestión de dependencias 101.
Este curso asume que tiene conocimientos básicos de inyección de dependencia y diseño de repositorio.
La inyección de dependencia es solo una forma elegante de que los objetos se comuniquen entre sí (a través de mensajes / señales / delegados / lo que sea) sin estar directamente acoplados.
Va por la frase: "
new
es pegamento".Estaré demostrando esto en C #.
Este es un sistema básico para entrada, movimiento y renderizado. La
Player
clase es la entidad en este caso y los componentes son las interfaces. LaPlayer
clase no sabe nada sobre el funcionamiento interno de la forma concretaIRenderSystem
,IMovementSystem
oIInputSystem
el trabajo. Sin embargo, las interfaces proporcionan una forma dePlayer
enviar señales (por ejemplo, invocar Draw en el IRenderSystem) sin depender de cómo se logre el resultado final.Por ejemplo, tome mi implementación de IMovementSystem:
MovementSystem
puede tener sus propias dependencias yPlayer
ni siquiera le importaría. Mediante el uso de interfaces, se puede crear una máquina de estado del juego:Y ese es el comienzo de un juego encantador (que también es comprobable por unidad).
Y con algunas adiciones y algo de polimorfismo:
Ahora tenemos un sistema de entidad / componente primitivo basado en interfaces de agregación y herencia suelta.
fuente