DragonECS-Unity/README-RU.md
2026-04-20 16:35:30 +08:00

29 KiB
Raw Blame History

Version GitHub Discord QQ

Интеграция с Unity для DragonECS

Readme Languages:

Русский

English(WIP)

Этот пакет делает работу с DragonECS в Unity удобнее и нагляднее: встроенная визуальная отладка и профайлинг, редакторские шаблоны и инструменты для привязки сущностей к GameObject.

Warning

Проект в стадии разработки. API может меняться.
Readme еще не завершен, если есть не ясные моменты, вопросы можно задать тут Обратная связь

Warning

Встроенные реализации шаблонов UnityComponent<T> были перенесены СЮДА Так как некоторые модули Unity отключаемы, и например отключение модуля физики приведет к тому что код реализации UnityComponent<Rigidbody> или UnityComponent<Collider> не будет компилироваться.

//https://gist.github.com/DCFApixels/c250f2561f09e09ab3e6a4bd4f3013cb#file-unitycomponenttemplates-cs

Оглавление


Установка

Семантика версионирования - Открыть

Окружение

Обязательные требования:

  • Зависимость: DragonECS
  • Минимальная версия C# 8.0;
  • Минимальная версия Unity 2021.2.0;

Установка для Unity

  • Unity-модуль

Поддерживается установка в виде Unity-модуля при помощи добавления git-URL в PackageManager:

https://github.com/DCFApixels/DragonECS-Unity.git

Или ручного добавления этой строчки в Packages/manifest.json:

"com.dcfa_pixels.dragonecs-unity": "https://github.com/DCFApixels/DragonECS-Unity.git",
  • В виде исходников

Можно также напрямую скопировать исходники пакета в проект.


Debug

Debug Сервис

UnityDebugService - реализация Debug-сервиса для EcsDebug. В редакторе он инициализируется автоматически и обеспечивает интеграцию: например, вызовы EcsDebug.Print направляются в консоль Unity, а EcsProfilerMarker подключается к встроенному профайлеру и т.д.

//Ручная активация.
UnityDebugService.Activate();

//Выведет сообщение в консоли Unity.
EcsDebug.Print(); 

var someMarker = new EcsProfilerMarker("SomeMarker");
someMarker.Begin();
//время выполнения этого участка будет отражено в профайлере Unity.
someMarker.End();

//Остановка игрового режима.
EcsDebug.Break();

Визуальная отладка

Реализовано в виде объектов-мониторов, в которых отображается состояние разных частей фреймворка. Найти эти мониторы можно в Play Mode в разделе DontDestroyOnLoad.

_pipeline = EcsPipeline.New()
    //...
    // Инициализация отладки для пайплайна и миров
    .AddUnityDebug(_world, _eventWorld)
    //...
    .BuildAndInit();


  • PipelineMonitor

Показывает состояние EcsPipeline. Системы отображаются в порядке их выполнения.


  • PipelineProcessMonitor

Отображает в виде матрицы процессы и системы. Системы отображаются в порядке их выполнения. Точка в пересечении системы и процесса означает что эта система является частью этого процесса.


  • WorldMonitor

Показывает состояние EcsWorld. На каждый мир, переданный в AddUnityDebug(...), создается отдельный монитор.


  • WorldQueriesMonitor

Расположен вместе с WorldMonitor, показывает список всех Where запросов которые выполняли системы. Вверху есть поле для поиска запросов по именам компонентам. Поисковую строку можно разделять символом / для одновременного поиска нескольких компонентов. Рядом с каждым Where запросом есть кнопка Snapshot, по нажатию откроется окно со списоком всех сущностей которые на данный момент удовлетворяют маске запроса.


  • EntityMonitor

Показывает состояние сущности мира, позволяет добавлять/изменять/удалять компоненты по время Play Mode. На каждую сущность в мире создается отдельный монитор. Все мониторы сущностей помещаются в монитор мира.



Шаблоны

Интеграция содержит шаблоны, расширяющие ITemplateNode, предназначенные для настройки сущностей из редактора.

ScriptableEntityTemplate

Хранится как отдельный ассет. Наследуется от ScriptableObject.

Создать ассет: Asset > Create > DragonECS > ScriptableEntityTemplate.

Чтобы добавить компонент в меню Add Component Нужен Шаблон компонента.


