Motor basado en el sistema de componentes de la entidad

9

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 Sceneen 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 2DSoundEmitterobjetos. 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 Scenepodrí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, ¡ Scenese 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 LightSourcecomponente que se pueda agregar a cualquier objeto, y la luz sería solo una entidad con LightSourcey Positioncomponentes. ¿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 Rendererser! 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.

jcora
fuente
2
En lugar de volver a publicar la respuesta en esta página, solo daré este enlace: gamedev.stackexchange.com/questions/23533/… La entidad no debe derivarse, las diferencias entre entidades deben lograrse a través de los componentes. En general, necesitará una interfaz para cada sistema principal (renderizado, física, redes, entrada, audio, etc.). La forma en que configuré mi renderizador es consultar la escena en busca de entidades renderizables, y el administrador de escena le pregunta a cada entidad que tiene un componente de renderizado su información de renderizado.
Nic Foster
1
Diseño de componentes en el blog T = Machine (ya que pidió uno bueno)
John McDonald
Código y discusión de un marco de entidad: gamadu.com/artemis
Patrick Hughes
@JohnMcDonald, escribí un comentario sobre ese artículo, aunque está esperando moderación. Puede verlo aquí: t-machine.org/index.php/2007/12/22/… . Soy "Yannbane".
jcora
Además, @NicFoster, el artículo al que John se vinculó en T = Machine describe algo diferente a su respuesta ... Para eso Dave, las entidades no tienen una lista de componentes, son solo un nombre. Como "flsjn304", eso es una entidad. Se almacena "en alguna parte". Y debo volver a leer la cosa para entender si realmente tiene componentes dentro de los sistemas , ¡lo cual me parece muy extraño!
jcora

Respuestas:

6

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:

//I'm going with simple objects of flags over arrays of component names
//easier to read and can provide an opt-out default
//Assume a genre-bending stealth assassin game

//new (function etc... is a lazy way to define a constructor and auto-instantiate
var npcEntities = new (function NpcEntities(){

    //note: {} in JS is an object literal, a simple obj namespace (a dictionary)
    //plain ol' internal var in JS is akin to a private member
    var default={ //most NPCs are humanoids and critters - why repeat things?
        speedAttributes:true,
        maneuverAttributes:true,
        combatAttributes:true,
        walkingAnimation:true,
        runningAnimation:true,
        combatAnimation:true,
        aiOblivious:true,
        aiAggro:true,
        aiWary:true, //"I heard something!"
        aiFearful:true
    };

    //this. exposes as public

    this.zombie={ //zombies are slow, but keep on coming so don't need these
        runningAnimation:false,
        aiFearful:false
    };

    this.laserTurret={ //most defaults are pointless so ignore 'em
        ignoreDefault:true,
        combatAttributes:true,
        maneuverAttrubtes:true, //turning speed only
    };
    //also this.nerd, this.lawyer and on and on...

    //loop runs on instantiation which we're forcing on the spot

    //note: it would be silly to repeat this loop in other entity collections
    //but I'm spelling it out to keep things straight-forward.
    //Probably a good example of a place where one-level inheritance from
    //a more general entity class might make sense with hurting the pattern.
    //In JS, of course, that would be completely unnecessary. I'd just build a
    //constructor factory with a looping function new objects could access via
    //closure.

    for(var x in npcEntities){

        var thisEntity = npcEntities[x];

        if(!thisEntity.ignoreDefaults){

            thisEntity = someObjectXCopyFunction(defaults,thisEntity);
            //copies entity properties over defaults

        }
        else {
            //remove nonComponent property since we loop again later
            delete thisEntity.ignoreDefaults;
        }
    }
})() //end of entity instantiation

