https://github.com/dcfapixels/dragonecs-vault
A set of notes and code recommendations for the DragonECS framework.
https://github.com/dcfapixels/dragonecs-vault
Last synced: 2 months ago
JSON representation
A set of notes and code recommendations for the DragonECS framework.
- Host: GitHub
- URL: https://github.com/dcfapixels/dragonecs-vault
- Owner: DCFApixels
- Created: 2024-04-10T04:34:58.000Z (about 1 year ago)
- Default Branch: main
- Last Pushed: 2024-06-13T13:25:36.000Z (12 months ago)
- Last Synced: 2025-02-02T10:13:47.715Z (4 months ago)
- Size: 84 KB
- Stars: 2
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README

# DragonECS-Vault
Данный репозитопий это сборник заметок и рекомендаций по коду для фреймворка [DragonECS](https://github.com/DCFApixels/DragonECS).
> Материал расположенный в этом репозитории не является сводом правил, не обязателен к ознакомлению, и имеет только рекомендательный характер.
>
> Местами материал может быть даже спорным, по спорным темам приглашаю вас обкашлять их в [Discord](https://discord.com/invite/kqmJjExuCf)# Стиль кода
Пример стиля
```csharp
class ApplyVelocitySystem : IEcsRun, IEcsInject, IEcsInject
{
class Aspect : EcsAspect
{
public EcsPool poses = Inc;
public EcsPool velocities = Inc;
public EcsTagPool freezedTags = Exc;
}public void Inject(EcsDefaultWorld obj) => _world = obj;
public void Inject(TimeService obj) => _time = obj;EcsDefaultWorld _world;
TimeService _time;public void Run()
{
foreach (var e in _world.Where(out Aspect a))
{
a.poses.Get(e).position += a.velocities.Get(e).value * _time.DeltaTime;
}
}
}
```
Тот же пример, но с Auto-Injections:
```csharp
class ApplyVelocitySystem : IEcsRun
{
class Aspect : EcsAspect
{
public EcsPool poses = Inc;
public EcsPool velocities = Inc;
public EcsTagPool freezedTags = Exc;
}[EcsInject] EcsDefaultWorld _world;
[EcsInject] TimeService _time;public void Run()
{
foreach (var e in _world.Where(out Aspect a))
{
a.poses.Get(e).position += a.velocities.Get(e).value * _time.DeltaTime;
}
}
}
```Начну с объявления аспектов. Аспекты, хоть и могут использоваться одновременно несколькими системами, удобнее всего объявлять для каждой системы свои прямо внутри систем. Объявление аспекта стоит делать в самом верху системы, так удобнее читать систему, сначала идет название системы из которого можно понять что она делает, далее идет описание аспектов с которыми система работает.
Именование полей в Аспектах. Поля для кеша пулов называть по названию компонента во множественном числе,например `EcsPool healths`. Для этого пулы фейково реализуют IEnumerable, чтобы автодополненте IDE предлагало такое наименование.
Модификаторы доступа private/public для членов систем не имеют применения, все взаимодейсвие с системами происходит либо косвенно через данные в компонентах, а если напрямую, то через интерфейсы. Поэтому для сокращения бойлерплейта и улучшения восприятия модификаторы доступа могут быть опушены где это возможно.
Именование.
Именование аспектов. Многие системы работают только с одним аспектом, так что аспекты можно называть просто `Aspect`. Если аспектов несколько то основной также называть `Aspect` а второстепенный с префиксом, например `EventAspect`.
Именование переменных. По той же причине что системы часто работают только с одним аспектом, возвращаемый запросом `Where` экземпляр аспекта можно называть просто `a`, а сущности внутри `foreach` просто `e`, например: `foreach(var e in _world.Where(out Aspect a)`. Аналогично именованию аспектов, если система работает с несколькими аспектами, то к `a` и `e` добавляется префикс, например: `foreach(var eventE in _world.Where(out EventAspect eventA)`.
# Модули
Группы систем и компонетов объединенные одной логикой или реализующие некую фичу лучше ораганизовывать в модули. Модули можно рассматривать как отедльные сборки, можно даже разбивать их на отдельные сборки.
Папка одного модуля имеет следующую иерархию:
```
.../
------+
| SomeModule/
+------+
| | Components/
| +------+
| | ¦ SomeComponent.cs
¦ | : IsTagged.cs
: | ·
· | Events/
· +------+
| ¦ SomeEvent.cs
| : SomeSignal.cs
| ·
| Systems/
+------+
| ¦ SomeSystem1.cs
| : SomeSystem2.cs
| ·
¦ SomeModule.cs
:
·
```
+ `SomeComponent.cs` - обычные компоненты.
+ `IsTagged.cs` - компоненты-теги которые используются просто как bool флаг, рекомендуется называть по аналогии с рекомендацией наименования bool полей, тоесть с префиксом Is.
+ `SomeSignal.cs` - компонент-событие крепящийся к цели события.
+ `SomeEvent.cs` - компонент-событие используемый в сущности-событие, для создания нескольких событий для одной сущности.
+ `SomeSystem.cs` - Системы.
+ `SomeModule.cs` - Класс реализующий интерфейс `IEcsModule`, добавляющий в пайплайн системы модуля.Модули так же удобно группировать в один надмодуль, тоесть создавать модуль который в `Import` методе просто добавляет другие модули. Например в своем проекте те модули которые могут работать независимо от Unity объединяю в модуль `ProjectCoreModule` и то что работает с Unity объединяю в `ProjectUnityModule`. Посде такого раздедения в `EcsRoot` достаточно просто добавить эти 2 модуля.
Для систем и компонентов присущих одному модулю добавлять мета-атрибут MetaGroup, в качесве корневой группы использовать название модуля. Пример:
```c#
// Слово модуль из SomeModule будет автоматически удаленно, останется только Some
[MetaGroup(nameof(SomeModule))]
public struct SomeComponent : IEcsComponent
{
//...
}
```
Далее можно следующей подгруппой выбрать то чем является тип, компонентом или системой. Для этого в EcsConsts есть предусмотренные константы.
```c#
[MetaGroup(nameof(SomeModule), EcsConsts.COMPONENTS_GROUP)]
public struct SomeComponent : IEcsComponent { /* ... */ }[MetaGroup(nameof(SomeModule), EcsConsts.SYSTEMS_GROUP)]
public class SomeSystem : IEcsRun { /* ... */ }
```# Мульти миры
Фреймворк поддерживает создание нескольких миров и их совместную обработку в системах. Сразу оговорюсь это опциональная возможность, и если вы только начинаете свой путь в ецс или вам не удобно пользоваться этой фишкой, все можно помещать в один мир. Разделение может быть полезно с точки зрения улучшения использования памяти, группы сущностей которые по своей специфики не могут иметь общих аспектов с другими, такие сущности можно выделять в отдельные миры. Довольно типичный пример разделения миров это дефолтный мир где обрабатываются игровые сущности, и мир событий где обрабатываются сущности-события, по своей специфики у игровых и событийных сущностей редко будут пересечения аспектов, поэтому их можно выделить в отдельный мир.Для хранения в компонентах ссылок на сущности между мирами категорически рекомендуется использовать entlong, так как у entlong есть привязка к миру. Иначе высока вероятность "магических" ошибок в саязи с путанницей идентификаторов.
# Unchecked
В фреймворке некоторые методы имеют 2 версии, основную и с суффиксом Unchecked. Unchecked методы это методы которые опускают все проверки и не правильное выполнение которых может привести к нестабильному состоянию компонентов фреймворка или всего проекта. К Unchecked методам стоит относиться как к Unsafe, лучше применять только в тонких для производительности местах и если вы не уверены что делаете, лучше не использовать.