Хорошо, что я знаю до сих пор; Сущность содержит компонент (хранилище данных), который содержит такую информацию, как; - Текстура / Спрайт - Шейдер - и т. Д.
И тогда у меня есть система рендеринга, которая рисует все это. Но что я не понимаю, так это то, как должен быть разработан рендер. Должен ли я иметь один компонент для каждого "визуального типа". Один компонент без шейдера, другой с шейдером и т. Д.?
Просто нужно немного узнать, что такое «правильный путь» для этого. Советы и подводные камни, на которые стоит обратить внимание.
Ответы:
На этот вопрос сложно ответить, потому что у каждого есть свое представление о том, как должна быть структурирована система компонентов объекта. Лучшее, что я могу сделать, это поделиться с вами некоторыми вещами, которые я считаю наиболее полезными для меня.
сущность
Я придерживаюсь подхода ECS, основанного на толстом классе, вероятно потому, что нахожу, что экстремальные методы программирования крайне неэффективны (с точки зрения продуктивности человека). Для этого сущность для меня является абстрактным классом, который наследуется более специализированными классами. У объекта есть ряд виртуальных свойств и простой флаг, который говорит мне, должен ли этот объект существовать. Итак, относительно вашего вопроса о системе рендеринга, это
Entity
выглядит так:Компоненты
Компоненты "глупы" в том, что они ничего не делают или не знают . У них нет ссылок на другие компоненты, и они, как правило, не имеют функций (я работаю в C #, поэтому я использую свойства для обработки методов получения / установки - если у них есть функции, они основаны на получении данных, которые они хранят).
системы
Системы менее «глупы», но все еще являются тупыми автоматами. Они не имеют контекста всей системы, не имеют ссылок на другие системы и не содержат данных, за исключением нескольких буферов, которые им могут понадобиться для их индивидуальной обработки. В зависимости от системы, он может иметь специализированный
Update
илиDraw
метод, или в некоторых случаях оба.Интерфейсы
Интерфейсы являются ключевой структурой в моей системе. Они используются для определения того, что
System
может обрабатывать и на чтоEntity
способен. Интерфейсы, которые важны для рендеринга:IRenderable
иIAnimatable
.Интерфейсы просто сообщают системе, какие компоненты доступны. Например, система рендеринга должна знать ограничивающую рамку объекта и изображение для рисования. В моем случае это было бы
SpatialComponent
иImageComponent
. Так это выглядит так:Система рендеринга
Так как же система рендеринга рисует сущность? Это на самом деле довольно просто, поэтому я просто покажу вам урезанный класс, чтобы дать вам идею:
Глядя на класс, система рендеринга даже не знает, что это
Entity
такое. Все, о чем он знает,IRenderable
это просто дать им список для рисования.Как все это работает
Это может также помочь понять, как я создаю новые игровые объекты и как я передаю их в системы.
Создание сущностей
Все игровые объекты наследуются от Entity и любых применимых интерфейсов, которые описывают, что может делать этот игровой объект. Почти все, что анимировано на экране, выглядит так:
Кормление систем
Я храню список всех сущностей, существующих в игровом мире, в едином списке
List<Entity> gameObjects
. Затем каждый кадр я просеиваю через этот список и копирую ссылки на объекты в другие списки на основе типа интерфейса, напримерList<IRenderable> renderableObjects
, иList<IAnimatable> animatableObjects
. Таким образом, если разные системы должны обрабатывать один и тот же объект, они могут. Затем я просто передаю эти списки каждой из системUpdate
илиDraw
методов и позволяю системам выполнять свою работу.Анимация
Вам может быть любопытно, как работает система анимации. В моем случае вы можете захотеть увидеть интерфейс IAnimatable:
Ключевым моментом, на который следует обратить внимание, является то, что
ImageComponent
аспектIAnimatable
интерфейса не только для чтения; у него есть сеттер .Как вы уже догадались, компонент анимации просто содержит данные об анимации; список кадров (которые являются компонентами изображения), текущий кадр, количество кадров в секунду, которые будут отображаться, время, прошедшее с момента последнего увеличения кадра, и другие параметры.
Система анимации использует преимущества системы рендеринга и взаимосвязи компонентов изображения. Он просто изменяет компонент изображения объекта по мере увеличения кадра анимации. Таким образом, анимация отрисовывается косвенно системой рендеринга.
источник
Посмотрите этот ответ, чтобы увидеть тип системы, о которой я говорю.
Компонент должен содержать сведения о том, что рисовать и как его рисовать. Система рендеринга берет эти детали и рисует объект так, как указано компонентом. Только если вы будете использовать существенно разные технологии рисования, у вас будут отдельные компоненты для разных стилей.
источник
Основная причина разделения логики на компоненты заключается в создании набора данных, которые при объединении в сущности дают полезное, многократно используемое поведение. Например, имеет смысл разделить сущность на PhysicsComponent и RenderComponent, поскольку вполне вероятно, что не все сущности будут иметь физику, а некоторые сущности могут не иметь Sprite.
Чтобы ответить на ваш вопрос, вам нужно взглянуть на свою архитектуру и задать себе два вопроса:
При разбиении компонента важно задать этот вопрос: если ответ на вопрос 1. да, то у вас, вероятно, есть хороший кандидат для создания двух отдельных компонентов, один с шейдером, а другой с текстурой. Ответ на вопрос 2. обычно да для таких компонентов, как Position, где несколько компонентов могут использовать position.
Например, и Physics, и Audio могут использовать одну и ту же позицию, вместо того, чтобы оба компонента, хранящие дублирующиеся позиции, вы реорганизуете их в один PositionComponent и требует, чтобы объекты, которые используют PhysicsComponent / AudioComponent, также имели PositionComponent.
Судя по предоставленной вами информации, не похоже, что ваш RenderComponent является хорошим кандидатом для разбиения на TextureComponent и ShaderComponent, поскольку шейдеры полностью зависят от текстуры и ничего более.
Предполагая, что вы используете что-то похожее на T-Machine: Entity Systems, пример реализации RenderComponent & RenderSystem в C ++ будет выглядеть примерно так:
источник
Подводный камень # 1: переработанный код. Подумайте, действительно ли вам нужно все, что вы реализуете, потому что вам придется жить с этим довольно долго.
Подводный камень # 2: слишком много объектов. Я бы не использовал систему со слишком большим количеством объектов (по одному для каждого типа, подтипа и т. Д.), Потому что это только усложняет автоматизированную обработку. На мой взгляд, гораздо приятнее, чтобы каждый объект управлял определенным набором функций (в отличие от одной функции). Например, создание компонентов для каждого бита данных, включенного в рендеринг (компонент текстуры, компонент шейдера), слишком разделено - в любом случае, как правило, все эти компоненты должны быть вместе, не так ли?
Подводный камень # 3: слишком строгий внешний контроль. Предпочитаю менять имена на шейдерные / текстурные объекты, потому что объекты могут меняться с помощью рендерера / типа текстуры / формата шейдера / чего угодно. Имена - это простые идентификаторы - решать, что из них делать, решать визуализатору. Однажды вы можете захотеть использовать материалы вместо простых шейдеров (например, добавить шейдеры, текстуры и режимы наложения из данных). С помощью текстового интерфейса это гораздо проще реализовать.
Что касается средства визуализации, это может быть простой интерфейс, который создает / уничтожает / поддерживает / отображает объекты, созданные компонентами. Самым примитивным представлением этого может быть что-то вроде этого:
Это позволило бы вам управлять этими объектами из ваших компонентов и располагать их достаточно далеко, чтобы вы могли отображать их любым удобным для вас способом.
источник