var npcComponents = {
    //all components should have public entityMap properties

    //No systems in use here. Just bundles of related attributes
    speedAttributes: new (function SpeedAttributes(){
        var shamblingBiped = {
            walkingAcceleration:1,
            topWalking:3
        },
        averageMan = {
            walkingAcceleration:3,
            runningAcceleration:4,
            topWalking: 4,
            topRunning: 6
        },
        programmer = {
            walkingAcceleration:1,
            runningAcceleration:100,
            topWalking:2
            topRunning:2000
        }; //end local/private vars

        //left is entity names | right is the component subcategory
        this.entityMap={
            zombie:shamblingBiped,
            lawyer:averageMan,
            nerd:programmer,
            gCostanza:programmer //makes a cameo during the fire-in-nursery stage
        }
    })(), //end speedAttributes

    //Now an example of an AI component - maps to function used to set eventHandlers
    //functions which, because JS is awesome we can pass around like candy
    //I'll just use some imaginary systems on this one

    aiFearful: new (function AiFearful(){
        var averageMan = Systems.AI({ //builds and returns eventSetting function
            fearThreshold:70, //%hitpoints remaining
            fleeFrom:'lastAttacker',
            tactic:'avoidIntercept',
            hazardAwareness:'distracted'
        }),
        programmer = Systems.AI({
            fearThreshold:95,
            fleeFrom:'anythingMoving',
            tactic:'beeline',
            hazardAwareness:'pantsCrappingPanic'
        });//end local vars/private members


         this.entityMap={
            lawyer:averageMan,
            nerd:averageMan, //nerds can run like programmers but are less cowardly
            gCostanza:programmer //makes a cameo during the fire-in-nursery stage
        }
    })(),//and more components...

    //Systems.AI is general and would get called for all the AI components.
    //It basically spits out functions used to set events on NPC objects that
    //determine their behavior. You could do it all in one shot but
    //the idea is to keep it granular enough for designers to actually tweak stuff
    //easily without tugging on developer pantlegs constantly.
    //e.g. SuperZombies, zombies, but slightly tougher, faster, smarter
}//end npcComponents

function createNPCConstructor(npcType){

    var components = npcEntities[npcType],

    //objConstructor is returned but components is still accessible via closure.

    objConstructor = function(){
        for(var x in components){
            //object iteration <property> in <object>

            var thisComponent = components[x];

            if(typeof thisComponent === 'function'){
                thisComponent.apply(this);
                //fires function as if it were a property of instance
                //would allow the function to add additional properties and set
                //event handlers via the 'this' keyword
            }
            else {
                objConstructor.prototype[x] = thisComponent;
                //public property accessed via reference to constructor prototype
                //good for low memory footprint among other things
            }
        }
    }
    return objConstructor;
}

var npcBuilders= {}; //empty object literal
for (var x in npcEntities){
    npcConstructors[x] = createNPCConstructor(x);
}

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.

Erik Reppen
fuente
Mirando esto seis años después, no estoy seguro de entender mi propia respuesta. ¿Se podría mejorar esto?
Erik Reppen
1

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 Sceneclase 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 Renderersimplemente puede implementarse como un sistema. Mantendría una lista de objetos dibujables y llamaría al draw()método de su componente de representación cada 16 ms.

jcora
fuente
1
"Las entidades y los componentes son simplemente titulares de datos tontos, mientras que los sistemas proporcionan toda la funcionalidad" "llame al método draw () de su componente de renderizado" todavía está confundido, además de que un método "draw" derrota el propósito del sistema de renderizado por completo. Además, no entiendo por qué su gráfico de escena no puede ser parte del Renderer, es solo una herramienta conveniente, siempre puede implementar su componente "dibujable" como un nodo. Hacer que el gráfico de la escena sea responsable de algo más que la escena es simplemente innecesario y estoy seguro de que será un desastre depurarlo.
dreta
@dreta, el renderizador actualmente (implementación del motor que no es ES) hace transformaciones, cambios de cámara, alfa, y en el futuro dibujará varios efectos, la GUI y las sombras. Parecía natural agrupar esas cosas. ¿No debería la escena ser responsable de crear una entidad de almacenamiento? ¿O debería algo más almacenarlos? La parte de creación es probablemente solo unas pocas líneas de agregación de componentes proporcionados por el usuario, no está realmente "creando" nada en absoluto, solo instancias.
jcora
no todos los objetos son renderizables, no todos los objetos pueden chocar o emitir un sonido, con su objeto de escena está haciendo un acoplamiento extremo, ¿por qué? esto va a ser una molestia para escribir y depurar. Las entidades se utilizan para identificar un objeto, los componentes contienen datos y los sistemas funcionan con los datos. ¿Por qué mezclaría todo eso en lugar de tener sistemas adecuados como RenderingSystem y SoundSystem y solo molestaría a esos sistemas si una entidad tiene todos los componentes necesarios?
dreta
1
La proyección de sombras generalmente se adjunta a las fuentes de luz, aunque puede crear un componente "CastsShadow" y buscarlo al representar objetos dinámicos. Si está haciendo 2D, entonces este es solo un problema básico de pedido, un algoritmo de pintor simple resolverá este problema por usted. TBH te preocupas demasiado pronto. lo descubrirás cuando sea el momento de hacerlo y solo tienes eso en mente, en este momento solo te estás confundiendo. no puedes esperar hacer todo bien la primera vez, simplemente no va a suceder. cruzarás ese puente cuando llegues a él.
dreta
1
"Las entidades y los componentes son simplemente titulares de datos tontos, mientras que los sistemas proporcionan toda la funcionalidad". No necesariamente. Están en los enfoques de algunas personas. Pero no otros. Mire el motor de Unity: todo el comportamiento está en los componentes.
Kylotan
-2

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: " newes pegamento".

