ecsproto-qol

0
README.md

Proto

LeoECS Proto QoL

Набор расширений для

LeoECS Proto
, призванных улучшить "качество жизни" (Quality of Life) разработчика.

ВАЖНО! Требует C#9 (или Unity >=2021.2).

ВАЖНО! Зависит от: Leopotam.EcsProto.

ВАЖНО! Не забывайте использовать

DEBUG
-версии билдов для разработки и
RELEASE
-версии билдов для релизов: все внутренние проверки/исключения будут работать только в
DEBUG
-версиях и удалены для увеличения производительности в
RELEASE
-версиях.

ВАЖНО! Проверено на Unity 2021.3 (не зависит от нее) и содержит asmdef-описания для компиляции в виде отдельных сборок и уменьшения времени рекомпиляции основного проекта.

Социальные ресурсы

Официальный блог: https://leopotam.ru

Установка

В виде unity модуля

Поддерживается установка в виде unity-модуля через git-ссылку в PackageManager или прямое редактирование

Packages/manifest.json
:

"ru.leopotam.ecsproto-qol": "https://gitverse.ru/leopotam/ecsproto-qol.git",

В виде исходников

Код так же может быть склонирован или получен в виде архива со страницы релизов.

Прочие источники

Официальная работоспособная версия размещена по адресу https://gitverse.ru/leopotam/ecsproto-qol, все остальные версии (включая nuget, npm и прочие репозитории) являются неофициальными клонами или сторонним кодом с неизвестным содержимым.

Итераторы

Инициализация

Итераторы получили возможность инициализации 3 способами (итератор по сущностям с компонентами C1 и C2, но без C3):

Если нужен итератор без исключений компонентов, для этого существует отдельный тип:

ВАЖНО! Рекомендованный способ - первый, с использованием

It.List()
. Варианты с обобщениями более лаконичны, но на большом количестве компонентов и их комбинаций увеличивают размер исполняемого файла.

Итераторы с кешированием

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

ВАЖНО! Если не вызвать

it.BeginCaching()
, то будет брошено исключение в DEBUG-версии.

ВАЖНО! Если был вызван

it.BeginCaching()
, в пару ему обязательно вызывать
it.EndCaching()
после завершения обработки, иначе зависимые пулы останутся в режиме ReadOnly. Вызовы не обязательно должны быть в одном методе и даже в одном цикле обработки если работа с заблокированными пулами допускает их работу в ReadOnly-режиме продолжительное время.

ВАЖНО! Если не требуется обработка в несколько проходов (например, только 1 итерация), то вариант с кешированием будет работать медленнее.

ВАЖНО! Если требуется проверить режим работы итератора (кешируется или нет), то для этого существует метод

it.IsCached()
.

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

Кеширующие итераторы поддерживают сортировку сущностей на основе данных их компонентов:

ВАЖНО! Для уменьшения аллокаций для обработчиков сортировки вместо лямбд рекомендуется использовать локальные функции или методы систем.

Сущности

Упаковка сущностей

Сущности отдаются в пользовательский код в виде

ProtoEntity
-идентификаторов и валидны только в пределах текущего метода - нельзя хранить ссылки на сущности если нет уверенности, что они не могут быть уничтожены где-то в коде. Если требуется сохранять сущности, то их следует упаковать:

Если используется несколько миров и важно сохранять привязку к ним, то можно упаковывать другим способом:

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

==
:

То же самое касается и упаковки сущности с миром:

Эмуляция апи сущностей из classic-версии

ВАЖНО! Это апи значительно медленнее штатного и не должно использоваться на большом количестве сущностей.

Реализуется через специальный тип

ProtoSlowEntity
:

Так же возможно преобразовать любую активную

ProtoEntity
-сущность в
ProtoSlowEntity
и обратно:

ВАЖНО! Любые операции с

ProtoSlowEntity
возможны только после проверки ее активности через
ProtoSlowEntity.IsAlive()
.

ВАЖНО! Поведение

ProtoSlowEntity.Add()
и
ProtoSlowEntity.Get()
отличается от поведения
EcsEntity.Get()
в
LeoECS Classic
- в случае отсутствия запрошенного компонента в мире пул для него не будет создан, а упадет исключение. Запрашивать можно только компоненты, зарегистрированные через аспекты мира.

Миры

Инъекции полей в аспекте

Для сокращения количества кода инициализации аспекта мира можно использовать наследование от специального типа - поддерживается инъекция полей, реализующих

