{"id":22549062,"url":"https://github.com/dcfapixels/dragonecs-recursivity","last_synced_at":"2025-09-13T22:23:25.486Z","repository":{"id":262429723,"uuid":"883467996","full_name":"DCFApixels/DragonECS-Recursivity","owner":"DCFApixels","description":null,"archived":false,"fork":false,"pushed_at":"2025-05-18T07:57:02.000Z","size":63,"stargazers_count":4,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-05-18T08:33:38.984Z","etag":null,"topics":["dragon-ecs","dragonecs","extension"],"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.md","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,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-11-05T02:35:04.000Z","updated_at":"2025-04-13T12:37:18.000Z","dependencies_parsed_at":"2024-11-12T12:18:35.813Z","dependency_job_id":"ed9db9a7-975d-4774-a044-0cf1dc9de71f","html_url":"https://github.com/DCFApixels/DragonECS-Recursivity","commit_stats":null,"previous_names":["dcfapixels/dragonecs-recursivity"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/DCFApixels/DragonECS-Recursivity","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCFApixels%2FDragonECS-Recursivity","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCFApixels%2FDragonECS-Recursivity/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCFApixels%2FDragonECS-Recursivity/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCFApixels%2FDragonECS-Recursivity/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DCFApixels","download_url":"https://codeload.github.com/DCFApixels/DragonECS-Recursivity/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DCFApixels%2FDragonECS-Recursivity/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275037264,"owners_count":25394583,"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","status":"online","status_checked_at":"2025-09-13T02:00:10.085Z","response_time":70,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["dragon-ecs","dragonecs","extension"],"created_at":"2024-12-07T16:07:48.115Z","updated_at":"2025-09-13T22:23:25.440Z","avatar_url":"https://github.com/DCFApixels.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n\u003cimg width=\"400\" src=\"https://github.com/user-attachments/assets/e2ae19e1-b121-46a2-94bc-eabf7378071b\"\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-Recursivity?color=%23ff4e85\u0026style=for-the-badge\"\u003e\n\u003cimg alt=\"License\" src=\"https://img.shields.io/github/license/DCFApixels/DragonECS-Recursivity?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# Recursivity для [DragonECS](https://github.com/DCFApixels/DragonECS)\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-Recursivity/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-Recursivity\"\u003e\n        \u003cimg src=\"https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930\"\u003e\u003c/br\u003e\n        \u003cspan\u003eEnglish(WIP)\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  \nУпрощает обработку событий симулируя поведение рекурсии, но в рамках стандартной структуры ECS. Гарантирует для событий которые должны быть обработаны в рамках одного кадра, что они будут обработаны всеми системами ровно один раз, в независимости от места появления.\n\n\u003e [!WARNING]\n\u003e Проект в стадии разработки. API может меняться.\n\n# Оглавление\n- [Установка](#установка)\n- [Инициализация](#инициализация)\n- [Обработка событий](#обработка-событий)\n\n\u003c/br\u003e\n\n# Установка\nСемантика версионирования - [Открыть](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)\n## Окружение\nОбязательные требования:\n+ Зависимость: [DragonECS](https://github.com/DCFApixels/DragonECS)\n+ Минимальная версия C# 7.3;\n\nОпционально:\n+ Игровые движки с C#: Unity, Godot, MonoGame и т.д.\n\nПротестировано:\n+ **Unity:** Минимальная версия 2020.1.0;\n\n## Установка для 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-Recursivity.git\n```\n* ### В виде исходников\nПакет так же может быть добавлен в проект в виде исходников.\n\n\u003c/br\u003e\n\n# Инициализация\nДля обработки событий используется процесс `IOn\u003cT\u003e.ToRun()` где `T` это тип компонента-события. Процесс `IOn\u003cT\u003e.ToRun()` контролирует специальная система, которую необходимо инициализировать в пайплайне.\n\n``` c#\n_world = new EcsDefaultWorld();\n_pipeline = EcsPipeline.New()\n    // ...\n    // Инициализация системы контролирующей процесс IOn\u003cDamageEvent\u003e.ToRun().\n    // Аргумент maxLoops устанавливает лимит на количество выполнений за один кадр.\n    .AddOn\u003cDamageEvent\u003e(maxLoops: 100)\n    // Добавление систем обрабатывающих этот процесс\n    .Add(new SomeDamageSystem())\n    .Add(new SomeReturnDamageAbilitySystem())\n    // ...\n    .Inject(_world)\n    .BuildAndInit();\n```\n\n\u003e За запуск всех систем которые выполняются рекурсивно, включая управляющих процессом `IOn\u003cT\u003e.ToRun()`, ответственна система рекурсии. По умолчанию система рекурсии добавляется на слой `EcsRecursivityConsts.RECURSIVE_LAYER`, а слой `EcsRecursivityConsts.RECURSIVE_LAYER` вставляется после `EcsConsts.BASIC_LAYER`.\n\n\u003c/br\u003e\n\n# Обработка событий\nСистемы с `IOn\u003cT\u003e.ToRun()` будут выполняться до тех пор пока в мире остается хотя бы один компонент-событие `T`. \n\nС точки зрения замедления производительности влияние не высоко по нескольким причинам:\n* Системы с `IOn\u003cT\u003e.ToRun()` не выполняются вовсе если в мире нет ни одного компонента `T`.\n* В инициализации `.AddOn\u003cDamageEvent\u003e(maxLoops)` Можно выставить лимит(maxLoops) вызова `IOn\u003cT\u003e.ToRun()`, тогда при достижении лимита, оставшиеся события будут обработаны в следующем кадре.\n\n\u003e Имеется защита от бесконечного зацикливания в виде глобального лимита в `100_000` повторений.\n\nНиже приведен пример системы обрабатывающей события. В примере реализована система применения урона к здоровью и система способности возврата урона атакующему, что-то вроде шипов.\n\n\u003cdetails\u003e\n\u003csummary\u003eИспользуемые в примере компоненты\u003c/summary\u003e\n\n``` c#\nusing DCFApixels.DragonECS;\npublic struct Health : IEcsComponent\n{\n    public float points;\n}\npublic struct DamageEvent : IEcsComponent\n{\n    public entlong source;\n    public entlong target;\n    public float points;\n}\npublic struct ReturnDamageAbility : IEcsComponent\n{\n    public float multiplier;\n}\n```\n\n\u003c/details\u003e\n\n\u003e Этот пример имеет некоторые проблемы, но как пример достаточно нагляден для понимания работы.\n\n\n``` c#\n// Система которая применяет полученный урон к здоровью.\npublic class SomeApplyDamageSystem : IOn\u003cDamageEvent\u003e, IEcsInject\u003cEcsDefaultWorld\u003e\n{\n    class EventAspect : EcsAspect\n    {\n        public EcsPool\u003cDamageEvent\u003e damageEvents = Inc;\n    }\n    class Aspect : EcsAspect\n    {\n        public EcsPool\u003cHealth\u003e healths = Inc;\n    }\n\n    EcsDefaultWorld _world;\n\n    // targetEntities содержит все сущности с компонентом DamageEvent. \n    // Сущности из этого же списка в конце цикла будут автоматически отчищены от компонента DamageEvent.\n    void IOn\u003cDamageEvent\u003e.ToRun(EcsSpan targetEntities)\n    {\n        var a = _world.GetAspect\u003cAspect\u003e();\n        // Итерироваться нужно по targetEntities, \n        // так можно гарантировать что системы будут обрабатывать каждое событие один раз.\n        foreach (var ee in targetEntities.Where(out EventAspect ea))\n        {\n            ref var damageEvent = ref ea.damageEvents.Get(ee);\n            // Извлечение ID сущности с проверкой что она не была удалена.\n            // И проверка на соответствие аспекту Aspect.\n            if (damageEvent.target.TryGetID(out int e) \u0026\u0026 \n                _world.IsMatchesMask(a, e))\n            {\n                ref var health = ref a.healths.Get(e);\n                health.points -= damageEvent.points;\n            }\n        }\n    }\n\n    public void Inject(EcsDefaultWorld obj) =\u003e _world = obj;\n}\n```\n``` c#\n// Система которая делает возвратный урон.\npublic class SomeReturnDamageAbilitySystem : IOn\u003cDamageEvent\u003e, IEcsInject\u003cEcsDefaultWorld\u003e\n{\n    class EventAspect : EcsAspect\n    {\n        public EcsPool\u003cDamageEvent\u003e damageEvents = Inc;\n    }\n    class Aspect : EcsAspect\n    {\n        public EcsPool\u003cReturnDamageAbility\u003e returnDamageAbilities = Inc;\n    }\n\n    EcsDefaultWorld _world;\n\n    void IOn\u003cDamageEvent\u003e.ToRun(EcsSpan targetEntities)\n    {\n        var a = _world.GetAspect\u003cAspect\u003e();\n        // Итерируемся по targetEntities.\n        foreach (var ee in targetEntities.Where(out EventAspect ea))\n        {\n            ref var damageEvent = ref ea.damageEvents.Get(ee);\n            if (damageEvent.target.TryGetID(out int targetE) \u0026\u0026\n                damageEvent.source.TryGetID(out int sourceE) \u0026\u0026\n                _world.IsMatchesMask(a, targetE))\n            {\n                ref var returnDamageAbility = ref a.returnDamageAbilities.Get(targetE);\n\n                // Создание события возвратного урона,\n                // которое будет обработано в следующем цикле.\n                int newEE = _world.NewEntity(ea);\n                ref var newDamageEvent = ref ea.damageEvents.Get(newEE);\n                newDamageEvent.target = damageEvent.source;\n                newDamageEvent.source = damageEvent.target;\n                newDamageEvent.points = damageEvent.points * returnDamageAbility.multiplier;\n            }\n        }\n    }\n\n    public void Inject(EcsDefaultWorld obj) =\u003e _world = obj;\n}\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcfapixels%2Fdragonecs-recursivity","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdcfapixels%2Fdragonecs-recursivity","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdcfapixels%2Fdragonecs-recursivity/lists"}