Estaré demostrando esto en C #.

public interface IEntity
{
    int[] Position { get; }
    int[] Size { get; }
    bool Update();
    void Render();
}

public interface IRenderSystem
{
    void Draw(IEntity entity);
}

public interface IMovementSystem
{
    bool CanMoveLeft();
    void MoveLeft();
    bool CanMoveRight();
    void MoveRight();
    bool CanMoveUp();
    void MoveUp();
    bool CanMoveDown();
    void MoveDown();
    bool Moved();
    int[] Position { get; set; }
}

public interface IInputSystem
{
    string Direction { get; set; }
}

public class Player : IEntity
{
    private readonly IInputSystem _inputSystem;
    private readonly IMovementSystem _movementSystem;
    private readonly IRenderSystem _renderSystem;
    private readonly int[] _size = new[] { 10, 10 };

    public Player(IRenderSystem renderSystem, IMovementSystem movementSystem, IInputSystem inputSystem)
    {
        _renderSystem = renderSystem;
        _movementSystem = movementSystem;
        _inputSystem = inputSystem;
    }

    public bool Update()
    {
        if (_inputSystem.Direction == "Left" && _movementSystem.CanMoveLeft())
            _movementSystem.MoveLeft();
        if (_inputSystem.Direction == "Right" && _movementSystem.CanMoveRight())
            _movementSystem.MoveRight();
        if (_inputSystem.Direction == "Up" && _movementSystem.CanMoveUp())
            _movementSystem.MoveUp();
        if (_inputSystem.Direction == "Down" && _movementSystem.CanMoveDown())
            _movementSystem.MoveDown();

        return _movementSystem.Moved();
    }

    public void Render()
    {
        if (_movementSystem.Moved())
            _renderSystem.Draw(this);
    }

    public int[] Position
    {
        get { return _movementSystem.Position; }
    }

    public int[] Size
    {
        get { return _size; }
    }
}

Este es un sistema básico para entrada, movimiento y renderizado. La Playerclase es la entidad en este caso y los componentes son las interfaces. La Playerclase no sabe nada sobre el funcionamiento interno de la forma concreta IRenderSystem, IMovementSystemo IInputSystemel trabajo. Sin embargo, las interfaces proporcionan una forma de Playerenviar 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:

public interface IGameMap
{
    string LeftOf(int[] currentPosition);
    string RightOf(int[] currentPosition);
    string UpOf(int[] currentPosition);
    string DownOf(int[] currentPosition);
}

public class MovementSystem : IMovementSystem
{
    private readonly IGameMap _gameMap;
    private int[] _previousPosition;
    private readonly int[] _currentPosition;
    public MovementSystem(IGameMap gameMap, int[] initialPosition)
    {
        _gameMap = gameMap;
        _currentPosition = initialPosition;
        _previousPosition = initialPosition;
    }

    public bool CanMoveLeft()
    {
        return _gameMap.LeftOf(_currentPosition) == "Unoccupied";
    }

    public void MoveLeft()
    {
        _previousPosition = _currentPosition;
        _currentPosition[0]--;
    }

    public bool CanMoveRight()
    {
        return _gameMap.RightOf(_currentPosition) == "Unoccupied";
    }

    public void MoveRight()
    {
        _previousPosition = _currentPosition;
        _currentPosition[0]++;
    }

    public bool CanMoveUp()
    {
        return _gameMap.UpOf(_currentPosition) == "Unoccupied";
    }

    public void MoveUp()
    {
        _previousPosition = _currentPosition;
        _currentPosition[1]--;
    }

    public bool CanMoveDown()
    {
        return _gameMap.DownOf(_currentPosition) == "Unoccupied";
    }

    public void MoveDown()
    {
        _previousPosition = _currentPosition;
        _currentPosition[1]++;
    }