IProtoAspect
,
IProtoPool
и
IProtoIt
:

Это идентично следующему коду:

Если нужна дополнительная кастомная инициализация, то можно выполнить ее через перегрузки методов

Init()
/
PostInit()
:

Поля могут быть проинициализированы экземплярами данных до начала инъекции - в этом случае они будут использоваться для дальнейшей настройки, это один из способов вызова кастомных конструкторов пулов и аспектов.

Для получения ссылки на мир аспекта можно воспользоваться специальным методом:

Создание авто-итератора из пулов аспекта

Возможно автоматическое создание итератора из полей-пулов

ProtoAspectInject
, для этого их следует пометить специальными атрибутами:

Использовать авто-итератор аспекта можно следующим образом:

ВАЖНО! Авто-итератор будет создан только в случае наличия в аспекте хотя бы одного пула, отмеченного атрибутом

[Include]
.

Список активных сущностей

Если требуется узнать только количество активных сущностей (быстрее):

Список компонентов на сущности

Системы

Инъекции полей в системы

Для инъекции в поля систем их достаточно пометить атрибутом

[DI]
:

ВАЖНО! Инъекция итератора подразумевает, что его экземпляр уже создан через инициализатор поля.

ВАЖНО! Так же поддерживается инъекция

ProtoWorld
-типа через атрибут
[DI]
, но для оптимизации рекомендуется использовать вызов
ProtoAspectInject.World()
. Прямая инъекция может быть полезна для инициализации полей сервисов.

Для корректной работы инъекции в поля систем необходимо подключить специальный модуль:

ВАЖНО! Модуль инъекций должен идти до регистрации остальных модулей и систем, использующих его - проще ставить его всегда самым первым. Подключать модуль нужно только один раз для каждой группы систем.

Существует возможность изменения поведения штатной инъекции, для этого следует подключить специальный сервис с указанием обработчика:

Технически, можно выполнять инъекции в поля не только систем, но и любых объектов через вызов метода

AutoInjectModule.Inject()
:

Можно выполнять инъекцию в подключенные сервисы автоматически, указав опциональный флаг в конструкторе

AutoInjectModule
:

Удаление всех компонентов нужного типа

Можно автоматизировать удаление компонентов определенного типа в нужном месте:

DelHere()
поддерживает указание мира для удаляемых компонентов и именованную точку вызова через явное указание дополнительных параметров.

Если этот метод-расширение не устраивает и нужно получить поведение в виде системы - можно воспользоваться ручным созданием экземпляра системы

DelHereSystem
- расширение
DelHere()
является оберткой над ней.

Дополнительная инициализация сервисов

Можно автоматизировать дополнительную инициализацию сервисов в нужном месте без создания дополнительных систем:

Чтобы сервис поддерживал дополнительную инициализацию, он должен реализовывать специальный интерфейс:

InitHere()
поддерживает указание именованной точки вызова через явное указание дополнительного параметра.

Если этот метод-расширение не устраивает и нужно получить поведение в виде системы - можно воспользоваться ручным созданием экземпляра системы

InitHereSystem
- расширение
InitHere()
является оберткой над ней.

Дополнительная деинициализация сервисов

Можно автоматизировать дополнительную деинициализацию сервисов в нужном месте без создания дополнительных систем:

Чтобы сервис поддерживал дополнительную деинициализацию, он должен реализовывать специальный интерфейс:

DestroyHere()
поддерживает указание именованной точки вызова через явное указание дополнительного параметра.

Если этот метод-расширение не устраивает и нужно получить поведение в виде системы - можно воспользоваться ручным созданием экземпляра системы

DestroyHereSystem
- расширение
DestroyHere()
является оберткой над ней.

Пулы

Запрос или добавление компонентов

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

Безопасное удаление компонентов

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

Модули

Инициализация

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

ВАЖНО! Если используется

ProtoModules
- регистрация отдельных модулей через
IProtoSystems.AddModule()
не рекомендуется, все модули должны проходить через
ProtoModules
единообразно.

Утилиты

Сервис-локатор

Экземпляра класса можно сохранить глобально:

Экземпляра сохраненного глобально класса можно получить в любом месте:

ВАЖНО! Если данные больше не нужны - следует сбросить ссылку на них путем передачи

null
в метод Set().

Лицензия

Расширение выпускается под лицензией MIT-ZARYA, подробности тут.