Entity Component System: где поставить логику рендеринга

В настоящее время я изучаю "Компонентную систему сущностей". После прочтения многих учебных пособий и веток форума я все еще задаюсь вопросом, куда должна идти логика рендеринга. Я не говорю о реальном коде рендеринга OpenGL/DirectX, который, например, берет спрайт и отображает его. Я имею в виду логику, которая решает, какой спрайт визуализировать.

Видимая сущность требует трех аспектов:

  1. Оценка ИИ (изменение позиции, состояния, ...)
  2. Оценка состояния рендеринга. Например, какой цикл спрайтов использовать, когда объект идет, поднимается, получает удар, ...
  3. На самом деле рендеринг спрайта

В большинстве уроков предлагается использовать что-то вроде AISystem (1.) для логики и RenderSystem (3.) для отображения спрайта (цикла), определенного в RenderComponent. То, что они не говорят, - то, где обновляется RenderComponent. Насколько я понимаю, просто поместить (2.) в (1.), смешивая логику символов с логикой рендеринга, было бы плохим дизайном.

Прямым решением было бы добавить определенную логическую систему рендеринга для каждого врага. Так, например, для Gumba я мог бы добавить GumbaLogicSystem, GumbaRenderLogicSystem и, для фактического рендеринга, универсальную SpriteRenderSystem, которую используют все сущности, основанные на спрайтах. Однако это означает создание двух компонентов * и двух систем для каждого типа сущности, что не кажется хорошим решением.

Есть ли хорошее решение, которое разделяет логику символов и логику рендеринга, сохраняя при этом небольшое количество систем?

Спасибо

(* = в простом подходе система обрабатывает сущность в зависимости от ее компонента. Чтобы система GumbaRenderLogicSystem работала над сущностью, ей требуется GumbaRenderingLogicComponent для распознавания этой системой. То же самое верно для символьной логики.)

Edit1: я знаю, что ECS является абстрактным паттерном. Мой вопрос направлен на передовой опыт о том, как сохранить количество систем небольшим. Мой пример основан на программировании игр, но не ограничивается этой областью. Позвольте мне объяснить более абстрактный пример:

Представь, что у меня есть какая-то сущность, которая видна. В иерархической архитектуре у меня было бы что-то вроде:

  • SomeModel (наследуется от AbstractModel)
  • SomeController (наследуется от AbstractController)
  • SomeRenderer (наследует от PixelRenderer, который, в свою очередь, наследует от AbstractRenderer).

В ECS мне нужна целая куча новых классов:

  • SomeModelSpecificDataComponent (то есть данные, которые являются определенными для этой семантической сущности)
  • SomeModelSystem (это логика)
  • SomeModelSpecificRenderComponent (т. Е. Рендеринг данных, относящихся к этой семантической сущности)
  • SomeModelSpecificRendererLogicSystem (система, которая решает, как визуализировать)
  • PixelRendererSystem

Можно ли как-то уменьшить количество новых систем, которые необходимо внедрить? Одним из решений может быть добавление "агентов" или "объектов поведения": к общему RenderingComponent прикрепляется экземпляр некоторого класса "RenderingAgent", который имеет единственный метод "Update", который вызывается в RenderSystem. Технически, компонент не содержит саму логику. Я боюсь, что это может быть слишком сильным, хотя.

2 ответа

Решение

Подумав об этом некоторое время и много обсуждений, я понял, что мой образ мышления мог быть неправильным. То, что я описал, на самом деле было бы верно для ванильного подхода ECS.

Единственный способ предотвратить взрыв систем и компонентов - создать правильную абстракцию игровых элементов, чтобы можно было описывать различные требования рендеринга, а не программировать.

Я имею в виду, что, например, механизм спрайтов должен обеспечивать достаточную абстракцию, чтобы вы могли выражать различные анимации и состояния только с помощью описаний, которые хранятся в компоненте рендеринга, который оценивается системой рендеринга. Что вам также нужно сделать, это правильно разделить ваше решение на части многократного использования.

Таким образом, ECS заставляет вас больше, чем другие шаблоны, задумываться о хорошей архитектуре.

Entity Component System - это шаблон, то есть способ решения проблем сложности в игровом коде. Будучи "шаблоном", он только описывает подход и остается довольно абстрактным. Отсюда нет явного упоминания.

Итак, давайте рассмотрим использование обычного цикла, когда вы явно вызываете этапы один за другим.

while(true)
{
    callAI();           // AI entities
    checkCollisions();  // some physics here
    updatePositions();  // movable objects
    render();           // visual elements.
}

С ECS у вас будет два цикла, внешний для каждой подсистемы, внутренний - для каждого подходящего компонента.

var types = {'ai', 'mechanic', 'visual'}; 

while(true)
{
    for(var t in types) 
    {
        var handler = getHandlerForType(t);
        for(var e in entities)
        {
            var c = e.getComponents(t); // receive e's components of required type
            // So this would handle ai, mechanics or visual component respectfully.
            handler.handle(c);
        }
    }
}

(и эти циклы, хотя и иллюстрируют общий подход, далеки от идеальной реализации. В действительности вы, вероятно, извлекаете все компоненты и затем обрабатываете их пакетами).

Таким образом, у вас больше нет явных этапов цикла. Вместо этого рендеринг превращается в одну из "подсистем", которая обрабатывает только те компоненты, которые способны визуализировать себя. Теоретически вы можете иметь эту часть где угодно, но, возможно, было бы более разумно иметь ее в конце этого конвейера.

Другие вопросы по тегам