Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/dcfapixels/dragonecs-recursivity


https://github.com/dcfapixels/dragonecs-recursivity

dragon-ecs dragonecs extension

Last synced: about 2 months ago
JSON representation

Awesome Lists containing this project

README

        




Version
License
Discord
QQ

# Recursivity для [DragonECS](https://github.com/DCFApixels/DragonECS)



Readme Languages:






Русский





English(WIP)



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

> [!WARNING]
> Проект в стадии разработки. API может меняться.

# Оглавление
- [Установка](#установка)
- [Инициализация](#инициализация)
- [Обработка событий](#обработка-событий)

# Установка
Семантика версионирования - [Открыть](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
## Окружение
Обязательные требования:
+ Зависимость: [DragonECS](https://github.com/DCFApixels/DragonECS)
+ Минимальная версия C# 7.3;

Опционально:
+ Игровые движки с C#: Unity, Godot, MonoGame и т.д.

Протестировано:
+ **Unity:** Минимальная версия 2020.1.0;

## Установка для Unity
* ### Unity-модуль
Поддерживается установка в виде Unity-модуля в при помощи добавления git-URL [в PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html) или ручного добавления в `Packages/manifest.json`:
```
https://github.com/DCFApixels/DragonECS-Recursivity.git
```
* ### В виде исходников
Пакет так же может быть добавлен в проект в виде исходников.

# Инициализация
Для обработки событий используется процесс `IOn.ToRun()` где `T` это тип компонента-события. Процесс `IOn.ToRun()` контролирует специальная система, которую необходимо инициализировать в пайплайне.

``` c#
_world = new EcsDefaultWorld();
_pipeline = EcsPipeline.New()
// ...
// Инициализация системы контролирующей процесс IOn.ToRun().
// Аргумент maxLoops устанавливает лимит на количество выполнений за один кадр.
.AddOn(maxLoops: 100)
// Добавление систем обрабатывающих этот процесс
.Add(new SomeDamageSystem())
.Add(new SomeReturnDamageAbilitySystem())
// ...
.Inject(_world)
.BuildAndInit();
```

> За запуск всех систем которые выполняются рекурсивно, включая управляющих процессом `IOn.ToRun()`, ответственна система рекурсии. По умолчанию система рекурсии добавляется на слой `EcsRecursivityConsts.RECURSIVE_LAYER`, а слой `EcsRecursivityConsts.RECURSIVE_LAYER` вставляется после `EcsConsts.BASIC_LAYER`.

# Обработка событий
Системы с `IOn.ToRun()` будут выполняться до тех пор пока в мире остается хотя бы один компонент-событие `T`.

С точки зрения замедления производительности влияние не высоко по нескольким причинам:
* Системы с `IOn.ToRun()` не выполняются вовсе если в мире нет ни одного компонента `T`.
* В инициализации `.AddOn(maxLoops)` Можно выставить лимит(maxLoops) вызова `IOn.ToRun()`, тогда при достижении лимита, оставшиеся события будут обработаны в следующем кадре.

> Имеется защита от бесконечного зацикливания в виде глобального лимита в `100_000` повторений.

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

Используемые в примере компоненты

``` c#
using DCFApixels.DragonECS;
public struct Health : IEcsComponent
{
public float points;
}
public struct DamageEvent : IEcsComponent
{
public entlong source;
public entlong target;
public float points;
}
public struct ReturnDamageAbility : IEcsComponent
{
public float multiplier;
}
```

> Этот пример имеет некоторые проблемы, но как пример достаточно нагляден для понимания работы.

``` c#
// Система которая применяет полученный урон к здоровью.
public class SomeApplyDamageSystem : IOn, IEcsInject
{
class EventAspect : EcsAspect
{
public EcsPool damageEvents = Inc;
}
class Aspect : EcsAspect
{
public EcsPool healths = Inc;
}

EcsDefaultWorld _world;

// targetEntities содержит все сущности с компонентом DamageEvent.
// Сущности из этого же списка в конце цикла будут автоматически отчищены от компонента DamageEvent.
void IOn.ToRun(EcsSpan targetEntities)
{
var a = _world.GetAspect();
// Итерироваться нужно по targetEntities,
// так можно гарантировать что системы будут обрабатывать каждое событие один раз.
foreach (var ee in targetEntities.Where(out EventAspect ea))
{
ref var damageEvent = ref ea.damageEvents.Get(ee);
// Извлечение ID сущности с проверкой что она не была удалена.
// И проверка на соответствие аспекту Aspect.
if (damageEvent.target.TryGetID(out int e) &&
_world.IsMatchesMask(a, e))
{
ref var health = ref a.healths.Get(e);
health.points -= damageEvent.points;
}
}
}

public void Inject(EcsDefaultWorld obj) => _world = obj;
}
```
``` c#
// Система которая делает возвратный урон.
public class SomeReturnDamageAbilitySystem : IOn, IEcsInject
{
class EventAspect : EcsAspect
{
public EcsPool damageEvents = Inc;
}
class Aspect : EcsAspect
{
public EcsPool returnDamageAbilities = Inc;
}

EcsDefaultWorld _world;

void IOn.ToRun(EcsSpan targetEntities)
{
var a = _world.GetAspect();
// Итерируемся по targetEntities.
foreach (var ee in targetEntities.Where(out EventAspect ea))
{
ref var damageEvent = ref ea.damageEvents.Get(ee);
if (damageEvent.target.TryGetID(out int targetE) &&
damageEvent.source.TryGetID(out int sourceE) &&
_world.IsMatchesMask(a, targetE))
{
ref var returnDamageAbility = ref a.returnDamageAbilities.Get(targetE);

// Создание события возвратного урона,
// которое будет обработано в следующем цикле.
int newEE = _world.NewEntity(ea);
ref var newDamageEvent = ref ea.damageEvents.Get(newEE);
newDamageEvent.target = damageEvent.source;
newDamageEvent.source = damageEvent.target;
newDamageEvent.points = damageEvent.points * returnDamageAbility.multiplier;
}
}
}

public void Inject(EcsDefaultWorld obj) => _world = obj;
}
```