@ -50,6 +50,7 @@ DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system)
## Оглавление
- [Установка](#установка)
- [Расширения](#расширения)
- [Основные концепции](#основные-концепции)
- [Entity](#entity)
- [Component](#component)
@ -77,7 +78,6 @@ DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system)
- [Компоненты мира](#компоненты-мира)
- [Конфиги](#конфиги)
- [Проекты на DragonECS](#Проекты-на-DragonECS)
- [Расширения](#расширения)
- [FAQ](#faq)
- [Обратная связь](#обратная-связь)
@ -94,24 +94,49 @@ DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system)
+ Игровые движки с C#: Unity, Godot, MonoGame и т.д.
Протестировано:
+ **Unity:** Минимальная версия 2020.1.0;
+ **Unity:** Минимальная версия 2020.3.0;
## Установка для Unity
> Рекомендуется так же установить расширение [Интеграция с движком Unity](https://github.com/DCFApixels/DragonECS-Unity)
* ### Unity-модуль
Поддерживается установка в виде Unity-модуля в при помощи добавления git-URL [в PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html) или ручного добавления в `Packages/manifest.json`:
Поддерживается установка в виде Unity-модуля при помощи добавления git-URL [в PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html):
```
https://github.com/DCFApixels/DragonECS.git
```
Или ручного добавления этой строчки в `Packages/manifest.json`:
* [Шаблоны кода IDE](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) и [для Unity](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
> *Твое расширение? Если разрабатываешь расширение для DragonECS, пиши [сюда](#обратная-связь).
</br>
# Основные концепции
## Entity
**Сущности** - это то к чему крепятся данные. Реализованы в виде идентификаторов, которых есть 2 вида:
* `int` - однократный идентификатор, применяется в пределах одного тика. Не рекомендуется хранить `int` идентификаторы, в место этого используйте `entlong`;
* `entlong` - долговременный идентификатор, содержит в себе полный набор информации для однозначной идентификации;
**Сущности** - это то к чему крепятся данные. Для доступа к сущности используются идентификаторы двух типов:
* `int` - однократный идентификатор, применяется в пределах одного тика. Не рекомендуется использовать для хранения;
* `entlong` - долговременный идентификатор, содержит метку поколения, что делает его уникальным навсегда. Подходит для хранения.
``` c#
// Создание новой сущности в мире.
int entityID = _world.NewEntity();
@ -129,10 +154,11 @@ int newEntityID = _world.CloneEntity(entityID);
<details>
<summary>Работа с entlong</summary>
``` c#
// Конвертация int в entlong.
entlong entity = _world.GetEntityLong(entityID);
// или
// упрощенная версия
entlong entity = (_world, entityID);
// Проверка что сущность еще жива.
@ -148,6 +174,8 @@ if (entity.TryGetID(out int entityID)) { }
```
</details>
>
> Сущности не могут существовать без компонентов, пустые сущности будут автоматически удаляться сразу после удаления последнего компонента либо в конце тика.
// Процесс Inject. Получение инстанса EcsDefaultWorld
void IEcsInject<EcsDefaultWorld>.Inject(EcsDefaultWorld obj) => world = obj;
}
// Компоненты
public struct DamageRequest : IEcsComponent
{
public float Points;
}
public struct Health : IEcsComponent
{
public float Points;
}
```
</details>
</br>
# Концепции фреймворка
@ -466,10 +536,13 @@ poses.Del(entityID);
</details>
## Маска
Применяется для фильтрации сущностей по наличию или отсутствию компонентов.
Набор требований к компонентам сущности: какие компоненты должны присутствовать, а какие отсутствовать. Маска — это просто данные, она не выполняет поиск, а используется запросами для фильтрации сущностей.
Обычно маски не используются в чистом виде, а являются частью [Аспекта](#Аспект).
``` c#
// Создание маски которая проверяет что у сущностей есть компоненты
// SomeCmp1 и SomeCmp2, но нет компонента SomeCmp3.
// Создание маски с улосвием наличия компонентов SomeCmp1 и SomeCmp2,
Пользовательские классы наследуемые от `EcsAspect` и используемые для взаимодействия с сущностями. Аспекты одновременно являются кешем пулов и содержат маску. Можно рассматривать аспекты как описание того с какими сущностями работает система.
Упрощенный синтаксис:
Пользовательские классы наследуемые от `EcsAspect`, описывающие наборы компонентов, с которыми работает система. Аспект одновременно выполняет две функции:
+ Маска — инициализирует и хранит [маску](#Маска), за счет чего может использоваться в запросах.
+ Кэш пулов — предоставляет быстрый доступ к пулам компонентов.
Проще говоря, аспект — это удобный способ описать "с какими сущностями я работаю и как получать их компоненты".
Пример:
``` c#
using DCFApixels.DragonECS;
// ...
class Aspect : EcsAspect
{
// Кешируется пул и Pose добавляется во включающее ограничение.
// Кешируется пул и тип Pose добавляется во включающее условие.
public EcsPool<Pose> poses = Inc;
// Кешируется пул и Velocity добавляется во включающее ограничение.
// Кешируется пул и тип Velocity добавляется во включающее условие.
public EcsPool<Velocity> velocities = Inc;
// Кешируется пул и FreezedTag добавляется в исключающее ограничение.
// Кешируется пул и тип FreezedTag добавляется в исключающее условие.
public EcsTagPool<FreezedTag> freezedTags = Exc;
// При запросах будет проверяться наличие компонентов
// из включающего ограничения маски и отсутствие из исключающего.
// Так же есть Opt - только кеширует пул, не влияя на маску.
}
```
Назначение статических свойств:
+ Inc — компонент должен быть у сущности (включающее условие) + кеширует пул
+ Exc — компонента не должно быть у сущности (исключающее условие) + кеширует пул
+ Opt — компонент может быть, но не влияет на фильтрацию (только кеширует пул для доступа)
+ Any — хотя бы один из компонентов с Any должен присутствовать + кеширует пул
<summary>Инициализация через метод (результат идентичен примеру выше):</summary>
``` c#
using DCFApixels.DragonECS;
// ...
class Aspect : EcsAspect
{
public EcsPool<Pose> poses;
public EcsPool<Velocity> velocities;
protected override void Init(Builder b)
{
poses = b.Include<Pose>();
velocities = b.Include<Velocity>();
b.Exclude<FreezedTag>();
poses = b.IncludePool<Pose>();
velocities = b.IncludePool<Velocity>();
b.ExcludePool<FreezedTag>();
}
}
```
@ -546,10 +624,8 @@ class Aspect : EcsAspect
<details>
<summary>Комбинирование аспектов</summary>
В аспекты можно добавлять другие аспекты, тем самым комбинируя их. Ограничения так же будут скомбинированы.
В аспекты можно добавлять другие аспекты, тем самым комбинируя их. Маски так же будут скомбинированы.
``` c#
using DCFApixels.DragonECS;
// ...
class Aspect : EcsAspect
{
public OtherAspect1 otherAspect1;
@ -562,7 +638,7 @@ class Aspect : EcsAspect
otherAspect1 = b.Combine<OtherAspect1>(1);
// Хотя для OtherAspect1 метод Combine был вызван раньше, сначала будет скомбинирован с OtherAspect2, так как по умолчанию order = 0.
otherAspect2 = b.Combine<OtherAspect2>();
// Если в OtherAspect1 или в OtherAspect2 было ограничение b.Exclude<Pose>() тут оно будет заменено на b.Include<Pose>().
// Если в OtherAspect1 или в OtherAspect2 было условие b.Exclude<Pose>() тут оно будет заменено на b.Include<Pose>().
poses = b.Include<Pose>();
}
}
@ -578,11 +654,11 @@ class Aspect : EcsAspect
</details>
## Запросы
Фильтруют сущности и выдают коллекции сущностей удовлетворяющие определенным условиям. Встроенный запрос `Where` фильтрует на соответствие условиям маски компонентов и имеет несколько перегрузок:
Запросы позволяют отфильтровать и получать коллекции сущностей, удовлетворяющих заданным условиям. Основной метод — `Where`, который фильтрует по маске компонентов и имеет несколько перегрузок:
+ `EcsWorld.Where<TAspect>(out TAspect aspect)` - Фильтрация по маске, определённой в аспекте, плюс получение самого аспекта с закешированными пулами.
+ `EcsWorld.Where(EcsMask mask)` - Обычная фильтрация по маске;
+ `EcsWorld.Where<TAspect>(out TAspect aspect)` - Сочетает в себе фильтрацию по маске из аспекта и получение аспекта;
Запрос `Where` применим как к `EcsWorld` так и коллекциям фреймворка (в этом плане Where чем-то похож на аналогичный из Linq). Так же имеются перегрузки для сортировки сущностей по `Comparison<int>`.
`Where` можно вызывать не только у мира, но и у любых коллекций фреймворка (в этом плане Where чем-то похож на аналогичный из Linq). Также доступны перегрузки для сортировки сущностей спомощью`Comparison<int>`.
Пример системы:
``` c#
@ -597,10 +673,9 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
public EcsTagPool<IsDiedSignal> isDiedSignals = Opt;
}
EcsDefaultWorld _world;
public void Inject(EcsDefaultWorld world) => _world = world;
public void Run()
{
// Запрашиваем сущности удовлетворяющее маске из Aspect
foreach (var e in _world.Where(out Aspect a))
{
// Сюда попадают сущности с компонентами Health, DamageSignal и без IsInvulnerable.
@ -615,6 +690,7 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
}
}
}
public void Inject(EcsDefaultWorld world) => _world = world;
}
```
@ -623,7 +699,7 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
## Коллекции
### EcsSpan
Коллекция сущностей, доступная только для чтения и выделяемая только в стеке. Состоит из ссылки на массив, длинны и идентификатора мира. Аналог `ReadOnlySpan<int>`.
`ref struct` коллекция сущностей, доступная только для чтения. Состоит из ссылки на массив, длинны и идентификатора мира. Аналог `ReadOnlySpan<int>`.
``` c#
// Запрос Where возвращает сущности в виде EcsSpan.
EcsSpan es = _world.Where(out Aspect a);
@ -641,7 +717,7 @@ for (int i = 0; i < es.Count; i++)
> Хотя `EcsSpan` является просто массивом, в нем не допускается дублирование сущностей.
### EcsGroup
Вспомогательная коллекция основанная на Sparse Set для хранения множества сущностей с O(1) операциями добавления/удаления/проверки и т.д.
Вспомогательная коллекция основанная на Sparse Set для хранения множества сущностей с`O(1)` операциями добавления/удаления/проверки и т.д.
``` c#
// Получаем новую группу. EcsWorld содержит в себе пул групп,
// поэтому будет переиспользована свободная или создана новая.
@ -1011,6 +1087,23 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.