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).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 新年快乐!
|
||||
|
||||
> [!WARNING]
|
||||
> Проект в стадии разработки. API может меняться.
|
||||
> Readme еще не завершен
|
||||
@ -35,18 +32,18 @@
|
||||
- [Слои](#слои)
|
||||
- [Процессы](#процессы)
|
||||
- [Мир](#мир)
|
||||
- [Компоненты мира](#компоненты-мира)
|
||||
- [Конфигурация мира](#конфигурация-мира)
|
||||
- [Пул](#пул)
|
||||
- [Аспект](#аспект)
|
||||
- [Запросы](#запросы)
|
||||
- [Группа](#группа)
|
||||
- [Корень ECS](#корень-ecs)
|
||||
- [Гибридность](#гибридность)
|
||||
- [Debug](#debug)
|
||||
- [Атрибуты](#атрибуты)
|
||||
- [Мета-Атрибуты](#мета-атрибуты)
|
||||
- [EcsDebug](#ecsdebug)
|
||||
- [Профилирование](#профилирование)
|
||||
- [Расширение фреймворка](#расширение-фреймворка)
|
||||
- [Компоненты мира](#компоненты-мира)
|
||||
- [Конфиги](#конфиги)
|
||||
- [Расширения](#расширения)
|
||||
- [FAQ](#faq)
|
||||
- [Обратная связь](#обратная-связь)
|
||||
@ -60,6 +57,7 @@
|
||||
+ Минимальная версия C# 7.3;
|
||||
|
||||
Опционально:
|
||||
+ Поддержка NativeAOT
|
||||
+ Минимальная версия Unity 2020.1.0;
|
||||
|
||||
## Установка для Unity
|
||||
@ -78,7 +76,7 @@ https://github.com/DCFApixels/DragonECS.git
|
||||
**Сущности** - это то к чему крепятся данные. Реализованы в виде идентификаторов, которых есть 2 вида:
|
||||
* `int` - однократный идентификатор, применяется в пределах одного тика. Не рекомендуется хранить `int` идентификаторы, в место этого используйте `entlong`;
|
||||
* `entlong` - долговременный идентификатор, содержит в себе полный набор информации для однозначной идентификации;
|
||||
``` csharp
|
||||
``` c#
|
||||
// Создание новой сущности в мире.
|
||||
int entityID = _world.NewEntity();
|
||||
|
||||
@ -95,7 +93,7 @@ int newEntityID = _world.CloneEntity(entityID);
|
||||
<details>
|
||||
<summary>Работа с entlong</summary>
|
||||
|
||||
``` csharp
|
||||
``` c#
|
||||
// Конвертация int в entlong.
|
||||
entlong entity = _world.GetEntityLong(entityID);
|
||||
// или
|
||||
@ -125,14 +123,13 @@ struct Health : IEcsComponent
|
||||
struct PlayerTag : IEcsTagComponent {}
|
||||
```
|
||||
Встроенные виды компонентов:
|
||||
* `IEcsComponent` - Компоненты с данными.
|
||||
* `IEcsComponent` - Компоненты с данными. Универсальный тип компонентов.
|
||||
* `IEcsTagComponent` - Компоненты-теги. Без данных.
|
||||
* `IEcsHybridComponent` - Гибридные компоненты. Испольщуются для реализации [гибридности](#Гибридность).
|
||||
|
||||
## System
|
||||
**Системы** - это основная логика, тут задается поведение сущностей. Существуют в виде пользовательских классов, реализующих как минимум один из интерфейсов процессов. Основные процессы:
|
||||
```c#
|
||||
class SomeSystem : IEcsPreInitProcess, IEcsInitProcess, IEcsRunProcess, IEcsDestroyProcess
|
||||
class SomeSystem : IEcsPreInit, IEcsInit, IEcsRun, IEcsDestroy
|
||||
{
|
||||
// Будет вызван один раз в момент работы EcsPipeline.Init() и до срабатывания IEcsInitProcess.Init()
|
||||
public void PreInit () { }
|
||||
@ -170,46 +167,31 @@ pipeline.Init(); // Инициализация пайплайна
|
||||
```
|
||||
> Для одновременного построения и инициализации есть метод Builder.BuildAndInit();
|
||||
### Внедрение зависимостей
|
||||
Внедрение зависимостей - это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в Builder.
|
||||
|
||||
> [!WARNING]
|
||||
> Внедрение идет параллельно с PreInit, поэтому в PreInit инъекция - не гарантируется.
|
||||
|
||||
> [!WARNING]
|
||||
> Экземпляр EcsPipeline автоматически внедряется еще до PreInit.
|
||||
Фреймворк реализует внедрение зависимостей для систем. это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в Builder.
|
||||
> Использование встроенного внедрения зависимостей опционально.
|
||||
``` c#
|
||||
SomeData _someData;
|
||||
class SomeDataA { /*...*/ }
|
||||
class SomeDataB : SomeDataA { /*...*/ }
|
||||
|
||||
//...
|
||||
SomeDataB _someDataB = new SomeDataB();
|
||||
EcsPipelone pipeline = EcsPipeline.New()
|
||||
//...
|
||||
.Inject(_someData) // Внедрит в системы экземпляр _someData
|
||||
// Внедрит _someDataB в системы реализующие IEcsInject<SomeDataB>.
|
||||
.Inject(_someDataB)
|
||||
// Добавит системы реализующие IEcsInject<SomeDataA> в дерево инъекции
|
||||
// теперь эти системы так же получат _someDataB.
|
||||
.Injector.AddNode<SomeDataA>() //
|
||||
//...
|
||||
.BuildAndInit();
|
||||
|
||||
//...
|
||||
|
||||
class SomeSystem : IEcsInject<SomeData>, IEcsRunProcess
|
||||
// Для внедрения используется интерфейс IEcsInject<T> и его метод Inject(T obj)
|
||||
class SomeSystem : IEcsInject<SomeDataA>, IEcsRunProcess
|
||||
{
|
||||
// Для внедрения используется интерфейс IEcsInject<T> и его метод Inject(T obj)
|
||||
SomeData _someData
|
||||
public void Inject(SomeData obj) => _someData = obj;
|
||||
|
||||
public void PreInit ()
|
||||
{
|
||||
// тут возможно еще не внедрен _someData
|
||||
}
|
||||
public void Init ()
|
||||
{
|
||||
// тут можно пользовать _someData
|
||||
}
|
||||
public void Run ()
|
||||
{
|
||||
// тут можно пользовать _someData
|
||||
}
|
||||
public void Destroy ()
|
||||
{
|
||||
// тут можно пользовать _someData
|
||||
}
|
||||
SomeDataA _someDataA
|
||||
//obj будет экземпляром типа SomeDataB
|
||||
public void Inject(SomeDataA obj) => _someDataA = obj;
|
||||
}
|
||||
```
|
||||
### Модули
|
||||
@ -226,7 +208,7 @@ class Module : IEcsModule
|
||||
}
|
||||
}
|
||||
```
|
||||
``` csharp
|
||||
``` c#
|
||||
EcsPipelone pipeline = EcsPipeline.New()
|
||||
//...
|
||||
.AddModule(new Module())
|
||||
@ -252,118 +234,78 @@ EcsPipelone pipeline = EcsPipeline.New()
|
||||
* `EcsConst.POST_END_LAYER`
|
||||
|
||||
## Процессы
|
||||
Процессы - это очереди систем реализующие общий интерфейс, например `IEcsRunProcess`. Для запуска процессов используются Runner-ы. Встроенные процессы вызываются автоматически, для запуска пользовательских процессов используйте раннеры получаемые из `EcsPipeline.GetRunner<TInterface>()`.
|
||||
> Рекомендуется кешировать полученные через GetRunner раннеры.
|
||||
Процессы - это очереди систем реализующие общий интерфейс, например `IEcsRun`. Для запуска процессов используются Runner-ы. Втроенные процессы запускаются автоматически. Есть возможность реализации пользовательских процессов.
|
||||
|
||||
<details>
|
||||
<summary>Встроенные процессы</summary>
|
||||
|
||||
* `IEcsPreInitProcess`, `IEcsInitProcess`, `IEcsRunProcess`, `IEcsDestroyProcess` - процессы жизненого цикла `EcsPipeline`.
|
||||
* `IEcsPreInject`, `IEcsInject<T>` - Процессы системы [внедрения зависимостей](#Внедрение-зависимостей).
|
||||
* `IEcsPreInitInjectProcess` - Так же процесс системы [внедрения зависимостей](#Внедрение-зависимостей), но работает в пределах до выполнения IEcsInitProcess, сигнализирует о начале и окончании предварительных внедрений.
|
||||
* `IEcsPreInit`, `IEcsInit`, `IEcsRun`, `IEcsDestroy` - процессы жизненого цикла `EcsPipeline`.
|
||||
* `IEcsInject<T>` - Процессы системы [внедрения зависимостей](#Внедрение-зависимостей).
|
||||
* `IOnInitInjectionComplete` - Так же процесс системы [внедрения зависимостей](#Внедрение-зависимостей), но сигнализирует о завершении инициализирующей инъекции.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Пользовательские процессы</summary>
|
||||
|
||||
Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от EcsRunner<TInterface>. А после к интерфейсу добавте атрибут `BindWithEcsRunner` для связи. Пример:
|
||||
```c#
|
||||
[BindWithEcsRunner(typeof(DoSomethingProcessRunner))]
|
||||
Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от `EcsRunner<TInterface>`. Пример:
|
||||
``` c#
|
||||
//Интерфейс.
|
||||
interface IDoSomethingProcess : IEcsProcess
|
||||
{
|
||||
void Do();
|
||||
}
|
||||
//Реализация раннера. Пример реализации можно так же посмотреть в встроенных процессах
|
||||
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<TInterface>` можно только напрямую;
|
||||
> * Раннер может содержать только один интерфейс(за исключением `IEcsSystem`);
|
||||
> * Наследуемый класс `EcsRunner<TInterface>,` в качестве `TInterface` должен принимать реализованный интерфейс;
|
||||
> * Раннер не может быть размещен внутри другого класса.
|
||||
> * Наследоваться от `EcsRunner<T>` можно только напрямую;
|
||||
> * Раннер может содержать только один интерфейс(за исключением `IEcsProcess`);
|
||||
> * Наследуемый класс `EcsRunner<T>,` далжен так же реализовавыть интерфейс `T`;
|
||||
|
||||
> Не рекомендуется в цикле вызывать `GetRunner`, иначе кешируйте полученный раннер.
|
||||
</details>
|
||||
|
||||
## Мир
|
||||
Является контейнером для сущностей и компонентов.
|
||||
> **NOTICE:** Необходимо вызывать EcsWorld.Destroy() у экземпляра мира если он больше не нужен.
|
||||
### Компоненты мира
|
||||
С помощью компонентов можно прикреплять дополнительные данные к мирам. В качестве компонентов используются `struct` типы.
|
||||
``` csharp
|
||||
ref WorldComponent component = ref _world.Get<WorldComponent>();
|
||||
``` c#
|
||||
//Создание экземпляра мира
|
||||
_world = new EcsDefaultWorld();
|
||||
//Пример из раздела Сущности
|
||||
var e = _world.NewEntity();
|
||||
_world.DelEntity(e);
|
||||
```
|
||||
Реализация компонента:
|
||||
``` 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>
|
||||
> **NOTICE:** Необходимо вызывать EcsWorld.Destroy() у экземпляра мира если он больше не используется, иначе он будет висеть в памяти.
|
||||
|
||||
### Конфигурация мира
|
||||
При создании мира, в конструктор можно передать реализацию интерфейса `IEcsWorldConfig`. Реализующие его классы своего рода контейнеры для конфигов. По умолчанию уже реализован стандартный контейнер `EcsWorldConfig` и `Get`/`Set` методы для базовой конфигурации мира.
|
||||
При создании мира, в конструктор можно передать экземпляр `EcsWorldConfig`.
|
||||
|
||||
``` csharp
|
||||
EcsWorldConfig config = new EcsWorldConfig()
|
||||
.Set_EntitiesCapacity(512);
|
||||
``` c#
|
||||
EcsWorldConfig config = new EcsWorldConfig(
|
||||
//предварительно инициализирует вместимость мира для 2000 сущностей
|
||||
entitiesCapacity: 2000,
|
||||
//предварительно инициализирует вместимость пулов для 2000 компонентов
|
||||
poolComponentsCapacity: 2000);
|
||||
_world = new EcsDefaultWorld(config);
|
||||
```
|
||||
|
||||
@ -371,11 +313,10 @@ _world = new EcsDefaultWorld(config);
|
||||
## Пул
|
||||
Является контейнером для компонентов, предоставляет методы для добавления/чтения/редактирования/удаления компонентов на сущности. Есть несколько видов пулов, для разных целей:
|
||||
* `EcsPool` - универсальный пул, хранит struct-компоненты реализующие интерфейс `IEcsComponent`;
|
||||
* `EcsTagPool` - подходит для хранения пустых компонентов-тегов, в сравнении с `EcsPool` имеет лучше оптимизацию памяти и скорости, хранит struct-компоненты `IEcsTagComponent`;
|
||||
* `EcsHybridPool` - пул для гибридных компонентов. Испольщуются для реализации [гибридности](#Гибридность), хранит struct-компоненты `IEcsHybridComponent`;
|
||||
* `EcsTagPool` - подходит для хранения пустых компонентов-тегов, в сравнении с `EcsPool` имеет лучше оптимизацию памяти и скорости, хранит struct-компоненты с `IEcsTagComponent`;
|
||||
|
||||
Пулы имеют 5 основных метода и их разновидности:
|
||||
``` csharp
|
||||
``` c#
|
||||
// Один из способов получить пул из мира.
|
||||
EcsPool<Pose> poses = _world.GetPool<Pose>();
|
||||
|
||||
@ -401,7 +342,7 @@ poses.Del(entityID);
|
||||
|
||||
## Аспект
|
||||
Это пользовательские классы наследуемые от `EcsAspect`, которые используются как посредник для взаимодействия с сущностями. Аспекты одновременно являются кешем пулов и ограничением для фильтрации сущностей.
|
||||
``` csharp
|
||||
``` c#
|
||||
using DCFApixels.DragonECS;
|
||||
...
|
||||
class Aspect : EcsAspect
|
||||
@ -424,7 +365,7 @@ class Aspect : EcsAspect
|
||||
}
|
||||
```
|
||||
В аспекты можно добавлять другие аспекты, тем самым комбинируя их. Ограничения так же будут скомбинированы
|
||||
``` csharp
|
||||
``` c#
|
||||
using DCFApixels.DragonECS;
|
||||
...
|
||||
class Aspect : EcsAspect
|
||||
@ -458,7 +399,7 @@ class Aspect : EcsAspect
|
||||
Используйте метод-запрос `EcsWorld.Where<TAspcet>(out TAspcet aspect)` для получения необходимого системе набора сущностей. Запросы работают в связке с аспектами, аспекты определяют ограничения запросов, результатом запроса становится группа сущностей удовлетворяющая условиям аспекта. По умолчанию запрос делает выборку из всех сущностей в мире, но так же можно сделать выборку из определенной группы сущностей, для этого используйте `EcsWorld.WhereFor<TAspcet>(EcsReadonlyGroup sourceGroup, out TAspcet aspect)`
|
||||
|
||||
## Группа
|
||||
Группы это структуры данных для хранения множества сущностей с быстрыми операциями добавления/удаления/проверки наличия и т.д. Реализованы классом `EcsGroup` и структурой `EcsReadonlyGroup`.
|
||||
Группы это структуры данных для хранения множества сущностей с O(1) операциями добавления/удаления/проверки наличия и т.д. Реализованы классом `EcsGroup` и структурой `EcsReadonlyGroup`.
|
||||
|
||||
``` c#
|
||||
//Получем новую группу. EcsWorld содержит в себе пул групп,
|
||||
@ -514,7 +455,7 @@ EcsGroup newGroup = EcsGroup.Inverse(groupA);
|
||||
## Корень ECS
|
||||
Это пользовательский класс который явялестя точкой входа для ECS. Основное назначение инициализация, запуск систем на каждый Update движка и очистка по окончанию сипользования.
|
||||
### Пример для Unity
|
||||
``` csharp
|
||||
``` c#
|
||||
using DCFApixels.DragonECS;
|
||||
using UnityEngine;
|
||||
public class EcsRoot : MonoBehaviour
|
||||
@ -560,7 +501,7 @@ public class EcsRoot : MonoBehaviour
|
||||
}
|
||||
```
|
||||
### Общий пример
|
||||
``` csharp
|
||||
``` c#
|
||||
using DCFApixels.DragonECS;
|
||||
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>
|
||||
|
||||
# Debug
|
||||
Фреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды. Так же многие типы имеют свой DebuggerProxy для более информативного отображения в IDE.
|
||||
## Атрибуты
|
||||
## Мета-Атрибуты
|
||||
В чистом виде мета-атрибуты не имеют применения, но могут быть использованы для генерации автоматической документации и используются в интеграциях с движками для задания отображения в отладочных инструментах и редакторах.
|
||||
``` c#
|
||||
using DCFApixels.DragonECS;
|
||||
@ -708,21 +567,41 @@ using DCFApixels.DragonECS;
|
||||
[MetaGroup("Abilities/Passive/")]
|
||||
|
||||
// Задает цвет типа в системе 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")]
|
||||
|
||||
// Добавляет строковые теги.
|
||||
[MetaTags(...)] // [MetaTags(MetaTags.HIDDEN))] чтобы скрыть в редакторе
|
||||
[MetaTags("Tag1", "Tag2", ...)] // [MetaTags(MetaTags.HIDDEN))] чтобы скрыть в редакторе
|
||||
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
|
||||
Имеет набор методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы - это посредники между системами отладки среды и EcsDebug. Это позволяет не изменяя отладочный код проекта, переносить проект на другие движки, достаточно только реализовать специальный Debug-сервис.
|
||||
|
||||
По умолчанию используется `DefaultDebugService` который выводит логи в консоль. Для реализации пользовательского создайте класс наследуемый от `DebugService` и реализуйте абстрактные члены класса.
|
||||
|
||||
``` csharp
|
||||
``` c#
|
||||
// Логирование.
|
||||
EcsDebug.Print("Message");
|
||||
|
||||
@ -737,7 +616,7 @@ EcsDebug.Set<OtherDebugService>();
|
||||
```
|
||||
|
||||
## Профилирование
|
||||
``` csharp
|
||||
``` c#
|
||||
// Создание маркера с именем SomeMarker.
|
||||
private static readonly EcsProfilerMarker marker = new EcsProfilerMarker("SomeMarker");
|
||||
|
||||
@ -757,6 +636,96 @@ using (marker.Auto())
|
||||
|
||||
</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)
|
||||
* [Поддержка классической 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).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> 新年快乐!
|
||||
|
||||
> [!WARNING]
|
||||
> 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).
|
||||
|
||||
# Versioning
|
||||
DragonECS uses this versioning semantics: [Open](https://gist.github.com/DCFApixels/c3b178a308b411f530361d1d56f1f929#file-dcfapixels_versioning_en-md)
|
||||
</br>
|
||||
|
||||
# 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
|
||||
* [Dependency autoinjections](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||
* [Classic C# multithreading support](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||
|
@ -8,7 +8,7 @@
|
||||
"displayName": "DragonECS",
|
||||
"description": "C# Entity Component System Framework",
|
||||
"unity": "2020.3",
|
||||
"version": "0.8.8",
|
||||
"version": "0.8.20",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DCFApixels/DragonECS.git"
|
||||
|
Loading…
Reference in New Issue
Block a user