DragonECS/README-RU.md
2023-05-31 04:38:04 +08:00

19 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 - долговременный идентификатор, содержит в себе полный набор информации для однозначной идентификации;
// Создание новой сущности в мире.
int entityID = _world.NewEmptyEntity();

// Удаление сущности.
_world.DelEntity(entityID);

// Копирование компонентов одной сущности в другую.
_world.CopyEntity(entityID, otherEntityID);

// Клонирование сущности.
int newEntityID = _world.CloneEntity(entityID);
Работа с entlong
// Конвертация int в entlong.
entlong entity = _world.GetEntityLong(entityID);
// или
entlong entity = entityID.ToEntityLong(_world);

// Проверка что сущность еще жива.
if (entity.IsAlive) { }

// Конвертация entlong в int. Если сущность уже не существует, будет брошено исключение. 
int entityID = entity.ID;

// Конвертация entlong в int. Вернет true и ее int идентификатор, если сущность еще жива.
if (entity.TryGetID(out int entityID)) { }

NOTICE: Сущности не могут существовать без компонентов, пустые сущности будут автоматически удаляться сразу после удаления последнего компонента либо в конце тика.

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) { }
}

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

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

Пайплайн

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

Построение

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

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

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

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

Внедрение зависимостей - это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в 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); // данная система будет добавлена в слой EcsConsts.END_LAYER
        b.Add(new System3());
    }
}
EcsPipelone pipeline = EcsPipeline.New()
    //...
    .AddModule(new Module())
    //...
    .BuildAndInit();

Слои

Очередь систем можно разбить на слои. Слой определяет место в очереди для вставки систем. Если необходимо чтобы какая-то система была вставлена в конце очереди, вне зависимости от места добавления, эту систему можно добавить в слой EcsConsts.END_LAYER.

const string SOME_LAYER = nameof(SOME_LAYER);
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

Процессы

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

Рекомендуется кешировать полученные через GetRunner раннеры.

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

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

interface IDoSomethingProcess : IEcsProcess
{
   void Do();
}
sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>, IDoSomethingProcess
{
   public void Do()
   {
       foreach (var item in targets) item.Do();
   }
}

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

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

Мир

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

NOTICE: Необходимо вызывать EcsWorld.Destroy() у экземпляра мира если он больше не нужен.

Компоненты мира

С помощью компонентов можно прикреплять дополнительные данные к мирам. Компоненты можно применять создания расширений в связке с методами расширений.

WorldComponent component = _world.Get<WorldComponent>();

Пул

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

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

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

эта функция будет описана в ближайшее время

Субъект

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

Запросы

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

Группа

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

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 и реализуйте абстрактные члены класса.

Расширения