DragonECS/README-RU.md
2023-05-30 23:18:03 +08:00

17 KiB
Raw Blame History

Version GitHub

DragonECS - C# Entity Component System Framework

Languages: Русский English(WIP)

Данный ECS Фреймворк нацелен на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей.

NOTICE: Проект в стадии разработки. API может меняться.

Оглавление

Установка

  • Unity-модуль

Поддерживается установка в виде Unity-модуля в при помощи добавления git-URL в PackageManager или ручного добавления в Packages/manifest.json:

https://github.com/DCFApixels/DragonECS.git 
  • В виде иходников

Фреймворк так же может быть добавлен в проект в виде исходников.

Версионирование

В DragonECS применяется следующая семантика версионирования: Открыть

Основные концепции

Entity

Сущности - это то к чему крепятся данные. Реализованны в виде идентификаторов, которых есть 2 вида:

  • int - однократный идентификатор, применяется в пределах одного тика. Не рекомендуется хранить int идентификаторы, в место этого используйте entlong;
  • entlong - долговременный идентификатор, содержит в себе полный набор информации для однозначной идентификации;

Component

Компоненты - это данные для сущностей. Обязаны реализовывать интерфейс IEcsComponent или другой указываюший вид компонента.

struct Health : IEcsComponent
{
    public float health;
    public int armor;
}
struct PlayerTag : IEcsTagComponent {}

Встроенные виды компонентов:

  • IEcsComponent - Компоненты с данными.
  • IEcsTagComponent - Компоненты-теги. Без данных.

Компоненты-теги хоть и не имеют данных, само наличие или отсутсвие такого компонента у сущности уже несет ифномрацию и может применяться для определения типа сущности.

System

Системы - это основная логика, тут задается поведение сущностей. Существуют в виде пользовательских классов, реализующих как минимум один из интерфейсов процессов. Основные процессы:

class SomeSystem : IEcsPreInitProcess, IEcsInitProcess, IEcsRunProcess, IEcsDestroyProcess
{
    // Будет вызван один раз в момент работы EcsPipeline.Init() и до срабатывания IEcsInitProcess.Init()
    public void PreInit (EcsPipeline pipeline) { }
    
    // Будет вызван один раз в момент работы EcsPipeline.Init() и после срабатывания IEcsPreInitProcess.PreInit()
    public void Init (EcsPipeline pipeline)  { }
    
    // Будет вызван один раз в момент работы EcsPipeline.Run().
    public void Run (EcsPipeline pipeline) { }
    
    // Будет вызван один раз в момент работы EcsPipeline.Destroy()
    public void Destroy (EcsPipeline pipeline) { }
}

Для реализации дополнительных процессов перейдите к разделу Процессы

Концепции фреймворка

Пайплайн

Является контейнером и двжиком систем, определяя поочередность их вызова, предоставляющий механизм для сообщений между системами и механизм внедрения зависимостей в системы.

Построение

За построение пайплайна отвечает Builder. В Builder добавляются системы, а в конце строится пайплайн. Пример:

const string SOME_LAYER = nameof(SOME_LAYER);
EcsPipelone pipeline = EcsPipeline.New() //Создает Builder пайплайна
    // Добавляет систему System1 в очередь систем
    .Add(new System1())
    // Добавляет System2 в очередь после System1
    .Add(new System2())
    // Добавляет System3 в очередь после System2, но в единичном экземпляре
    .AddUnique(new System3())
    // Завершает построение пайплайна и возвращает его экземпляр 
    .Build(); 
pipeline.Init(); // Инициализация пайплайна

Есть упрощенная инициализация пайплайна сразу в момент построение, для используйте Builder.BuildAndInit();

Слои

Очередь систем можно разбить на слои

EcsPipelone pipeline = EcsPipeline.New()
    //...
    .Layers.Insert(EcsConsts.END_LAYER, SOME_LAYER) // Вставляет новый слой перед конечным слоем EcsConsts.END_LAYER
    //...
    .BuildAndInit();

Встроенные слои расположены в следующем порядке:

  • EcsConst.PRE_BEGIN_LAYER
  • EcsConst.BEGIN_LAYER
  • EcsConst.BASIC_LAYER (Если при добавблении системы не казать слой, то она будет доавблена сюда)
  • EcsConst.END_LAYER
  • EcsConst.POST_END_LAYER

Внедрение зависимостей

Внедрение зависимостей - это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в Builder.

EcsPipelone pipeline = EcsPipeline.New()
    //...
    .Inject(_someData) // Внедрит в системы экземлпяр _someData
    //...
    .BuildAndInit();

Модули

Группы систем реализующие общую фичу можно объединять в модули, и просто добавлять модули в Pipeline.

using DCFApixels.DragonECS;
class Module : IEcsModule 
{
    public void Import(EcsPipeline.Builder b) 
    {
        b.Add(new System1());
        b.Add(new System2(), EcsConsts.END_LAYER);
        b.Add(new System3());
    }
}
EcsPipelone pipeline = EcsPipeline.New()
    //...
    .AddModule(new Module())
    //...
    .BuildAndInit();

