{"id":22549059,"url":"https://github.com/dcfapixels/dragonecs","last_synced_at":"2025-04-10T01:52:59.013Z","repository":{"id":171109926,"uuid":"596004844","full_name":"DCFApixels/DragonECS","owner":"DCFApixels","description":"C# Entity Component System framework","archived":false,"fork":false,"pushed_at":"2024-04-10T15:37:32.000Z","size":1133,"stargazers_count":62,"open_issues_count":0,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-04-10T23:58:49.067Z","etag":null,"topics":["csharp","dragon-ecs","dragonecs","ecs","ecs-framework","entity-component-system","game-development","gamedev","no-dependencies","unity"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DCFApixels.png","metadata":{"files":{"readme":"README-RU.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2023-02-01T09:07:08.000Z","updated_at":"2024-04-14T18:29:41.356Z","dependencies_parsed_at":null,"dependency_job_id":"8784407c-3ab5-4e26-a5c5-d8ba4d275d39","html_url":"https://github.com/DCFApixels/DragonECS","commit_stats":null,"previous_names":["dcfapixels/dragonecs"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCFApixels%2FDragonECS","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCFApixels%2FDragonECS/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCFApixels%2FDragonECS/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCFApixels%2FDragonECS/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DCFApixels","download_url":"https://codeload.github.com/DCFApixels/DragonECS/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248142940,"owners_count":21054671,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dragon-ecs","dragonecs","ecs","ecs-framework","entity-component-system","game-development","gamedev","no-dependencies","unity"],"created_at":"2024-12-07T16:07:47.497Z","updated_at":"2025-04-10T01:52:58.998Z","avatar_url":"https://github.com/DCFApixels.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg width=\"660\" src=\"https://github.com/DCFApixels/DragonECS/assets/99481254/c09e385e-08c1-4c04-904a-36ad7e25e45b\"\u003e\n\u003c/p\u003e\n\n\u003cp align=\"center\"\u003e\n\u003cimg alt=\"Version\" src=\"https://img.shields.io/github/package-json/v/DCFApixels/DragonECS?color=%23ff4e85\u0026style=for-the-badge\"\u003e\n\u003cimg alt=\"License\" src=\"https://img.shields.io/github/license/DCFApixels/DragonECS?color=ff4e85\u0026style=for-the-badge\"\u003e\n\u003ca href=\"https://discord.gg/kqmJjExuCf\"\u003e\u003cimg alt=\"Discord\" src=\"https://img.shields.io/badge/Discord-JOIN-00b269?logo=discord\u0026logoColor=%23ffffff\u0026style=for-the-badge\"\u003e\u003c/a\u003e\n\u003ca href=\"http://qm.qq.com/cgi-bin/qm/qr?_wv=1027\u0026k=IbDcH43vhfArb30luGMP1TMXB3GCHzxm\u0026authKey=s%2FJfqvv46PswFq68irnGhkLrMR6y9tf%2FUn2mogYizSOGiS%2BmB%2B8Ar9I%2Fnr%2Bs4oS%2B\u0026noverify=0\u0026group_code=949562781\"\u003e\u003cimg alt=\"QQ\" src=\"https://img.shields.io/badge/QQ-JOIN-00b269?logo=tencentqq\u0026logoColor=%23ffffff\u0026style=for-the-badge\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\n# DragonECS - C# Entity Component System Фреймворк\n\n\u003ctable\u003e\n  \u003ctr\u003e\u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd colspan=\"3\"\u003eReadme Languages:\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd nowrap width=\"100\"\u003e\n      \u003ca href=\"https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md\"\u003e\n        \u003cimg src=\"https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721\"\u003e\u003c/br\u003e\n        \u003cspan\u003eРусский\u003c/span\u003e\n      \u003c/a\u003e  \n    \u003c/td\u003e\n    \u003ctd nowrap width=\"100\"\u003e\n      \u003ca href=\"https://github.com/DCFApixels/DragonECS\"\u003e\n        \u003cimg src=\"https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930\"\u003e\u003c/br\u003e\n        \u003cspan\u003eEnglish\u003c/span\u003e\n      \u003c/a\u003e  \n    \u003c/td\u003e\n    \u003ctd nowrap width=\"100\"\u003e\n      \u003ca href=\"https://github.com/DCFApixels/DragonECS/blob/main/README-ZH.md\"\u003e\n        \u003cimg src=\"https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927\"\u003e\u003c/br\u003e\n        \u003cspan\u003e中文\u003c/span\u003e\n      \u003c/a\u003e  \n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c/br\u003e\n\nDragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system) фреймворк нацеленный на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Разработан на чистом C#, без зависимостей и генерации кода. Вдохновлен [LeoEcs Lite](https://github.com/Leopotam/ecslite).\n\n\u003e [!WARNING]\n\u003e Проект предрелизной версии, поэтому API может меняться. В ветке main актуальная и рабочая версия.\n\u003e\n\u003e Если есть неясные моменты, вопросы можно задать тут [Обратная связь](#обратная-связь)\n\n## Оглавление\n- [Установка](#установка)\n- [Основные концепции](#основные-концепции)\n  - [Entity](#entity)\n  - [Component](#component)\n  - [System](#system)\n- [Концепции фреймворка](#концепции-фреймворка)\n  - [Пайплайн](#пайплайн)\n    - [Построение](#построение)\n    - [Внедрение зависимостей](#внедрение-зависимостей)\n    - [Модули](#модули)\n    - [Сортировка](#сортировка)\n  - [Процессы](#процессы)\n  - [Мир](#мир)\n  - [Пул](#пул)\n  - [Маска](#маска)\n  - [Аспект](#аспект)\n  - [Запросы](#запросы)\n  - [Коллекции](#Коллекции)\n  - [Корень ECS](#корень-ecs)\n- [Debug](#debug)\n  - [Мета-Атрибуты](#мета-атрибуты)\n  - [EcsDebug](#ecsdebug)\n  - [Профилирование](#профилирование)\n- [Define Symbols](#define-symbols)\n- [Расширение фреймворка](#расширение-фреймворка)\n  - [Компоненты мира](#компоненты-мира)\n  - [Конфиги](#конфиги)\n- [Проекты на DragonECS](#Проекты-на-DragonECS)\n- [Расширения](#расширения)\n- [FAQ](#faq)\n- [Обратная связь](#обратная-связь)\n\n\u003c/br\u003e\n\n# Установка\nСемантика версионирования - [Открыть](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)\n## Окружение\nОбязательные требования:\n+ Минимальная версия C# 7.3;\n\nОпционально:\n+ Поддержка NativeAOT\n+ Игровые движки с C#: Unity, Godot, MonoGame и т.д.\n\nПротестировано:\n+ **Unity:** Минимальная версия 2020.1.0;\n\n## Установка для Unity\n\u003e Рекомендуется так же установить расширение [Интеграция с движком Unity](https://github.com/DCFApixels/DragonECS-Unity)\n* ### Unity-модуль\nПоддерживается установка в виде Unity-модуля в  при помощи добавления git-URL [в PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html) или ручного добавления в `Packages/manifest.json`: \n```\nhttps://github.com/DCFApixels/DragonECS.git\n```\n* ### В виде исходников\nФреймворк так же может быть добавлен в проект в виде исходников.\n\u003c/br\u003e\n\n# Основные концепции\n## Entity\n**Сущности** - это то к чему крепятся данные. Реализованы в виде идентификаторов, которых есть 2 вида:\n* `int` - однократный идентификатор, применяется в пределах одного тика. Не рекомендуется хранить `int` идентификаторы, в место этого используйте `entlong`;\n* `entlong` - долговременный идентификатор, содержит в себе полный набор информации для однозначной идентификации;\n``` c#\n// Создание новой сущности в мире.\nint entityID = _world.NewEntity();\n\n// Удаление сущности.\n_world.DelEntity(entityID);\n\n// Копирование компонентов одной сущности в другую.\n_world.CopyEntity(entityID, otherEntityID);\n\n// Клонирование сущности.\nint newEntityID = _world.CloneEntity(entityID);\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eРабота с entlong\u003c/summary\u003e\n \n``` c#\n// Конвертация int в entlong.\nentlong entity = _world.GetEntityLong(entityID);\n// или\nentlong entity = (_world, entityID);\n\n// Проверка что сущность еще жива.\nif (entity.IsAlive) { }\n\n// Конвертация entlong в int. Если сущность уже не существует, будет брошено исключение. \nint entityID = entity.ID;\n// или\nvar (entityID, world) = entity;\n \n// Конвертация entlong в int. Вернет true и ее int идентификатор, если сущность еще жива.\nif (entity.TryGetID(out int entityID)) { }\n```\n \n \u003c/details\u003e\n \n\u003e Сущности не могут существовать без компонентов, пустые сущности будут автоматически удаляться сразу после удаления последнего компонента либо в конце тика.\n## Component\n**Компоненты** - это данные которые крепятся к сущностям.\n```c#\n// Компоненты IEcsComponent хранятся в обычном хранилище.\nstruct Health : IEcsComponent\n{\n    public float health;\n    public int armor;\n}\n// Компоненты с IEcsTagComponent хранятся в оптимизированном для тегов хранилище.\nstruct PlayerTag : IEcsTagComponent {}\n```\n\n## System\n**Системы** - это основная логика, тут задается поведение сущностей. Существуют в виде пользовательских классов, реализующих как минимум один из интерфейсов процессов. Основные процессы:\n```c#\nclass SomeSystem : IEcsPreInit, IEcsInit, IEcsRun, IEcsDestroy\n{\n    // Будет вызван один раз в момент работы EcsPipeline.Init() и до срабатывания IEcsInit.Init().\n    public void PreInit () { }\n    \n    // Будет вызван один раз в момент работы EcsPipeline.Init() и после срабатывания IEcsPreInit.PreInit().\n    public void Init ()  { }\n    \n    // Будет вызван один раз в момент работы EcsPipeline.Run().\n    public void Run () { }\n    \n    // Будет вызван один раз в момент работы EcsPipeline.Destroy().\n    public void Destroy () { }\n}\n```\n\u003e Для реализации дополнительных процессов перейдите к разделу [Процессы](#Процессы).\n\n\u003c/br\u003e\n\n# Концепции фреймворка\n## Пайплайн\nКонтейнер и движок систем. Отвечает за настройку очереди вызова систем, предоставляет механизм для сообщений между системами и механизм внедрения зависимостей. Реализован в виде класса `EcsPipeline`.\n### Построение\nЗа построение пайплайна отвечает Builder. В Builder добавляются системы, а в конце строится пайплайн. Пример:\n```c#\nEcsPipeline pipeline = EcsPipeline.New() // Создает Builder пайплайна.\n    // Добавляет систему System1 в очередь систем.\n    .Add(new System1())\n    // Добавляет System2 в очередь после System1.\n    .Add(new System2())\n    // Добавляет System3 в очередь после System2, но в единичном экземпляре.\n    .AddUnique(new System3())\n    // Завершает построение пайплайна и возвращает его экземпляр.\n    .Build(); \npipeline.Init(); // Инициализация пайплайна.\n```\n\n```c#\nclass SomeSystem : IEcsRun, IEcsPipelineMember\n{\n    // Получить экземпляр пайплайна к которому принадлежит система.\n    public EcsPipeline Pipeline { get ; set; }\n\n    public void Run () { }\n}\n```\n\u003e Для одновременного построения и инициализации есть метод Builder.BuildAndInit();\n### Внедрение зависимостей\nФреймворк реализует внедрение зависимостей для систем. это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в Builder.\n\n``` c#\nclass SomeDataA { /* ... */ }\nclass SomeDataB : SomeDataA { /* ... */ }\n\n// ...\nSomeDataB _someDataB = new SomeDataB();\nEcsPipeline pipeline = EcsPipeline.New()\n    // ...\n    // Внедрит _someDataB в системы реализующие IEcsInject\u003cSomeDataB\u003e.\n    .Inject(_someDataB) \n    // Добавит системы реализующие IEcsInject\u003cSomeDataA\u003e в дерево инъекции,\n    // теперь эти системы так же получат _someDataB.\n    .Injector.AddNode\u003cSomeDataA\u003e()\n    // ...\n    .Add(new SomeSystem())\n    // ...\n    .BuildAndInit();\n\n// ...\n// Для внедрения используется интерфейс IEcsInject\u003cT\u003e и его метод Inject(T obj)\nclass SomeSystem : IEcsInject\u003cSomeDataA\u003e, IEcsRun\n{\n    SomeDataA _someDataA\n    // obj будет экземпляром типа SomeDataB.\n    public void Inject(SomeDataA obj) =\u003e _someDataA = obj;\n\n    public void Run () \n    {\n        _someDataA.DoSomething();\n    }\n}\n```\n\u003e Использование встроенного внедрения зависимостей опционально. \n\n\u003e Имеется [Расширение](#расширения) упрощающее синтаксис инъекций - [Автоматическое внедрение зависимостей](https://github.com/DCFApixels/DragonECS-AutoInjections).\n\n### Модули\nГруппы систем реализующие общую фичу можно объединять в модули, и просто добавлять модули в Pipeline.\n``` c#\nusing DCFApixels.DragonECS;\nclass Module1 : IEcsModule \n{\n    public void Import(EcsPipeline.Builder b) \n    {\n        b.Add(new System1());\n        b.Add(new System2());\n        b.AddModule(new Module2());\n        // ...\n    }\n}\n```\n``` c#\nEcsPipeline pipeline = EcsPipeline.New()\n    // ...\n    .AddModule(new Module1())\n    // ...\n    .BuildAndInit();\n```\n\n### Сортировка\nДла управления расположением систем в пайплайне, вне зависимости от порядка добавления, есть 2 способа: Слои и Порядок сортировки.\n#### Слои\nСлой определяет место в пайплайне для вставки систем. Например, если необходимо чтобы система была вставлена в конце пайплайна, эту систему можно добавить в слой `EcsConsts.END_LAYER`.\n``` c#\nconst string SOME_LAYER = nameof(SOME_LAYER);\nEcsPipeline pipeline = EcsPipeline.New()\n    // ...\n    // Вставляет новый слой перед конечным слоем EcsConsts.END_LAYER\n    .Layers.Insert(EcsConsts.END_LAYER, SOME_LAYER)\n    // Система SomeSystem будет вставлена в слой SOME_LAYER\n    .Add(New SomeSystem(), SOME_LAYER) \n    // ...\n    .BuildAndInit();\n```\nВстроенные слои расположены в следующем порядке:\n* `EcsConst.PRE_BEGIN_LAYER`\n* `EcsConst.BEGIN_LAYER`\n* `EcsConst.BASIC_LAYER` (По умолчанию системы добавляются сюда)\n* `EcsConst.END_LAYER`\n* `EcsConst.POST_END_LAYER`\n#### Порядок сортировки\nДля сортировки систем в рамках слоя используется int значение порядка сортировки. По умолчанию системы добавляются с `sortOrder = 0`.\n``` c#\nEcsPipeline pipeline = EcsPipeline.New()\n    // ...\n    // Система SomeSystem будет вставлена в слой EcsConsts.BEGIN_LAYER \n    // и расположена после систем с sortOrder меньше 10.\n    .Add(New SomeSystem(), EcsConsts.BEGIN_LAYER, 10)\n    // ...\n    .BuildAndInit();\n```\n\n## Процессы\nПроцессы - это очереди систем реализующие общий интерфейс, например `IEcsRun`. Для запуска процессов используются Runner-ы. Встроенные процессы запускаются автоматически. Есть возможность реализации пользовательских процессов.\n\n\u003cdetails\u003e\n\u003csummary\u003eВстроенные процессы\u003c/summary\u003e\n \n* `IEcsPreInit`, `IEcsInit`, `IEcsRun`, `IEcsDestroy` - процессы жизненного цикла `EcsPipeline`.\n* `IEcsInject\u003cT\u003e` - Процессы системы [внедрения зависимостей](#Внедрение-зависимостей).\n* `IOnInitInjectionComplete` - Так же процесс системы [внедрения зависимостей](#Внедрение-зависимостей), но сигнализирует о завершении инициализирующей инъекции.\n\n\u003c/details\u003e\n \n\u003cdetails\u003e\n\u003csummary\u003eПользовательские процессы\u003c/summary\u003e\n \nДля добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от `EcsRunner\u003cTInterface\u003e`. Пример:\n``` c#\n// Интерфейс процесса.\ninterface IDoSomethingProcess : IEcsProcess\n{\n    void Do();\n}\n```\n``` c#\n// Реализация раннера. Пример реализации можно так же посмотреть в встроенном процессе IEcsRun \nsealed class DoSomethingProcessRunner : EcsRunner\u003cIDoSomethingProcess\u003e, IDoSomethingProcess\n{\n    public void Do() \n    {\n        foreach (var item in Process) item.Do();\n    }\n}\n```\n``` c#\n// Добавление раннера при создании пайплайна.\n_pipeline = EcsPipeline.New()\n    // ...\n    .AddRunner\u003cDoSomethingProcessRunner\u003e()\n    // ...\n    .BuildAndInit();\n\n// Запуск раннера если раннер был добавлен.\n_pipeline.GetRunner\u003cIDoSomethingProcess\u003e.Do()\n\n// Или если раннер не был добавлен(Вызов GetRunnerInstance так же добавит раннер в пайплайн).\n_pipeline.GetRunnerInstance\u003cDoSomethingProcessRunner\u003e.Do()\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eРасширенная реализация раннера\u003c/summary\u003e\n\n``` c#\ninternal sealed class DoSomethingProcessRunner : EcsRunner\u003cIDoSomethingProcess\u003e, IDoSomethingProcess\n{\n    // RunHelper упрощает реализацию по подобию реализации встроенных процессов. \n    // Автоматически вызывает маркер профайлера, а так же содержит try catch блок.\n    private RunHelper _helper;\n    protected override void OnSetup()\n    {\n        // Вторым аргументом задается имя маркера, если не указать, то имя будет выбрано автоматически.\n        _helper = new RunHelper(this, nameof(Do));\n    }\n    public void Do()\n    {\n        _helper.Run(p =\u003e p.Do());\n    }\n}\n```\n\n\u003c/details\u003e\n\n\u003e Требования реализации раннеров: \n\u003e * Наследоваться от `EcsRunner\u003cT\u003e` можно только напрямую;\n\u003e * Раннер может содержать только один интерфейс(за исключением `IEcsProcess`);\n\u003e * Наследуемый класс `EcsRunner\u003cT\u003e`, должен так же реализовать интерфейс `T`;\n    \n\u003e Не рекомендуется в цикле вызывать `GetRunner`, иначе кешируйте полученный раннер.\n\n\u003c/details\u003e\n\n## Мир\nКонтейнер для сущностей и компонентов.\n``` c#\n// Создание экземпляра мира.\n_world = new EcsDefaultWorld();\n\n// Создание и удаление сущности по примеру из раздела Сущности.\nvar e = _world.NewEntity();\n_world.DelEntity(e);\n\n// Уничтожение мира и освобождение ресурсов. Обязательно вызывать, иначе он будет висеть в памяти.\n_world.Destroy();\n```\n\u003e Миры изолированы друг от друга и могут обрабатываться в отдельных потоках. Но мультипоточная обработка одного мира поддерживается только при отсутствии добавления/удаления компонентов у сущностей.\n\nДля инициализации мира сразу необходимого размера и сокращения времени прогрева, в конструктор можно передать экземпляр `EcsWorldConfig`.\n\n``` c#\nEcsWorldConfig config = new EcsWorldConfig(\n    // Предварительно инициализирует вместимость мира для 2000 сущностей.\n    entitiesCapacity: 2000, \n    // Предварительно инициализирует вместимость пулов для 2000 компонентов.\n    poolComponentsCapacity: 2000\n    // ... Есть и другие параметры\n    );  \n_world = new EcsDefaultWorld(config);\n```\n\n## Пул\nХранилище для компонентов, пул предоставляет методы для добавления/чтения/редактирования/удаления компонентов на сущности. Есть несколько видов пулов, для разных видов компонентов:\n* `EcsPool` - универсальный пул, хранит struct-компоненты реализующие интерфейс `IEcsComponent`;\n* `EcsTagPool` - специальный пул, оптимизированный под компоненты-теги, хранит struct-компоненты с `IEcsTagComponent`;\n\nПулы имеют 5 основных метода и их разновидности:\n``` c#\n// Один из способов получить пул из мира.\nEcsPool\u003cPose\u003e poses = _world.GetPool\u003cPose\u003e();\n \n// Добавит компонент на сущность, бросит исключение если компонент уже есть у сущности.\nref var addedPose = ref poses.Add(entityID);\n \n// Вернет компонент, бросит исключение если у сущности нет этого компонента. \nref var gettedPose = ref poses.Get(entityID);\n \n// Вернет компонент доступный только для чтения, бросит исключение если у сущности нет этого компонента. \nref readonly var readonlyPose = ref poses.Read(entityID);\n \n// Вернет true если у сущности есть компонент, в противном случае false.\nif (poses.Has(entityID)) { /* ... */ }\n \n// Удалит компонент у сущности, бросит исключение если у сущности нет этого компонента.\nposes.Del(entityID);\n```\n\u003e [!WARNING]\n\u003e В `Release` сборке отключаются проверки на исключения.\n\n\u003e Есть \"безопасные\" методы, которые сначала выполнят проверку наличия/отсутствия компонента, названия таких методов начинаются с `Try`.\n\n\u003cdetails\u003e\n\u003csummary\u003eПользовательские пулы\u003c/summary\u003e\n\nПулом выступает любой тип реализующий интерфейс `IEcsPoolImplementation\u003cT\u003e` и имеющий конструктор без параметров.\n\nКлючевые моменты при реализации пула:\n* За примером реализации пула можно обратиться к реализации встроенного пула `EcsPool\u003cT\u003e`.\n* Интерфейс `IEcsPoolImplementation` и его члены не предназначены для публичного использования, члены интерфейса рекомендуется реализовывать явно.\n* Подставленный в интерфейсе `IEcsPoolImplementation\u003cT\u003e` тип `T` и тип возвращаемый в свойствах `ComponentType` с `ComponentTypeID` должны совпадать.\n* Обязательно регистрировать все изменения пула в экземпляре `EcsWorld.PoolsMediator` передаваемом в методе `OnInit`.\n* `EcsWorld.PoolsMediator` предназначен только для использования внутри пула.\n* Дефайн `DISABLE_POOLS_EVENTS` отключает реализуемые методы `AddListener` и `RemoveListener`.\n* В статическом классе `EcsPoolThrowHelper` определены бросания наиболее распространенных видов исключений.\n* В методе `OnReleaseDelEntityBuffer` происходит очистка удаленных сущностей.\n* Рекомендуется определить интерфейс которым обозначаются компоненты для нового пула. На основе этого интерфейса можно реализовать методы расширения вроде `GetPool\u003cT\u003e()` для упрощенного доступа к пулам.\n* Пулы должны реализовывать блокировку. Блокировка пула работает только в `Debug` режиме, и должна бросать исключения при попытке добавления или удаления компонента.\n\n\u003c/details\u003e\n\n## Маска\nПрименяется для фильтрации сущностей по наличию или отсутствию компонентов.  \n``` c#\n// Создание маски которая проверяет что у сущностей есть компоненты \n// SomeCmp1 и SomeCmp2, но нет компонента SomeCmp3.\nEcsMask mask = EcsMask.New(_world)\n    // Inc - Условие наличия компонента.\n    .Inc\u003cSomeCmp1\u003e()\n    .Inc\u003cSomeCmp2\u003e()\n    // Exc - Условие отсутствия компонента.\n    .Exc\u003cSomeCmp3\u003e()\n    .Build();\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eСтатическая маска\u003c/summary\u003e\n\n`EcsMask` привязаны к конкретным экземплярам мира которые необходимо передавать в `EcsMask.New(world)`, но есть `EcsStaticMask` которую можно создать без привязки к миру.\n\n``` c#\nclass SomeSystem : IEcsRun \n{\n    // EcsStaticMask можно создавать в статических полях.\n    static readonly EcsStaticMask _staticMask = EcsStaticMask.Inc\u003cSomeCmp1\u003e().Inc\u003cSomeCmp2\u003e().Exc\u003cSomeCmp3\u003e().Build();\n\n    // ...\n}\n```\n``` c#\n// Конвертация в обычную маску.\nEcsMask mask = _staticMask.ToMask(_world);\n```\n\n\u003c/details\u003e\n\n## Аспект\nПользовательские классы наследуемые от `EcsAspect` и используемые для взаимодействия с сущностями. Аспекты одновременно являются кешем пулов и содержат маску. Можно рассматривать аспекты как описание того с какими сущностями работает система.\n\nУпрощенный синтаксис:\n``` c#\nusing DCFApixels.DragonECS;\n// ...\nclass Aspect : EcsAspect\n{\n    // Кешируется пул и Pose добавляется во включающее ограничение.\n    public EcsPool\u003cPose\u003e poses = Inc;\n    // Кешируется пул и Velocity добавляется во включающее ограничение.\n    public EcsPool\u003cVelocity\u003e velocities = Inc;\n    // Кешируется пул и FreezedTag добавляется в исключающее ограничение.\n    public EcsTagPool\u003cFreezedTag\u003e freezedTags = Exc;\n\n    // При запросах будет проверяться наличие компонентов\n    // из включающего ограничения маски и отсутствие из исключающего.\n    // Так же есть Opt - только кеширует пул, не влияя на маску. \n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eЯвный синтаксис (результат идентичен примеру выше):\u003c/summary\u003e\n\n``` c#\nusing DCFApixels.DragonECS;\n// ...\nclass Aspect : EcsAspect\n{\n    public EcsPool\u003cPose\u003e poses;\n    public EcsPool\u003cVelocity\u003e velocities;\n    protected override void Init(Builder b)\n    {\n        poses = b.Include\u003cPose\u003e();\n        velocities = b.Include\u003cVelocity\u003e();\n        b.Exclude\u003cFreezedTag\u003e();\n    }\n}\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eКомбинирование аспектов\u003c/summary\u003e\n\nВ аспекты можно добавлять другие аспекты, тем самым комбинируя их. Ограничения так же будут скомбинированы.\n``` c#\nusing DCFApixels.DragonECS;\n// ...\nclass Aspect : EcsAspect\n{\n    public OtherAspect1 otherAspect1;\n    public OtherAspect2 otherAspect2;\n    public EcsPool\u003cPose\u003e poses;\n \n    protected override void Init(Builder b)\n    {\n        // Комбинирует с SomeAspect1.\n        otherAspect1 = b.Combine\u003cOtherAspect1\u003e(1);\n        // Хотя для OtherAspect1 метод Combine был вызван раньше, сначала будет скомбинирован с OtherAspect2, так как по умолчанию order = 0.\n        otherAspect2 = b.Combine\u003cOtherAspect2\u003e();\n        // Если в OtherAspect1 или в OtherAspect2 было ограничение b.Exclude\u003cPose\u003e() тут оно будет заменено на b.Include\u003cPose\u003e().\n        poses = b.Include\u003cPose\u003e();\n    }\n}\n```\nЕсли будут конфликтующие ограничения у комбинируемых аспектов, то новые ограничения будут заменять добавленные ранее. Ограничения корневого аспекта всегда заменяют ограничения из добавленных аспектов. Визуальный пример комбинации ограничений:\n| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | разрешение конфликтных ограничений|\n| :--- | :--- | :--- | :--- | :--- | :--- |:--- |\n| OtherAspect2 | :heavy_check_mark: | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | |\n| OtherAspect1 | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_minus_sign: | Для `cmp2` будет выбрано :heavy_check_mark: |\n| Aspect | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | Для `cmp1` будет выбрано :x: |\n| Итоговые ограничения | :x: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_check_mark: | |\n\n\u003c/details\u003e\n\n## Запросы\nФильтруют сущности и выдают коллекции сущностей удовлетворяющие определенным условиям. Встроенный запрос `Where` фильтрует на соответствие условиям маски компонентов и имеет несколько перегрузок:\n+ `EcsWorld.Where(EcsMask mask)` - Обычная фильтрация по маске;\n+ `EcsWorld.Where\u003cTAspect\u003e(out TAspect aspect)` - Сочетает в себе фильтрацию по маске из аспекта и получение аспекта;\n\nЗапрос `Where` применим как к `EcsWorld` так и коллекциям фреймворка (в этом плане Where чем-то похож на аналогичный из Linq). Так же имеются перегрузки для сортировки сущностей по `Comparison\u003cint\u003e`. \n\nПример системы:\n``` c#\npublic class SomeDamageSystem : IEcsRun, IEcsInject\u003cEcsDefaultWorld\u003e\n{\n    class Aspect : EcsAspect\n    {\n        public EcsPool\u003cHealth\u003e healths = Inc;\n        public EcsPool\u003cDamageSignal\u003e damageSignals = Inc;\n        public EcsTagPool\u003cIsInvulnerable\u003e isInvulnerables = Exc;\n        // Наличие или отсутствие этого компонента не проверяется.\n        public EcsTagPool\u003cIsDiedSignal\u003e isDiedSignals = Opt;\n    }\n    EcsDefaultWorld _world;\n    public void Inject(EcsDefaultWorld world) =\u003e _world = world;\n\n    public void Run()\n    {\n        foreach (var e in _world.Where(out Aspect a))\n        {\n            // Сюда попадают сущности с компонентами Health, DamageSignal и без IsInvulnerable.\n            ref var health = ref a.healths.Get(e);\n            if(health.points \u003e 0)\n            {\n                health.points -= a.damageSignals.Get(e).points;\n                if(health.points \u003c= 0)\n                { // Создаем сигнал другим системам о том что сущность умерла.\n                    a.isDiedSignals.TryAdd(e);\n                }\n            }\n        }\n    }\n}\n```\n\n\u003e Имеется [Расширение](#расширения) упрощающее синтаксис запросов и обращения к компонентам - [Упрощенный синтаксис](https://github.com/DCFApixels/DragonECS-AutoInjections).\n \n## Коллекции\n\n### EcsSpan\nКоллекция сущностей, доступная только для чтения и выделяемая только в стеке. Состоит из ссылки на массив, длинны и идентификатора мира. Аналог `ReadOnlySpan\u003cint\u003e`.\n``` c#\n// Запрос Where возвращает сущности в виде EcsSpan.\nEcsSpan es = _world.Where(out Aspect a);\n// Итерироваться можно по foreach и for.\nforeach (var e in es)\n{\n    // ...\n}\nfor (int i = 0; i \u003c es.Count; i++)\n{\n    int e = es[i];\n    // ...\n}\n```\n\u003e Хотя `EcsSpan` является просто массивом, в нем не допускается дублирование сущностей. \n\n### EcsGroup\nВспомогательная коллекция основанная на Sparse Set для хранения множества сущностей с O(1) операциями добавления/удаления/проверки и т.д.\n``` c#\n// Получаем новую группу. EcsWorld содержит в себе пул групп,\n// поэтому будет переиспользована свободная или создана новая.\nEcsGroup group = EcsGroup.New(_world);\n// Освобождаем группу.\ngroup.Dispose();\n```\n``` c#\n// Добавляем сущность entityID.\ngroup.Add(entityID);\n// Проверяем наличие сущности entityID.\ngroup.Has(entityID);\n// Удаляем сущность entityID.\ngroup.Remove(entityID);\n```\n``` c#\n// Запрос WhereToGroup возвращает сущности в виде группы только для чтения EcsReadonlyGroup.\nEcsReadonlyGroup group = _world.WhereToGroup(out Aspect a);\n// Итерироваться можно по foreach и for.\nforeach (var e in group) \n{ \n    // ...\n}\nfor (int i = 0; i \u003c group.Count; i++)\n{\n    int e = group[i];\n    // ...\n}\n```\nГруппы являются множествами и реализуют интерфейс `ISet\u003cint\u003e`. Редактирующие методы имеет 2 варианта, с записью результата в groupA, либо с возвращением новой группы. \n                                \n``` c#\n// Объединение groupA и groupB.\ngroupA.UnionWith(groupB);\nEcsGroup newGroup = EcsGroup.Union(groupA, groupB);\n\n// Пересечение groupA и groupB.\ngroupA.IntersectWith(groupB);\nEcsGroup newGroup = EcsGroup.Intersect(groupA, groupB);\n\n// Разность groupA и groupB.\ngroupA.ExceptWith(groupB);\nEcsGroup newGroup = EcsGroup.Except(groupA, groupB);\n\n// Симметрическая разность groupA и groupB.\ngroupA.SymmetricExceptWith(groupB);\nEcsGroup newGroup = EcsGroup.SymmetricExcept(groupA, groupB);\n\n// Разница всех сущностей в мире и groupA.\ngroupA.Inverse();\nEcsGroup newGroup = EcsGroup.Inverse(groupA);\n```\n\n## Корень ECS\nЭто пользовательский класс который является точкой входа для ECS. Основное назначение инициализация, запуск систем на каждый Update движка и освобождение ресурсов по окончанию использования.\n### Пример для Unity\n``` c#\nusing DCFApixels.DragonECS;\nusing UnityEngine;\npublic class EcsRoot : MonoBehaviour\n{\n    private EcsPipeline _pipeline;\n    private EcsDefaultWorld _world;\n    private void Start()\n    {\n        // Создание мира для сущностей и компонентов.\n        _world = new EcsDefaultWorld();\n        // Создание пайплайна для систем.\n        _pipeline = EcsPipeline.New()\n            // Добавление систем.\n            // .Add(new SomeSystem1())\n            // .Add(new SomeSystem2())\n            // .Add(new SomeSystem3())\n\n            // Внедрение мира в системы.\n            .Inject(_world)\n            // Прочие внедрения.\n            // .Inject(SomeData)\n\n            // Завершение построения пайплайна.\n            .Build();\n        // Инициализация пайплайна и запуск IEcsPreInit.PreInit()\n        // и IEcsInit.Init() у всех добавленных систем.\n        _pipeline.Init();\n    }\n    private void Update()\n    {\n        // Запуск IEcsRun.Run() у всех добавленных систем.\n        _pipeline.Run();\n    }\n    private void OnDestroy()\n    {\n        // Запускает IEcsDestroy.Destroy() у всех добавленных систем.\n        _pipeline.Destroy();\n        _pipeline = null;\n        // Обязательно удалять миры которые больше не будут использованы.\n        _world.Destroy();\n        _world = null;\n    }\n}\n```\n### Общий пример\n``` c#\nusing DCFApixels.DragonECS;\npublic class EcsRoot\n{\n    private EcsPipeline _pipeline;\n    private EcsDefaultWorld _world;\n    // Инициализация окружения.\n    public void Init()\n    {\n        //Создание мира для сущностей и компонентов.\n        _world = new EcsDefaultWorld();\n        //Создание пайплайна для систем.\n        _pipeline = EcsPipeline.New()\n            // Добавление систем.\n            // .Add(new SomeSystem1())\n            // .Add(new SomeSystem2())\n            // .Add(new SomeSystem3())\n\n            // Внедрение мира в системы.\n            .Inject(_world)\n            // Прочие внедрения.\n            // .Inject(SomeData)\n\n            // Завершение построения пайплайна.\n            .Build();\n        // Инициализация пайплайна и запуск IEcsPreInit.PreInit()\n        // и IEcsInit.Init() у всех добавленных систем.\n        _pipeline.Init();\n    }\n\n    // Update-цикл движка.\n    public void Update()\n    {\n        // Запуск IEcsRun.Run() у всех добавленных систем.\n        _pipeline.Run();\n    }\n\n    // Очистка окружения.\n    public void Destroy()\n    {\n        // Запускает IEcsDestroy.Destroy() у всех добавленных систем.\n        _pipeline.Destroy();\n        _pipeline = null;\n        // Обязательно удалять миры которые больше не будут использованы.\n        _world.Destroy();\n        _world = null;\n    }\n}\n```\n\n\u003c/br\u003e\n\n# Debug\nФреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды. Так же многие типы имеют свой DebuggerProxy для более информативного отображения в IDE.\n## Мета-Атрибуты\nПо умолчанию мета-атрибуты не имеют применения, но используются в интеграциях с движками для задания отображения в отладочных инструментах и редакторах. А так же могут быть использованы для генерации автоматической документации.\n``` c#\nusing DCFApixels.DragonECS;\n\n// Задает пользовательское название типа, по умолчанию используется имя типа.\n[MetaName(\"SomeComponent\")]\n\n// Используется для группировки типов.\n[MetaGroup(\"Abilities\", \"Passive\", ...)] // или [MetaGroup(\"Abilities/Passive/\")]\n\n// Задает цвет типа в RGB кодировке, где каждый канал принимает значение от 0 до 255, по умолчанию белый. \n[MetaColor(MetaColor.Red)] // или [MetaColor(255, 0, 0)]\n \n// Добавляет описание типу.\n[MetaDescription(\"The quick brown fox jumps over the lazy dog\")] \n\n// Добавляет строковый уникальный идентификатор.\n[MetaID(\"8D56F0949201D0C84465B7A6C586DCD6\")] // Строки должны быть уникальными, и не допускают символы ,\u003c\u003e .\n \n// Добавляет строковые теги.\n[MetaTags(\"Tag1\", \"Tag2\", ...)] // [MetaTags(MetaTags.HIDDEN))] чтобы скрыть в редакторе \npublic struct Component : IEcsComponent { /* ... */ }\n```\n``` c#\n// Получение мета-информации:\nTypeMeta typeMeta = someComponent.GetMeta();\n// или\nTypeMeta typeMeta = pool.ComponentType.ToMeta();\n\nvar name = typeMeta.Name; // [MetaName]\nvar group = typeMeta.Group; // [MetaGroup]\nvar color = typeMeta.Color; // [MetaColor]\nvar description = typeMeta.Description; // [MetaDescription]\nvar metaID = typeMeta.MetaID; // [MetaID]\nvar tags = typeMeta.Tags; // [MetaTags]\n```\n\u003e Для автоматической генерации уникальных идентификаторов MetaID есть метод `MetaID.GenerateNewUniqueID()` и [Браузерный генератор](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/)\n\n## EcsDebug\nВспомогательный тип с набором методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы - это посредники между EcsDebug и инструментами отладки среды. Такая реализация позволяет не изменяя отладочный код, менять его поведение или переносить проект в другие среды, достаточно только реализовать соответствующий Debug-сервис.\n\n``` c#\n// Вывод лога.\nEcsDebug.Print(\"Message\");\n\n// Вывод лога с тегом.\nEcsDebug.Print(\"Tag\", \"Message\");\n\n// Прерывание игры.\nEcsDebug.Break();\n\n// Установка другого Debug-Сервиса.\nEcsDebug.Set\u003cOtherDebugService\u003e();\n```\n\n\u003e По умолчанию используется `DefaultDebugService` который выводит логи в консоль. Для реализации пользовательского создайте класс наследуемый от `DebugService` и реализуйте абстрактные члены класса.\n\n\u003e `EcsDebug` потокобезопасен, за счет того что каждый поток использует свой изолированный экземпляр сервиса. Экземпляры для потоков создаются в абстрактном методе `DebugService.CreateThreadInstance`.\n\n## Профилирование\nЗа реализацию профайлера так же отвечает Debug-сервис. Для выделения участка кода используется `EcsProfilerMarker`;\n``` c#\n// Создание маркера с именем SomeMarker.\nprivate static readonly EcsProfilerMarker _marker = new EcsProfilerMarker(\"SomeMarker\");\n```\n``` c#\n_marker.Begin();\n// Код для которого замеряется скорость.\n_marker.End();\n\n// или\n\nusing (_marker.Auto())\n{\n    // Код для которого замеряется скорость.\n}\n```\n\u003e `DefaultDebugService` использует реализацию на основе `Stopwatch` и выводом в консоль.\n\n\u003c/br\u003e\n\n# Define Symbols\n+ `DRAGONECS_DISABLE_POOLS_EVENTS` - выключает реактивное поведение в пулах.\n+ `DRAGONECS_ENABLE_DEBUG_SERVICE` - включает работу EcsDebug в релизном билде.\n+ `DRAGONECS_STABILITY_MODE` - включает опускаемые в релизном билде проверки.\n+ `DRAGONECS_DISABLE_CATH_EXCEPTIONS` - Выключает поведение по умолчанию по обработке исключений. По умолчанию фреймворк будет ловить исключения с выводом информации из исключений через EcsDebug и продолжать работу.\n+ `REFLECTION_DISABLED` - Полностью ограничивает работу фреймворка с Reflection.\n+ `DISABLE_DEBUG` - Для среды где не поддерживается ручное отключение DEBUG, например Unity.\n+ `ENABLE_DUMMY_SPAN` - На случай если в среде не поддерживаются Span типы, включает его замену.\n\n\u003c/br\u003e\n\n# Расширение фреймворка\nДополнительные инструменты полезные для создания расширений.\n\n## Конфиги\nКонструкторы классов `EcsWorld` и `EcsPipeline` могут принимать контейнеры конфигов реализующие интерфейс `IConfigContainer` или `IConfigContainerWriter`. С помощью этих контейнеров можно передавать данные и зависимости. Встроенная реализация контейнера - `ConfigContainer`, но можно так же использовать свою реализацию.\u003c/br\u003e\nПример использования конфигов для мира:\n``` c#\nvar configs = new ConfigContainer()\n    .Set(new EcsWorldConfig(entitiesCapacity: 2000, poolsCapacity: 2000)\n    .Set(new SomeDataA(/* ... */))\n    .Set(new SomeDataB(/* ... */)));\nEcsDefaultWorld _world = new EcsDefaultWorld(configs);\n// ...\nvar _someDataA = _world.Configs.Get\u003cSomeDataA\u003e();\nvar _someDataB = _world.Configs.Get\u003cSomeDataB\u003e();\n```\nПример использования конфигов для пайплайна:\n``` c#\n_pipeline = EcsPipeline.New()// аналогично _pipeline = EcsPipeline.New(new ConfigContainer())\n    .Configs.Set(new SomeDataA(/* ... */))\n    .Configs.Set(new SomeDataB(/* ... */))\n    // ...\n    .BuildAndInit();\n// ...\nvar _someDataA = _pipeline.Configs.Get\u003cSomeDataA\u003e();\nvar _someDataB = _pipeline.Configs.Get\u003cSomeDataB\u003e();\n```\n\n## Компоненты Мира\nС помощью компонентов можно прикреплять дополнительные данные к мирам. В качестве компонентов используются `struct` типы. Доступ к компонентам через `Get` оптимизирован, скорость почти такая же как доступ к полям класса.\n``` c#\n// Получить компонент.\nref WorldComponent component = ref _world.Get\u003cWorldComponent\u003e();\n```\nРеализация компонента:\n``` c#\npublic struct WorldComponent\n{\n    // Данные.\n}\n```\nИли:\n``` c#\npublic struct WorldComponent : IEcsWorldComponent\u003cWorldComponent\u003e\n{\n    // Данные.\n    void IEcsWorldComponent\u003cWorldComponent\u003e.Init(ref WorldComponent component, EcsWorld world)\n    {\n        // Действия при инициализации компонента. Вызывается до первого возвращения из EcsWorld.Get .\n    }\n    void IEcsWorldComponent\u003cWorldComponent\u003e.OnDestroy(ref WorldComponent component, EcsWorld world)\n    {\n        // Действия когда вызывается EcsWorld.Destroy.\n        // Вызов OnDestroy, обязует пользователя вручную обнулять компонент, если это необходимо. \n        component = default;\n    }\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eПример использования\u003c/summary\u003e\n\nСобытия интерфейса IEcsWorldComponent\u003cT\u003e, могут быть использованы для автоматической инициализации полей компонента, и освобождения ресурсов.\n``` c#\npublic struct WorldComponent : IEcsWorldComponent\u003cWorldComponent\u003e\n{\n    private SomeClass _object; // Объект который будет утилизироваться.\n    private SomeReusedClass _reusedObject; // Объект который будет переиспользоваться.\n    public SomeClass Object =\u003e _object;\n    public SomeReusedClass ReusedObject =\u003e _reusedObject;\n    void IEcsWorldComponent\u003cWorldComponent\u003e.Init(ref WorldComponent component, EcsWorld world)\n    {\n        if (component._reusedObject == null)\n            component._reusedObject = new SomeReusedClass();\n        component._object = new SomeClass();\n        // Теперь при получении компонента через EcsWorld.Get, _reusedObject и _object уже будут созданы.\n    }\n    void IEcsWorldComponent\u003cWorldComponent\u003e.OnDestroy(ref WorldComponent component, EcsWorld world)\n    {\n        // Утилизируем не нужный объект, и освобождаем ссылку на него, чтобы GC мог его собрать.\n        component._object.Dispose();\n        component._object = null;\n        \n        // Как вариант тут можно сделать сброс значений у переиспользуемого объекта.\n        //component._reusedObject.Reset();\n        \n        // Так как в этом примере не нужно полное обнуление компонента, то строчка ниже не нужна.\n        // component = default;\n    }\n}\n```\n\u003c/details\u003e\n\n\u003e Компоненты и конфиги можно применять для создания расширений в связке с методами расширений.\n\n\u003c/br\u003e\n\n# Проекты на DragonECS\n## С исходниками:\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\n      \u003ca href=\"https://github.com/DCFApixels/3D-Platformer-DragonECS-Demo\"\u003e\n        3D Platformer (Example)\n        \u003cimg src=\"https://github.com/user-attachments/assets/c593dba7-eeaa-4706-a043-946f132f3f83\" alt=\"screenshot\"\u003e\n      \u003c/a\u003e \n    \u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\n      \u003ca href=\"https://github.com/DCFApixels/LD_56_Tiny_Aliens\"\u003e\n        Tiny Aliens (Ludum Dare 56)\n        \u003cimg src=\"https://github.com/user-attachments/assets/1a8f06ed-c68d-483a-b880-c9faaf7e0b5f\" alt=\"screenshot\"\u003e\n      \u003c/a\u003e\n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n## Опубликованные проекты:\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003ctd align=\"center\"\u003e\n      \u003ca href=\"https://yandex.ru/games/app/206024?utm_source=game_popup_menu\"\u003e\n        Башенки Смерти\n        \u003cimg src=\"https://github.com/user-attachments/assets/70fc55a0-c911-49f8-ba75-f503437f087f\" alt=\"screenshot\"\u003e\n      \u003c/a\u003e \n    \u003c/td\u003e\n    \u003ctd align=\"center\"\u003e\n      \u003cspan\u003e\n        ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ\n        \u003cimg tabindex=\"-1\" src=\"https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490\" alt=\"screenshot\"\u003e\n      \u003c/span\u003e \n    \u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\u003c/br\u003e\n\n# Расширения\n* Пакеты:\n    * [Интеграция с движком Unity](https://github.com/DCFApixels/DragonECS-Unity)\n    * [Автоматическое внедрение зависимостей](https://github.com/DCFApixels/DragonECS-AutoInjections)\n    * [Классическая C# многопоточность](https://github.com/DCFApixels/DragonECS-ClassicThreads)\n    * [Recursivity](https://github.com/DCFApixels/DragonECS-Recursivity)\n    * [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)\n    * [Графы](https://github.com/DCFApixels/DragonECS-Graphs)\n* Утилиты:\n    * [Упрощенный синтаксис](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)\n    * [Однокадровые компоненты](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)\n    * [Шаблоны кода IDE](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) и [для Unity](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)\n\u003e *Твое расширение? Если разрабатываешь расширение для DragonECS, пиши [сюда](#обратная-связь).\n\n\u003c/br\u003e\n \n# FAQ\n## 'ReadOnlySpan\u003c\u003e' could not be found\nВ версии Unity 2020.1.х в консоли может выпадать ошибка:\n```\nThe type or namespace name 'ReadOnlySpan\u003c\u003e' could not be found (are you missing a using directive or an assembly reference?)\n``` \nЧтобы починить добавьте директиву `ENABLE_DUMMY_SPAN` в `Project Settings/Player/Other Settings/Scripting Define Symbols`.\n\n## Как Выключать/Включать системы?\nНапрямую - никак. \u003c/br\u003e\nОбычно потребность выключить/включить систему появляется когда поменялось общее состояние игры, это может так же значить что нужно переключить сразу группу систем, все это в совокупности можно рассматривать как изменения процессов. Есть 2 решения：\u003c/br\u003e\n+ Если изменения процесса глобальные, то создать новый `EcsPipeline` и в цикле обновления движка запускать соответствующий пайплайн.\n+ Разделить `IEcsRun` на несколько процессов и в цикле обновления движка запускать соответствующий процесс. Для этого создайте новый интерфейс процесса, раннер для запуска этого интерфейса и получайте раннер через `EcsPipeline.GetRunner\u003cT\u003e()`.\n## Перечень рекомендаций [DragonECS-Vault](https://github.com/DCFApixels/DragonECS-Vault)\n\u003c/br\u003e\n\n# Обратная связь\n+ Discord (RU-EN) [https://discord.gg/kqmJjExuCf](https://discord.gg/kqmJjExuCf)\n+ QQ (中文) [949562781](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027\u0026k=IbDcH43vhfArb30luGMP1TMXB3GCHzxm\u0026authKey=s%2FJfqvv46PswFq68irnGhkLrMR6y9tf%2FUn2mogYizSOGiS%2BmB%2B8Ar9I%2Fnr%2Bs4oS%2B\u0026noverify=0\u0026group_code=949562781)\n\n\u003c/br\u003e\u003c/br\u003e\u003c/br\u003e\n\u003c/br\u003e\u003c/br\u003e\u003c/br\u003e\n\u003c/br\u003e\u003c/br\u003e\u003c/br\u003e\n\u003c/br\u003e\u003c/br\u003e\u003c/br\u003e\n\u003c/br\u003e\u003c/br\u003e\u003c/br\u003e\n\u003c/br\u003e\u003c/br\u003e\u003c/br\u003e\n\u003cimg width=\"0\" src=\"https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755\"\u003e\u003c!--Чтоб флаг подгружался в любом случае--\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcfapixels%2Fdragonecs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdcfapixels%2Fdragonecs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcfapixels%2Fdragonecs/lists"}