На этот вопрос сложно ответить, потому что у каждого есть свое представление о том, как должна быть структурирована система компонентов объекта. Лучшее, что я могу сделать, это поделиться с вами некоторыми вещами, которые я считаю наиболее полезными для меня.
сущность
Я придерживаюсь подхода ECS, основанного на толстом классе, вероятно потому, что нахожу, что экстремальные методы программирования крайне неэффективны (с точки зрения продуктивности человека). Для этого сущность для меня является абстрактным классом, который наследуется более специализированными классами. У объекта есть ряд виртуальных свойств и простой флаг, который говорит мне, должен ли этот объект существовать. Итак, относительно вашего вопроса о системе рендеринга, это Entity
выглядит так:
public abstract class Entity {
public bool IsAlive = true;
public virtual SpatialComponent Spatial { get; set; }
public virtual ImageComponent Image { get; set; }
public virtual AnimationComponent Animation { get; set; }
public virtual InputComponent Input { get; set; }
}
Компоненты
Компоненты "глупы" в том, что они ничего не делают или не знают . У них нет ссылок на другие компоненты, и они, как правило, не имеют функций (я работаю в C #, поэтому я использую свойства для обработки методов получения / установки - если у них есть функции, они основаны на получении данных, которые они хранят).
системы
Системы менее «глупы», но все еще являются тупыми автоматами. Они не имеют контекста всей системы, не имеют ссылок на другие системы и не содержат данных, за исключением нескольких буферов, которые им могут понадобиться для их индивидуальной обработки. В зависимости от системы, он может иметь специализированный Update
или Draw
метод, или в некоторых случаях оба.
Интерфейсы
Интерфейсы являются ключевой структурой в моей системе. Они используются для определения того, что System
может обрабатывать и на что Entity
способен. Интерфейсы, которые важны для рендеринга: IRenderable
и IAnimatable
.
Интерфейсы просто сообщают системе, какие компоненты доступны. Например, система рендеринга должна знать ограничивающую рамку объекта и изображение для рисования. В моем случае это было бы SpatialComponent
и ImageComponent
. Так это выглядит так:
public interface IRenderable {
SpatialComponent Component { get; }
ImageComponent Image { get; }
}
Система рендеринга
Так как же система рендеринга рисует сущность? Это на самом деле довольно просто, поэтому я просто покажу вам урезанный класс, чтобы дать вам идею:
public class RenderSystem {
private SpriteBatch batch;
public RenderSystem(SpriteBatch batch) {
this.batch = batch;
}
public void Draw(List<IRenderable> list) {
foreach(IRenderable obj in list) {
this.batch.draw(
obj.Image.Texture,
obj.Spatial.Position,
obj.Image.Source,
Color.White);
}
}
}
Глядя на класс, система рендеринга даже не знает, что это Entity
такое. Все, о чем он знает, IRenderable
это просто дать им список для рисования.
Как все это работает
Это может также помочь понять, как я создаю новые игровые объекты и как я передаю их в системы.
Создание сущностей
Все игровые объекты наследуются от Entity и любых применимых интерфейсов, которые описывают, что может делать этот игровой объект. Почти все, что анимировано на экране, выглядит так:
public class MyAnimatedWidget : Entity, IRenderable, IAnimatable {}
Кормление систем
Я храню список всех сущностей, существующих в игровом мире, в едином списке List<Entity> gameObjects
. Затем каждый кадр я просеиваю через этот список и копирую ссылки на объекты в другие списки на основе типа интерфейса, например List<IRenderable> renderableObjects
, и List<IAnimatable> animatableObjects
. Таким образом, если разные системы должны обрабатывать один и тот же объект, они могут. Затем я просто передаю эти списки каждой из систем Update
или Draw
методов и позволяю системам выполнять свою работу.
Анимация
Вам может быть любопытно, как работает система анимации. В моем случае вы можете захотеть увидеть интерфейс IAnimatable:
public interface IAnimatable {
public AnimationComponent Animation { get; }
public ImageComponent Image { get; set; }
}
Ключевым моментом, на который следует обратить внимание, является то, что ImageComponent
аспект IAnimatable
интерфейса не только для чтения; у него есть сеттер .
Как вы уже догадались, компонент анимации просто содержит данные об анимации; список кадров (которые являются компонентами изображения), текущий кадр, количество кадров в секунду, которые будут отображаться, время, прошедшее с момента последнего увеличения кадра, и другие параметры.
Система анимации использует преимущества системы рендеринга и взаимосвязи компонентов изображения. Он просто изменяет компонент изображения объекта по мере увеличения кадра анимации. Таким образом, анимация отрисовывается косвенно системой рендеринга.