##### Суть ECS - добавлять новые фичи, не трогая старые. Это возможно за счет модульности систем
<br>
### Системы
##### Изолированность
Системы не должны вызывать друг друга или изменять внутреннее состояние друг друга. Вместо этого, они должны взаимодействовать друг с другом через компоненты. Например, если Система A должна влиять на Систему B, она может сделать это, изменив компоненты, которые Система B использует. Система B затем может обнаружить эти изменения в компонентах во время своего следующего обновления и отреагировать соответствующим образом.
**Четкое определение обязанностей**
Каждая система должна иметь четко определенную зону ответственности. Например, система движения не должна заниматься рендерингом или обработкой звука.
<br>
**Порядок обновления**
Необходимо продумать порядок обновления систем, особенно в ситуациях, когда одна система должна "видеть" результаты работы другой системы в том же кадре. В большинстве случаев системы обновляются в определенном порядке, определенном в главном цикле игры.
<br>
<br>
Заранее создавать и добавлять в сущность все необходимые компоненты. Тем самым мы как бы просто формируем переменные, которые нужны сущности для работы.
<br>
##### Не перегружать методы, а решать свои дела в несколько строк кода (В целом, не только про ECS)
Не нужно пытаться уместить все параметры в один метод (даже если используется хороше решение, что называется «перегрузка»).
Я пришел к выводу, что чем ломать голову, как красиво уместить все в один метод ради того, чтобы при использовании данного метода «все сделать» за одну строчку кода. Лучше будет на стороне «пользователя-разработчика» выполнить необходимые операции за несколько строк.
Например. Я хотел сделать метод CreateGameObject, который бы позволял:
- Указывать локальные или глобальные transform
- Указать родителя
- Что-то из этого не указывать или указать только некоторые из.
Суть программирования и состоит в том, что ты модульно (по кусочкам) собираешь то, что тебе нужно.
- Нужно тебе создать просто gameobject без лишних параметров? - вызывай одну строчку кода.
- Нужно создать с локальной позицией и внутри родителя? - напиши 3 строчки кода.
##### Не хранить в компонентах прямые ссылки на сущности.
Напрямую ссылаться на другие сущности может привести к сложности управления и разрешения этих ссылок, особенно в больших и сложных игровых мирах.
Вместо этого, вы можете использовать систему идентификаторов сущностей или ссылок, которая позволяет вам ссылаться на другие сущности через их идентификаторы. Это обеспечивает гибкость и удобство управления связями между сущностями.
От себя добавлю, что дебажить легче, когда в полях хранится int, а не ссылка на Entity.
Прилетает такая ошибка
NullReferenceException: Object reference not set to an instance of an object
А ты не понимаешь, как в поле воявился null. Не понимаешь, в каком коде (где работают с данной сущностью) образовался null.
<br>
```csharp
public Entity Build(EntityId ownerId,
{
```
Исключение - когда метод возвращает сущность, то не нужно возвращать специально int, вместо ссылки (если на то нет архитектурных причин). Так как такой момент не подпадает под данный пункт. Удобнее, когда метод возвращает ссылку. Это никак не нарушает принципы ECS и не усложняет отлов ошибок.
<br>
##### Не нужно создать новый Entity, если у этой «вещи» нет своего поведения вне контекста другого Entity, в котором эта «вещь» применяется
«Предметы не обязательно должны быть представлены как отдельные Entity, если они не имеют своего поведения или состояния вне контекста слота инвентаря.»
##### Если у «чего-то» есть поведение и состояние, то это сущность, а не компонент.
Если у "чего-то" есть уникальное поведение, которое обрабатывается системой, то "что-то" вероятно является сущностью с набором компонентов, которые определяют эту уникальность.
состояние - имеется ввиду данные или информация.
<br>
##### Системы независимы друг от друга.
Системы не лезут друг к другу. Одна система не использует мемберы другой системы. Все взаимодействие между системами происходит на уровне данных (компонентов)
<br>
##### Каждая система обрабатывает только определенный тип компонента
Обычно в ECS системе каждая система отвечает только за обработку определенного типа компонентов, и только эта система знает, когда ее компоненты меняются.
##### Система, что изменяет состояние компонента, должна уведомлять об изменениях.
Если есть потребность в том, чтобы другие системы узнавали об изменениях определенного типа компонента, то:
Обычно системы или сервисы, которые работают с компонентами и изменяют их состояние, также отвечают за уведомление системы ECS об этих изменениях. Это позволяет сохранить простоту и чистоту дизайна компонентов и управлять процессом уведомления более гибко.
<br>
##### Компоненты должны быть «глупыми»
Компоненты должны быть "глупыми" и содержать только данные, а вся логика должна быть инкапсулирована в системах. Компоненты не должны знать о существовании систем, и наоборот, системы не должны зависеть от конкретных экземпляров компонентов. Вместо этого, системы должны быть спроектированы так, чтобы обрабатывать любой компонент соответствующего типа.
Появление логики в компоненте - нарушение принципов ECS.
##### Системы должны быть на каждый чих
##### Не создавать для второстепенных компонентов полноценный Mono-компонент.
Если есть маркеры, ордеры и прочие компоненты, которые нужно добавлять в сущность при его создании и при этом, сущность создается в EntityMono, то наилучшим решением будет добавить код на создание этих компонентов в релевантный Mono-компонент. Например в BuildingMono, если нам нужно при создании сущности добавлять маркер при строительстве здания.
<br>
<br>
##### Избегайте частого удаления/добавления компонента
Избегайте постоянного (например в update) добавления и удаления компонентов, если это возможно.
Сюда же входит постоянная проверка на наличие компонента, для его создания в случае отсутствия.
Не уверен насчет примера, но пусть будет раз написал. Например, система создала компонент типа orderComponent (дала команду другой системе). При этом в каждом update идет проверка есть ли этот компонент, и если нету то, создать. И так каждый фрейм.
Вместо этого, добавляйте компоненты, когда создается сущность, или когда происходит некое событие. Затем обновляйте значения полей в компонентах, вместо постоянного удаления и добавления самого компонента. Это снижает нагрузку на память и ускоряет выполнение систем.
<br>
##### Не использовать Delegates/Callbacks
А использовать отложенную реактивность (однокадровые компоненты)
Отложенная реактивность - это когда вместо вызова логики напрямую в момент события, мы создаем данные о том, что событие произошло, а все желающие просто отреагируют на событие в нужное для них время.
В ECS мы просто создаем компонент с данными или без(можно просто добавить boolean-флаг в существующий компонент), который система обработает, когда наступит ее очередь обработки.
Нередко такие компоненты существуют в мире только один кадр, чтобы оповестить все системы, но не повторять логику в следующем кадре. Удалением может заниматься как система генерирующая событие, так и какая-то отдельная система, которая удалит все компоненты типа X, там где это вам будет нужно.