MonoEntityTemplate

Крепится к GameObject. Наследуется от MonoBehaviour.

Повесить компонент: Add Component > DragonECS > MonoEntityTemplate.

Чтобы добавить компонент в меню Add Component Нужен Шаблон компонента.


Шаблон компонента

Чтобы компонент попал в меню Add Component требуется шаблон. Шаблоны компонента это типы реализующие IComponentTemplate или компоненты реализующие ITemplateNode вместе с IEcsComponentMember.

Реализация

  • Упрощенная реализация:
// Обязательно добавить [Serializable] к типу компонента.
[Serializable]
struct SomeComponent : IEcsComponent { /* ... */ }
class SomeComponentTemplate : ComponentTemplate<SomeComponent> { }
// Тоже самое но для компонентов-тегов.
[Serializable]
struct SomeTagComponent : IEcsTagComponent { }
class SomeTagComponentTemplate : TagComponentTemplate<SomeComponent> { }
Другие способы

Реализация ITemplateNode у компонента

Такой способ может быть удобен тем что не требует создания отдельного класса шаблона, компонент сам выступает как шаблон, и он так же прост в реализации. Минус данного подхода, что проще случайно переименовать компонент и получить Missing Reference в местах с атрибутом [SerializeReference].

public struct Health : IEcsComponent, ITemplateNode
{
    public float Points;
    public void Apply(short worldID, int entityID)
    {
        EcsPool<Health>.Apply(worldID, entityID) = this;
    }
}

Реализация кастомного шаблона

Если не подходят встроенные ComponentTemplate<T> или TagComponentTemplate<T>, можно создать свой шаблон реализующий IComponentTemplate. Например это может пригодиться для кастомного пула. В большинстве случаев достаточно использовать встроенные шаблоны.

[Serializable] 
struct SomeComponent : IEcsComponent { /* ... */ }
class SomeComponentTemplate : IComponentTemplate
{
    [SerializeField]
    protected SomeComponent component;
    public Type Type { get { return typeof(SomeComponent); } }
    public bool IsUnique { get { return true; } }
    public void Apply(int worldID, int entityID)
    {
        EcsPool<SomeComponent>.Apply(worldID, entityID) = component;
    }
    public object GetRaw() { return component; }
    public void SetRaw(object raw) { component = (SomeComponent)raw; }
    public void OnGizmos(Transform transform, IComponentTemplate.GizmosMode mode) { /*...*/ }
    public void OnValidate(UnityEngine.Object obj) { /*...*/ }
}

В разделе Кастомизация инспектора описана кастомизация отображения компонентов и использование вне шаблонов сущьностей.


Связь с GameObject

Связываются сущности и GameObject-ы с помощью коннектов. Коннекты со стороны GameObject - EcsEntityConnect, со стороны сущности - GameObjectConnect. EcsEntityConnect - управляющий коннект, GameObjectConnect - создается/удаляется автоматически.

EcsEntityConnect connect = /*...*/;
entlong entity = _world.NewEntityLong();

// Связывание сущности с GameObject.
// Автоматически добавляется GameObjectConnect в сущность
// и применяются шаблоны.
connect.ConnectWith(entity);

// Или создать без применения шаблонов.
connect.ConnectWith(entity, false);

// Отвязать.
// Автоматически удалится GameObjectConnect.
connect.Disconnect();
Повесить компонент: Add Component > DragonECS > EcsEntityConnect.

Просмотреть все компоненты связанной сущности можно развернув RUNTIME COMPONENTS.

На панели внизу есть вспомогательные кнопки: 1) Отвязать сущность. 2) Удалить сущность. 3) Автоматическое заполнение массива шаблонов. 4) Каскадный вызов автозаполнения для всех дочерних коннектов в иерархии.


AutoEntityCreator автоматический создает сущность и связывает с GameObject. В инспекторе ему нужно указать EcsEntityConnect с которым связывать сущность и Провайдер мира в котором создать сущность.

Повесить компонент: Add Component > DragonECS > AutoEntityCreator.

На панели внизу есть вспомогательные кнопки: 1) Автоматическое заполнение ссылки на коннект. 2) Каскадный вызов автозаполнения для всех дочерних экземпляров в иерархии.


World Provider