Процессы

Процессы - это очереди систем реализующие общий интерфейс, например IEcsRunProcess. Для запуска процессов используются Runner-ы. Система ранеров и процессов может использоваться для создания реактивного поведения или для управления очередью вызова систем. Встроенные процессы вызываются автоматически, для ручного запуска испольщуйте раннеры получаемые из EcsPipeline.GetRunner().

Метод GetRunner относительно медленный, поэтому рекомендуется кешировать полученные раннеры.

Встроенные процессы:

  • IEcsPreInitProcess, IEcsInitProcess, IEcsRunProcess, IEcsDestroyProcess - процессы жизненого цикла Pipeline
  • IEcsPreInject, IEcsInject<T> - процессы системы внедрения зависимостей для Pipeline. Через них прокидываются зависимости
  • IEcsPreInitInjectProcess - Так же процесс системы внедрения зависимостей, но работает в пределах до выполнения IEcsInitProcess, сигнализирует о инициализации предварительных внедрений и окончании.

Пользовательские Раннеры и Процессы

Для добавления нового процесса создайте интерфейс наследованный от IEcsSystem и создайте раннер для него. Раннеры это классы реализующие интерфейс запускаемого процесса и наследуемые от EcsRunner. Пример реализации раннера для IEcsRunProcess:

public sealed class EcsRunRunner : EcsRunner<IEcsRunProcess>, IEcsRunProcess
{
   public void Run(EcsSession session)
   {
       foreach (var item in targets) item.Run(session);
   }
}

Раннеры имеют ряд требований к реализации:

  • Для одного интерфейса может быть только одна реализация раннера;
  • Наследоваться от EcsRunner<TInterface> можно только напрямую;
  • Раннер может содержать только один интерфейс(за исключением IEcsSystem);
  • Наследуемый класс EcsRunner<TInterface>, в качестве TInterface должен принимать реализованный интерфейс;
  • Раннер не может быть размещен внутри другого класса.

Мир

Является контейнером для сущностей и компонентов.

Группа

Группы это структуры данных для хранения списка сущностей и с быстрыми операциями добавления/удаления/проверки наличия и т.д. Реализованы классом EcsGroup и структурой EcsReadonlyGroup.

Пул

Является контейнером для компонентов, предоставляет методы для добавления/чтения/редактирования/удаления компонентов на сущности. Есть несколько видов пулов, для разных целей

  • EcsPool - универсальный пул, хранит struct-компоненты реализующие интерфейс IEcsComponent;
  • EcsTagPool - подходит для хранения пустых компонентов-тегов, в сравнении с EcsPool имеет лучше оптимизацию памяти и дейсвий с пулом, хранит в себе struct-компоненты реализующие IEcsTagComponent;

Так же имеется возможность реализации пользовательского пула

Субъект

Это классы наследуемые от EcsSubject, которые используются как посредник для взаимодейсвия с сушностями.

Запросы

Используйте метод-запрос EcsWorld.Where<TSubject>(out TSubject subject) для получения необходимого системе набора сущностей. Запросы работают в связке с субъектами, субъекты определяют ограничения запросов, результатом запроса становится группа сущностей удовлетворяющия условиям субъекта. По умолчанию запрос делает выборку из всех сущностей в мире, но так же можно сделать выборку из определенной группы сущностей, для этого используйте EcsWorld.WhereFor<TSubject>(EcsReadonlyGroup sourceGroup, out TSubject subject)

Рекомендации

Рекомендации по реаоизации систем

Системы делать максимально независимыми, без ссылок на другие системы.

Debug

Фреймворк предоставляет дополнительные интрументы для отладки и логирования, не зависящие от среды.

Атрибуты

В чистом виде атрибуты не имеют применения, но испольщуются в интеграциях с движками для задания отображения в отладочных интурментах и редакторах.

// Задает пользовательское название типа, по умолчанию используется имя типа.
using DCFApixels.DragonECS;
...
[DebugName("SomeComponent")] 
// Задает цвет типа в системе rgb, где каждый канал принимает занчение от 0 до 255, по умолчанию белый. 
// ВЫбрать цвет можно как вручную задав rgb значения, так и использовать заранее заготовленные цвета в enum DebugColor.
[DebugColor(DebugColor.Red)] 
// Добавляет описание типу.
[DebugDescription("The quick brown fox jumps over the lazy dog")] 
// Скрывает тип.
[DebugHide] 
public struct Component { }

EcsDebug

Имеет набор методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы являются посредниками между системами отладки среды и EcsDebug. Это позволяет не изменяя отладочный код проекта, переносить его на другие движки, достаточно только реализовать специальный Debug-сервис.
По умолчанию используется DefaultDebugService который выводит логи в консоль. Для реализации пользовательского создайте класс наследуемый от DebugService и реализайте абстрактные члены класса.

Расширения