mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2025-09-18 01:44:35 +08:00
up version to 0.8.20
This commit is contained in:
parent
256b72ad7c
commit
3ab89c7a4b
411
README-RU.md
411
README-RU.md
@ -14,9 +14,6 @@
|
|||||||
|
|
||||||
Данный [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Фреймворк нацелен на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Без генерации кода и зависимостей. Вднохновлен [LeoEcs](https://github.com/Leopotam/ecslite).
|
Данный [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Фреймворк нацелен на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Без генерации кода и зависимостей. Вднохновлен [LeoEcs](https://github.com/Leopotam/ecslite).
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> 新年快乐!
|
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> Проект в стадии разработки. API может меняться.
|
> Проект в стадии разработки. API может меняться.
|
||||||
> Readme еще не завершен
|
> Readme еще не завершен
|
||||||
@ -35,18 +32,18 @@
|
|||||||
- [Слои](#слои)
|
- [Слои](#слои)
|
||||||
- [Процессы](#процессы)
|
- [Процессы](#процессы)
|
||||||
- [Мир](#мир)
|
- [Мир](#мир)
|
||||||
- [Компоненты мира](#компоненты-мира)
|
|
||||||
- [Конфигурация мира](#конфигурация-мира)
|
|
||||||
- [Пул](#пул)
|
- [Пул](#пул)
|
||||||
- [Аспект](#аспект)
|
- [Аспект](#аспект)
|
||||||
- [Запросы](#запросы)
|
- [Запросы](#запросы)
|
||||||
- [Группа](#группа)
|
- [Группа](#группа)
|
||||||
- [Корень ECS](#корень-ecs)
|
- [Корень ECS](#корень-ecs)
|
||||||
- [Гибридность](#гибридность)
|
|
||||||
- [Debug](#debug)
|
- [Debug](#debug)
|
||||||
- [Атрибуты](#атрибуты)
|
- [Мета-Атрибуты](#мета-атрибуты)
|
||||||
- [EcsDebug](#ecsdebug)
|
- [EcsDebug](#ecsdebug)
|
||||||
- [Профилирование](#профилирование)
|
- [Профилирование](#профилирование)
|
||||||
|
- [Расширение фреймворка](#расширение-фреймворка)
|
||||||
|
- [Компоненты мира](#компоненты-мира)
|
||||||
|
- [Конфиги](#конфиги)
|
||||||
- [Расширения](#расширения)
|
- [Расширения](#расширения)
|
||||||
- [FAQ](#faq)
|
- [FAQ](#faq)
|
||||||
- [Обратная связь](#обратная-связь)
|
- [Обратная связь](#обратная-связь)
|
||||||
@ -60,6 +57,7 @@
|
|||||||
+ Минимальная версия C# 7.3;
|
+ Минимальная версия C# 7.3;
|
||||||
|
|
||||||
Опционально:
|
Опционально:
|
||||||
|
+ Поддержка NativeAOT
|
||||||
+ Минимальная версия Unity 2020.1.0;
|
+ Минимальная версия Unity 2020.1.0;
|
||||||
|
|
||||||
## Установка для Unity
|
## Установка для Unity
|
||||||
@ -78,7 +76,7 @@ https://github.com/DCFApixels/DragonECS.git
|
|||||||
**Сущности** - это то к чему крепятся данные. Реализованы в виде идентификаторов, которых есть 2 вида:
|
**Сущности** - это то к чему крепятся данные. Реализованы в виде идентификаторов, которых есть 2 вида:
|
||||||
* `int` - однократный идентификатор, применяется в пределах одного тика. Не рекомендуется хранить `int` идентификаторы, в место этого используйте `entlong`;
|
* `int` - однократный идентификатор, применяется в пределах одного тика. Не рекомендуется хранить `int` идентификаторы, в место этого используйте `entlong`;
|
||||||
* `entlong` - долговременный идентификатор, содержит в себе полный набор информации для однозначной идентификации;
|
* `entlong` - долговременный идентификатор, содержит в себе полный набор информации для однозначной идентификации;
|
||||||
``` csharp
|
``` c#
|
||||||
// Создание новой сущности в мире.
|
// Создание новой сущности в мире.
|
||||||
int entityID = _world.NewEntity();
|
int entityID = _world.NewEntity();
|
||||||
|
|
||||||
@ -95,7 +93,7 @@ int newEntityID = _world.CloneEntity(entityID);
|
|||||||
<details>
|
<details>
|
||||||
<summary>Работа с entlong</summary>
|
<summary>Работа с entlong</summary>
|
||||||
|
|
||||||
``` csharp
|
``` c#
|
||||||
// Конвертация int в entlong.
|
// Конвертация int в entlong.
|
||||||
entlong entity = _world.GetEntityLong(entityID);
|
entlong entity = _world.GetEntityLong(entityID);
|
||||||
// или
|
// или
|
||||||
@ -125,14 +123,13 @@ struct Health : IEcsComponent
|
|||||||
struct PlayerTag : IEcsTagComponent {}
|
struct PlayerTag : IEcsTagComponent {}
|
||||||
```
|
```
|
||||||
Встроенные виды компонентов:
|
Встроенные виды компонентов:
|
||||||
* `IEcsComponent` - Компоненты с данными.
|
* `IEcsComponent` - Компоненты с данными. Универсальный тип компонентов.
|
||||||
* `IEcsTagComponent` - Компоненты-теги. Без данных.
|
* `IEcsTagComponent` - Компоненты-теги. Без данных.
|
||||||
* `IEcsHybridComponent` - Гибридные компоненты. Испольщуются для реализации [гибридности](#Гибридность).
|
|
||||||
|
|
||||||
## System
|
## System
|
||||||
**Системы** - это основная логика, тут задается поведение сущностей. Существуют в виде пользовательских классов, реализующих как минимум один из интерфейсов процессов. Основные процессы:
|
**Системы** - это основная логика, тут задается поведение сущностей. Существуют в виде пользовательских классов, реализующих как минимум один из интерфейсов процессов. Основные процессы:
|
||||||
```c#
|
```c#
|
||||||
class SomeSystem : IEcsPreInitProcess, IEcsInitProcess, IEcsRunProcess, IEcsDestroyProcess
|
class SomeSystem : IEcsPreInit, IEcsInit, IEcsRun, IEcsDestroy
|
||||||
{
|
{
|
||||||
// Будет вызван один раз в момент работы EcsPipeline.Init() и до срабатывания IEcsInitProcess.Init()
|
// Будет вызван один раз в момент работы EcsPipeline.Init() и до срабатывания IEcsInitProcess.Init()
|
||||||
public void PreInit () { }
|
public void PreInit () { }
|
||||||
@ -170,46 +167,31 @@ pipeline.Init(); // Инициализация пайплайна
|
|||||||
```
|
```
|
||||||
> Для одновременного построения и инициализации есть метод Builder.BuildAndInit();
|
> Для одновременного построения и инициализации есть метод Builder.BuildAndInit();
|
||||||
### Внедрение зависимостей
|
### Внедрение зависимостей
|
||||||
Внедрение зависимостей - это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в Builder.
|
Фреймворк реализует внедрение зависимостей для систем. это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в Builder.
|
||||||
|
> Использование встроенного внедрения зависимостей опционально.
|
||||||
> [!WARNING]
|
|
||||||
> Внедрение идет параллельно с PreInit, поэтому в PreInit инъекция - не гарантируется.
|
|
||||||
|
|
||||||
> [!WARNING]
|
|
||||||
> Экземпляр EcsPipeline автоматически внедряется еще до PreInit.
|
|
||||||
``` c#
|
``` c#
|
||||||
SomeData _someData;
|
class SomeDataA { /*...*/ }
|
||||||
|
class SomeDataB : SomeDataA { /*...*/ }
|
||||||
|
|
||||||
//...
|
//...
|
||||||
|
SomeDataB _someDataB = new SomeDataB();
|
||||||
EcsPipelone pipeline = EcsPipeline.New()
|
EcsPipelone pipeline = EcsPipeline.New()
|
||||||
//...
|
//...
|
||||||
.Inject(_someData) // Внедрит в системы экземпляр _someData
|
// Внедрит _someDataB в системы реализующие IEcsInject<SomeDataB>.
|
||||||
|
.Inject(_someDataB)
|
||||||
|
// Добавит системы реализующие IEcsInject<SomeDataA> в дерево инъекции
|
||||||
|
// теперь эти системы так же получат _someDataB.
|
||||||
|
.Injector.AddNode<SomeDataA>() //
|
||||||
//...
|
//...
|
||||||
.BuildAndInit();
|
.BuildAndInit();
|
||||||
|
|
||||||
//...
|
//...
|
||||||
|
// Для внедрения используется интерфейс IEcsInject<T> и его метод Inject(T obj)
|
||||||
class SomeSystem : IEcsInject<SomeData>, IEcsRunProcess
|
class SomeSystem : IEcsInject<SomeDataA>, IEcsRunProcess
|
||||||
{
|
{
|
||||||
// Для внедрения используется интерфейс IEcsInject<T> и его метод Inject(T obj)
|
SomeDataA _someDataA
|
||||||
SomeData _someData
|
//obj будет экземпляром типа SomeDataB
|
||||||
public void Inject(SomeData obj) => _someData = obj;
|
public void Inject(SomeDataA obj) => _someDataA = obj;
|
||||||
|
|
||||||
public void PreInit ()
|
|
||||||
{
|
|
||||||
// тут возможно еще не внедрен _someData
|
|
||||||
}
|
|
||||||
public void Init ()
|
|
||||||
{
|
|
||||||
// тут можно пользовать _someData
|
|
||||||
}
|
|
||||||
public void Run ()
|
|
||||||
{
|
|
||||||
// тут можно пользовать _someData
|
|
||||||
}
|
|
||||||
public void Destroy ()
|
|
||||||
{
|
|
||||||
// тут можно пользовать _someData
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
### Модули
|
### Модули
|
||||||
@ -226,7 +208,7 @@ class Module : IEcsModule
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
``` csharp
|
``` c#
|
||||||
EcsPipelone pipeline = EcsPipeline.New()
|
EcsPipelone pipeline = EcsPipeline.New()
|
||||||
//...
|
//...
|
||||||
.AddModule(new Module())
|
.AddModule(new Module())
|
||||||
@ -252,118 +234,78 @@ EcsPipelone pipeline = EcsPipeline.New()
|
|||||||
* `EcsConst.POST_END_LAYER`
|
* `EcsConst.POST_END_LAYER`
|
||||||
|
|
||||||
## Процессы
|
## Процессы
|
||||||
Процессы - это очереди систем реализующие общий интерфейс, например `IEcsRunProcess`. Для запуска процессов используются Runner-ы. Встроенные процессы вызываются автоматически, для запуска пользовательских процессов используйте раннеры получаемые из `EcsPipeline.GetRunner<TInterface>()`.
|
Процессы - это очереди систем реализующие общий интерфейс, например `IEcsRun`. Для запуска процессов используются Runner-ы. Втроенные процессы запускаются автоматически. Есть возможность реализации пользовательских процессов.
|
||||||
> Рекомендуется кешировать полученные через GetRunner раннеры.
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Встроенные процессы</summary>
|
<summary>Встроенные процессы</summary>
|
||||||
|
|
||||||
* `IEcsPreInitProcess`, `IEcsInitProcess`, `IEcsRunProcess`, `IEcsDestroyProcess` - процессы жизненого цикла `EcsPipeline`.
|
* `IEcsPreInit`, `IEcsInit`, `IEcsRun`, `IEcsDestroy` - процессы жизненого цикла `EcsPipeline`.
|
||||||
* `IEcsPreInject`, `IEcsInject<T>` - Процессы системы [внедрения зависимостей](#Внедрение-зависимостей).
|
* `IEcsInject<T>` - Процессы системы [внедрения зависимостей](#Внедрение-зависимостей).
|
||||||
* `IEcsPreInitInjectProcess` - Так же процесс системы [внедрения зависимостей](#Внедрение-зависимостей), но работает в пределах до выполнения IEcsInitProcess, сигнализирует о начале и окончании предварительных внедрений.
|
* `IOnInitInjectionComplete` - Так же процесс системы [внедрения зависимостей](#Внедрение-зависимостей), но сигнализирует о завершении инициализирующей инъекции.
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Пользовательские процессы</summary>
|
<summary>Пользовательские процессы</summary>
|
||||||
|
|
||||||
Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от EcsRunner<TInterface>. А после к интерфейсу добавте атрибут `BindWithEcsRunner` для связи. Пример:
|
Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от `EcsRunner<TInterface>`. Пример:
|
||||||
```c#
|
``` c#
|
||||||
[BindWithEcsRunner(typeof(DoSomethingProcessRunner))]
|
//Интерфейс.
|
||||||
interface IDoSomethingProcess : IEcsProcess
|
interface IDoSomethingProcess : IEcsProcess
|
||||||
{
|
{
|
||||||
void Do();
|
void Do();
|
||||||
}
|
}
|
||||||
|
//Реализация раннера. Пример реализации можно так же посмотреть в встроенных процессах
|
||||||
sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>, IDoSomethingProcess
|
sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>, IDoSomethingProcess
|
||||||
{
|
{
|
||||||
public void Do()
|
public void Do()
|
||||||
{
|
{
|
||||||
foreach (var item in targets) item.Do();
|
foreach (var item in Process) item.Do();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
//...
|
||||||
|
|
||||||
|
//Добавление раннера при создании пайплайна.
|
||||||
|
_pipeline = EcsPipeline.New()
|
||||||
|
//...
|
||||||
|
.AddRunner<DoSomethingProcessRunner>()
|
||||||
|
//...
|
||||||
|
.BuildAndInit();
|
||||||
|
|
||||||
|
//Запуск раннера если раннер был добавлен.
|
||||||
|
_pipeline.GetRunner<IDoSomethingProcess>.Do()
|
||||||
|
|
||||||
|
//Или если раннер небыл добавлен(Вызов GetRunnerInstance так же добавит раннер в пайплайн).
|
||||||
|
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
|
||||||
```
|
```
|
||||||
> Раннеры имеют ряд требований к реализации:
|
> Раннеры имеют ряд требований к реализации:
|
||||||
> * Для одного интерфейса может быть только одна реализация раннера;
|
> * Наследоваться от `EcsRunner<T>` можно только напрямую;
|
||||||
> * Наследоваться от `EcsRunner<TInterface>` можно только напрямую;
|
> * Раннер может содержать только один интерфейс(за исключением `IEcsProcess`);
|
||||||
> * Раннер может содержать только один интерфейс(за исключением `IEcsSystem`);
|
> * Наследуемый класс `EcsRunner<T>,` далжен так же реализовавыть интерфейс `T`;
|
||||||
> * Наследуемый класс `EcsRunner<TInterface>,` в качестве `TInterface` должен принимать реализованный интерфейс;
|
|
||||||
> * Раннер не может быть размещен внутри другого класса.
|
|
||||||
|
|
||||||
|
> Не рекомендуется в цикле вызывать `GetRunner`, иначе кешируйте полученный раннер.
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Мир
|
## Мир
|
||||||
Является контейнером для сущностей и компонентов.
|
Является контейнером для сущностей и компонентов.
|
||||||
> **NOTICE:** Необходимо вызывать EcsWorld.Destroy() у экземпляра мира если он больше не нужен.
|
``` c#
|
||||||
### Компоненты мира
|
//Создание экземпляра мира
|
||||||
С помощью компонентов можно прикреплять дополнительные данные к мирам. В качестве компонентов используются `struct` типы.
|
_world = new EcsDefaultWorld();
|
||||||
``` csharp
|
//Пример из раздела Сущности
|
||||||
ref WorldComponent component = ref _world.Get<WorldComponent>();
|
var e = _world.NewEntity();
|
||||||
|
_world.DelEntity(e);
|
||||||
```
|
```
|
||||||
Реализация компонента:
|
> **NOTICE:** Необходимо вызывать EcsWorld.Destroy() у экземпляра мира если он больше не используется, иначе он будет висеть в памяти.
|
||||||
``` csharp
|
|
||||||
public struct WorldComponent
|
|
||||||
{
|
|
||||||
// Данные.
|
|
||||||
}
|
|
||||||
```
|
|
||||||
Или:
|
|
||||||
``` csharp
|
|
||||||
public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
|
||||||
{
|
|
||||||
// Данные.
|
|
||||||
void IEcsWorldComponent<WorldComponent>.Init(ref WorldComponent component, EcsWorld world)
|
|
||||||
{
|
|
||||||
// Действия при инициализации компонента. Вызывается до первого возвращения из EcsWorld.Get
|
|
||||||
}
|
|
||||||
void IEcsWorldComponent<WorldComponent>.OnDestroy(ref WorldComponent component, EcsWorld world)
|
|
||||||
{
|
|
||||||
// Действия когда вызывается EcsWorld.Destroy.
|
|
||||||
// Вызов OnDestroy, обязует пользователя вручную обнулять компонент, если это необходимо.
|
|
||||||
component = default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Пример использования</summary>
|
|
||||||
|
|
||||||
События интерфейса IEcsWorldComponent<T>, могут быть использованы для автоматической инициализации полей компонента, и освобождения ресурсов.
|
|
||||||
``` csharp
|
|
||||||
public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
|
||||||
{
|
|
||||||
private SomeClass _object; // Объект который будет утилизироваться.
|
|
||||||
private SomeReusedClass _resusedObject; // Объект который будет переиспользоваться.
|
|
||||||
public SomeClass Object => _object;
|
|
||||||
public SomeReusedClass ResusedObject => _resusedObject;
|
|
||||||
void IEcsWorldComponent<WorldComponent>.Init(ref WorldComponent component, EcsWorld world)
|
|
||||||
{
|
|
||||||
if (component._resusedObject == null)
|
|
||||||
component._resusedObject = new SomeReusedClass();
|
|
||||||
component._object = new SomeClass();
|
|
||||||
// Теперь при получении компонента через EcsWorld.Get, _resusedObject и _object уже будут созданы.
|
|
||||||
}
|
|
||||||
void IEcsWorldComponent<WorldComponent>.OnDestroy(ref WorldComponent component, EcsWorld world)
|
|
||||||
{
|
|
||||||
// Утилизируем не нужный объект, и освобождаем ссылку на него, чтобы GC мог его собрать.
|
|
||||||
component._object.Dispose();
|
|
||||||
component._object = null;
|
|
||||||
|
|
||||||
// Как вариант тут можно сделать сброс значений у переиспользуемого объекта.
|
|
||||||
//component._resusedObject.Reset();
|
|
||||||
|
|
||||||
//Так как в этом примере не нужно полное обнуление компонента, то строчка ниже не нужна.
|
|
||||||
//component = default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
</details>
|
|
||||||
|
|
||||||
### Конфигурация мира
|
### Конфигурация мира
|
||||||
При создании мира, в конструктор можно передать реализацию интерфейса `IEcsWorldConfig`. Реализующие его классы своего рода контейнеры для конфигов. По умолчанию уже реализован стандартный контейнер `EcsWorldConfig` и `Get`/`Set` методы для базовой конфигурации мира.
|
При создании мира, в конструктор можно передать экземпляр `EcsWorldConfig`.
|
||||||
|
|
||||||
``` csharp
|
``` c#
|
||||||
EcsWorldConfig config = new EcsWorldConfig()
|
EcsWorldConfig config = new EcsWorldConfig(
|
||||||
.Set_EntitiesCapacity(512);
|
//предварительно инициализирует вместимость мира для 2000 сущностей
|
||||||
|
entitiesCapacity: 2000,
|
||||||
|
//предварительно инициализирует вместимость пулов для 2000 компонентов
|
||||||
|
poolComponentsCapacity: 2000);
|
||||||
_world = new EcsDefaultWorld(config);
|
_world = new EcsDefaultWorld(config);
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -371,11 +313,10 @@ _world = new EcsDefaultWorld(config);
|
|||||||
## Пул
|
## Пул
|
||||||
Является контейнером для компонентов, предоставляет методы для добавления/чтения/редактирования/удаления компонентов на сущности. Есть несколько видов пулов, для разных целей:
|
Является контейнером для компонентов, предоставляет методы для добавления/чтения/редактирования/удаления компонентов на сущности. Есть несколько видов пулов, для разных целей:
|
||||||
* `EcsPool` - универсальный пул, хранит struct-компоненты реализующие интерфейс `IEcsComponent`;
|
* `EcsPool` - универсальный пул, хранит struct-компоненты реализующие интерфейс `IEcsComponent`;
|
||||||
* `EcsTagPool` - подходит для хранения пустых компонентов-тегов, в сравнении с `EcsPool` имеет лучше оптимизацию памяти и скорости, хранит struct-компоненты `IEcsTagComponent`;
|
* `EcsTagPool` - подходит для хранения пустых компонентов-тегов, в сравнении с `EcsPool` имеет лучше оптимизацию памяти и скорости, хранит struct-компоненты с `IEcsTagComponent`;
|
||||||
* `EcsHybridPool` - пул для гибридных компонентов. Испольщуются для реализации [гибридности](#Гибридность), хранит struct-компоненты `IEcsHybridComponent`;
|
|
||||||
|
|
||||||
Пулы имеют 5 основных метода и их разновидности:
|
Пулы имеют 5 основных метода и их разновидности:
|
||||||
``` csharp
|
``` c#
|
||||||
// Один из способов получить пул из мира.
|
// Один из способов получить пул из мира.
|
||||||
EcsPool<Pose> poses = _world.GetPool<Pose>();
|
EcsPool<Pose> poses = _world.GetPool<Pose>();
|
||||||
|
|
||||||
@ -401,7 +342,7 @@ poses.Del(entityID);
|
|||||||
|
|
||||||
## Аспект
|
## Аспект
|
||||||
Это пользовательские классы наследуемые от `EcsAspect`, которые используются как посредник для взаимодействия с сущностями. Аспекты одновременно являются кешем пулов и ограничением для фильтрации сущностей.
|
Это пользовательские классы наследуемые от `EcsAspect`, которые используются как посредник для взаимодействия с сущностями. Аспекты одновременно являются кешем пулов и ограничением для фильтрации сущностей.
|
||||||
``` csharp
|
``` c#
|
||||||
using DCFApixels.DragonECS;
|
using DCFApixels.DragonECS;
|
||||||
...
|
...
|
||||||
class Aspect : EcsAspect
|
class Aspect : EcsAspect
|
||||||
@ -424,7 +365,7 @@ class Aspect : EcsAspect
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
В аспекты можно добавлять другие аспекты, тем самым комбинируя их. Ограничения так же будут скомбинированы
|
В аспекты можно добавлять другие аспекты, тем самым комбинируя их. Ограничения так же будут скомбинированы
|
||||||
``` csharp
|
``` c#
|
||||||
using DCFApixels.DragonECS;
|
using DCFApixels.DragonECS;
|
||||||
...
|
...
|
||||||
class Aspect : EcsAspect
|
class Aspect : EcsAspect
|
||||||
@ -458,7 +399,7 @@ class Aspect : EcsAspect
|
|||||||
Используйте метод-запрос `EcsWorld.Where<TAspcet>(out TAspcet aspect)` для получения необходимого системе набора сущностей. Запросы работают в связке с аспектами, аспекты определяют ограничения запросов, результатом запроса становится группа сущностей удовлетворяющая условиям аспекта. По умолчанию запрос делает выборку из всех сущностей в мире, но так же можно сделать выборку из определенной группы сущностей, для этого используйте `EcsWorld.WhereFor<TAspcet>(EcsReadonlyGroup sourceGroup, out TAspcet aspect)`
|
Используйте метод-запрос `EcsWorld.Where<TAspcet>(out TAspcet aspect)` для получения необходимого системе набора сущностей. Запросы работают в связке с аспектами, аспекты определяют ограничения запросов, результатом запроса становится группа сущностей удовлетворяющая условиям аспекта. По умолчанию запрос делает выборку из всех сущностей в мире, но так же можно сделать выборку из определенной группы сущностей, для этого используйте `EcsWorld.WhereFor<TAspcet>(EcsReadonlyGroup sourceGroup, out TAspcet aspect)`
|
||||||
|
|
||||||
## Группа
|
## Группа
|
||||||
Группы это структуры данных для хранения множества сущностей с быстрыми операциями добавления/удаления/проверки наличия и т.д. Реализованы классом `EcsGroup` и структурой `EcsReadonlyGroup`.
|
Группы это структуры данных для хранения множества сущностей с O(1) операциями добавления/удаления/проверки наличия и т.д. Реализованы классом `EcsGroup` и структурой `EcsReadonlyGroup`.
|
||||||
|
|
||||||
``` c#
|
``` c#
|
||||||
//Получем новую группу. EcsWorld содержит в себе пул групп,
|
//Получем новую группу. EcsWorld содержит в себе пул групп,
|
||||||
@ -514,7 +455,7 @@ EcsGroup newGroup = EcsGroup.Inverse(groupA);
|
|||||||
## Корень ECS
|
## Корень ECS
|
||||||
Это пользовательский класс который явялестя точкой входа для ECS. Основное назначение инициализация, запуск систем на каждый Update движка и очистка по окончанию сипользования.
|
Это пользовательский класс который явялестя точкой входа для ECS. Основное назначение инициализация, запуск систем на каждый Update движка и очистка по окончанию сипользования.
|
||||||
### Пример для Unity
|
### Пример для Unity
|
||||||
``` csharp
|
``` c#
|
||||||
using DCFApixels.DragonECS;
|
using DCFApixels.DragonECS;
|
||||||
using UnityEngine;
|
using UnityEngine;
|
||||||
public class EcsRoot : MonoBehaviour
|
public class EcsRoot : MonoBehaviour
|
||||||
@ -560,7 +501,7 @@ public class EcsRoot : MonoBehaviour
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
### Общий пример
|
### Общий пример
|
||||||
``` csharp
|
``` c#
|
||||||
using DCFApixels.DragonECS;
|
using DCFApixels.DragonECS;
|
||||||
public class EcsRoot
|
public class EcsRoot
|
||||||
{
|
{
|
||||||
@ -609,94 +550,12 @@ public class EcsRoot
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
## Гибридность
|
|
||||||
Для смешивания архитектурных подходов классического OOP и ECS используется специальный пул `EcsHybridPool<T>`. Принцип работы этого пула несколько отличается от других и он упрощает поддержу наследования и полиморфизма.
|
|
||||||
|
|
||||||
<details>
|
|
||||||
<summary>Как это работает?</summary>
|
|
||||||
|
|
||||||
При добавлении элемента в пул, пул сканирует его иерархию наследования и реализуемые интерфейсы в поиске типов у которых есть интерфес `IEcsHybridComponent` и автоматически добавляет компонент в соответсвующие этим типам пулы. Таким же образом происходит удаление. Сканирвоание просиходит не для типа T а для типа экземпляра, поэтому в примере ниже строчка в `_world.GetPool<ITransform>().Add(entity, _rigidbody);` добавляет не только в пул `EcsHybridPool<ITransform>` но и в остальные.
|
|
||||||
|
|
||||||
</details>
|
|
||||||
|
|
||||||
Пример использования:
|
|
||||||
``` csharp
|
|
||||||
public interface ITransform : IEcsHybridComponent
|
|
||||||
{
|
|
||||||
Vector3 Position { get; set; }
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
public class Transform : ITransform
|
|
||||||
{
|
|
||||||
public Vector3 Position { get; set; }
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
public class Rigidbody : Transform
|
|
||||||
{
|
|
||||||
public Vector3 Position { get; set; }
|
|
||||||
public float Mass { get; set; }
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
public class Camera : ITransform
|
|
||||||
{
|
|
||||||
Vector3 Position { get; set; }
|
|
||||||
// ...
|
|
||||||
}
|
|
||||||
public TransformAspect : EcsAspect
|
|
||||||
{
|
|
||||||
public EcsHybridPool<Transform> transforms;
|
|
||||||
public Aspect(Builder b)
|
|
||||||
{
|
|
||||||
transforms = b.Include<Transform>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// ...
|
|
||||||
|
|
||||||
EcsWorld _world;
|
|
||||||
Rigidbody _rigidbody;
|
|
||||||
// ...
|
|
||||||
|
|
||||||
// Создадим пустую сущность.
|
|
||||||
int entity = _world.NewEmptyEntity();
|
|
||||||
// Получаем пул EcsHybridPool<ITransform> и добавляем в него для сущности компонент _rigidbody.
|
|
||||||
// Если вместо ITransform подставить Transform или Rigidbody, то результат будет одинаковый
|
|
||||||
_world.GetPool<ITransform>().Add(entity, _rigidbody);
|
|
||||||
// ...
|
|
||||||
|
|
||||||
//Все эти строчки вернут экземпляр _rigidbody.
|
|
||||||
ITransform iTransform = _world.GetPool<ITransform>().Get(entity);
|
|
||||||
Transform transform = _world.GetPool<Transform>().Get(entity);
|
|
||||||
Rigidbody rigidbody = _world.GetPool<Rigidbody>().Get(entity);
|
|
||||||
//Исключение - отсутсвует компонент. Camera не является наследником или наследуемым классом для _rigidbody.
|
|
||||||
Camera camera = _world.GetPool<Camera>().Get(entity);
|
|
||||||
|
|
||||||
//Вернет True. Поэтому фишка гибридных пулов будет работать и в запросах сущностей
|
|
||||||
bool isMatches = _world.GetAspect<TransformAspect>().IsMatches(entity);
|
|
||||||
|
|
||||||
//Все эти строчки вернут True.
|
|
||||||
bool isITransform = _world.GetPool<ITransform>().Has(entity);
|
|
||||||
bool isTransform = _world.GetPool<Transform>().Has(entity);
|
|
||||||
bool isRigidbody = _world.GetPool<Rigidbody>().Has(entity);
|
|
||||||
//Эта строчка вернет False.
|
|
||||||
bool isCamera = _world.GetPool<Camera>().Has(entity);
|
|
||||||
// ...
|
|
||||||
|
|
||||||
// Удалим у сущности компонент.
|
|
||||||
_world.GetPool<ITransform>().Del(entity);
|
|
||||||
// ...
|
|
||||||
//Все эти строчки вернут False.
|
|
||||||
bool isITransform = _world.GetPool<ITransform>().Has(entity);
|
|
||||||
bool isTransform = _world.GetPool<Transform>().Has(entity);
|
|
||||||
bool isRigidbody = _world.GetPool<Rigidbody>().Has(entity);
|
|
||||||
bool isCamera = _world.GetPool<Camera>().Has(entity);
|
|
||||||
// ...
|
|
||||||
```
|
|
||||||
|
|
||||||
</br>
|
</br>
|
||||||
|
|
||||||
# Debug
|
# Debug
|
||||||
Фреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды. Так же многие типы имеют свой DebuggerProxy для более информативного отображения в IDE.
|
Фреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды. Так же многие типы имеют свой DebuggerProxy для более информативного отображения в IDE.
|
||||||
## Атрибуты
|
## Мета-Атрибуты
|
||||||
В чистом виде мета-атрибуты не имеют применения, но могут быть использованы для генерации автоматической документации и используются в интеграциях с движками для задания отображения в отладочных инструментах и редакторах.
|
В чистом виде мета-атрибуты не имеют применения, но могут быть использованы для генерации автоматической документации и используются в интеграциях с движками для задания отображения в отладочных инструментах и редакторах.
|
||||||
``` c#
|
``` c#
|
||||||
using DCFApixels.DragonECS;
|
using DCFApixels.DragonECS;
|
||||||
@ -708,21 +567,41 @@ using DCFApixels.DragonECS;
|
|||||||
[MetaGroup("Abilities/Passive/")]
|
[MetaGroup("Abilities/Passive/")]
|
||||||
|
|
||||||
// Задает цвет типа в системе rgb, где каждый канал принимает значение от 0 до 255, по умолчанию белый.
|
// Задает цвет типа в системе rgb, где каждый канал принимает значение от 0 до 255, по умолчанию белый.
|
||||||
[MetaColor(MetaColor.Red)] // или [DebugColor(255, 0, 0)]
|
[MetaColor(MetaColor.Red)] // или [MetaColor(255, 0, 0)]
|
||||||
|
|
||||||
// Добавляет описание типу.
|
// Добавляет описание типу.
|
||||||
[MetaDescription("The quick brown fox jumps over the lazy dog")]
|
[MetaDescription("The quick brown fox jumps over the lazy dog")]
|
||||||
|
|
||||||
// Добавляет строковые теги.
|
// Добавляет строковые теги.
|
||||||
[MetaTags(...)] // [MetaTags(MetaTags.HIDDEN))] чтобы скрыть в редакторе
|
[MetaTags("Tag1", "Tag2", ...)] // [MetaTags(MetaTags.HIDDEN))] чтобы скрыть в редакторе
|
||||||
public struct Component { }
|
public struct Component { }
|
||||||
```
|
```
|
||||||
|
Получение мета-информации. С помощью TypeMeta:
|
||||||
|
``` c#
|
||||||
|
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;
|
||||||
|
```
|
||||||
|
С помощью EcsDebugUtility:
|
||||||
|
``` c#
|
||||||
|
EcsDebugUtility.GetMetaName(someComponent);
|
||||||
|
EcsDebugUtility.GetColor(someComponent);
|
||||||
|
EcsDebugUtility.GetDescription(someComponent);
|
||||||
|
EcsDebugUtility.GetGroup(someComponent);
|
||||||
|
EcsDebugUtility.GetTags(someComponent);
|
||||||
|
```
|
||||||
## EcsDebug
|
## EcsDebug
|
||||||
Имеет набор методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы - это посредники между системами отладки среды и EcsDebug. Это позволяет не изменяя отладочный код проекта, переносить проект на другие движки, достаточно только реализовать специальный Debug-сервис.
|
Имеет набор методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы - это посредники между системами отладки среды и EcsDebug. Это позволяет не изменяя отладочный код проекта, переносить проект на другие движки, достаточно только реализовать специальный Debug-сервис.
|
||||||
|
|
||||||
По умолчанию используется `DefaultDebugService` который выводит логи в консоль. Для реализации пользовательского создайте класс наследуемый от `DebugService` и реализуйте абстрактные члены класса.
|
По умолчанию используется `DefaultDebugService` который выводит логи в консоль. Для реализации пользовательского создайте класс наследуемый от `DebugService` и реализуйте абстрактные члены класса.
|
||||||
|
|
||||||
``` csharp
|
``` c#
|
||||||
// Логирование.
|
// Логирование.
|
||||||
EcsDebug.Print("Message");
|
EcsDebug.Print("Message");
|
||||||
|
|
||||||
@ -737,7 +616,7 @@ EcsDebug.Set<OtherDebugService>();
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Профилирование
|
## Профилирование
|
||||||
``` csharp
|
``` c#
|
||||||
// Создание маркера с именем SomeMarker.
|
// Создание маркера с именем SomeMarker.
|
||||||
private static readonly EcsProfilerMarker marker = new EcsProfilerMarker("SomeMarker");
|
private static readonly EcsProfilerMarker marker = new EcsProfilerMarker("SomeMarker");
|
||||||
|
|
||||||
@ -757,6 +636,96 @@ using (marker.Auto())
|
|||||||
|
|
||||||
</br>
|
</br>
|
||||||
|
|
||||||
|
# Расширение фреймворка
|
||||||
|
Для большей расширемости фреймворкеа есть дополнительные инструменты.
|
||||||
|
|
||||||
|
## Конфиги
|
||||||
|
Конструкторы классов `EcsWorld` и `EcsPipeline` могут принимать контейнеры конфигов реализующие интерфейс `IConfigContainer` или `IConfigContainerWriter`. С помощью этих контейнеров можно передавать данные и зависимости. Встроенная реализация контейнера - `ConfigContainer`</br>
|
||||||
|
Пример использования конфигов для мира:
|
||||||
|
``` c#
|
||||||
|
var configs = new ConfigContainer()
|
||||||
|
.Set(new EcsWorldConfig(entitiesCapacity: 2000, poolsCapacity: 2000)
|
||||||
|
.Set(new SomeDataA(/* ... */))
|
||||||
|
.Set(new SomeDataB(/* ... */)));
|
||||||
|
EcsDefaultWorld _world = new EcsDefaultWorld(configs);
|
||||||
|
```
|
||||||
|
Пример использования конфигов для пайплайна:
|
||||||
|
``` c#
|
||||||
|
_pipeline = EcsPipeline.New()// аналогично _pipeline = EcsPipeline.New(new ConfigContainer())
|
||||||
|
.Configs.Set(new SomeDataA(/* ... */))
|
||||||
|
.Configs.Set(new SomeDataB(/* ... */))
|
||||||
|
//...
|
||||||
|
.BuildAndInit();
|
||||||
|
//...
|
||||||
|
var _someDataA = _pipeline.Configs.Get<SomeDataA>();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Компоненты Миры
|
||||||
|
С помощью компонентов можно прикреплять дополнительные данные к мирам. В качестве компонентов используются `struct` типы.
|
||||||
|
``` c#
|
||||||
|
ref WorldComponent component = ref _world.Get<WorldComponent>();
|
||||||
|
```
|
||||||
|
Реализация компонента:
|
||||||
|
``` c#
|
||||||
|
public struct WorldComponent
|
||||||
|
{
|
||||||
|
// Данные.
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Или:
|
||||||
|
``` c#
|
||||||
|
public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||||
|
{
|
||||||
|
// Данные.
|
||||||
|
void IEcsWorldComponent<WorldComponent>.Init(ref WorldComponent component, EcsWorld world)
|
||||||
|
{
|
||||||
|
// Действия при инициализации компонента. Вызывается до первого возвращения из EcsWorld.Get
|
||||||
|
}
|
||||||
|
void IEcsWorldComponent<WorldComponent>.OnDestroy(ref WorldComponent component, EcsWorld world)
|
||||||
|
{
|
||||||
|
// Действия когда вызывается EcsWorld.Destroy.
|
||||||
|
// Вызов OnDestroy, обязует пользователя вручную обнулять компонент, если это необходимо.
|
||||||
|
component = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
<details>
|
||||||
|
<summary>Пример использования</summary>
|
||||||
|
|
||||||
|
События интерфейса IEcsWorldComponent<T>, могут быть использованы для автоматической инициализации полей компонента, и освобождения ресурсов.
|
||||||
|
``` c#
|
||||||
|
public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||||
|
{
|
||||||
|
private SomeClass _object; // Объект который будет утилизироваться.
|
||||||
|
private SomeReusedClass _resusedObject; // Объект который будет переиспользоваться.
|
||||||
|
public SomeClass Object => _object;
|
||||||
|
public SomeReusedClass ResusedObject => _resusedObject;
|
||||||
|
void IEcsWorldComponent<WorldComponent>.Init(ref WorldComponent component, EcsWorld world)
|
||||||
|
{
|
||||||
|
if (component._resusedObject == null)
|
||||||
|
component._resusedObject = new SomeReusedClass();
|
||||||
|
component._object = new SomeClass();
|
||||||
|
// Теперь при получении компонента через EcsWorld.Get, _resusedObject и _object уже будут созданы.
|
||||||
|
}
|
||||||
|
void IEcsWorldComponent<WorldComponent>.OnDestroy(ref WorldComponent component, EcsWorld world)
|
||||||
|
{
|
||||||
|
// Утилизируем не нужный объект, и освобождаем ссылку на него, чтобы GC мог его собрать.
|
||||||
|
component._object.Dispose();
|
||||||
|
component._object = null;
|
||||||
|
|
||||||
|
// Как вариант тут можно сделать сброс значений у переиспользуемого объекта.
|
||||||
|
//component._resusedObject.Reset();
|
||||||
|
|
||||||
|
//Так как в этом примере не нужно полное обнуление компонента, то строчка ниже не нужна.
|
||||||
|
//component = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
</details>
|
||||||
|
|
||||||
|
</br>
|
||||||
|
|
||||||
# Расширения
|
# Расширения
|
||||||
* [Автоматическое внедрение зависимостей](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
* [Автоматическое внедрение зависимостей](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||||
* [Поддержка классической C# многопоточности](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
* [Поддержка классической C# многопоточности](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||||
|
28
README.md
28
README.md
@ -14,16 +14,34 @@
|
|||||||
|
|
||||||
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](https://github.com/Leopotam/ecslite).
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> 新年快乐!
|
|
||||||
|
|
||||||
> [!WARNING]
|
> [!WARNING]
|
||||||
> The project is a work in progress, API may change.
|
> The project is a work in progress, API may change.
|
||||||
>
|
>
|
||||||
> While the English version of the README is incomplete, you can view the [Russian version](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md).
|
> While the English version of the README is incomplete, you can view the [Russian version](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md).
|
||||||
|
|
||||||
# Versioning
|
</br>
|
||||||
DragonECS uses this versioning semantics: [Open](https://gist.github.com/DCFApixels/c3b178a308b411f530361d1d56f1f929#file-dcfapixels_versioning_en-md)
|
|
||||||
|
# Installation
|
||||||
|
Versioning semantics - [Open](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
|
||||||
|
## Environment
|
||||||
|
Requirements:
|
||||||
|
+ Minimum C# version 7.3;
|
||||||
|
|
||||||
|
Optional:
|
||||||
|
+ NativeAOT support;
|
||||||
|
+ Minimum Unity version 2020.1.0;
|
||||||
|
|
||||||
|
## Unity Installation
|
||||||
|
* ### Unity Package
|
||||||
|
The framework can be installed as a Unity package by adding the Git URL [in the PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html) or manually adding it to `Packages/manifest.json`:
|
||||||
|
```
|
||||||
|
https://github.com/DCFApixels/DragonECS.git
|
||||||
|
```
|
||||||
|
* ### Source Code
|
||||||
|
The framework can also be added to the project as source code.
|
||||||
|
|
||||||
|
</br>
|
||||||
|
|
||||||
# Extensions
|
# Extensions
|
||||||
* [Dependency autoinjections](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
* [Dependency autoinjections](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||||
* [Classic C# multithreading support](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
* [Classic C# multithreading support](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
"displayName": "DragonECS",
|
"displayName": "DragonECS",
|
||||||
"description": "C# Entity Component System Framework",
|
"description": "C# Entity Component System Framework",
|
||||||
"unity": "2020.3",
|
"unity": "2020.3",
|
||||||
"version": "0.8.8",
|
"version": "0.8.20",
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://github.com/DCFApixels/DragonECS.git"
|
"url": "https://github.com/DCFApixels/DragonECS.git"
|
||||||
|
Loading…
Reference in New Issue
Block a user