    public bool Moved()
    {
        return _previousPosition == _currentPosition;
    }

    public int[] Position
    {
        get { return _currentPosition; }
    }
}

MovementSystempuede tener sus propias dependencias y Playerni siquiera le importaría. Mediante el uso de interfaces, se puede crear una máquina de estado del juego:

public class GameEngine
{
    private readonly List<IEntity> _entities;
    private List<IEntity> _renderQueue; 

    public GameEngine()
    {
        _entities = new List<IEntity>();
    }

    public void RegisterEntity(IEntity entity)
    {
        _entities.Add(entity);
    }

    public void Update()
    {
        _renderQueue = new List<IEntity>();
        foreach (var entity in _entities)
        {
            if(entity.Update())
                _renderQueue.Add(entity);
        }
        // Linq version for those interested
        //_renderQueue.AddRange(_entities.Where(e => e.Update()));
    }

    public void Render()
    {
        foreach (var entity in _renderQueue)
        {
            entity.Render();
        }
    }
}

Y ese es el comienzo de un juego encantador (que también es comprobable por unidad).

Y con algunas adiciones y algo de polimorfismo:

public interface IEntity
{
}

public interface IRenderableEntity : IEntity
{
    void Render();        
}

public interface IUpdateableEntity : IEntity
{
    void Update();
    bool Updated { get; }
}

public interface IRenderSystem
{
    void Draw(IRenderableEntity entity);
}

// new player class
public class Player : IRenderableEntity, IUpdateableEntity
{
    private readonly IInputSystem _inputSystem;
    private readonly IMovementSystem _movementSystem;
    private readonly IRenderSystem _renderSystem;
    private readonly int[] _size = new[] { 10, 10 };

    public Player(IRenderSystem renderSystem, IMovementSystem movementSystem, IInputSystem inputSystem)
    {
        _renderSystem = renderSystem;
        _movementSystem = movementSystem;
        _inputSystem = inputSystem;
    }

    public void Update()
    {
        if (_inputSystem.Direction == "Left" && _movementSystem.CanMoveLeft())
            _movementSystem.MoveLeft();
        if (_inputSystem.Direction == "Right" && _movementSystem.CanMoveRight())
            _movementSystem.MoveRight();
        if (_inputSystem.Direction == "Up" && _movementSystem.CanMoveUp())
            _movementSystem.MoveUp();
        if (_inputSystem.Direction == "Down" && _movementSystem.CanMoveDown())
            _movementSystem.MoveDown();
    }

    public bool Updated
    {
        get { return _movementSystem.Moved(); }
    }

    public void Render()
    {
        if (_movementSystem.Moved())
            _renderSystem.Draw(this);
    }

    public int[] Position
    {
        get { return _movementSystem.Position; }
    }

    public int[] Size
    {
        get { return _size; }
    }
}

public class GameEngine
{
    private readonly List<IEntity> _entities;
    private List<IRenderableEntity> _renderQueue; 

    public GameEngine()
    {
        _entities = new List<IEntity>();
    }

    public void RegisterEntity(IEntity entity)
    {
        _entities.Add(entity);
    }

    public void Update()
    {
        _renderQueue = new List<IRenderableEntity>();
        foreach (var entity in _entities)
        {
            if (entity is IUpdateableEntity)
            {
                var updateEntity = entity as IUpdateableEntity;
                updateEntity.Update();
            }

            if (entity is IRenderableEntity)
            {
                var renderEntity = entity as IRenderableEntity;
                _renderQueue.Add(renderEntity);
            }
        }
    }

    public void Render()
    {
        foreach (var entity in _renderQueue)
        {
            entity.Render();
        }
    }
}

Ahora tenemos un sistema de entidad / componente primitivo basado en interfaces de agregación y herencia suelta.

Dustin Kingen
fuente
1
Esto va en contra del diseño de componentes :) ¿Qué haría si quisiera que un jugador emitiera sonidos y otro no?
Kikaimaru
@Kikaimaru Pase en un ISoundSystem que no reproduce sonido. es decir, no hace nada.
Dustin Kingen
3
-1, no porque sea un código incorrecto, sino porque no es relevante en absoluto para la arquitectura basada en componentes; de hecho, es la proliferación de interfaces como esta lo que los componentes intentan evitar.
Kylotan
@Kylotan Creo que mi comprensión debe estar equivocada.
Dustin Kingen