update readme

This commit is contained in:
Mikhail 2024-10-19 16:04:56 +08:00
parent ef18594f82
commit 0f3b1c1e1f
3 changed files with 116 additions and 34 deletions

View File

@ -41,7 +41,7 @@
</br>
DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system) фреймворк нацеленный на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Разработан на чистом C#, без зависимостей и генерации кода. Вдохновлен [LeoEcs](https://github.com/Leopotam/ecslite).
DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system) фреймворк нацеленный на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Разработан на чистом C#, без зависимостей и генерации кода. Вдохновлен [LeoEcs Lite](https://github.com/Leopotam/ecslite).
> [!WARNING]
> Проект предрелизной версии, поэтому API может меняться. В ветке main актуальная и рабочая версия.</br>
@ -58,7 +58,7 @@ DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system)
- [Построение](#построение)
- [Внедрение зависимостей](#внедрение-зависимостей)
- [Модули](#модули)
- [Слои](#слои)
- [Сортировка](#сортировка)
- [Процессы](#процессы)
- [Мир](#мир)
- [Пул](#пул)
@ -149,18 +149,17 @@ if (entity.TryGetID(out int entityID)) { }
> **NOTICE:** Сущности не могут существовать без компонентов, пустые сущности будут автоматически удаляться сразу после удаления последнего компонента либо в конце тика.
## Component
**Компоненты** - это данные для сущностей. Обязаны реализовывать интерфейс `IEcsComponent` или другой указывающий вид компонента.
**Компоненты** - это данные которые крепятся к сущностям.
```c#
// Компоненты IEcsComponent хранятся в обычном хранилище.
struct Health : IEcsComponent
{
public float health;
public int armor;
}
// Компоненты с IEcsTagComponent хранятся в оптимизированном для тегов хранилище.
struct PlayerTag : IEcsTagComponent {}
```
Встроенные виды компонентов:
* `IEcsComponent` - Компоненты с данными. Универсальный тип компонентов.
* `IEcsTagComponent` - Компоненты-теги. Без данных.
## System
**Системы** - это основная логика, тут задается поведение сущностей. Существуют в виде пользовательских классов, реализующих как минимум один из интерфейсов процессов. Основные процессы:
@ -271,8 +270,10 @@ EcsPipeline pipeline = EcsPipeline.New()
.BuildAndInit();
```
### Слои
Очередь систем можно разбить на слои. Слой определяет место в очереди для вставки систем. Например, если необходимо чтобы какая-то система была вставлена в конце очереди, вне зависимости от места добавления, эту систему можно добавить в слой EcsConsts.END_LAYER.
### Сортировка
Дла управления расположением систем в пайплайне, вне зависимости от порядка добавления, есть 2 способа: Слои и Порядок сортировки.
#### Слои
Слой определяет место в пайплайне для вставки систем. Например, если необходимо чтобы система была вставлена в конце пайплайна, эту систему можно добавить в слой `EcsConsts.END_LAYER`.
``` c#
const string SOME_LAYER = nameof(SOME_LAYER);
EcsPipeline pipeline = EcsPipeline.New()
@ -287,9 +288,20 @@ EcsPipeline pipeline = EcsPipeline.New()
Встроенные слои расположены в следующем порядке:
* `EcsConst.PRE_BEGIN_LAYER`
* `EcsConst.BEGIN_LAYER`
* `EcsConst.BASIC_LAYER` (Если при добавлении системы не указать слой, то она будет добавлена сюда)
* `EcsConst.BASIC_LAYER` (По умолчанию системы добавляются сюда)
* `EcsConst.END_LAYER`
* `EcsConst.POST_END_LAYER`
#### Порядок сортировки
Для сортировки систем в рамках слоя используется int значение порядка сортировки. По умолчанию системы добавляются с sortOrder = 0.
``` c#
EcsPipeline pipeline = EcsPipeline.New()
// ...
// Система SomeSystem будет вставлена в слой EcsConsts.BEGIN_LAYER
// и расположена после систем с sortOrder меньше 10.
.Add(New SomeSystem(), EcsConsts.BEGIN_LAYER, 10)
// ...
.BuildAndInit();
```
## Процессы
Процессы - это очереди систем реализующие общий интерфейс, например `IEcsRun`. Для запуска процессов используются Runner-ы. Встроенные процессы запускаются автоматически. Есть возможность реализации пользовательских процессов.
@ -345,17 +357,20 @@ _pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
</details>
## Мир
Является контейнером для сущностей и компонентов.
Контейнер для сущностей и компонентов.
``` c#
// Создание экземпляра мира.
_world = new EcsDefaultWorld();
// Создание и удаление сущности по примеру из раздела Сущности.
var e = _world.NewEntity();
_world.DelEntity(e);
```
> **NOTICE:** Необходимо вызывать EcsWorld.Destroy() у экземпляра мира если он больше не используется, иначе он будет висеть в памяти.
### Конфигурация мира
// Уничтожение мира и освобождение ресурсов. Обязательно вызывать, иначе он будет висеть в памяти.
_world.Destroy();
```
> Миры изолированы друг от друга и могут обрабатываться в отдельных потоках. Но мультипоточная обработка одного мира поддерживается только при отсутсвии добавления/удаляения компонентов у сущностей.
Для инициализации мира сразу необходимого размера и сокращения времени прогрева, в конструктор можно передать экземпляр `EcsWorldConfig`.
``` c#
@ -363,14 +378,16 @@ EcsWorldConfig config = new EcsWorldConfig(
// Предварительно инициализирует вместимость мира для 2000 сущностей.
entitiesCapacity: 2000,
// Предварительно инициализирует вместимость пулов для 2000 компонентов.
poolComponentsCapacity: 2000);
poolComponentsCapacity: 2000
// ... Есть и другие параметры
);
_world = new EcsDefaultWorld(config);
```
## Пул
Является хранилищем для компонентов, предоставляет методы для добавления/чтения/редактирования/удаления компонентов на сущности. Есть несколько видов пулов, для разных целей:
Хранилище для компонентов, пул предоставляет методы для добавления/чтения/редактирования/удаления компонентов на сущности. Есть несколько видов пулов, для разных видов компонентов:
* `EcsPool` - универсальный пул, хранит struct-компоненты реализующие интерфейс `IEcsComponent`;
* `EcsTagPool` - специальный пул для пустых компонентов-тегов, хранит struct-компоненты с `IEcsTagComponent` как bool значения, что в сравнении с реализацией `EcsPool` имеет лучше оптимизацию памяти и скорости;
* `EcsTagPool` - специальный пул, оптимизированный под компоненты-теги, хранит struct-компоненты с `IEcsTagComponent`;
Пулы имеют 5 основных метода и их разновидности:
``` c#
@ -392,12 +409,50 @@ if (poses.Has(entityID)) { /* ... */ }
// Удалит компонент у сущности, бросит исключение если у сущности нет этого компонента.
poses.Del(entityID);
```
> [!WARNING]
> В `Release` сброке отключаются проверки на исключения.
> Есть "безопасные" методы, которые сначала выполнят проверку наличия/отсутствия компонента, названия таких методов начинаются с `Try`.
> Имеется возможность реализации пользовательского пула. Эта функция будет описана в ближайшее время.
## Маска
Применяется для фильтрации сущностей по наличию или отсутствию компонентов.
``` c#
// Создание маски которая проверяет что у сущностей есть компоненты
// SomeCmp1 и SomeCmp2, но нет компонента SomeCmp3.
EcsMask mask = EcsMask.New(_world)
// Inc - Условие наличия компонента.
.Inc<SomeCmp1>()
.Inc<SomeCmp2>()
// Exc - Условие отсутствия компонента.
.Exc<SomeCmp3>()
.Build();
```
<details>
<summary>Статическая маска</summary>
`EcsMask` привязаны к конкретным экземплярам мира которые необходимо передавать в `EcsMask.New(world)`, но есть `EcsStaticMask` которую можно создать без привязки к миру.
``` c#
class SomeSystem : IEcsRun
{
// EcsStaticMask можно создавать в статических полях.
static EcsStaticMask _staticMask = EcsStaticMask.Inc<SomeCmp1>().Inc<SomeCmp2>().Exc<SomeCmp3>().Build();
// ...
}
```
``` c#
// Конвертация в обычную маску.
EcsMask mask = _staticMask.ToMask(_world);
```
</details>
## Аспект
Это пользовательские классы наследуемые от `EcsAspect` и используемые для взаимодействия с сущностями. Аспекты одновременно являются кешем пулов и маской компонентов для фильтрации сущностей. Можно рассматривать аспекты как описание того с какими сущностями работает система.
Пользовательские классы наследуемые от `EcsAspect` и используемые для взаимодействия с сущностями. Аспекты одновременно являются кешем пулов и содержат маску. Можно рассматривать аспекты как описание того с какими сущностями работает система.
Упрощенный синтаксис:
``` c#
@ -418,7 +473,9 @@ class Aspect : EcsAspect
}
```
Явный синтаксис (результат идентичен примеру выше):
<details>
<summary>Явный синтаксис (результат идентичен примеру выше):</summary>
``` c#
using DCFApixels.DragonECS;
// ...
@ -435,6 +492,8 @@ class Aspect : EcsAspect
}
```
</details>
<details>
<summary>Комбинирование аспектов</summary>
@ -470,16 +529,23 @@ class Aspect : EcsAspect
</details>
## Запросы
Что бы получить необходимый набор сущностей используется метод-запрос `EcsWorld.Where<TAspect>(out TAspect aspect)`. В качестве `TAspect` указывается аспект, сущности будут отфильтрованны по маске указанного аспекта. Запрос `Where` применим как к `EcsWorld` так и коллекциям фреймворка (в этом плане Where чем-то похож на аналогичный из Linq).
Пример:
Фильтруют сущности и выдают коллекции сущностей удовлетворяющие определенным условиям. Встроенный запрос `Where` фильтрует на соответствие условиям маски компонентов и имеет несколько перегрузок:
+ `EcsWorld.Where(EcsMask mask)` - Обычная фильтрация по маске;
+ `EcsWorld.Where<TAspect>(out TAspect aspect)` - Сочетает в себе фильтрацию по маске из аспекта и получение аспекта;
Запрос `Where` применим как к `EcsWorld` так и коллекциям фреймворка (в этом плане Where чем-то похож на аналогичный из Linq). Так же имеются перегрузки для сортировки сущностей по `Comparison<int>`.
Пример системы:
``` c#
public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
{
class Aspect : EcsAspect
{
public EcsPool<Health> healths = Inc;
public EcsPool<DamageSignal> damageSignals = Inc;
public EcsPool<Health> healths = Inc;
public EcsPool<DamageSignal> damageSignals = Inc;
public EcsTagPool<IsInvulnerable> isInvulnerables = Exc;
// Наличие или отсутвие этого компонента не проверяется.
public EcsTagPool<IsDiedSignal> isDiedSignals = Opt;
}
EcsDefaultWorld _world;
public void Inject(EcsDefaultWorld world) => _world = world;
@ -489,7 +555,15 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
foreach (var e in _world.Where(out Aspect a))
{
// Сюда попадают сущности с компонентами Health, DamageSignal и без IsInvulnerable.
a.healths.Get(e).points -= a.damageSignals.Get(e).points;
ref var health = ref a.healths.Get(e);
if(health.points > 0)
{
health.points -= a.damageSignals.Get(e).points;
if(health.points <= 0)
{ // Создаем сигнал другим системам о том что сущность умерла.
a.isDiedSignals.TryAdd(e);
}
}
}
}
}
@ -689,6 +763,9 @@ using DCFApixels.DragonECS;
// Добавляет описание типу.
[MetaDescription("The quick brown fox jumps over the lazy dog")]
// Добавляет строковый уникальный идентификатор.
[MetaID("8D56F0949201D0C84465B7A6C586DCD6")] // Строки должны быть уникальными, и не допускают символы ,<> .
// Добавляет строковые теги.
[MetaTags("Tag1", "Tag2", ...)] // [MetaTags(MetaTags.HIDDEN))] чтобы скрыть в редакторе
@ -700,17 +777,16 @@ TypeMeta typeMeta = someComponent.GetMeta();
// или
TypeMeta typeMeta = pool.ComponentType.ToMeta();
var name = typeMeta.Name;
var color = typeMeta.Color;
var description = typeMeta.Description;
var group = typeMeta.Group;
var tags = typeMeta.Tags;
var name = typeMeta.Name; // [MetaName]
var group = typeMeta.Group; // [MetaGroup]
var color = typeMeta.Color; // [MetaColor]
var description = typeMeta.Description; // [MetaDescription]
var metaID = typeMeta.MetaID; // [MetaID]
var tags = typeMeta.Tags; // [MetaTags]
```
## EcsDebug
Имеет набор методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы - это посредники между системами отладки среды и EcsDebug. Это позволяет не изменяя отладочный код проекта, переносить проект на другие движки, достаточно только реализовать соответствующий Debug-сервис.
По умолчанию используется `DefaultDebugService` который выводит логи в консоль. Для реализации пользовательского создайте класс наследуемый от `DebugService` и реализуйте абстрактные члены класса.
Вспомогательный тип с набором методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы - это посредники между EcsDebug и инструментами отладки среды. Такая реализация позволяет не изменяя отладочный код, менять его поведение или переносить проект в другие среды, достаточно только реализовать соответствующий Debug-сервис.
``` c#
// Вывод лога.
@ -726,12 +802,17 @@ EcsDebug.Break();
EcsDebug.Set<OtherDebugService>();
```
> По умолчанию используется `DefaultDebugService` который выводит логи в консоль. Для реализации пользовательского создайте класс наследуемый от `DebugService` и реализуйте абстрактные члены класса.
> `EcsDebug` потокобезопасен, за счет того что каждый поток использует свой изолированный экземпляр сервиса. Экземпляры для потоков создаются в абстрактном методе `DebugService.CreateThreadInstance`.
## Профилирование
За реализацию профайлера так же отвечает Debug-сервис. Для выделения участка кода используется `EcsProfilerMarker`;
``` c#
// Создание маркера с именем SomeMarker.
private static readonly EcsProfilerMarker marker = new EcsProfilerMarker("SomeMarker");
...
// ...
marker.Begin();
// Код для которого замеряется скорость.
@ -744,6 +825,7 @@ using (marker.Auto())
// Код для которого замеряется скорость.
}
```
> `DefaultDebugService` использует реализацию на основе `Stopwatch` и выводом в консоль.
</br>

View File

@ -41,7 +41,7 @@
</br>
DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框架。专注于提升便利性、模块性、可扩展性和动态实体修改性能。 用纯C#开发的,没有依赖和代码生成。灵感来自于[LeoEcs](https://github.com/Leopotam/ecslite)。
DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框架。专注于提升便利性、模块性、可扩展性和动态实体修改性能。 用纯C#开发的,没有依赖和代码生成。灵感来自于[LeoEcs Lite](https://github.com/Leopotam/ecslite)。
> [!WARNING]
> 该框架是预发布版本,因此 API 可能会有变化。在 `main` 分支中是当前的工作版本。</br>

View File

@ -43,7 +43,7 @@
</br>
The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims to maximize usability, modularity, extensibility and performance of dynamic entity changes. Without code generation and dependencies. Inspired by [LeoEcs](https://github.com/Leopotam/ecslite).
The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims to maximize usability, modularity, extensibility and performance of dynamic entity changes. Without code generation and dependencies. Inspired by [LeoEcs Lite](https://github.com/Leopotam/ecslite).
> [!WARNING]
> The project is a work in progress, API may change.