EcsWorldProvider<TWorld> - это ScriptableObject обертка над TWorld, предназначенная для пробрасывания экземпляра мира и настройки через инспектор Unity. Для простых случаев достаточно будет использовать синглтон версию провайдера EcsDefaultWorldSingletonProvider.

// Синглтон провайдер создается автоматически в папке "Assets/Resource".
EcsDefaultWorldSingletonProvider provider = EcsDefaultWorldSingletonProvider.Instance;
// ...

EcsDefaultWorld world = new EcsDefaultWorld();
// Устанавливаем экземпляр мира в провайдер.
provider.Set(world);

// ...

//Получаем экземпляр мира, если провайдер был пуст, то он создаст новый мир.
EcsDefaultWorld world = provider.Get();

EcsPipeline pipeline = EcsPipeline.New()
    //...
    // Внедряем в системы полученный из провайдера мир.
    .Inject(world)
    //...
    .BuildAndInit();
Пример реализации провайдера для своего типа мира
//Пример реализации своего провайдера для пробрасывания мира своего типа
[CreateAssetMenu(fileName = nameof(EcsMyWorldProvider), menuName = EcsConsts.FRAMEWORK_NAME + "/WorldProviders/" + nameof(EcsMyWorldProvider), order = 1)]
public class EcsMyWorldProvider : EcsWorldProvider<EcsMyWorld> { }

//Пример реализации синглтон версии для мира своего типа
public class EcsMyWorldSingletonProvider : EcsWorldProvider<EcsMyWorld>
{
    private static EcsMyWorldSingletonProvider _instance;
    public static EcsMyWorldSingletonProvider Instance
    {
        get
        {
            if (_instance == null) { _instance = FindOrCreateSingleton<EcsMyWorldSingletonProvider>("SingletonMyWorld"); }
            return _instance;
        }
    }
}
Создать ассет провайдера: Asset > Create > DragonECS > WorldProviders > Выбрать тип мира.


Шаблон Пайплайна

Пайплайн как и сущности можно собирать из шаблонов. Шаблоны пайплайна это модули, реализующие интерфейс IEcsModule

По умолчанию расширение содержит 2 вида шаблонов: ScriptablePipelineTemplate, MonoPipelineTemplate.

ScriptablePipelineTemplate

Хранится как отдельный ассет. Наследуется от ScriptableObject. Действия чтобы создать ScriptableEntityTemplate ассет: Asset > Create > DragonECS > ScriptablePipelineTemplate.

MonoPipelineTemplate

Крепится к GameObject. Наследуется от MonoBehaviour. Повесить компонент: Add Component > DragonECS > MonoPipelineTemplate.


EcsRootUnity

Упрощённая реализация Ecs Root для Unity; собирает пайплайн из шаблонов. Наследуется от MonoBehaviour. Чтобы добавить на GameObject: Add Component > DragonECS > EcsRootUnity.


FixedUpdate и LateUpdate

using DCFApixels.DragonECS;
using UnityEngine;
public class EcsRoot : MonoBehaviour
{
    private EcsPipeline _pipeline;
    //...
    private void Update()
    {
        // Стандартный Run из фреймворка.
        _pipeline.Run();
    }
    private void FixedUpdate()
    {
        // Специальный Run для трансляции FixedUpdate.
        _pipeline.FixedRun();
    }
    private void LateUpdate()
    {
        // Специальный Run для трансляции LateUpdate.
        _pipeline.LateRun();
    }
    // ...
}

Кастомизация инспектора

Атрибуты инспектора

  • [ReferenceDropDown] - Применяется к полю с [SerializeReference]. Добавляет кнопку выбора типа из списка. Можно ограничить набор доступных типов, передав список в конструктор.
  • [ReferenceDropDownWithout] - Используется вместе с [ReferenceDropDown], чтобы исключить указанные типы (и их наследников) из списка выбора.
  • [DragonMetaBlock] - Отображает значение в инспекторе так же, как компоненты в шаблонах сущности. Учитывает meta-атрибуты (MetaGroup, MetaColor, MetaDescription, MetaID и др.).

