ECS - важные изменения
Обновления. Они могут быть болезненными, но позволяют развиваться проекту и двигают его вперед. Самое время сломать что-нибудь в моем ECS фреймворке.
Изменения
- Название. Теперь фреймворк официально называется
"LeoECS"
. Да, глупо и неоригинально, но нужно было что-то придумать для явного указания в статьях и обсуждениях. - Больше нет событий в фильтрах и реактивности, основанной на них. Да, плохая новость для некоторых пользователей, но это позволило увеличить производительность и уменьшить размер кода.
- Фильтры изменились в сторону использования генериков. Это главное изменение, ломающее обратную совместимость, но оно позволило убрать не только большую часть магии внутри ядра, но так же уменьшить размер кода пользователя:Фильтры поддерживают до 5 элементов в “компонент должен присутствовать обязательно”-ограничениях и до 2 элементов в “компонент обязательно должен отсутствовать”-ограничениях. Если потребуется больше, то можно запросить такой функционал через github-тикет, но лучше переосмыслить архитектуру проекта - возможно с ней что-то не так.
1
2
3
4
5
6
7
8
9
10
11// before
[ ]
EcsFilter _filter1;
[ ]
[ ]
EcsFilter _filter2;
// after
EcsFilter<Component1> _filter1;
EcsFilter<Component1, Component2>.Exclude<Component3> _filter2;
- Компоненты автоматически инжектятся в соответствующие типизированные
ComponentsX
-коллекции фильтра:Components1
,Components2
, и т.д. Тип коллекций выводится из на типов-ограничений фильтра:Если в инжекте компонентов нет необходимости (например, для компонентов-флагов без данных), такие компоненты могут быть помечены атрибутом1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24EcsWorld _world;
EcsFilter<MyComponent1> _filter1;
EcsFilter<MyComponent1, MyComponent2> _filter2;
// EntitiesCount should be used for walk over valid number of enitities in filter.
for (var i = 0; i < _filter1.EntitiesCount; i++) {
// int[] Entities array contains entity ID-s.
int entity = _filter1.Entities[i];
// You can use standard method for getting component.
MyComponent1 component1 = _world.GetComponent<Component1> (entity);
// But better to use new way to do same thing.
// MyComponent1[] Components1 array contains instances from filtered entities.
MyComponent1 component11 = _filter1.Components1[i];
// component1 and component11 are equals - no need to write additional code and waste performance.
}
for (var i = 0; i < _filter2.EntitesCount; i++) {
// We can get first component from filter constraint - Component1 typed.
MyComponent1 component1 = _filter2.Components1[i];
// We can get second component from filter constraint - Component2 typed.
MyComponent2 component2 = _filter2.Components2[i];
}EcsIgnoreInFilter
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19class Component1 { }
[ ]
class Component2 { }
[ ]
class TestSystem : IEcsSystem {
EcsFilter<Component1, Component2> _filter;
public Test() {
for (var i = 0; i < _filter.EntitiesCount; i++) {
// its valid code.
var component1 = _filter.Components1[i];
// its invalid code due to _filter.Components2 is null for memory / performance reasons.
var component2 = _filter.Components2[i];
}
}
} - Внедрение зависимостей. Опциональное и отключенное по умолчанию поведение. Больше никаких
EcsWorld
,EcsFilterInclude
иEcsFilterExclude
атрибутов для разметки. Для активации инжекта используется одинEcsInject
-атрибут на класс системы:В результате будут инициализированы все поля типов1
2
3
4
5
6[ ]
class MySystem : IEcsSystem {
EcsWorld _world;
EcsFilter<MyComponent1> _filter1;
EcsFilter<MyComponent2>.Exclude<Component3> _filter2;
}EcsWorld
иEcsFilter
и все заработает как раньше.
Я надеюсь, что изменения были не настолько ужасными и позволят писать более чистый и производительный код.
Оформить подписку можно здесь: