Двигун на базі системних компонентів


9

Примітка: я програмую це в JavaScript, але це має бути мовною агностикою.

Я думаю про перетворення свого двигуна на базі ECS.

Я отримую основну думку ( зауважте: це неправильно, дивіться мою відповідь ):

Суб'єкти - це ігрові об’єкти.
Компоненти - це біти функціональності ( reactToInput()) або стану ( position), які можуть бути "приклеєні" до сутностей.
Системи мають перелік об'єктів, якими вони керують та оновлюють.

Але я не зовсім впевнений, що отримую реалізацію та деякі деталі ...

Питання: чи може система функціонувати на різних типах сутностей? Я зазвичай навожу приклад класу, який називається Sceneв моєму двигуні, і він буде виконувати цю мету і зараз. Сцена - це контейнер з усіма об'єктами, які можуть бути відтворені, оновлені, впливати на візуалізацію (вогні), а, можливо, у майбутньому навіть 2DSoundEmitterоб’єкти. Він має інтерфейс високого рівня, тому користувачеві не потрібно турбуватися про тип об'єкта, який він використовує scene.add(), та всі подібні речі.

Я усвідомлюю, що Sceneможе бути системою. Він бере участь у об'єктах, зберігає їх, а потім він може викликати їх методи оновлення, а може навіть і змінити стан. Але є проблема: як я описав вище, Sceneможна подавати різні типи предметів! Що мені робити, скажімо, у ситуації, коли на сцені є як відтворювані об'єкти ("малюнки"), так і вогні? Чи потрібно зробити це для перевірки типу даних перед взаємодією? Або я повинен вирішити це на ще нижчому рівні: зробіть LightSourceкомпонент, який можна додати до будь-якого об’єкта, і світло буде просто сутністю LightSourceі Positionкомпонентами. Це прийнятно?

Також, чи є хорошою практикою все-таки використовувати традиційне успадкування та традиційні класи? Наприклад, я просто не можу зрозуміти, що б мені Rendererбуло! Це не система, оскільки її єдина функція - знімати камеру та сцену, рендерувати все та застосовувати ефекти (наприклад, тіні). Він також керує контекстом, шириною та висотою гри, робить переклади ... Але це все ще не система!

Редагувати: чи могли ви зв’язати будь-які ресурси, знайдені на ECS? У мене виникають проблеми з пошуком хороших.


2
Замість того, щоб перезаписати відповідь на цій сторінці, я просто наведу це посилання: gamedev.stackexchange.com/questions/23533/… Суб'єкт не повинен виходити з цього, будь-які відмінності між сутностями мають бути досягнуті через компоненти. Як правило, вам знадобиться інтерфейс для кожної основної системи (Візуалізація, Фізика, Мережа, Введення, Аудіо тощо). Те, як я налаштував мого рендерінга, - це запитувати сцену для відтворюваних об'єктів, а менеджер сцени запитує інформацію про візуалізацію у кожної сутності, яка має компонент візуалізації.
Нік Фостер

1
Дизайн компонентів у блозі T = Machine (оскільки ви попросили хорошого)
Джон Макдональд

Код та обговорення структури сутності: gamadu.com/artemis
Патрік Х'юз

@JohnMcDonald, я написав коментар до цієї статті, хоча очікує поміркування. Ви можете побачити його тут: t-machine.org/index.php/2007/12/22/… . Я "Янбан".
jcora

Крім того, @NicFoster, стаття, до якої Джон посилається на T = Machine, описує щось своєрідне, ніж ваша відповідь ... На те, що Дейв, у суб'єктів немає списку компонентів, вони є лише назвою. Як "flsjn304" - це сутність. Він зберігається «десь». І я повинен перечитати річ, щоб зрозуміти, чи він насправді зберігає компоненти всередині систем , що мені здається дуже дивним!
jcora

Відповіді:


6

Дозвольте мені побачити, якщо, намагаючись зрозуміти, як веб / UI JS Dev, я можу вам допомогти. Крім того, не надто далеко в мовному агностицизмі. Багато моделей, встановлених іншими мовами, варто вивчити, але їх можна застосовувати в JS дуже по-різному через його гнучкість або насправді не потрібні через спритний характер мови. Ви можете створити певні можливості, якщо ви пишете свій код, думаючи про JS, як той, що має той самий набір меж, що і більш класична мова, орієнтована на ООП.

Перш за все, про фактор "не використовувати OOP" пам’ятайте, що об’єкти JavaScript схожі на playdough порівняно з іншими мовами, і ви насправді мусите вийти зі свого шляху, щоб створити кошмарну схему наслідування каскаду, оскільки JS не є класом -на основі та композиції до цього йде набагато природніше. Якщо ви впроваджуєте в своєму JS якусь нерозумну систему класу чи прототипу "руко-мене", подумайте, як це зробити. У JS ми використовуємо закриття, прототипи і передаємо функції навколо, як цукерки. Це огидно, брудно і неправильно, але також потужно, лаконічно, і це нам подобається.

Підходи до успадкування важких підходів насправді прописані як анти-модель у шаблонах дизайну і з поважної причини, як кожен, хто знизив 15+ рівнів, що варті класових або класоподібних структур, щоб спробувати розібратися, куди, до біса, розбита версія методу приїжджав з може сказати вам.

Я не знаю, чому так багато програмістів люблять це робити (особливо хлопці з Java, які чомусь пишуть JavaScript), але це жахливо, нерозбірливо і зовсім нездійсненно, коли використовується надмір. Спадщина тут і там добре, але насправді не потрібно в JS. На мовах, де це більш привабливий ярлик, він дійсно повинен бути зарезервований для більш абстрактних архітектурних проблем, а не для більш буквальних схем моделювання, таких як франкенстенінг реалізації зомбі через ланцюг спадкування, який включав BunnyRabbit, оскільки це трапилось. Це не добре використання коду. Це кошмар технічного обслуговування.

Як двигуни, засновані на JS Entity / Component / System, вражають мене системою / зразком для розв’язання проблем дизайну та складання об'єктів для реалізації на високому рівні. Іншими словами, дитяча гра такою мовою, як JavaScript. Але дозвольте мені побачити, чи спочатку я правильно це вирішу.

  • Сутність - конкретна річ, яку ви проектуєте. Ми говоримо більше в напрямку власних іменників (але насправді, звичайно). Не "Сцена", а "IntroAreaLevelOne". IntroAreaLevelOne може сидіти в ящику sceneEntity якогось типу, але ми зосереджуємось на чомусь конкретному, що відрізняється від інших пов'язаних речей. У коді суб'єкт справді є лише іменем (або ідентифікатором), пов'язаним з купою речей, які йому потрібно впровадити або встановити (компоненти), щоб бути корисним.

  • Компоненти - типи речей, які потребує суб'єкт господарювання. Це загальні іменники. Як і WalkingAnimation. У WalkingAnimation ми можемо отримати більш конкретні, наприклад, "Shambling" (хороший вибір для зомбі та монстрів рослин) або "ChickenWalker" (чудово підходить для роботів типу "реверс-спільний" ed-209ish). Примітка: Не впевнений, як це могло б відірватися від візуалізації такої 3D-моделі - тож, можливо, приклад, але я більше скористаюся професіоналом JS, ніж досвідченим розробником гри. У JS я б поставив механізм картографування в одне поле з компонентами. Складові самі по собі, ймовірно, виявляють логіку та більше дорожню карту, яка розповість вашим системам, що потрібно реалізувати, якщо такі системи навіть потрібні (в моїй спробі ECS деякі компоненти - це лише колекції наборів властивостей). Після того, як компонент встановлений, він '

  • Системи - Справжнє програмне м'ясо тут. Системи AI будуються та пов’язуються, досягається візуалізація, встановлюються послідовності анімації тощо ... Я викидаю їх і залишаю це в основному уяві, але в прикладі System.AI приймає купу властивостей і виділяє функцію, яка використовується для додавання обробників подій до об'єкта, який врешті-решт звикає до реалізації. Ключовим у System.AI є те, що він охоплює кілька типів компонентів. Ви можете розібрати всі елементи AI з одним компонентом, але зробити це - неправильно зрозуміти сенс деталізації речей.

Майте на увазі цілі: Ми хочемо спростити підключення якогось інтерфейсу GUI для недизайнерів, щоб легко налаштувати різні види матеріалів шляхом максимізації та відповідності компонентів у парадигмі, яка має для них сенс, і ми хочемо піти від популярні довільні схеми коду, які писати набагато простіше, ніж їх змінювати або підтримувати.

Тож у JS, можливо, щось подібне. Ігри-розробники, скажіть, будь ласка, чи я зрозумів це жахливо неправильно:

//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);
}

Тепер будь-який час, коли вам потрібен NPC, ви можете створити його npcBuilders.<npcName>();

Графічний інтерфейс може підключатися до об'єктів npcEntities та компонентів і дозволяти дизайнерам налаштовувати старі об'єкти або створювати нові об'єкти шляхом простого змішування та узгодження компонентів (хоча там немає механізму для компонентів, що не замовчуються, але спеціальні компоненти можуть бути додані під час руху в код, поки для нього був визначений компонент.


Дивлячись на це через шість років, я не впевнений, що розумію власну відповідь. Чи можна це покращити?
Ерік Реппен

1

Я читав про Entity Systems у статтях, які люб’язні люди надавали в коментарях, але у мене все ще були сумніви, тому я задав ще одне питання .

По-перше, мої визначення були помилковими. Суб'єкти та компоненти - просто немічні власники даних, тоді як системи забезпечують всю функціональність.

Я навчився достатньо, щоб висвітлити більшу частину свого питання тут, тому відповім на нього.

SceneКлас я говорив не повинна бути система. Він, однак, повинен бути центральним менеджером, який може вміщувати всі об'єкти, полегшувати повідомлення та, можливо, навіть керувати системами. Він також може функціонувати як фабрика для підприємств, і я вирішив використовувати його так. Він може приймати будь-який тип сутності, але тоді він повинен подати цю сутність у відповідну систему (яка з міркувань продуктивності не повинна здійснювати перевірки типу, якщо вони не є побітними).

Я не повинен використовувати жодного OOP під час впровадження ES, пропонує Адам, але я не знаходжу причин не мати об'єктів із методами як для сутності, так і для компонентів, а не лише тупих власників даних.

RendererМоже просто бути реалізований у вигляді системи. Він підтримуватиме список об'єктів, що малюються, і називав би draw()метод їх візуалізації кожні 16 мс.


1
"Суб'єкти та компоненти - просто немічні власники даних, тоді як системи надають всю функціональність" "викликають метод draw () рендерінгу вашого компонента візуалізації", ви все ще плутаєтесь, окрім того, що метод "draw" загалом перемагає функцію системи візуалізації. Крім того, я не розумію, чому ваш графік Scene не може бути частиною рендерінга, це просто зручний інструмент, ви завжди можете реалізувати свій "малюючий" компонент як вузол. Зробити графік сцени нести відповідальність за більше, ніж сцену просто непотрібно, і я впевнений, що це буде безлад налагодження.
дрета

@dreta, рендерінг в даний час (не-ES реалізація двигуна) робить перетворення, зміни камери, альфа-речі, і в майбутньому він буде мати різні ефекти, графічний інтерфейс і тіні. Просто здавалося природним групувати цей матеріал. Чи не повинна сцена відповідати за створення об'єктів, що зберігають? Або варто щось інше їх зберігати? Частина створення - це, мабуть, лише декілька рядків, що об'єднує компоненти, що надаються користувачем разом, насправді взагалі нічого не "створює", а лише миттєво.
jcora

не кожен об'єкт є видатним, не кожен об'єкт може бути зіткнений з або видає звук. З вашим об'єктом сцени ви робите екстремальну зв'язок, чому? це просто буде біль писати та налагоджувати. Суб'єкти використовуються для ідентифікації об'єкта, компоненти зберігають дані і системи працюють на даних. Чому б ви збивати все це разом, а не мати належні системи, такі як RenderingSystem і SoundSystem, і турбувати ці системи лише тоді, коли суб'єкт господарювання має всі необхідні компоненти.
дрета

1
тіньове лиття зазвичай приєднується до джерел світла, хоча ви можете просто створити компонент "CastsShadow" і шукати його під час надання динамічних об'єктів. якщо ви робите 2D, то це лише основне питання замовлення, простий алгоритм художника вирішить цю проблему для вас. TBH, ти рано чинишся. ти зрозумієш це, коли настав час це зробити, і ти маєш це лише на увазі, зараз ти просто плутаєш себе. Ви не можете сподіватися, що все вийде все правильно, це просто не відбудеться. ви перетнете цей міст, коли дістанетесь до нього.
дрета

1
"Суб'єкти та компоненти - просто немічні власники даних, тоді як системи забезпечують всю функціональність." Не обов'язково. Вони є в підходах деяких людей. Але не інші. Подивіться на двигун Unity - вся поведінка у складових.
Килотан

-2

Вступ до управління залежностями 101.

Цей курс передбачає, що ви володієте базовими знаннями щодо введення залежностей та дизайну сховищ.

Введення залежності - це просто фантазійний спосіб, коли об'єкти можуть говорити один одному (через повідомлення / сигнали / делегати / що завгодно), не будучи безпосередньо пов'язаними.

Це йде за фразою: " newє клей".

Я продемонструю це на 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; }
    }
}

Це основна система для введення, переміщення та надання. PlayerКлас є об'єктом в даному випадку і компоненти є інтерфейсами. PlayerКлас нічого не знає про внутрішній , як конкретна IRenderSystem, IMovementSystemчи IInputSystemроботі. Однак інтерфейси забезпечують спосіб Playerпередачі сигналів (наприклад, виклик Draw в IRenderSystem), не залежно від того, яким чином виконаний кінцевий результат.

Для прикладу візьміть мою реалізацію 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; }
    }
}

MovementSystemможе мати власні залежності і Playerнавіть не хвилює. За допомогою інтерфейсів може бути створений ігровий автомат:

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();
        }
    }
}

І це початок чудової гри (це теж одиничне випробування).

І з кількома доповненнями та деяким поліморфізмом:

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();
        }
    }
}

Зараз у нас є примітивна система об'єктів / компонентів, заснована на агрегуванні інтерфейсів і вільному успадкуванні.


1
Це так проти компонентного дизайну :) Що б ви зробили, якщо хочете, щоб один гравець видавав звуки, а інший ні?
Кікаймару

@Kikaimaru Pass в системі ISoundS, яка не відтворює звук. тобто нічого не робить.
Дастін Кінген

3
-1, не тому, що це поганий код, а тому, що це зовсім не стосується архітектури, що базується на компонентах - насправді це поширення таких інтерфейсів, яких компоненти намагаються уникати.
Килотан

@Kylotan Я думаю, моє розуміння має бути неправильним.
Дастін Кінген
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.