Поведение Meta-атрибутов

  • Иерархическая группировка элементов в меню Add Component или [ReferenceDropDown] задаётся через [MetaGroup].
  • Цвет компонента в инспекторе по умолчанию определяется именем типа. Режим окраски можно изменить в окне настроек. Явный цвет задаётся через [MetaColor].
  • При совпадении имени типа и файла (или при наличии [MetaID]) рядом с кнопкой удаления появляется иконка файла: один клик — выделение скрипта в проекте, двойной клик — открытие.
  • Если указан [MetaDescription], рядом отображается иконка подсказки с текстом описания.
  • При восстановлении Missing Reference с помощью Reference Repairer, инструмент ищет соответствие старого и нового имени типа по атрибуту [MetaID(id)].

Примеры:

Атрибут DragonMetaBlock:

// Отображение поля настраиваемое мета-атрибутами.
// Аналогично компонентам в MonoEntityTemplate или ScriptableEntityTemplate.
[DragonMetaBlock]
public SomeComponent Component;

// Можно применять к любому полю любого типа.
[DragonMetaBlock]
public Foo Foo;

ReferenceDropDown и ReferenceDropDownWithout:

// Добавляет кнопку выбора реализации ITemplateNode из выпадающего списка.
[SerializeReference]
[ReferenceDropDown]
public ITemplateNode Template;
// Так же можно применять к любому полю любого типа. 
// В списке будут только тип Foo и его наследники, исключая FooExc и его наследников.
[SerializeReference]
[ReferenceDropDown(typeof(Foo))]
[ReferenceDropDownWithout(typeof(FooExc))]
public object Template;

Комбинирование и другие варианты использования:

// Атрибуты можно комбинировать.
[DragonMetaBlock]
[ReferenceDropDown]
public ITemplateNode Template;

// Обертка над ITemplateNode, аналогично примеру выше.
public ComponentTemplateProperty Template;

// Атрибуты корректно работают с массивами и листами.
[DragonMetaBlock]
[ReferenceDropDown]
public ITemplateNode[] Template;

Поддержка Jobs

DragonECS по умолчанию совместим с Job системой Unity. Пример:

EcsWorld _world;
class Aspect : EcsAspect
{
    // Пул для unmanaged компонентов.
    public EcsValuePool<Cmp> Cmps = Inc;
}
public void Run()
{
    var job = new Job()
    {
        // Идентично Where, но возвращает unmanaged список сущностей.
        Entities = _world.WhereUnsafe(out Aspect a),
        // Конвертация пула в unmanaged версию
        Cmps = a.Cmps.AsNative(),
        X = 10f,
    };
    JobHandle jobHandle = job.Schedule(job.Entities.Count, 64);
    jobHandle.Complete();
}
// Unmanaged компонент.
public struct Cmp : IEcsValueComponent
{
    public float A;
}
private struct Job : IJobParallelFor
{
    public EcsUnsafeSpan Entities;
    public NativeEcsValuePool<Cmp> Cmps;
    public float X;
    public Job(EcsUnsafeSpan entities, float x)
    {
        Entities = entities;
        X = x;
    }
    public void Execute(int index)
    {
        var e = Entities[index];
        Cmps[e].A += X;
    }
}

Документация проекта

В интеграции также есть окно документации проекта на основе мета-атрибутов. Открыть документацию: Tools > DragonECS > Documentation. Документация формируется при первом открытии окна и при нажатии кнопки Update.


Окно настроек

В окне настроек доступно несколько опций, включая режимы отображения компонентов в инспекторе. Внизу находятся переключатели для define-переменных, используемых в фреймворке. Открыть окно настроек: Tools > DragonECS > Settings.


Reference Repairer

Инструмент для восстановления Missing Reference. Некоторые части интеграции активно задействует [SerializeReference], у которого есть известная проблема с потерей типов при переименовании. Reference Repairer упрощает процесс восстановления. Он может собирать все ассеты с потерянными типами, после этого предоставляет окно для указания новых имён потерянных типов и выполнит их восстановление в собранных ассетах. Открыть окно инструмента: Tools > DragonECS > Reference Repairer.

Если потерянные типы имеют атрибут [MetaID(id)], инструмент попытается автоматически сопоставить новое имя типа.


FAQ

Не могу повесить EcsEntityConnect или другие компоненты

Иногда это происходит после обновления пакета. Решения: выполните Assets -> Reimport All или перезапустите Unity после удаления папки Library в корне проекта.