mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2026-04-22 01:45:55 +08:00
Compare commits
84 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4f28618277 | ||
|
|
2cca65e37e | ||
|
|
b45e9ea608 | ||
|
|
c3ff8b0b58 | ||
|
|
a37939a620 | ||
|
|
2b50fbdb55 | ||
|
|
acf8c7f89a | ||
|
|
0ecf5e378f | ||
|
|
91d196e1a9 | ||
|
|
0f8d619468 | ||
|
|
211f5a657c | ||
|
|
d3e77026ff | ||
|
|
87c98f708e | ||
|
|
d52c3031f2 | ||
|
|
9deb8aa3a9 | ||
|
|
23cee3e4c7 | ||
|
|
371472e851 | ||
|
|
68d3d2bc4f | ||
|
|
20875d420a | ||
|
|
a71a77989f | ||
|
|
68059e9487 | ||
|
|
8042256c61 | ||
|
|
04ba0a3549 | ||
|
|
1f263726f9 | ||
|
|
f8e35f58c6 | ||
|
|
94aeb54756 | ||
|
|
9d7fcf3d71 | ||
|
|
310c01e3ec | ||
|
|
3127af7371 | ||
|
|
3914fca7f7 | ||
|
|
8647f62335 | ||
|
|
59d2f07425 | ||
|
|
bfac073340 | ||
|
|
15edd9ea54 | ||
|
|
978026e0a1 | ||
|
|
b2dace441e | ||
|
|
120fa9779d | ||
|
|
22e09a7ed2 | ||
|
|
018a0e7d41 | ||
|
|
7a8cbf9f0a | ||
|
|
b01b406e9e | ||
|
|
b1244e42a0 | ||
|
|
1c6edc6e6b | ||
|
|
c347011452 | ||
|
|
0d777fd794 | ||
|
|
975720ecf3 | ||
|
|
8bb2a1cbd8 | ||
|
|
5b9e23f852 | ||
|
|
cfaed5b6d7 | ||
|
|
72729c3222 | ||
|
|
b101f029ce | ||
|
|
3715857f0f | ||
|
|
18db9a24b5 | ||
|
|
492416aba1 | ||
|
|
cc834c4a92 | ||
|
|
015c6be9f0 | ||
|
|
37e692fdda | ||
|
|
2a0cb1085f | ||
|
|
56fed70c15 | ||
|
|
37718d354a | ||
|
|
f0d9a806ff | ||
|
|
b6e43f98fb | ||
|
|
b6953c919c | ||
|
|
25a987ea58 | ||
|
|
fe660f5500 | ||
|
|
7422d47b35 | ||
|
|
ee65362a1b | ||
|
|
01985d7ffb | ||
|
|
e13e332aab | ||
|
|
856682b28e | ||
|
|
8472280d4b | ||
|
|
b686e4ab5f | ||
|
|
c30d4ec5f0 | ||
|
|
14bf846135 | ||
|
|
821fd37d85 | ||
|
|
bc5278c021 | ||
|
|
48014f3b73 | ||
|
|
b1192d32d9 | ||
|
|
47d8e100a1 | ||
|
|
ec26a1623a | ||
|
|
b6d3da46f7 | ||
|
|
5e11f255e6 | ||
|
|
3c029036c8 | ||
|
|
7da652e78c |
@ -10,7 +10,7 @@
|
||||
<RootNamespace>DCFApixels.DragonECS</RootNamespace>
|
||||
|
||||
<Title>DragonECS</Title>
|
||||
<Version>0.9.21</Version>
|
||||
<Version>1.0.1</Version>
|
||||
<Authors>DCFApixels</Authors>
|
||||
<Description>ECS Framework for Game Engines with C# and .Net Platform</Description>
|
||||
<Copyright>DCFApixels</Copyright>
|
||||
|
||||
156
README-RU.md
156
README-RU.md
@ -20,33 +20,29 @@
|
||||
<tr>
|
||||
<td nowrap width="100">
|
||||
<a href="README-RU.md">
|
||||
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"></br>
|
||||
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"><br/>
|
||||
<span>Русский</span>
|
||||
</a>
|
||||
</td>
|
||||
<td nowrap width="100">
|
||||
<a href="https://github.com/DCFApixels/DragonECS">
|
||||
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"></br>
|
||||
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"><br/>
|
||||
<span>English</span>
|
||||
</a>
|
||||
</td>
|
||||
<td nowrap width="100">
|
||||
<a href="README-ZH.md">
|
||||
<img src="https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927"></br>
|
||||
<img src="https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927"><br/>
|
||||
<span>中文</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</br>
|
||||
DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system) фреймворк, нацеленный на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Разработан на чистом C#, без зависимостей и генерации кода. Вдохновлен [LeoEcs Lite](https://github.com/Leopotam/ecslite).
|
||||
|
||||
DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system) фреймворк нацеленный на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Разработан на чистом C#, без зависимостей и генерации кода. Вдохновлен [LeoEcs Lite](https://github.com/Leopotam/ecslite).
|
||||
|
||||
> [!WARNING]
|
||||
> Проект предрелизной версии, поэтому API может меняться. В ветке main актуальная и рабочая версия.
|
||||
>
|
||||
> Если есть неясные моменты, вопросы можно задать тут [Обратная связь](#обратная-связь)
|
||||
> [!Note]
|
||||
> Есть вопросы или хотите быть в курсе разработки? Присоединяйтесь к нашему комьюнити! -> [Обратная связь](#обратная-связь)
|
||||
|
||||
## Оглавление
|
||||
- [Установка](#установка)
|
||||
@ -77,24 +73,24 @@ DragonECS - это [ECS](https://en.wikipedia.org/wiki/Entity_component_system)
|
||||
- [Расширение фреймворка](#расширение-фреймворка)
|
||||
- [Компоненты мира](#компоненты-мира)
|
||||
- [Конфиги](#конфиги)
|
||||
- [Проекты на DragonECS](#Проекты-на-DragonECS)
|
||||
- [Проекты на DragonECS](#проекты-на-dragonecs)
|
||||
- [FAQ](#faq)
|
||||
- [Обратная связь](#обратная-связь)
|
||||
- [Лицензия](#лицензия)
|
||||
|
||||
</br>
|
||||
|
||||
# Установка
|
||||
Семантика версионирования - [Открыть](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
|
||||
Семантика версионирования - [Открыть](https://gist.github.com/DCFApixels/af79284955bf40e9476cdcac79d7b098#file-dcfapixels_versioning-md)
|
||||
## Окружение
|
||||
Обязательные требования:
|
||||
+ Минимальная версия C# 7.3;
|
||||
+ Минимальная версия C#: 7.3.
|
||||
|
||||
Опционально:
|
||||
+ Поддержка NativeAOT
|
||||
Поддерживает:
|
||||
+ NativeAOT;
|
||||
+ Игровые движки с C#: Unity, Godot, MonoGame и т.д.
|
||||
|
||||
Протестировано:
|
||||
+ **Unity:** Минимальная версия 2020.3.0;
|
||||
Протестировано на:
|
||||
* **Unity:** Минимальная версия 2021.2.0.
|
||||
|
||||
## Установка для Unity
|
||||
> Рекомендуется так же установить расширение [Интеграция с движком Unity](https://github.com/DCFApixels/DragonECS-Unity)
|
||||
@ -109,9 +105,9 @@ https://github.com/DCFApixels/DragonECS.git
|
||||
```
|
||||
|
||||
* ### В виде исходников
|
||||
Можно так же напрямую скопировать в проект исходники фреймворка.
|
||||
Можно так же напрямую скопировать исходники фреймворка в проект.
|
||||
|
||||
|
||||
</br>
|
||||
|
||||
# Расширения
|
||||
* Интеграции:
|
||||
@ -125,16 +121,17 @@ https://github.com/DCFApixels/DragonECS.git
|
||||
* [Графы](https://github.com/DCFApixels/DragonECS-Graphs)
|
||||
* Утилиты:
|
||||
* [Упрощенный синтаксис](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
|
||||
* [EcsRefPool](https://gist.github.com/DCFApixels/73e392ccabdd98b3d4a517017d8a3f22)
|
||||
* [Таймеры](https://gist.github.com/DCFApixels/71a416275660c465ece76242290400df)
|
||||
* [Однокадровые компоненты](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
|
||||
* [Шаблоны кода IDE](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) и [для Unity](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
|
||||
> *Твое расширение? Если разрабатываешь расширение для DragonECS, пиши [сюда](#обратная-связь).
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
# Основные концепции
|
||||
## Entity
|
||||
**Сущности** - это то к чему крепятся данные. Для доступа к сущности используются идентификаторы двух типов:
|
||||
**Сущности** - это то, к чему крепятся данные. Для доступа к сущности используются идентификаторы двух типов:
|
||||
* `int` - однократный идентификатор, применяется в пределах одного тика. Не рекомендуется использовать для хранения;
|
||||
* `entlong` - долговременный идентификатор, содержит метку поколения, что делает его уникальным навсегда. Подходит для хранения.
|
||||
```c#
|
||||
@ -253,7 +250,7 @@ public struct Health : IEcsComponent
|
||||
|
||||
</details>
|
||||
|
||||
</br>
|
||||
|
||||
|
||||
# Концепции фреймворка
|
||||
## Пайплайн
|
||||
@ -284,7 +281,7 @@ class SomeSystem : IEcsRun, IEcsPipelineMember
|
||||
```
|
||||
> Для одновременного построения и инициализации есть метод Builder.BuildAndInit();
|
||||
### Внедрение зависимостей
|
||||
Фреймворк реализует внедрение зависимостей для систем. это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в Builder.
|
||||
Фреймворк реализует внедрение зависимостей для систем. Это процесс, который запускается вместе с инициализацией пайплайна и внедряет данные, переданные в Builder.
|
||||
|
||||
```c#
|
||||
class SomeDataA { /* ... */ }
|
||||
@ -346,26 +343,29 @@ EcsPipeline pipeline = EcsPipeline.New()
|
||||
```
|
||||
|
||||
### Сортировка
|
||||
Дла управления расположением систем в пайплайне, вне зависимости от порядка добавления, есть 2 способа: Слои и Порядок сортировки.
|
||||
Для управления расположением систем в пайплайне, вне зависимости от порядка добавления, есть 2 способа: Слои и Порядок сортировки.
|
||||
#### Слои
|
||||
Слой определяет место в пайплайне для вставки систем. Например, если необходимо чтобы система была вставлена в конце пайплайна, эту систему можно добавить в слой `EcsConsts.END_LAYER`.
|
||||
```c#
|
||||
const string SOME_LAYER = nameof(SOME_LAYER);
|
||||
const string SOME_LAYER_2 = nameof(SOME_LAYER_2);
|
||||
EcsPipeline pipeline = EcsPipeline.New()
|
||||
// ...
|
||||
// Вставляет новый слой перед конечным слоем EcsConsts.END_LAYER
|
||||
.Layers.Insert(EcsConsts.END_LAYER, SOME_LAYER)
|
||||
.Layers.Add(SOME_LAYER).Before(EcsConsts.END_LAYER)
|
||||
// Вставляет SOME_LAYER_2 слой в позицию после EcsConsts.BASIC_LAYER
|
||||
.Layers.Add(SOME_LAYER).After(EcsConsts.BASIC_LAYER)
|
||||
// Система SomeSystem будет вставлена в слой SOME_LAYER
|
||||
.Add(New SomeSystem(), SOME_LAYER)
|
||||
// ...
|
||||
.BuildAndInit();
|
||||
```
|
||||
Встроенные слои расположены в следующем порядке:
|
||||
* `EcsConst.PRE_BEGIN_LAYER`
|
||||
* `EcsConst.BEGIN_LAYER`
|
||||
* `EcsConst.BASIC_LAYER` (По умолчанию системы добавляются сюда)
|
||||
* `EcsConst.END_LAYER`
|
||||
* `EcsConst.POST_END_LAYER`
|
||||
* `EcsConsts.PRE_BEGIN_LAYER`
|
||||
* `EcsConsts.BEGIN_LAYER`
|
||||
* `EcsConsts.BASIC_LAYER` (По умолчанию системы добавляются сюда)
|
||||
* `EcsConsts.END_LAYER`
|
||||
* `EcsConsts.POST_END_LAYER`
|
||||
#### Порядок сортировки
|
||||
Для сортировки систем в рамках слоя используется int значение порядка сортировки. По умолчанию системы добавляются с `sortOrder = 0`.
|
||||
```c#
|
||||
@ -420,10 +420,10 @@ _pipeline = EcsPipeline.New()
|
||||
.BuildAndInit();
|
||||
|
||||
// Запуск раннера если раннер был добавлен.
|
||||
_pipeline.GetRunner<IDoSomethingProcess>.Do()
|
||||
_pipeline.GetRunner<IDoSomethingProcess>().Do();
|
||||
|
||||
// Или если раннер не был добавлен(Вызов GetRunnerInstance так же добавит раннер в пайплайн).
|
||||
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
|
||||
// Или если раннер не был добавлен (вызов GetRunnerInstance также добавит раннер в пайплайн).
|
||||
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>().Do();
|
||||
```
|
||||
|
||||
<details>
|
||||
@ -541,8 +541,8 @@ poses.Del(entityID);
|
||||
Обычно маски не используются в чистом виде, а являются частью [Аспекта](#Аспект).
|
||||
|
||||
```c#
|
||||
// Создание маски с улосвием наличия компонентов SomeCmp1 и SomeCmp2,
|
||||
// и с улосвием отсутсвия компонента SomeCmp3.
|
||||
// Создание маски с условием наличия компонентов SomeCmp1 и SomeCmp2,
|
||||
// и с условием отсутствия компонента SomeCmp3.
|
||||
EcsMask mask = EcsMask.New(_world)
|
||||
// Inc - Условие наличия компонента.
|
||||
.Inc<SomeCmp1>()
|
||||
@ -612,9 +612,9 @@ class Aspect : EcsAspect
|
||||
public EcsPool<Velocity> velocities;
|
||||
protected override void Init(Builder b)
|
||||
{
|
||||
poses = b.IncludePool<Pose>();
|
||||
velocities = b.IncludePool<Velocity>();
|
||||
b.ExcludePool<FreezedTag>();
|
||||
poses = b.Inc<Pose>();
|
||||
velocities = b.Inc<Velocity>();
|
||||
b.Exc<FreezedTag>();
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -638,8 +638,8 @@ class Aspect : EcsAspect
|
||||
otherAspect1 = b.Combine<OtherAspect1>(1);
|
||||
// Хотя для OtherAspect1 метод Combine был вызван раньше, сначала будет скомбинирован с OtherAspect2, так как по умолчанию order = 0.
|
||||
otherAspect2 = b.Combine<OtherAspect2>();
|
||||
// Если в OtherAspect1 или в OtherAspect2 было условие b.Exclude<Pose>() тут оно будет заменено на b.Include<Pose>().
|
||||
poses = b.Include<Pose>();
|
||||
// Если в OtherAspect1 или в OtherAspect2 было условие b.Exc<Pose>() тут оно будет заменено на b.Inc<Pose>().
|
||||
poses = b.Inc<Pose>();
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -703,7 +703,7 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
|
||||
``` c#
|
||||
// Запрос Where возвращает сущности в виде EcsSpan.
|
||||
EcsSpan es = _world.Where(out Aspect a);
|
||||
// Итерироваться можно по foreach и for.
|
||||
// Итерироваться можно с помощью `foreach` и `for`.
|
||||
foreach (var e in es)
|
||||
{
|
||||
// ...
|
||||
@ -736,7 +736,7 @@ group.Remove(entityID);
|
||||
``` c#
|
||||
// Запрос WhereToGroup возвращает сущности в виде группы только для чтения EcsReadonlyGroup.
|
||||
EcsReadonlyGroup group = _world.WhereToGroup(out Aspect a);
|
||||
// Итерироваться можно по foreach и for.
|
||||
// Итерироваться можно с помощью `foreach` и `for`.
|
||||
foreach (var e in group)
|
||||
{
|
||||
// ...
|
||||
@ -913,6 +913,40 @@ var tags = typeMeta.Tags; // [MetaTags]
|
||||
```
|
||||
> Для автоматической генерации уникальных идентификаторов MetaID есть метод `MetaID.GenerateNewUniqueID()` и [Браузерный генератор](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/)
|
||||
|
||||
<details>
|
||||
<summary>[MetaProxy]</summary>
|
||||
|
||||
`[MetaProxy(typeof(MetaProxyType))]` атрибут для гибкой настройки метаданных. Он позволяет обходить ограничения обычных атрибутов (например, невозможность передавать в них неконстантные данные) и задавать метаданные программно. Кроме того, метаданные, полученные через `MetaProxy`, наследуются.
|
||||
|
||||
API напоминает `[DebuggerTypeProxy]`, но вместо экземпляра объекта принимает тип `Type`. А класс для `MetaProxy` должен наследовать `MetaProxyBase`.
|
||||
|
||||
Пример использования:
|
||||
|
||||
``` c#
|
||||
// Применение атрибута
|
||||
[MetaProxy(typeof(UnityComponent<>.MetaProxy))]
|
||||
// В данном примере UnityComponent оборачивает компонент Unity
|
||||
public struct UnityComponent<T> where T : Component
|
||||
{
|
||||
// ...
|
||||
|
||||
// Реализация MetaProxyBase. MetaProxy копирует метаданные оборачиваемого типа,
|
||||
// чтобы в инспекторе Unity отображалась его мета.
|
||||
private class MetaProxy : MetaProxyBase
|
||||
{
|
||||
protected TypeMeta Meta = typeof(T).GetMeta();
|
||||
public override string Name { get { return Meta?.Name; } }
|
||||
public override MetaColor? Color { get { return Meta != null && Meta.IsCustomColor ? Meta.Color : null; } }
|
||||
public override MetaGroup Group { get { return Meta?.Group; } }
|
||||
public override MetaDescription Description { get { return Meta?.Description; } }
|
||||
public override IEnumerable<string> Tags { get { return Meta?.Tags; } }
|
||||
public MetaProxy(Type type) : base(type) { }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## EcsDebug
|
||||
Вспомогательный тип с набором методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы - это посредники между EcsDebug и инструментами отладки среды. Такая реализация позволяет не изменяя отладочный код, менять его поведение или переносить проект в другие среды, достаточно только реализовать соответствующий Debug-сервис.
|
||||
|
||||
@ -962,8 +996,7 @@ using (_marker.Auto())
|
||||
+ `DRAGONECS_STABILITY_MODE` - включает опускаемые в релизном билде проверки.
|
||||
+ `DRAGONECS_DISABLE_CATH_EXCEPTIONS` - Выключает поведение по умолчанию по обработке исключений. По умолчанию фреймворк будет ловить исключения с выводом информации из исключений через EcsDebug и продолжать работу.
|
||||
+ `REFLECTION_DISABLED` - Полностью ограничивает работу фреймворка с Reflection.
|
||||
+ `DISABLE_DEBUG` - Для среды где не поддерживается ручное отключение DEBUG, например Unity.
|
||||
+ `ENABLE_DUMMY_SPAN` - На случай если в среде не поддерживаются Span типы, включает его замену.
|
||||
+ `DISABLE_DEBUG` - Для среды, где не поддерживается ручное отключение DEBUG, например Unity.
|
||||
|
||||
</br>
|
||||
|
||||
@ -977,7 +1010,7 @@ using (_marker.Auto())
|
||||
var configs = new ConfigContainer()
|
||||
.Set(new EcsWorldConfig(entitiesCapacity: 2000, poolsCapacity: 2000)
|
||||
.Set(new SomeDataA(/* ... */))
|
||||
.Set(new SomeDataB(/* ... */)));
|
||||
.Set(new SomeDataB(/* ... */));
|
||||
EcsDefaultWorld _world = new EcsDefaultWorld(configs);
|
||||
// ...
|
||||
var _someDataA = _world.Configs.Get<SomeDataA>();
|
||||
@ -1072,7 +1105,7 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
<td align="center">
|
||||
<a href="https://github.com/DCFApixels/3D-Platformer-DragonECS-Demo">
|
||||
3D Platformer (Example)
|
||||
<img src="https://github.com/user-attachments/assets/c593dba7-eeaa-4706-a043-946f132f3f83" alt="screenshot">
|
||||
<img src="https://github.com/user-attachments/assets/6aba814d-a70b-432f-a905-84d1b6872581" />
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
@ -1082,6 +1115,21 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr></tr>
|
||||
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Evileptic/Arkanoid">
|
||||
Arkanoid
|
||||
<img src="https://github.com/user-attachments/assets/bbdb4a7f-2f59-4a3a-ab51-a3e4fe0ad35e" alt="screenshot">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
_____________
|
||||
<img tabindex="-1" src="https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt="screenshot">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Опубликованные проекты:
|
||||
@ -1123,15 +1171,8 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
|
||||
# FAQ
|
||||
|
||||
## 'ReadOnlySpan<>' could not be found
|
||||
В версии Unity 2020.1.х в консоли может выпадать ошибка:
|
||||
```
|
||||
The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing a using directive or an assembly reference?)
|
||||
```
|
||||
Чтобы починить добавьте директиву `ENABLE_DUMMY_SPAN` в `Project Settings/Player/Other Settings/Scripting Define Symbols`.
|
||||
|
||||
## Как Выключать/Включать системы?
|
||||
Напрямую - никак. </br>
|
||||
Напрямую — никак.
|
||||
Обычно потребность выключить/включить систему появляется когда поменялось общее состояние игры, это может так же значить что нужно переключить сразу группу систем, все это в совокупности можно рассматривать как изменения процессов. Есть 2 решения:</br>
|
||||
+ Если изменения процесса глобальные, то создать новый `EcsPipeline` и в цикле обновления движка запускать соответствующий пайплайн.
|
||||
+ Разделить `IEcsRun` на несколько процессов и в цикле обновления движка запускать соответствующий процесс. Для этого создайте новый интерфейс процесса, раннер для запуска этого интерфейса и получайте раннер через `EcsPipeline.GetRunner<T>()`.
|
||||
@ -1142,6 +1183,11 @@ The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing
|
||||
+ Discord (RU-EN) [https://discord.gg/kqmJjExuCf](https://discord.gg/kqmJjExuCf)
|
||||
+ QQ (中文) [949562781](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=IbDcH43vhfArb30luGMP1TMXB3GCHzxm&authKey=s%2FJfqvv46PswFq68irnGhkLrMR6y9tf%2FUn2mogYizSOGiS%2BmB%2B8Ar9I%2Fnr%2Bs4oS%2B&noverify=0&group_code=949562781)
|
||||
|
||||
</br>
|
||||
|
||||
# Лицензия
|
||||
MIT Лицензия: [Открыть](LICENSE.md)
|
||||
|
||||
</br></br></br>
|
||||
</br></br></br>
|
||||
</br></br></br>
|
||||
|
||||
227
README-ZH.md
227
README-ZH.md
@ -20,19 +20,19 @@
|
||||
<tr>
|
||||
<td nowrap width="100">
|
||||
<a href="README-RU.md">
|
||||
<img src="https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755"></br>
|
||||
<img src="https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755"><br/>
|
||||
<span>Русский</span>
|
||||
</a>
|
||||
</td>
|
||||
<td nowrap width="100">
|
||||
<a href="https://github.com/DCFApixels/DragonECS">
|
||||
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"></br>
|
||||
<img src="https://github.com/user-attachments/assets/30528cb5-f38e-49f0-b23e-d001844ae930"><br/>
|
||||
<span>English</span>
|
||||
</a>
|
||||
</td>
|
||||
<td nowrap width="100">
|
||||
<a href="README-ZH.md">
|
||||
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"></br>
|
||||
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"><br/>
|
||||
<span>中文</span>
|
||||
</a>
|
||||
</td>
|
||||
@ -43,15 +43,12 @@
|
||||
|
||||
DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框架。专注于提升便利性、模块性、可扩展性和动态实体修改性能。 用纯C#开发的,没有依赖和代码生成。灵感来自于[LeoEcs Lite](https://github.com/Leopotam/ecslite)。
|
||||
|
||||
> [!WARNING]
|
||||
> 该框架是预发布版本,因此 API 可能会有变化。在 `main` 分支中是当前的工作版本。
|
||||
>
|
||||
> 最新版本的 README 是[俄文版](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md)。
|
||||
>
|
||||
> 如果有不清楚的地方,可以在这里提问 [反馈](#反馈)。
|
||||
> [!Note]
|
||||
> 有疑问或想了解开发进度?欢迎加入我们的社区! -> [Feedback](#Feedback)
|
||||
|
||||
## 目录
|
||||
- [安装](#安装)
|
||||
- [扩展](#扩展)
|
||||
- [基础概念](#基础概念)
|
||||
- [实体](#实体)
|
||||
- [组件](#组件)
|
||||
@ -71,7 +68,7 @@ DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框
|
||||
- [集合](#集合)
|
||||
- [ECS入口](#ECS入口)
|
||||
- [Debug](#debug)
|
||||
- [元属性](#元属性)
|
||||
- [Meta Attributes](#meta-attributes)
|
||||
- [EcsDebug](#ecsdebug)
|
||||
- [性能分析](#性能分析)
|
||||
- [Define Symbols](#define-symbols)
|
||||
@ -79,42 +76,64 @@ DragonECS 是一个[实体组件系统](https://www.imooc.com/article/331544)框
|
||||
- [世界组件](#世界组件)
|
||||
- [配置](#配置)
|
||||
- [使用DragonECS的项目](#使用dragonecs的项目)
|
||||
- [扩展](#扩展)
|
||||
- [FAQ](#faq)
|
||||
- [反馈](#反馈)
|
||||
- [License](#license)
|
||||
|
||||
</br>
|
||||
|
||||
# 安装
|
||||
版本的语义 [[打开]](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
|
||||
版本的语义 [[打开]](https://gist.github.com/DCFApixels/af79284955bf40e9476cdcac79d7b098#file-dcfapixels_versioning-md)
|
||||
## 环境
|
||||
必备要求:
|
||||
+ C# 7.3 的最低版本;
|
||||
* 最低 C# 版本:7.3。
|
||||
|
||||
可选要求:
|
||||
+ 支持NativeAOT;
|
||||
+ 使用 C# 的游戏引擎:Unity、Godot、MonoGame等。
|
||||
支持:
|
||||
* NativeAOT;
|
||||
* 使用 C# 的游戏引擎:Unity、Godot、MonoGame 等。
|
||||
|
||||
已测试:
|
||||
+ **Unity:** 最低版本 2020.1.0;
|
||||
已测试:
|
||||
* **Unity:** 最低版本 2021.2.0。
|
||||
|
||||
## 为 Unity 安装
|
||||
> 还建议安装[Unity 引擎集成](https://github.com/DCFApixels/DragonECS-Unity)扩展。
|
||||
* ### Unity-软件包
|
||||
支持以Unity软件包的形式安装。可以通过[git-url添加到PackageManager](https://docs.unity3d.com/cn/2023.2/Manual/upm-ui-giturl.html)或手动添加到`Packages/manifest.json`:
|
||||
* ### Unity 软件包
|
||||
支持以 Unity 软件包的形式安装。可以通过在 PackageManager 中添加 Git URL(参见 [教程](https://docs.unity3d.com/cn/2023.2/Manual/upm-ui-giturl.html))或手动将条目添加到 `Packages/manifest.json`:
|
||||
```
|
||||
https://github.com/DCFApixels/DragonECS.git
|
||||
```
|
||||
或者在 `Packages/manifest.json` 中添加包条目:
|
||||
```
|
||||
"com.dcfa_pixels.dragonecs": "https://github.com/DCFApixels/DragonECS.git",
|
||||
```
|
||||
|
||||
* ### 作为源代码
|
||||
框架也可以通过复制源代码添加到项目中。
|
||||
</br>
|
||||
也可以通过将框架的源代码直接复制到项目中来使用该框架。
|
||||
|
||||
# 扩展
|
||||
* Integrations:
|
||||
* [Unity](https://github.com/DCFApixels/DragonECS-Unity)
|
||||
* [Godot](https://gitlab.com/InauniusOwn/Libraries/DraGodot)
|
||||
* Packages:
|
||||
* [自动依赖注入](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||
* [经典C#多线程](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||
* [Recursivity](https://github.com/DCFApixels/DragonECS-Recursivity)
|
||||
* [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)
|
||||
* [Graphs](https://github.com/DCFApixels/DragonECS-Graphs)
|
||||
* Utilities:
|
||||
* [简单语法](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
|
||||
* [EcsRefPool](https://gist.github.com/DCFApixels/73e392ccabdd98b3d4a517017d8a3f22)
|
||||
* [计时器](https://gist.github.com/DCFApixels/71a416275660c465ece76242290400df)
|
||||
* [单帧组件](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
|
||||
* [IDE代码模板](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) 和 [Unity 模板](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
|
||||
> *你的扩展?如果你正在开发 DragonECS 的扩展,可以[在此处发布](#反馈).
|
||||
|
||||
|
||||
# 基础概念
|
||||
## 实体
|
||||
**实体**是附加数据的基础。它们以标识符的形式实现,有两种类型:
|
||||
* `int` - 是在单个更新中使用的一次性标识符。不建议存储`int`标识符,而应使用 `entlong`;
|
||||
* `entlong` - 是一个长期标识符,包含一整套用于明确识别的信息;
|
||||
**实体**是附加数据的基础。有两种用于引用实体的标识符:
|
||||
* `int` - 短期标识符,仅在单次更新(tick)内有效。不建议用于长期存储;
|
||||
* `entlong` - 长期标识符,包含一个世代标记(generation tag),使其在实体的整个生命周期内保持唯一。适合长期保存和存储使用;
|
||||
```c#
|
||||
// 在世界中创建一个新实体。
|
||||
int entityID = _world.NewEntity();
|
||||
@ -251,6 +270,12 @@ class SomeSystem : IEcsInject<SomeDataA>, IEcsRun
|
||||
}
|
||||
```
|
||||
|
||||
静态标记的含义:
|
||||
* `Inc` — 组件必须存在(包含条件)并缓存该池。
|
||||
* `Exc` — 组件不得存在(排除条件)并缓存该池。
|
||||
* `Opt` — 组件可以存在,但不影响过滤(仅缓存池以便访问)。
|
||||
* `Any` — 至少要存在被 `Any` 标记的组件之一;同时缓存该池。
|
||||
|
||||
### 模块
|
||||
实现一个共同特性的系统组可以组合成模块,模块也可以简单地添加到管线中。
|
||||
```c#
|
||||
@ -283,18 +308,20 @@ const string SOME_LAYER = nameof(SOME_LAYER);
|
||||
EcsPipeline pipeline = EcsPipeline.New()
|
||||
// ...
|
||||
// 在最终的 EcsConsts.END_LAYER 层前面插入一个新 SOME_LAYER 层。
|
||||
.Layers.Insert(EcsConsts.END_LAYER, SOME_LAYER)
|
||||
.Layers.Add(SOME_LAYER).Before(EcsConsts.END_LAYER)
|
||||
// 将 SOME_LAYER_2 层级入到 EcsConsts.BASIC_LAYER 之后的位置
|
||||
.Layers.Add(SOME_LAYER).After(EcsConsts.BASIC_LAYER)
|
||||
// SomeSystem 系统将插入 SAME_LAYER 层。
|
||||
.Add(New SomeSystem(), SOME_LAYER)
|
||||
// ...
|
||||
.BuildAndInit();
|
||||
```
|
||||
嵌入层按以下顺序排列:
|
||||
* `EcsConst.PRE_BEGIN_LAYER`
|
||||
* `EcsConst.BEGIN_LAYER`
|
||||
* `EcsConst.BASIC_LAYER`(默认情况下,系统添加到此层)
|
||||
* `EcsConst.END_LAYER`
|
||||
* `EcsConst.POST_END_LAYER`
|
||||
* `EcsConsts.PRE_BEGIN_LAYER`
|
||||
* `EcsConsts.BEGIN_LAYER`
|
||||
* `EcsConsts.BASIC_LAYER`(默认情况下,系统添加到此层)
|
||||
* `EcsConsts.END_LAYER`
|
||||
* `EcsConsts.POST_END_LAYER`
|
||||
#### 排序顺序
|
||||
在同一层内,可以使用 `int` 类型的排序值来排序系统。默认情况下,系统的排序值为 `sortOrder = 0`。
|
||||
```c#
|
||||
@ -322,7 +349,7 @@ EcsPipeline pipeline = EcsPipeline.New()
|
||||
<details>
|
||||
<summary>用户流程</summary>
|
||||
|
||||
Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от `EcsRunner<TInterface>`. Пример:
|
||||
要添加新的流程,请创建一个继承自 `IEcsProcess` 的接口,并为其创建一个 Runner。Runner 是一个实现该流程接口并继承自 `EcsRunner<TInterface>` 的类。示例:
|
||||
``` c#
|
||||
// 流程接口。
|
||||
interface IDoSomethingProcess : IEcsProcess
|
||||
@ -347,10 +374,10 @@ _pipeline = EcsPipeline.New()
|
||||
.BuildAndInit();
|
||||
|
||||
// 如果启动器已经添加,运行它。
|
||||
_pipeline.GetRunner<IDoSomethingProcess>.Do()
|
||||
_pipeline.GetRunner<IDoSomethingProcess>().Do();
|
||||
|
||||
// 如果启动器尚未添加,使用 GetRunnerInstance 将其添加并运行
|
||||
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
|
||||
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>().Do();
|
||||
```
|
||||
|
||||
<details>
|
||||
@ -437,7 +464,7 @@ poses.Del(entityID);
|
||||
> 可以实现用户池。稍后将介绍这一功能。
|
||||
|
||||
## 掩码
|
||||
用于根据组件的存在与否来过滤实体。
|
||||
用于根据组件的存在与否来过滤实体。通常掩码不单独使用,而是作为 `EcsAspect` 的一部分,用于查询时过滤实体。
|
||||
``` c#
|
||||
// 创建一个掩码,检查实体是否具有组件
|
||||
// SomeCmp1 和 SomeCmp2,但没有组件 SomeCmp3。
|
||||
@ -472,7 +499,11 @@ EcsMask mask = _staticMask.ToMask(_world);
|
||||
</details>
|
||||
|
||||
## 方面
|
||||
这些是继承自 EcsAspect 的用户类,用于与实体进行交互。方面同时充当池的缓存和实体组件的过滤掩码。可以把方面视为系统处理哪些实体的描述。
|
||||
方面是继承自 `EcsAspect` 的用户自定义类,用于描述系统要处理的一组组件。方面同时承担两个职责:
|
||||
- 掩码 — 初始化并保存一个 `EcsMask`,使其可在查询中用于过滤实体。
|
||||
- 池缓存 — 提供对组件池的快速访问。
|
||||
|
||||
简而言之,方面是描述“我处理哪些实体以及如何访问它们的组件”的便捷方式。
|
||||
|
||||
简化语法:
|
||||
``` c#
|
||||
@ -503,9 +534,9 @@ class Aspect : EcsAspect
|
||||
public EcsPool<Velocity> velocities;
|
||||
protected override void Init(Builder b)
|
||||
{
|
||||
poses = b.Include<Pose>();
|
||||
velocities = b.Include<Velocity>();
|
||||
b.Exclude<FreezedTag>();
|
||||
poses = b.Inc<Pose>();
|
||||
velocities = b.Inc<Velocity>();
|
||||
b.Exc<FreezedTag>();
|
||||
}
|
||||
}
|
||||
```
|
||||
@ -529,13 +560,13 @@ class Aspect : EcsAspect
|
||||
otherAspect1 = b.Combine<OtherAspect1>(1);
|
||||
// 即使对 OtherAspect1 调用 Combine 方法更早,Aspect 会首先与 OtherAspect2 进行组合,因为默认情况下 order = 0。
|
||||
otherAspect2 = b.Combine<OtherAspect2>();
|
||||
// 如果 OtherAspect1 或 OtherAspect2 中有 b.Exclude<Pose>() 的限制条件,这里将被替换为 b.Include<Pose>()。
|
||||
poses = b.Include<Pose>();
|
||||
// 如果 OtherAspect1 或 OtherAspect2 中有 b.Exc<Pose>() 的限制条件,这里将被替换为 b.Inc<Pose>().
|
||||
poses = b.Inc<Pose>();
|
||||
}
|
||||
}
|
||||
```
|
||||
如果组合的方面存在冲突的限制条件,则新的限制条件将替换先前添加的限制条件。根方面的限制条件始终会替换添加的方面中的限制条件。限制条件组合的视觉示例:
|
||||
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | разрешение конфликтных ограничений|
|
||||
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | 冲突限制的解析 |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- |:--- |
|
||||
| OtherAspect2 | :heavy_check_mark: | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | |
|
||||
| OtherAspect1 | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_minus_sign: | 对于 `cmp2` 将选择 :heavy_check_mark: |
|
||||
@ -765,7 +796,7 @@ public class EcsRoot
|
||||
|
||||
# Debug
|
||||
该框架提供了额外的调试和日志记录工具,不依赖于环境此外,许多类型都有自己的 DebuggerProxy,以便在 IDE 中更详细地显示信息。
|
||||
## 元属性
|
||||
## Meta Attributes
|
||||
默认情况下,元属性没有用处,在与引擎集成时用于指定在调试工具和编辑器中的显示方式。还可以用于生成自动文档。
|
||||
``` c#
|
||||
using DCFApixels.DragonECS;
|
||||
@ -804,6 +835,40 @@ var tags = typeMeta.Tags; // [MetaTags]
|
||||
```
|
||||
> 为了自动生成唯一的标识符 MetaID,可以使用 `MetaID.GenerateNewUniqueID()` 方法和 [浏览器生成器](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/)。
|
||||
|
||||
<details>
|
||||
<summary>MetaProxy</summary>
|
||||
|
||||
`[MetaProxy(typeof(MetaProxyType))]` 特性用于灵活配置元数据。它允许绕过常规特性的限制(例如无法向其传递非常量数据),并以编程方式设置元数据。此外,通过 `MetaProxy` 获得的元数据是可继承的。
|
||||
|
||||
该 API 类似于 `[DebuggerTypeProxy]`,但不同之处在于它接受的是 `Type` 类型,而不是对象实例。`MetaProxy` 的类必须继承自 `MetaProxyBase`。
|
||||
|
||||
使用示例:
|
||||
|
||||
``` c#
|
||||
// 应用特性
|
||||
[MetaProxy(typeof(UnityComponent<>.MetaProxy))]
|
||||
// 在此示例中,UnityComponent 包装了一个 Unity 组件
|
||||
public struct UnityComponent<T> where T : Component
|
||||
{
|
||||
// ...
|
||||
|
||||
// MetaProxyBase 的实现。MetaProxy 复制被包装类型的元数据,
|
||||
// 以便在 Unity 检查器中显示其元数据。
|
||||
private class MetaProxy : MetaProxyBase
|
||||
{
|
||||
protected TypeMeta Meta = typeof(T).GetMeta();
|
||||
public override string Name { get { return Meta?.Name; } }
|
||||
public override MetaColor? Color { get { return Meta != null && Meta.IsCustomColor ? Meta.Color : null; } }
|
||||
public override MetaGroup Group { get { return Meta?.Group; } }
|
||||
public override MetaDescription Description { get { return Meta?.Description; } }
|
||||
public override IEnumerable<string> Tags { get { return Meta?.Tags; } }
|
||||
public MetaProxy(Type type) : base(type) { }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## EcsDebug
|
||||
具有调试和日志记录方法集. 实现为一个静态类,调用 DebugService 的方法. DebugService 是环境调试系统与 EcsDebug 之间的中介. 这使得可以将项目移植到其他引擎上,而无需修改项目的调试代码,只需要实现特定的 DebugService 即可。
|
||||
|
||||
@ -963,7 +1028,7 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
<td align="center">
|
||||
<a href="https://github.com/DCFApixels/3D-Platformer-DragonECS-Demo">
|
||||
3D Platformer (Example)
|
||||
<img src="https://github.com/user-attachments/assets/c593dba7-eeaa-4706-a043-946f132f3f83" alt="screenshot">
|
||||
<img src="https://github.com/user-attachments/assets/6aba814d-a70b-432f-a905-84d1b6872581" />
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
@ -973,11 +1038,43 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr></tr>
|
||||
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Evileptic/Arkanoid">
|
||||
Arkanoid
|
||||
<img src="https://github.com/user-attachments/assets/bbdb4a7f-2f59-4a3a-ab51-a3e4fe0ad35e" alt="screenshot">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
_____________
|
||||
<img tabindex="-1" src="https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt="screenshot">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Released games:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://dcfapixels.github.io/Project8.html">
|
||||
Crystal Siege
|
||||
<img src="https://github.com/user-attachments/assets/1aa60a50-2668-4919-aca9-d6d2b980c3dd">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://play.google.com/store/apps/details?id=com.ZlodeyStudios.OrdersMatter">
|
||||
Order matters
|
||||
<img src="https://github.com/user-attachments/assets/c55b2647-9b6e-4145-98ff-c3d094600fa1">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr></tr>
|
||||
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://yandex.ru/games/app/206024?utm_source=game_popup_menu">
|
||||
@ -986,46 +1083,38 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<span>
|
||||
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ
|
||||
_____________
|
||||
<img tabindex="-1" src="https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt="screenshot">
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</br>
|
||||
|
||||
# 扩展
|
||||
* Packages:
|
||||
* [Unity集成](https://github.com/DCFApixels/DragonECS-Unity)
|
||||
* [自动依赖注入](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||
* [经典C#多线程](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||
* [Recursivity](https://github.com/DCFApixels/DragonECS-Recursivity)
|
||||
* [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)
|
||||
* [Graphs](https://github.com/DCFApixels/DragonECS-Graphs)
|
||||
* Utilities:
|
||||
* [简单语法](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
|
||||
* [单帧组件](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
|
||||
* [IDE代码模板](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) и [Unity代码模板](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
|
||||
|
||||
> *你的扩展?如果你正在开发 DragonECS 的扩展,可以[在此处发布](#反馈).
|
||||
|
||||
</br>
|
||||
|
||||
# FAQ
|
||||
## 'ReadOnlySpan<>' could not be found
|
||||
在Unity 2020.1.x版本中,控制台可能会出现以下错误:
|
||||
```
|
||||
The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing a using directive or an assembly reference?)
|
||||
```
|
||||
要解决这个问题,需要在`Project Settings/Player/Other Settings/Scripting Define Symbols`中添加`ENABLE_DUMMY_SPAN`指令.
|
||||
|
||||
## 如何启用/禁用系统?
|
||||
直接——不能。
|
||||
|
||||
通常当游戏的整体状态发生变化时,会需要启用或禁用系统;有时需要切换一组系统。从概念上讲,这可以视为流程(process)的变更。这里有两种解决方案:
|
||||
|
||||
- 如果流程变更是全局性的,可以创建一个新的 `EcsPipeline`,并在引擎的更新循环中运行相应的管线(pipeline)。
|
||||
- 将 `IEcsRun` 拆分为多个流程,并在引擎更新循环中运行所需的流程。为此,创建一个新的流程接口,为其实现一个 Runner,并通过 `EcsPipeline.GetRunner<T>()` 获取该 Runner。
|
||||
|
||||
## 建议清单: [DragonECS-Vault](https://github.com/DCFApixels/DragonECS-Vault)
|
||||
|
||||
</br>
|
||||
|
||||
# 反馈
|
||||
+ Discord (RU-EN) [https://discord.gg/kqmJjExuCf](https://discord.gg/kqmJjExuCf)
|
||||
+ QQ (中文) [949562781](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=IbDcH43vhfArb30luGMP1TMXB3GCHzxm&authKey=s%2FJfqvv46PswFq68irnGhkLrMR6y9tf%2FUn2mogYizSOGiS%2BmB%2B8Ar9I%2Fnr%2Bs4oS%2B&noverify=0&group_code=949562781)
|
||||
|
||||
</br>
|
||||
|
||||
# License
|
||||
The MIT License: [Open](LICENSE.md)
|
||||
|
||||
</br></br></br>
|
||||
</br></br></br>
|
||||
</br></br></br>
|
||||
|
||||
420
README.md
420
README.md
@ -22,38 +22,33 @@
|
||||
<tr>
|
||||
<td nowrap width="100">
|
||||
<a href="README-RU.md">
|
||||
<img src="https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755"></br>
|
||||
<img src="https://github.com/user-attachments/assets/7bc29394-46d6-44a3-bace-0a3bae65d755"><br/>
|
||||
<span>Русский</span>
|
||||
</a>
|
||||
</td>
|
||||
<td nowrap width="100">
|
||||
<a href="https://github.com/DCFApixels/DragonECS">
|
||||
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"></br>
|
||||
<img src="https://github.com/user-attachments/assets/3c699094-f8e6-471d-a7c1-6d2e9530e721"><br/>
|
||||
<span>English</span>
|
||||
</a>
|
||||
</td>
|
||||
<td nowrap width="100">
|
||||
<a href="README-ZH.md">
|
||||
<img src="https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927"></br>
|
||||
<img src="https://github.com/user-attachments/assets/8e598a9a-826c-4a1f-b842-0c56301d2927"><br/>
|
||||
<span>中文</span>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</br>
|
||||
The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims to maximize usability, modularity, extensibility and performance for dynamic entity changes, without code generation or external dependencies. Inspired by [LeoEcs Lite](https://github.com/Leopotam/ecslite).
|
||||
|
||||
The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims to maximize usability, modularity, extensibility and performance of dynamic entity changes. Without code generation and dependencies. Inspired by [LeoEcs Lite](https://github.com/Leopotam/ecslite).
|
||||
|
||||
> [!WARNING]
|
||||
> The project is a work in progress, API may change.
|
||||
>
|
||||
> The most current version of the README is in [Russian version](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md).
|
||||
>
|
||||
> If there are unclear points, you can ask questions here [Feedback](#Feedback)
|
||||
> [!Note]
|
||||
> Have questions or want to stay updated on development? Join our community! -> [Feedback](#Feedback)
|
||||
|
||||
## Table of Contents
|
||||
- [Installation](#installation)
|
||||
- [Extensions](#extensions)
|
||||
- [Basic Concepts](#basic-concepts)
|
||||
- [Entity](#entity)
|
||||
- [Component](#component)
|
||||
@ -84,38 +79,61 @@ The [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Framework aims
|
||||
- [Extensions](#extensions)
|
||||
- [FAQ](#faq)
|
||||
- [Feedback](#feedback)
|
||||
- [License](#license)
|
||||
|
||||
</br>
|
||||
|
||||
# Installation
|
||||
Versioning semantics - [Open](https://gist.github.com/DCFApixels/e53281d4628b19fe5278f3e77a7da9e8#file-dcfapixels_versioning_ru-md)
|
||||
Versioning semantics - [Open](https://gist.github.com/DCFApixels/af79284955bf40e9476cdcac79d7b098#file-dcfapixels_versioning-md)
|
||||
## Environment
|
||||
Requirements:
|
||||
+ Minimum version of C# 7.3;
|
||||
* Minimum C# version: 7.3.
|
||||
|
||||
Optional:
|
||||
+ Support for NativeAOT
|
||||
+ Game engines with C#: Unity, Godot, MonoGame, etc.
|
||||
Supported:
|
||||
* NativeAOT;
|
||||
* Game engines with C#: Unity, Godot, MonoGame, etc.
|
||||
|
||||
Tested with:
|
||||
+ **Unity:** Minimum version 2020.1.0;
|
||||
* **Unity:** Minimum version 2021.2.0.
|
||||
|
||||
## Unity Installation
|
||||
* ### Unity Package
|
||||
The framework can be installed as a Unity package by adding the Git URL [in the PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html) or manually adding it to `Packages/manifest.json`:
|
||||
> It is also recommended to install the Unity engine integration extension [Unity integration](https://github.com/DCFApixels/DragonECS-Unity)
|
||||
* ### Unity package
|
||||
Installation as a Unity package is supported by adding the Git URL [in PackageManager](https://docs.unity3d.com/2023.2/Documentation/Manual/upm-ui-giturl.html):
|
||||
```
|
||||
https://github.com/DCFApixels/DragonECS.git
|
||||
```
|
||||
* ### Source Code
|
||||
The framework can also be added to the project as source code.
|
||||
Or add the package entry to `Packages/manifest.json`:
|
||||
```
|
||||
"com.dcfa_pixels.dragonecs": "https://github.com/DCFApixels/DragonECS.git",
|
||||
```
|
||||
|
||||
</br>
|
||||
* ### As source code
|
||||
The framework sources can be copied directly into the project.
|
||||
|
||||
# Extensions
|
||||
* Integrations:
|
||||
* [Unity](https://github.com/DCFApixels/DragonECS-Unity)
|
||||
* [Godot](https://gitlab.com/InauniusOwn/Libraries/DraGodot)
|
||||
* Packages:
|
||||
* [Dependency autoinjections](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||
* [Classic C# multithreading](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||
* [Recursivity](https://github.com/DCFApixels/DragonECS-Recursivity)
|
||||
* [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)
|
||||
* [Graphs](https://github.com/DCFApixels/DragonECS-Graphs)
|
||||
* Utilities:
|
||||
* [Simple syntax](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
|
||||
* [EcsRefPool](https://gist.github.com/DCFApixels/73e392ccabdd98b3d4a517017d8a3f22)
|
||||
* [Timers](https://gist.github.com/DCFApixels/71a416275660c465ece76242290400df)
|
||||
* [One-Frame Components](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
|
||||
* [Code Templates for IDE](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) and [for Unity](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
|
||||
> *Your extension? Extensions for DragonECS are welcome, you can share it [here](#feedback).
|
||||
|
||||
# Basic Concepts
|
||||
## Entity
|
||||
Сontainer for components. They are implemented as identifiers, of which there are two types:
|
||||
* `int` - a short-term identifier used within a single tick. Storing `int` identifiers is not recommended, use `entlong` instead;
|
||||
* `entlong` - long-term identifier, contains a full set of information for unique identification;
|
||||
Container for components. Two identifier types are used to reference entities:
|
||||
* `int` - short-lived identifier, valid within a single tick. Not recommended for long-term storage;
|
||||
* `entlong` - long-term identifier that includes a generation tag, making it unique across entity lifetimes. Suitable for long-term storage.
|
||||
```c#
|
||||
// Creating a new entity in the world.
|
||||
int entityID = _world.NewEntity();
|
||||
@ -153,7 +171,7 @@ if (entity.TryGetID(out int entityID)) { }
|
||||
|
||||
</details>
|
||||
|
||||
> Entities cannot exist without components, empty entities will be automatically deleted immediately after the last component is deleted.
|
||||
> Entities cannot exist without components. Removing the last component automatically deletes the entity along with it.
|
||||
|
||||
## Component
|
||||
Data for entities.
|
||||
@ -169,7 +187,7 @@ struct PlayerTag : IEcsTagComponent {}
|
||||
```
|
||||
|
||||
## System
|
||||
Represent the core logic defining entity behaviors. They are implemented as user-defined classes that implement at least one of the process interfaces. Key processes include:
|
||||
Represents the core logic defining entity behaviors. Systems are implemented as user-defined classes that implement one or more process interfaces. Key processes include:
|
||||
```c#
|
||||
class SomeSystem : IEcsPreInit, IEcsInit, IEcsRun, IEcsDestroy
|
||||
{
|
||||
@ -192,11 +210,11 @@ class SomeSystem : IEcsPreInit, IEcsInit, IEcsRun, IEcsDestroy
|
||||
|
||||
# Framework Concepts
|
||||
## Pipeline
|
||||
Container and engine of systems. Responsible for setting up the system call queue, provides mechanisms for communication between systems, and dependency injection. Implemented as the `EcsPipeline` class.
|
||||
Container and engine of systems. Responsible for setting up the system call queue, providing mechanisms for communication between systems, and dependency injection. Implemented as the `EcsPipeline` class.
|
||||
### Building
|
||||
Builder is responsible for building the pipeline. Systems are added to the Builder and at the end, the pipeline is built. Example:
|
||||
Builder is responsible for building the pipeline. Systems are added to the Builder, and the pipeline is built at the end. Example:
|
||||
```c#
|
||||
EcsPipeline pipeline = EcsPipeline.New() // Создает Builder пайплайна.
|
||||
EcsPipeline pipeline = EcsPipeline.New() // Creates a pipeline builder.
|
||||
// Adds System1 to the systems queue.
|
||||
.Add(new System1())
|
||||
// Adds System2 to the queue after System1.
|
||||
@ -217,11 +235,11 @@ class SomeSystem : IEcsRun, IEcsPipelineMember
|
||||
public void Run () { }
|
||||
}
|
||||
```
|
||||
> For simultaneous building and initialization, there is the method `Builder.BuildAndInit();`
|
||||
> For simultaneous building and initialization, use `Builder.BuildAndInit()`.
|
||||
|
||||
### Dependency Injection
|
||||
The framework implements dependency injection for systems. This process begins during pipeline initialization and injects data passed to the Builder.
|
||||
> Using built-in dependency injection is optional.
|
||||
The framework implements dependency injection for systems. Injection process occurs during pipeline initialization and injects data passed to the Builder.
|
||||
> Built-in Dependency injection is optional.
|
||||
```c#
|
||||
class SomeDataA { /* ... */ }
|
||||
class SomeDataB : SomeDataA { /* ... */ }
|
||||
@ -244,7 +262,7 @@ EcsPipeline pipeline = EcsPipeline.New()
|
||||
// Injection uses the interface IEcsInject<T> and its method Inject(T obj).
|
||||
class SomeSystem : IEcsInject<SomeDataA>, IEcsRun
|
||||
{
|
||||
SomeDataA _someDataA
|
||||
SomeDataA _someDataA;
|
||||
// obj will be an instance of type SomeDataB.
|
||||
public void Inject(SomeDataA obj) => _someDataA = obj;
|
||||
|
||||
@ -256,7 +274,7 @@ class SomeSystem : IEcsInject<SomeDataA>, IEcsRun
|
||||
```
|
||||
|
||||
### Modules
|
||||
Groups of systems that implement a common feature can be grouped into modules and easily added to the Pipeline.
|
||||
Groups of systems that implement a common feature can be grouped into modules and added to the Pipeline.
|
||||
```c#
|
||||
using DCFApixels.DragonECS;
|
||||
class Module1 : IEcsModule
|
||||
@ -279,62 +297,64 @@ EcsPipeline pipeline = EcsPipeline.New()
|
||||
```
|
||||
|
||||
### Sorting
|
||||
To manage the position of systems in the pipeline, regardless of the order in which they are added, there are two methods: Layers and Sorting Order.
|
||||
To manage system order in the pipeline regardless of addition order, use Layers and Sorting Order.
|
||||
#### Layers
|
||||
Queues in the system can be segmented into layers. A layer defines a position in the queue for inserting systems. For example, if a system needs to be inserted at the end of the queue regardless of where it is added, you can add this system to the `EcsConsts.END_LAYER` layer.
|
||||
Queues of systems can be segmented into layers. A layer defines a position in the queue for inserting systems. For example, to insert a system at the end of the queue regardless of where it is added, add it to the `EcsConsts.END_LAYER` layer.
|
||||
``` c#
|
||||
const string SOME_LAYER = nameof(SOME_LAYER);
|
||||
EcsPipeline pipeline = EcsPipeline.New()
|
||||
// ...
|
||||
// Inserts a new layer before the end layer EcsConsts.END_LAYER
|
||||
.Layers.Insert(EcsConsts.END_LAYER, SOME_LAYER)
|
||||
.Layers.Add(SOME_LAYER).Before(EcsConsts.END_LAYER)
|
||||
// Inserts SOME_LAYER_2 layer at the position after EcsConsts.BASIC_LAYER
|
||||
.Layers.Add(SOME_LAYER).After(EcsConsts.BASIC_LAYER)
|
||||
// System SomeSystem will be added to the SOME_LAYER layer
|
||||
.Add(New SomeSystem(), SOME_LAYER)
|
||||
.Add(new SomeSystem(), SOME_LAYER)
|
||||
// ...
|
||||
.BuildAndInit();
|
||||
```
|
||||
The built-in layers are arranged in the following order:
|
||||
* `EcsConst.PRE_BEGIN_LAYER`
|
||||
* `EcsConst.BEGIN_LAYER`
|
||||
* `EcsConst.BASIC_LAYER` (Systems are added here if no layer is specified during addition)
|
||||
* `EcsConst.END_LAYER`
|
||||
* `EcsConst.POST_END_LAYER`
|
||||
* `EcsConsts.PRE_BEGIN_LAYER`
|
||||
* `EcsConsts.BEGIN_LAYER`
|
||||
* `EcsConsts.BASIC_LAYER` (Systems are added here if no layer is specified during addition)
|
||||
* `EcsConsts.END_LAYER`
|
||||
* `EcsConsts.POST_END_LAYER`
|
||||
#### Sorting Order
|
||||
The sort order int value is used to sort systems within a layer. By default, systems are added with `sortOrder = 0`.
|
||||
The sort order integer value is used to sort systems within a layer. By default, systems are added with `sortOrder = 0`.
|
||||
|
||||
```c#
|
||||
EcsPipeline pipeline = EcsPipeline.New()
|
||||
// ...
|
||||
// System SomeSystem will be inserted into the layer EcsConsts.BEGIN_LAYER
|
||||
// and placed after systems with sortOrder less than 10.
|
||||
.Add(New SomeSystem(), EcsConsts.BEGIN_LAYER, 10)
|
||||
.Add(new SomeSystem(), EcsConsts.BEGIN_LAYER, 10)
|
||||
// ...
|
||||
.BuildAndInit();
|
||||
```
|
||||
|
||||
## Processes
|
||||
Processes are queues of systems that implement a common interface, such as `IEcsRun`. Runners are used to start processes. Built-in processes are started automatically. It is possible to implement custom processes.
|
||||
Processes are queues of systems that implement a common interface, such as `IEcsRun`. Runners are used to start processes. Built-in processes are started automatically; custom processes can be implemented.
|
||||
|
||||
<details>
|
||||
<summary>Built-in processes</summary>
|
||||
|
||||
* `IEcsPreInit`, `IEcsInit`, `IEcsRun`, `IEcsDestroy` - lifecycle processes of `EcsPipeline`.
|
||||
* `IEcsInject<T>` - [Dependency Injection](#Dependency-Injection) processes.
|
||||
* `IOnInitInjectionComplete` - Similar to the [Dependency Injection](#Dependency-Injection) process, but signals the completion of initialization injection.
|
||||
* `IOnInitInjectionComplete` - Similar to the [Dependency Injection](#Dependency-Injection) process, but signals completion of initialization injection.
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Custom Processes</summary>
|
||||
|
||||
To add a new process, create an interface inherited from `IEcsProcess` and create a runner for it. A runner is a class that implements the interface of the process to be run and inherits from `EcsRunner<TInterface>`. Example:
|
||||
To add a new process, create an interface inheriting from `IEcsProcess` and create a runner for it. A runner is a class that implements the process interface and inherits from `EcsRunner<TInterface>`. Example:
|
||||
``` c#
|
||||
// Process interface.
|
||||
interface IDoSomethingProcess : IEcsProcess
|
||||
{
|
||||
void Do();
|
||||
}
|
||||
// Implementation of a runner. An example of implementation can also be seen in built-in processes.
|
||||
// Runner implementation. An example of implementation can also be seen in built-in processes.
|
||||
sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>, IDoSomethingProcess
|
||||
{
|
||||
public void Do()
|
||||
@ -352,10 +372,10 @@ _pipeline = EcsPipeline.New()
|
||||
.BuildAndInit();
|
||||
|
||||
// Running the runner if it was added
|
||||
_pipeline.GetRunner<IDoSomethingProcess>.Do()
|
||||
_pipeline.GetRunner<IDoSomethingProcess>().Do();
|
||||
|
||||
// or if the runner was not added (calling GetRunnerInstance will also add the runner to the pipeline).
|
||||
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>.Do()
|
||||
_pipeline.GetRunnerInstance<DoSomethingProcessRunner>().Do();
|
||||
```
|
||||
|
||||
<details>
|
||||
@ -367,10 +387,9 @@ internal sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>,
|
||||
// RunHelper simplifies the implementation similar to the built-in processes implementation.
|
||||
// It automatically triggers the profiler marker and also includes a try-catch block.
|
||||
private RunHelper _helper;
|
||||
private RunHelper _helper;
|
||||
protected override void OnSetup()
|
||||
{
|
||||
// The second argument specifies the name of the marker, if not specified, the name will be chosen automatically.
|
||||
// The second argument specifies the name of the marker; if not specified, the name will be chosen automatically.
|
||||
_helper = new RunHelper(this, nameof(Do));
|
||||
}
|
||||
public void Do()
|
||||
@ -382,17 +401,15 @@ internal sealed class DoSomethingProcessRunner : EcsRunner<IDoSomethingProcess>,
|
||||
|
||||
</details>
|
||||
|
||||
> Runners have several implementation requirements:
|
||||
> Runner requirements:
|
||||
> * Inheritance from `EcsRunner<T>` must be direct.
|
||||
> * Runner can only contain one interface (except `IEcsProcess`);
|
||||
> * The inheriting class of `EcsRunner<T>,` must also implement the `T` interface;
|
||||
|
||||
> It's not recommended to call `GetRunner` in a loop; instead, cache the retrieved runner instance.
|
||||
> * A runner may only contain one interface (excluding `IEcsProcess`).
|
||||
> * The inheriting class of `EcsRunner<T>` must also implement the `T` interface.
|
||||
|
||||
</details>
|
||||
|
||||
## World
|
||||
Is a container for entities and components.
|
||||
A container for entities and components.
|
||||
``` c#
|
||||
// Creating an instance of the world.
|
||||
_world = new EcsDefaultWorld();
|
||||
@ -400,10 +417,10 @@ _world = new EcsDefaultWorld();
|
||||
var e = _world.NewEntity();
|
||||
_world.DelEntity(e);
|
||||
```
|
||||
> **NOTICE:** It's necessary to call EcsWorld.Destroy() on the world instance when it's no longer needed, otherwise it will remain in memory.
|
||||
> **NOTICE:** Call `EcsWorld.Destroy()` on the world instance when it is no longer needed to release resources. Otherwise it will remain in memory.
|
||||
|
||||
### World Configuration
|
||||
To initialize the world with a required size upfront and reduce warm-up time, you can pass an `EcsWorldConfig` instance to the constructor.
|
||||
To initialize the world with a required size upfront and reduce warm-up time, pass an `EcsWorldConfig` instance to the constructor.
|
||||
|
||||
``` c#
|
||||
EcsWorldConfig config = new EcsWorldConfig(
|
||||
@ -415,11 +432,11 @@ _world = new EcsDefaultWorld(config);
|
||||
```
|
||||
|
||||
## Pool
|
||||
Stash of components, providing methods for adding, reading, editing, and removing components on entities. There are several types of pools designed for different purposes:
|
||||
* `EcsPool` - universal pool, stores struct components implementing the `IEcsComponent` interface;
|
||||
* `EcsTagPool` - special pool optimized for tag components, stores struct-components with `IEcsTagComponent`;
|
||||
Stash of components, providing methods for adding, reading, editing, and removing components on entities. Several pool types are available:
|
||||
* `EcsPool` - universal pool for struct components implementing `IEcsComponent`;
|
||||
* `EcsTagPool` - pool optimized for tag components implementing `IEcsTagComponent`;
|
||||
|
||||
Pools have 5 main methods and their variations:
|
||||
Pools provide 5 common methods and their variations:
|
||||
``` c#
|
||||
// One way to get a pool from the world.
|
||||
EcsPool<Pose> poses = _world.GetPool<Pose>();
|
||||
@ -427,8 +444,8 @@ EcsPool<Pose> poses = _world.GetPool<Pose>();
|
||||
// Adds component to entity, throws an exception if the entity already has the component.
|
||||
ref var addedPose = ref poses.Add(entityID);
|
||||
|
||||
// Returns exist component, throws an exception if the entity does not have this component.
|
||||
ref var gettedPose = ref poses.Get(entityID);
|
||||
// Returns existing component, throws an exception if the entity does not have this component.
|
||||
ref var gottenPose = ref poses.Get(entityID);
|
||||
|
||||
// Returns a read-only component, throwing an exception if the entity does not have this component.
|
||||
ref readonly var readonlyPose = ref poses.Read(entityID);
|
||||
@ -439,12 +456,33 @@ if (poses.Has(entityID)) { /* ... */ }
|
||||
// Removes component from entity, throws an exception if the entity does not have this component.
|
||||
poses.Del(entityID);
|
||||
```
|
||||
> There are "safe" methods that first perform a check for the presence or absence of a component. Such methods are prefixed with `Try`.
|
||||
> [!WARNING]
|
||||
> Exceptions are disabled in the `Release` build.
|
||||
|
||||
> It is possible to implement a user pool. This feature will be described shortly.
|
||||
> There are "Safe" methods that first check presence or absence of a component are prefixed with `Try`.
|
||||
|
||||
<details>
|
||||
<summary>Custom Pools</summary>
|
||||
|
||||
A pool can be any type that implements the IEcsPoolImplementation<T> interface and has a parameterless constructor.
|
||||
|
||||
Key points when implementing a pool:
|
||||
|
||||
* For an example of a pool implementation, reference can be made to the implementation of the built-in EcsPool<T>.
|
||||
* The IEcsPoolImplementation interface and its members are not intended for public use; the interface members should be implemented explicitly.
|
||||
* The type T substituted in the IEcsPoolImplementation<T> interface and the type returned by the ComponentType and ComponentTypeID properties must match.
|
||||
* All pool changes must be registered with the EcsWorld.PoolsMediator instance passed in the OnInit method.
|
||||
* EcsWorld.PoolsMediator is intended for use only inside the pool.
|
||||
* The DISABLE_POOLS_EVENTS define disables the implemented AddListener and RemoveListener methods.
|
||||
* The static class EcsPoolThrowHelper defines throwing of the most common exception types.
|
||||
* The OnReleaseDelEntityBuffer method handles cleanup of deleted entities.
|
||||
* It is recommended to define an interface that marks components intended for the new pool. Based on this interface, extension methods such as GetPool<T>() can be implemented to simplify access to pools.
|
||||
* Pools must implement locking. Pool locking works only in Debug mode and should throw exceptions when attempting to add or remove a component.
|
||||
|
||||
</details>
|
||||
|
||||
## Mask
|
||||
Used to filter entities by the presence or absence of components.
|
||||
Used to filter entities by presence or absence of components. Usually masks are not used standalone, they are part of `EcsAspect` and used by queries to filter entities.
|
||||
``` c#
|
||||
// Creating a mask that checks if entities have components
|
||||
// SomeCmp1 and SomeCmp2, but do not have component SomeCmp3.
|
||||
@ -460,7 +498,7 @@ EcsMask mask = EcsMask.New(_world)
|
||||
<details>
|
||||
<summary>Static Mask</summary>
|
||||
|
||||
`EcsMask` is tied to specific world instances, which need to be passed to `EcsMask.New(world)`, but there is also `EcsStaticMask`, which can be created without being tied to a world.
|
||||
`EcsMask` is tied to specific world instances, which need to be passed to `EcsMask.New(world)`. `EcsStaticMask` can be created without a world reference.
|
||||
|
||||
``` c#
|
||||
class SomeSystem : IEcsRun
|
||||
@ -479,7 +517,11 @@ EcsMask mask = _staticMask.ToMask(_world);
|
||||
</details>
|
||||
|
||||
## Aspect
|
||||
These are custom classes inherited from `EcsAspect` and used to interact with entities. Aspects are both a pool cache and a component mask for filtering entities. You can think of aspects as a description of what entities the system is working with.
|
||||
User-defined classes that inherit from `EcsAspect` and describe sets of components a system works with. An aspect serves two purposes:
|
||||
- Mask — initializes and holds an `EcsMask` used by queries for filtering entities.
|
||||
- Pool cache — provides fast access to component pools.
|
||||
|
||||
In short, an aspect is a convenient way to declare "which entities I work with and how to access their components."
|
||||
|
||||
Simplified syntax:
|
||||
``` c#
|
||||
@ -496,11 +538,20 @@ class Aspect : EcsAspect
|
||||
|
||||
// During queries, it checks for the presence of components
|
||||
// in the inclusive constraint and absence in the exclusive constraint.
|
||||
// There is also Opt - it only caches the pool without affecting the mask.
|
||||
// Opt only caches the pool without affecting the mask.
|
||||
}
|
||||
```
|
||||
|
||||
Explicit syntax (the result is identical to the example above):
|
||||
Purpose of the static markers:
|
||||
* `Inc` — component must be present (inclusive) and caches the pool.
|
||||
* `Exc` — component must NOT be present (exclusive) and caches the pool.
|
||||
* `Opt` — component may be present; does not affect filtering (only caches the pool).
|
||||
* `Any` — at least one of the components marked with `Any` must be present; caches the pool.
|
||||
|
||||
<details>
|
||||
<summary>Initialization via a method (the result is identical to the example above):</summary>
|
||||
|
||||
Explicit syntax (equivalent result):
|
||||
``` c#
|
||||
using DCFApixels.DragonECS;
|
||||
// ...
|
||||
@ -510,17 +561,19 @@ class Aspect : EcsAspect
|
||||
public EcsPool<Velocity> velocities;
|
||||
protected override void Init(Builder b)
|
||||
{
|
||||
poses = b.Include<Pose>();
|
||||
velocities = b.Include<Velocity>();
|
||||
b.Exclude<FreezedTag>();
|
||||
poses = b.Inc<Pose>();
|
||||
velocities = b.Inc<Velocity>();
|
||||
b.Exc<FreezedTag>();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Combining aspects</summary>
|
||||
|
||||
Aspects can have additional aspects added to them, thus combining them. The constraints will also be combined accordingly.
|
||||
Aspects can be combined by adding other aspects; constraints are merged accordingly.
|
||||
``` c#
|
||||
using DCFApixels.DragonECS;
|
||||
// ...
|
||||
@ -532,31 +585,32 @@ class Aspect : EcsAspect
|
||||
|
||||
protected override void Init(Builder b)
|
||||
{
|
||||
// Combines with SomeAspect1.
|
||||
// Combines with OtherAspect1.
|
||||
otherAspect1 = b.Combine<OtherAspect1>(1);
|
||||
// Although Combine was called earlier for OtherAspect1, it will first combine with OtherAspect2 because the default order is 0.
|
||||
otherAspect2 = b.Combine<OtherAspect2>();
|
||||
// If b.Exclude<Pose>() was specified in OtherAspect1 or OtherAspect2, it will be replaced with b.Include<Pose>() here.
|
||||
poses = b.Include<Pose>();
|
||||
// If b.Exc<Pose>() was specified in OtherAspect1 or OtherAspect2, it will be replaced with b.Inc<Pose>() here.
|
||||
poses = b.Inc<Pose>();
|
||||
}
|
||||
}
|
||||
```
|
||||
If there are conflicting constraints between the combined aspects, the new constraints will replace those added earlier. Constraints from the root aspect always replace constraints from added aspects. Here's a visual example of constraint combination:
|
||||
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | разрешение конфликтных ограничений|
|
||||
|
||||
If conflicting constraints exist between combined aspects, the newer constraints replace earlier ones. Constraints from the root aspect always replace constraints from added aspects. Example of constraint combination:
|
||||
| | cmp1 | cmp2 | cmp3 | cmp4 | cmp5 | Conflict resolution |
|
||||
| :--- | :--- | :--- | :--- | :--- | :--- |:--- |
|
||||
| OtherAspect2 | :heavy_check_mark: | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | |
|
||||
| OtherAspect1 | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_minus_sign: | For `cmp2` will be chosen. :heavy_check_mark: |
|
||||
| Aspect | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | For `cmp1` will be chosen. :x: |
|
||||
| OtherAspect1 | :heavy_minus_sign: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_minus_sign: | For `cmp2`, :heavy_check_mark: will be chosen. |
|
||||
| Aspect | :x: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_minus_sign: | :heavy_check_mark: | For `cmp1`, :x: will be chosen. |
|
||||
| Final Constraints | :x: | :heavy_check_mark: | :heavy_minus_sign: | :x: | :heavy_check_mark: | |
|
||||
|
||||
</details>
|
||||
|
||||
## Queries
|
||||
Filter entities and return collections of entities that matching conditions. The built-in `Where` query filters by component mask matching and has several overloads:
|
||||
+ `EcsWorld.Where(EcsMask mask)` - Standard filtering by mask;
|
||||
+ `EcsWorld.Where<TAspect>(out TAspect aspect)` - Combines filtering by aspect mask and aspect return;
|
||||
Filter entities and return collections of entities that match conditions. The built-in `Where` query filters by component mask and has several overloads:
|
||||
+ `EcsWorld.Where(EcsMask mask)` - standard filtering by mask;
|
||||
+ `EcsWorld.Where<TAspect>(out TAspect aspect)` - combines aspect mask filtering and returns the aspect;
|
||||
|
||||
The `Where` query can be applied to both `EcsWorld` and framework collections (in this sense, `Where` is somewhat similar to the one in Linq). There are also overloads for sorting entities using `Comparison<int>`.
|
||||
`Where` can be applied to both `EcsWorld` and framework collections (similar to LINQ). Overloads for sorting using `Comparison<int>` are available.
|
||||
|
||||
Example system:
|
||||
``` c#
|
||||
@ -597,7 +651,7 @@ public class SomeDamageSystem : IEcsRun, IEcsInject<EcsDefaultWorld>
|
||||
## Collections
|
||||
|
||||
### EcsSpan
|
||||
Collection of entities that is read-only and stack-allocated. It consists of a reference to an array, its length, and the world identifier. Similar to `ReadOnlySpan<int>`.
|
||||
A `ref struct` collection of entities, available only for reading. Consists of a reference to an array, a length, and a world identifier. Similar to `ReadOnlySpan<int>`.
|
||||
``` c#
|
||||
// Where query returns entities as EcsSpan.
|
||||
EcsSpan es = _world.Where(out Aspect a);
|
||||
@ -612,13 +666,13 @@ for (int i = 0; i < es.Count; i++)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
> Although `EcsSpan` is just an array, it does not allow duplicate entities.
|
||||
> Although `EcsSpan` is an array reference, duplicate entities are not allowed.
|
||||
|
||||
### EcsGroup
|
||||
Sparse Set based auxiliary collection for storing a set of entities with O(1) add/delete/check operations, etc.
|
||||
``` c#
|
||||
// Getting a new group. EcsWorld contains pool of groups,
|
||||
// so a new one will be created or a free one will be reused.
|
||||
// Getting a new group. EcsWorld contains a pool of groups,
|
||||
// a new one will be created or a free one reused.
|
||||
EcsGroup group = EcsGroup.New(_world);
|
||||
// Release the group.
|
||||
group.Dispose();
|
||||
@ -645,32 +699,36 @@ for (int i = 0; i < group.Count; i++)
|
||||
// ...
|
||||
}
|
||||
```
|
||||
Groups are sets and implement the `ISet<int>` interface. The editing methods have two variants: one that writes the result to `groupA`, and another that returns a new group.
|
||||
Groups implement the `ISet<int>` interface. Editing methods have two variants: one that writes the result to `groupA`, and another that returns a new group.
|
||||
|
||||
``` c#
|
||||
// Union of groupA and groupB.
|
||||
groupA.UnionWith(groupB);
|
||||
EcsGroup newGroup = EcsGroup.Union(groupA, groupB);
|
||||
|
||||
```
|
||||
``` c#
|
||||
// Intersection of groupA and groupB.
|
||||
groupA.IntersectWith(groupB);
|
||||
EcsGroup newGroup = EcsGroup.Intersect(groupA, groupB);
|
||||
|
||||
```
|
||||
``` c#
|
||||
// Difference of groupA and groupB.
|
||||
groupA.ExceptWith(groupB);
|
||||
EcsGroup newGroup = EcsGroup.Except(groupA, groupB);
|
||||
|
||||
```
|
||||
``` c#
|
||||
// Symmetric difference of groupA and groupB.
|
||||
groupA.SymmetricExceptWith(groupB);
|
||||
EcsGroup newGroup = EcsGroup.SymmetricExcept(groupA, groupB);
|
||||
|
||||
```
|
||||
``` c#
|
||||
// Difference of all entities in world and groupA.
|
||||
groupA.Inverse();
|
||||
EcsGroup newGroup = EcsGroup.Inverse(groupA);
|
||||
```
|
||||
|
||||
## ECS Root
|
||||
This is a custom class that is the entry point for ECS. Its main purpose is to initialize, start systems on each engine Update and release resources when no longer needed.
|
||||
A custom class that serves as the entry point for ECS. Its main purpose is to initialize systems, run them on each engine update, and release resources when no longer needed.
|
||||
### Example for Unity
|
||||
``` c#
|
||||
using DCFApixels.DragonECS;
|
||||
@ -736,7 +794,7 @@ public class EcsRoot
|
||||
// .Add(new SomeSystem2())
|
||||
// .Add(new SomeSystem3())
|
||||
|
||||
// Внедрение мира в системы.
|
||||
// Injecting world into systems.
|
||||
.Inject(_world)
|
||||
// Other injections.
|
||||
// .Inject(SomeData)
|
||||
@ -771,7 +829,7 @@ public class EcsRoot
|
||||
</br>
|
||||
|
||||
# Debug
|
||||
The framework provides additional tools for debugging and logging, independent of the environment. Also many types have their own DebuggerProxy for more informative display in IDE.
|
||||
The framework provides tools for debugging and logging, independent of the environment. Many types have DebuggerProxy implementations for more informative display in IDEs.
|
||||
## Meta Attributes
|
||||
By default, meta-attributes have no use, but are used in integrations with engines to specify display in debugging tools and editors. And can also be used to generate automatic documentation.
|
||||
``` c#
|
||||
@ -809,12 +867,47 @@ var description = typeMeta.Description; // [MetaDescription]
|
||||
var metaID = typeMeta.MetaID; // [MetaID]
|
||||
var tags = typeMeta.Tags; // [MetaTags]
|
||||
```
|
||||
> To automatically generate unique identifiers MetaID, there is the method `MetaID.GenerateNewUniqueID()` and the [Browser Generator](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/).
|
||||
> To simplify generate unique MetaID values, use `MetaID.GenerateNewUniqueID()` or the [Browser Generator](https://dcfapixels.github.io/DragonECS-MetaID_Generator_Online/).
|
||||
|
||||
<details>
|
||||
<summary>[MetaProxy]</summary>
|
||||
|
||||
The `[MetaProxy(typeof(MetaProxyType))]` attribute enables flexible metadata configuration. It allows bypassing the limitations of regular attributes (for example, the inability to pass non-constant data into them) and setting metadata programmatically. Furthermore, metadata obtained via `MetaProxy` is inherited.
|
||||
|
||||
The API resembles `[DebuggerTypeProxy]`, but instead of an object instance, it accepts a Type. The class for `MetaProxy` must inherit from `MetaProxyBase`.
|
||||
|
||||
Usage example:
|
||||
|
||||
``` c#
|
||||
// Applying the attribute
|
||||
[MetaProxy(typeof(UnityComponent<>.MetaProxy))]
|
||||
// In this example, UnityComponent wraps a Unity component
|
||||
public struct UnityComponent<T> where T : Component
|
||||
{
|
||||
// ...
|
||||
|
||||
// Implementation of MetaProxyBase. MetaProxy copies the metadata of the wrapped type
|
||||
// so that its metadata is displayed in the Unity inspector.
|
||||
private class MetaProxy : MetaProxyBase
|
||||
{
|
||||
protected TypeMeta Meta = typeof(T).GetMeta();
|
||||
public override string Name { get { return Meta?.Name; } }
|
||||
public override MetaColor? Color { get { return Meta != null && Meta.IsCustomColor ? Meta.Color : null; } }
|
||||
public override MetaGroup Group { get { return Meta?.Group; } }
|
||||
public override MetaDescription Description { get { return Meta?.Description; } }
|
||||
public override IEnumerable<string> Tags { get { return Meta?.Tags; } }
|
||||
public MetaProxy(Type type) : base(type) { }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
## EcsDebug
|
||||
Has a set of methods for debugging and logging. It is implemented as a static class calling methods of Debug services. Debug services are intermediaries between the debugging systems of the environment and EcsDebug. This allows projects to be ported to other engines without modifying the debug code, by implementing the corresponding Debug service.
|
||||
Provides methods for debugging and logging. Implemented as a static class that forwards calls to a Debug service. Debug services act as intermediaries between environment-specific debugging systems and EcsDebug, enabling portability.
|
||||
|
||||
By default, `DefaultDebugService` is used, which outputs logs to the console. To implement a custom one, create a class inherited from `DebugService` and implement abstract class members.
|
||||
By default, `DefaultDebugService` outputs logs to the console. To implement a custom service, inherit from `DebugService` and implement its abstract members.
|
||||
|
||||
``` c#
|
||||
// Output log.
|
||||
@ -853,26 +946,25 @@ using (_marker.Auto())
|
||||
# Define Symbols
|
||||
+ `DRAGONECS_DISABLE_POOLS_EVENTS` - Disables reactive behavior in pools.
|
||||
+ `DRAGONECS_ENABLE_DEBUG_SERVICE` - Enables EcsDebug functionality in release builds.
|
||||
+ `DRAGONECS_STABILITY_MODE` - By default, for optimization purposes, the framework skips many exception checks in the release build. This define, instead of disabling checks, replaces them with code that resolves errors. This increases stability but reduces execution speed.
|
||||
+ `DRAGONECS_STABILITY_MODE` - By default, many exception checks are skipped in release builds for performance. This define replaces checks with error-resilient code to increase stability at the cost of some performance.
|
||||
+ `DRAGONECS_DISABLE_CATH_EXCEPTIONS` - Turns off the default exception handling behavior. By default, the framework will catch exceptions with the exception information output via EcsDebug and continue working.
|
||||
+ `REFLECTION_DISABLED` - completely restricts the framework's use of Reflection.
|
||||
+ `DISABLE_DEBUG` - for environments where manual DEBUG disabling is not supported, e.g., Unity.
|
||||
+ `ENABLE_DUMMY_SPAN` - For environments where Span types are not supported, enables its replacement.
|
||||
|
||||
+ `REFLECTION_DISABLED` - Restricts the framework's use of Reflection.
|
||||
+ `DISABLE_DEBUG` - For environments where manual DEBUG disabling is not supported (e.g., Unity).
|
||||
|
||||
</br>
|
||||
|
||||
# Framework Extension Tools
|
||||
There are additional tools for greater extensibility of the framework.
|
||||
Additional tools improve framework extensibility.
|
||||
|
||||
## Configs
|
||||
Constructors of `EcsWorld` and `EcsPipeline` classes can accept config containers implementing `IConfigContainer` or `IConfigContainerWriter` interface. These containers can be used to pass data and dependencies. The built-in container implementation is `ConfigContainer`, but you can also use your own implementation.</br>
|
||||
Constructors of `EcsWorld` and `EcsPipeline` accept config containers implementing `IConfigContainer` or `IConfigContainerWriter`. These containers pass data and dependencies. The built-in container implementation is `ConfigContainer`. Сustom implementations are supported.
|
||||
|
||||
Example of using configs for EcsWorld:
|
||||
``` c#
|
||||
var configs = new ConfigContainer()
|
||||
.Set(new EcsWorldConfig(entitiesCapacity: 2000, poolsCapacity: 2000)
|
||||
.Set(new EcsWorldConfig(entitiesCapacity: 2000, poolsCapacity: 2000))
|
||||
.Set(new SomeDataA(/* ... */))
|
||||
.Set(new SomeDataB(/* ... */)));
|
||||
.Set(new SomeDataB(/* ... */));
|
||||
EcsDefaultWorld _world = new EcsDefaultWorld(configs);
|
||||
// ...
|
||||
var _someDataA = _world.Configs.Get<SomeDataA>();
|
||||
@ -891,20 +983,20 @@ var _someDataB = _pipeline.Configs.Get<SomeDataB>();
|
||||
```
|
||||
|
||||
## World Components
|
||||
Components can be used to attach additional data to worlds. World components are `struct` types. Access to components via `Get` is optimized, the speed is almost the same as access to class fields.
|
||||
World components attach additional data to worlds. World components are `struct` types. Access via `Get` is optimized and performs similarly to class field access.
|
||||
|
||||
Get component:
|
||||
``` c#
|
||||
ref WorldComponent component = ref _world.Get<WorldComponent>();
|
||||
```
|
||||
Component Implementation:
|
||||
Component implementation:
|
||||
``` c#
|
||||
public struct WorldComponent
|
||||
{
|
||||
// Data.
|
||||
}
|
||||
```
|
||||
Or:
|
||||
Or with lifecycle callbacks:
|
||||
``` c#
|
||||
public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
{
|
||||
@ -917,7 +1009,7 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
void IEcsWorldComponent<WorldComponent>.OnDestroy(ref WorldComponent component, EcsWorld world)
|
||||
{
|
||||
// Actions when EcsWorld.Destroy is called.
|
||||
// Calling OnDestroy, obliges the user to manually reset the component if necessary.
|
||||
// OnDestroy requires manual reset of the component if necessary.
|
||||
component = default;
|
||||
}
|
||||
}
|
||||
@ -926,12 +1018,12 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
<details>
|
||||
<summary>Example of use</summary>
|
||||
|
||||
IEcsWorldComponent<T> interface events, can be used to automatically initialize component fields, and release resources.
|
||||
The `IEcsWorldComponent<T>` interface events can be used to initialize component fields and release resources automatically.
|
||||
``` c#
|
||||
public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
{
|
||||
private SomeClass _object; // Объект который будет утилизироваться.
|
||||
private SomeReusedClass _reusedObject; // Объект который будет переиспользоваться.
|
||||
private SomeClass _object; // Object to be disposed.
|
||||
private SomeReusedClass _reusedObject; // Object to be reused.
|
||||
public SomeClass Object => _object;
|
||||
public SomeReusedClass ReusedObject => _reusedObject;
|
||||
void IEcsWorldComponent<WorldComponent>.Init(ref WorldComponent component, EcsWorld world)
|
||||
@ -939,18 +1031,16 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
if (component._reusedObject == null)
|
||||
component._reusedObject = new SomeReusedClass();
|
||||
component._object = new SomeClass();
|
||||
// Теперь при получении компонента через EcsWorld.Get, _reusedObject и _object уже будут созданы.
|
||||
// After getting the component via EcsWorld.Get, _reusedObject and _object will be ready.
|
||||
}
|
||||
void IEcsWorldComponent<WorldComponent>.OnDestroy(ref WorldComponent component, EcsWorld world)
|
||||
{
|
||||
// Утилизируем не нужный объект, и освобождаем ссылку на него, чтобы GC мог его собрать.
|
||||
// Dispose and clear the reference so GC can collect it.
|
||||
component._object.Dispose();
|
||||
component._object = null;
|
||||
|
||||
// Как вариант тут можно сделать сброс значений у переиспользуемого объекта.
|
||||
// Optionally reset the reused object.
|
||||
// component._reusedObject.Reset();
|
||||
|
||||
// Так как в этом примере не нужно полное обнуление компонента, то строчка ниже не нужна.
|
||||
// If full reset is needed, uncomment the line below.
|
||||
// component = default;
|
||||
}
|
||||
}
|
||||
@ -969,7 +1059,7 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
<td align="center">
|
||||
<a href="https://github.com/DCFApixels/3D-Platformer-DragonECS-Demo">
|
||||
3D Platformer (Example)
|
||||
<img src="https://github.com/user-attachments/assets/c593dba7-eeaa-4706-a043-946f132f3f83" alt="screenshot">
|
||||
<img src="https://github.com/user-attachments/assets/6aba814d-a70b-432f-a905-84d1b6872581" />
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
@ -979,11 +1069,43 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr></tr>
|
||||
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://github.com/Evileptic/Arkanoid">
|
||||
Arkanoid
|
||||
<img src="https://github.com/user-attachments/assets/bbdb4a7f-2f59-4a3a-ab51-a3e4fe0ad35e" alt="screenshot">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
_____________
|
||||
<img tabindex="-1" src="https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt="screenshot">
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Released games:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://dcfapixels.github.io/Project8.html">
|
||||
Crystal Siege
|
||||
<img src="https://github.com/user-attachments/assets/1aa60a50-2668-4919-aca9-d6d2b980c3dd">
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<a href="https://play.google.com/store/apps/details?id=com.ZlodeyStudios.OrdersMatter">
|
||||
Order matters
|
||||
<img src="https://github.com/user-attachments/assets/c55b2647-9b6e-4145-98ff-c3d094600fa1">
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr></tr>
|
||||
|
||||
<tr>
|
||||
<td align="center">
|
||||
<a href="https://yandex.ru/games/app/206024?utm_source=game_popup_menu">
|
||||
@ -992,39 +1114,26 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
</a>
|
||||
</td>
|
||||
<td align="center">
|
||||
<span>
|
||||
ㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤㅤ
|
||||
_____________
|
||||
<img tabindex="-1" src="https://github.com/user-attachments/assets/3fa1ca6d-29f6-43e6-aafe-cc9648d20490" alt="screenshot">
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</br>
|
||||
|
||||
# Extensions
|
||||
* Packages:
|
||||
* [Unity integration](https://github.com/DCFApixels/DragonECS-Unity)
|
||||
* [Dependency autoinjections](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||
* [Classic C# multithreading](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||
* [Recursivity](https://github.com/DCFApixels/DragonECS-Recursivity)
|
||||
* [Hybrid](https://github.com/DCFApixels/DragonECS-Hybrid)
|
||||
* [Graphs](https://github.com/DCFApixels/DragonECS-Graphs)
|
||||
* Utilities:
|
||||
* [Simple syntax](https://gist.github.com/DCFApixels/d7bfbfb8cb70d141deff00be24f28ff0)
|
||||
* [One-Frame Components](https://gist.github.com/DCFApixels/46d512dbcf96c115b94c3af502461f60)
|
||||
* [Code Templates for IDE](https://gist.github.com/ctzcs/0ba948b0e53aa41fe1c87796a401660b) and [for Unity](https://gist.github.com/ctzcs/d4c7730cf6cd984fe6f9e0e3f108a0f1)
|
||||
> *Your extension? If you are developing an extension for DragonECS, you can share it [here](#feedback).
|
||||
|
||||
</br>
|
||||
|
||||
# FAQ
|
||||
## 'ReadOnlySpan<>' could not be found
|
||||
In Unity 2020.1.x, you may encounter this error in the console:
|
||||
```
|
||||
The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing a using directive or an assembly reference?)
|
||||
```
|
||||
To fix this, add the define symbol `ENABLE_DUMMY_SPAN` to `Project Settings/Player/Other Settings/Scripting Define Symbols`.
|
||||
|
||||
## How to enable/disable systems?
|
||||
Directly — not supported.
|
||||
|
||||
The need to enable or disable systems usually appears when the overall game state changes; this may also mean switching a group of systems. Conceptually this is a change of processes. Two solutions are recommended:
|
||||
|
||||
- For global process changes, create a new `EcsPipeline` and run the appropriate pipeline in the engine update loop.
|
||||
- Split `IEcsRun` into multiple processes and run the desired process in the engine update loop. Create a new process interface, implement a runner for it, and obtain the runner via `EcsPipeline.GetRunner<T>()`.
|
||||
|
||||
## Recommendations list: [DragonECS-Vault](https://github.com/DCFApixels/DragonECS-Vault)
|
||||
|
||||
</br>
|
||||
|
||||
@ -1032,6 +1141,11 @@ To fix this, add the define symbol `ENABLE_DUMMY_SPAN` to `Project Settings/Play
|
||||
+ Discord (RU-EN) [https://discord.gg/kqmJjExuCf](https://discord.gg/kqmJjExuCf)
|
||||
+ QQ (中文) [949562781](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=IbDcH43vhfArb30luGMP1TMXB3GCHzxm&authKey=s%2FJfqvv46PswFq68irnGhkLrMR6y9tf%2FUn2mogYizSOGiS%2BmB%2B8Ar9I%2Fnr%2Bs4oS%2B&noverify=0&group_code=949562781)
|
||||
|
||||
</br>
|
||||
|
||||
# License
|
||||
The MIT License: [Open](LICENSE.md)
|
||||
|
||||
</br></br></br>
|
||||
</br></br></br>
|
||||
</br></br></br>
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
},
|
||||
"displayName": "DragonECS",
|
||||
"description": "C# Entity Component System Framework",
|
||||
"unity": "2020.3",
|
||||
"version": "0.9.21",
|
||||
"unity": "2021.2",
|
||||
"version": "1.0.1",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DCFApixels/DragonECS.git"
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#if DISABLE_DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
using DCFApixels.DragonECS.PoolsCore;
|
||||
using DCFApixels.DragonECS.Core;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
#if DISABLE_DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
using DCFApixels.DragonECS.RunnersCore;
|
||||
using System;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
@ -60,7 +59,6 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
#endif
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)]
|
||||
[MetaDescription(EcsConsts.AUTHOR, "...")]
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaID("DragonECS_3273527C9201285BAA0A463F700A50FB")]
|
||||
internal sealed class EcsPreInitRunner : EcsRunner<IEcsPreInit>, IEcsPreInit
|
||||
@ -81,7 +79,6 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
#endif
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)]
|
||||
[MetaDescription(EcsConsts.AUTHOR, "...")]
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaID("DragonECS_ED85527C9201A391AB8EC0B734917859")]
|
||||
internal sealed class EcsInitRunner : EcsRunner<IEcsInit>, IEcsInit
|
||||
@ -102,7 +99,6 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
#endif
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)]
|
||||
[MetaDescription(EcsConsts.AUTHOR, "...")]
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaID("DragonECS_2098527C9201F260C840BFD50BC7E0BA")]
|
||||
internal sealed class EcsRunRunner : EcsRunner<IEcsRun>, IEcsRun
|
||||
@ -183,7 +179,6 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
#endif
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP)]
|
||||
[MetaDescription(EcsConsts.AUTHOR, "...")]
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaID("DragonECS_06A6527C92010430ACEB3DA520F272CC")]
|
||||
internal sealed class EcsDestroyRunner : EcsRunner<IEcsDestroy>, IEcsDestroy
|
||||
|
||||
@ -196,7 +196,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
if (_groupSparsePagePoolCount <= 0)
|
||||
{
|
||||
return MemoryAllocator.AllocAndInit<int>(EcsGroup.PAGE_SIZE).As<int>();
|
||||
return MemoryAllocator.AllocAndInit<int>(EcsGroup.PAGE_SIZE).Ptr;
|
||||
}
|
||||
var takedPage = _groupSparsePagePool[--_groupSparsePagePoolCount];
|
||||
_groupSparsePagePool[_groupSparsePagePoolCount] = MemoryAllocator.Handler.Empty;
|
||||
@ -228,7 +228,7 @@ namespace DCFApixels.DragonECS
|
||||
for (int i = 0; i < _groupSparsePagePoolCount; i++)
|
||||
{
|
||||
ref var page = ref _groupSparsePagePool[i];
|
||||
if (page.IsEmpty == false)
|
||||
if (page.IsCreated)
|
||||
{
|
||||
MemoryAllocator.FreeAndClear(ref page);
|
||||
}
|
||||
@ -291,7 +291,7 @@ namespace DCFApixels.DragonECS
|
||||
private int _count = 0;
|
||||
internal bool _isReleased = true;
|
||||
|
||||
internal static readonly int* _nullPage = MemoryAllocator.AllocAndInit<int>(PageSlot.SIZE).As<int>();
|
||||
internal static readonly int* _nullPage = MemoryAllocator.AllocAndInit<int>(PageSlot.SIZE).Ptr;
|
||||
internal static readonly long _nullPagePtrFake = (long)_nullPage;
|
||||
|
||||
#region Properties
|
||||
@ -344,17 +344,6 @@ namespace DCFApixels.DragonECS
|
||||
#endif
|
||||
return _dense[++index];
|
||||
}
|
||||
// [MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
// set
|
||||
// {
|
||||
// // TODO добавить лок енумератора на изменение
|
||||
//#if DEBUG || DRAGONECS_STABILITY_MODE
|
||||
// if (index < 0 || index >= Count) { Throw.ArgumentOutOfRange(); }
|
||||
//#endif
|
||||
// var oldValue = _dense[index];
|
||||
// _dense[index] = value;
|
||||
// _sparse[oldValue] = 0;
|
||||
// }
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -394,7 +383,7 @@ namespace DCFApixels.DragonECS
|
||||
page.IndexesXOR = 0;
|
||||
page.Count = 0;
|
||||
}
|
||||
_sparsePagesHandler.Dispose();
|
||||
_sparsePagesHandler.DisposeAndReset();
|
||||
}
|
||||
}
|
||||
public void Dispose()
|
||||
@ -555,7 +544,6 @@ namespace DCFApixels.DragonECS
|
||||
ref PageSlot page = ref _sparsePages[i];
|
||||
if (page.Indexes != _nullPage)
|
||||
{
|
||||
//TODO тут надо оптимизировать отчисткой не всего а по dense списку
|
||||
for (int j = 0; j < PageSlot.SIZE; j++)
|
||||
{
|
||||
page.Indexes[j] = 0;
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#if DISABLE_DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using DCFApixels.DragonECS.Core.Unchecked;
|
||||
using System;
|
||||
@ -50,7 +51,7 @@ namespace DCFApixels.DragonECS
|
||||
public bool IsSourceEntities
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return this == EcsWorld.GetWorld(_worldID).GetCurrentEntities_Internal(); }
|
||||
get { return _values == EcsWorld.GetWorld(_worldID).GetCurrentEntities_Internal()._values; }
|
||||
}
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, true)]
|
||||
@ -117,8 +118,8 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region operators
|
||||
public static bool operator ==(EcsSpan left, EcsSpan right) { return left._values == right._values; }
|
||||
public static bool operator !=(EcsSpan left, EcsSpan right) { return left._values != right._values; }
|
||||
public static bool operator ==(EcsSpan left, EcsSpan right) { return left._values == right._values && left._worldID == right._worldID; }
|
||||
public static bool operator !=(EcsSpan left, EcsSpan right) { return left._values != right._values || left._worldID != right._worldID; }
|
||||
#endregion
|
||||
|
||||
#region Enumerator
|
||||
@ -127,6 +128,7 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
public ReadOnlySpan<int> AsSystemSpan() { return _values; }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int First() { return _values[0]; }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -170,6 +172,7 @@ namespace DCFApixels.DragonECS
|
||||
_worldID = span._worldID;
|
||||
}
|
||||
public DebuggerProxy(EcsLongsSpan span) : this(span.ToSpan()) { }
|
||||
public DebuggerProxy(EcsUnsafeSpan span) : this(span.ToSpan()) { }
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@ -317,3 +320,165 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
namespace DCFApixels.DragonECS.Core
|
||||
{
|
||||
#if ENABLE_IL2CPP
|
||||
using Unity.IL2CPP.CompilerServices;
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[DebuggerTypeProxy(typeof(EcsSpan.DebuggerProxy))]
|
||||
public unsafe readonly struct EcsUnsafeSpan
|
||||
{
|
||||
private readonly int* _values;
|
||||
private readonly int _length;
|
||||
private readonly short _worldID;
|
||||
|
||||
#region Properties
|
||||
public bool IsNull
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _worldID == 0; }
|
||||
}
|
||||
public short WorldID
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _worldID; }
|
||||
}
|
||||
public EcsWorld World
|
||||
{
|
||||
get { return EcsWorld.GetWorld(_worldID); }
|
||||
}
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _length; }
|
||||
}
|
||||
public EcsLongsSpan Longs
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return ToSpan().Longs; }
|
||||
}
|
||||
public bool IsSourceEntities
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return ToSpan().IsSourceEntities; }
|
||||
}
|
||||
|
||||
public int this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
#if DEBUG
|
||||
if ((uint)index >= (uint)_length || (uint)index < 0)
|
||||
{
|
||||
ThrowHelper.ThrowIndexOutOfRangeException();
|
||||
}
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
return EcsConsts.NULL_ENTITY_ID;
|
||||
#endif
|
||||
return _values[index];
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal EcsUnsafeSpan(short worldID, int* array, int length)
|
||||
{
|
||||
_worldID = worldID;
|
||||
_values = array;
|
||||
_length = length;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal EcsUnsafeSpan(short worldID, int* array, int start, int length)
|
||||
{
|
||||
_worldID = worldID;
|
||||
_values = array + start;
|
||||
_length = length;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Slice/ToArray
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsUnsafeSpan Slice(int start)
|
||||
{
|
||||
if ((uint)start > (uint)_length)
|
||||
{
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||
}
|
||||
return new EcsUnsafeSpan(_worldID, _values, start, _length - start);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsUnsafeSpan Slice(int start, int length)
|
||||
{
|
||||
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
|
||||
{
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||
}
|
||||
return new EcsUnsafeSpan(_worldID, _values, start, length);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsSpan ToSpan() { return new EcsSpan(_worldID, new ReadOnlySpan<int>(_values, _length)); }
|
||||
public int[] ToArray() { return new ReadOnlySpan<int>(_values, _length).ToArray(); }
|
||||
public int ToArray(ref int[] dynamicBuffer)
|
||||
{
|
||||
if (dynamicBuffer.Length < _length)
|
||||
{
|
||||
Array.Resize(ref dynamicBuffer, ArrayUtility.CeilPow2(_length));
|
||||
}
|
||||
int i = 0;
|
||||
foreach (var e in this)
|
||||
{
|
||||
dynamicBuffer[i++] = e;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
public void ToCollection(ICollection<int> collection)
|
||||
{
|
||||
foreach (var e in this)
|
||||
{
|
||||
collection.Add(e);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region operators
|
||||
public static bool operator ==(EcsUnsafeSpan left, EcsUnsafeSpan right) { return left._values == right._values && left._length == right._length && left._worldID == right._worldID; }
|
||||
public static bool operator !=(EcsUnsafeSpan left, EcsUnsafeSpan right) { return left._values != right._values || left._length != right._length || left._worldID != right._worldID; }
|
||||
public static implicit operator EcsSpan(EcsUnsafeSpan a) { return a.ToSpan(); }
|
||||
#endregion
|
||||
|
||||
#region Enumerator
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan<int>.Enumerator GetEnumerator() { return new ReadOnlySpan<int>(_values, _length).GetEnumerator(); }
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int First() { return _values[0]; }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Last() { return _values[_length - 1]; }
|
||||
public override string ToString()
|
||||
{
|
||||
return CollectionUtility.EntitiesToString(ToArray(), "span");
|
||||
}
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
return obj is EcsUnsafeSpan other && other == this;
|
||||
}
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return *_values ^ _length ^ (_worldID << 16);
|
||||
}
|
||||
private static class ThrowHelper
|
||||
{
|
||||
public static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException();
|
||||
public static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException();
|
||||
public static void ThrowInvalidOperationException() => throw new InvalidOperationException();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,6 +1,4 @@
|
||||
using System;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public static class EcsConsts
|
||||
{
|
||||
@ -88,42 +86,6 @@ namespace DCFApixels.DragonECS
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
public const bool ENABLE_DUMMY_SPAN =
|
||||
#if ENABLE_DUMMY_SPAN
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
|
||||
|
||||
[Obsolete("DRAGONECS_ENABLE_DEBUG_SERVICE")]
|
||||
public const bool ENABLE_DRAGONECS_DEBUGGER =
|
||||
#if ENABLE_DRAGONECS_DEBUGGER
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
[Obsolete("DRAGONECS_DISABLE_POOLS_EVENTS")]
|
||||
public const bool DISABLE_POOLS_EVENTS =
|
||||
#if DISABLE_POOLS_EVENTS
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
[Obsolete("DRAGONECS_DISABLE_CATH_EXCEPTIONS")]
|
||||
public const bool DISABLE_CATH_EXCEPTIONS =
|
||||
#if DISABLE_CATH_EXCEPTIONS
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
[Obsolete("DRAGONECS_STABILITY_MODE")]
|
||||
public const bool ENABLE_DRAGONECS_ASSERT_CHEKS =
|
||||
#if ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
true;
|
||||
#else
|
||||
false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@ -11,63 +11,84 @@ namespace DCFApixels.DragonECS.Core
|
||||
void Init(ref T component, EcsWorld world);
|
||||
void OnDestroy(ref T component, EcsWorld world);
|
||||
}
|
||||
public static class EcsWorldComponentHandler<T>
|
||||
public static class EcsWorldComponent<T>
|
||||
{
|
||||
public static readonly IEcsWorldComponent<T> instance;
|
||||
public static readonly bool isHasHandler;
|
||||
static EcsWorldComponentHandler()
|
||||
public static readonly IEcsWorldComponent<T> CustomHandler;
|
||||
public static readonly bool IsCustom;
|
||||
static EcsWorldComponent()
|
||||
{
|
||||
T def = default;
|
||||
if (def is IEcsWorldComponent<T> intrf)
|
||||
T raw = default;
|
||||
if (raw is IEcsWorldComponent<T> handler)
|
||||
{
|
||||
isHasHandler = true;
|
||||
instance = intrf;
|
||||
IsCustom = true;
|
||||
CustomHandler = handler;
|
||||
}
|
||||
else
|
||||
{
|
||||
isHasHandler = false;
|
||||
instance = new DummyHandler();
|
||||
IsCustom = false;
|
||||
CustomHandler = new DummyHandler();
|
||||
}
|
||||
}
|
||||
private class DummyHandler : IEcsWorldComponent<T>
|
||||
private sealed class DummyHandler : IEcsWorldComponent<T>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Init(ref T component, EcsWorld world) { }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void OnDestroy(ref T component, EcsWorld world) { }
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IEcsComponentReset
|
||||
#region IEcsComponentLifecycle
|
||||
public interface IEcsComponentLifecycle<T>
|
||||
{
|
||||
void Enable(ref T component);
|
||||
void Disable(ref T component);
|
||||
void OnAdd(ref T component, short worldID, int entityID);
|
||||
void OnDel(ref T component, short worldID, int entityID);
|
||||
}
|
||||
public static class EcsComponentLifecycleHandler<T>
|
||||
public static class EcsComponentLifecycle<T> where T : struct
|
||||
{
|
||||
public static readonly IEcsComponentLifecycle<T> instance;
|
||||
public static readonly bool isHasHandler;
|
||||
static EcsComponentLifecycleHandler()
|
||||
public static readonly IEcsComponentLifecycle<T> CustomHandler;
|
||||
public static readonly bool IsCustom;
|
||||
static EcsComponentLifecycle()
|
||||
{
|
||||
T def = default;
|
||||
if (def is IEcsComponentLifecycle<T> intrf)
|
||||
T raw = default;
|
||||
if (raw is IEcsComponentLifecycle<T> handler)
|
||||
{
|
||||
isHasHandler = true;
|
||||
instance = intrf;
|
||||
IsCustom = true;
|
||||
CustomHandler = handler;
|
||||
}
|
||||
else
|
||||
{
|
||||
isHasHandler = false;
|
||||
instance = new DummyHandler();
|
||||
IsCustom = false;
|
||||
CustomHandler = new DummyHandler();
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void OnAdd(bool isCustom, IEcsComponentLifecycle<T> custom, ref T component, short worldID, int entityID)
|
||||
{
|
||||
if (isCustom)
|
||||
{
|
||||
custom.OnAdd(ref component, worldID, entityID);
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void OnDel(bool isCustom, IEcsComponentLifecycle<T> custom, ref T component, short worldID, int entityID)
|
||||
{
|
||||
if (isCustom)
|
||||
{
|
||||
custom.OnDel(ref component, worldID, entityID);
|
||||
}
|
||||
else
|
||||
{
|
||||
component = default;
|
||||
}
|
||||
}
|
||||
private sealed class DummyHandler : IEcsComponentLifecycle<T>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Enable(ref T component) { component = default; }
|
||||
public void OnAdd(ref T component, short worldID, int entityID) { component = default; }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Disable(ref T component) { component = default; }
|
||||
public void OnDel(ref T component, short worldID, int entityID) { component = default; }
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
@ -77,22 +98,34 @@ namespace DCFApixels.DragonECS.Core
|
||||
{
|
||||
void Copy(ref T from, ref T to);
|
||||
}
|
||||
public static class EcsComponentCopyHandler<T>
|
||||
public static class EcsComponentCopy<T> where T : struct
|
||||
{
|
||||
public static readonly IEcsComponentCopy<T> instance;
|
||||
public static readonly bool isHasHandler;
|
||||
static EcsComponentCopyHandler()
|
||||
public static readonly IEcsComponentCopy<T> CustomHandler;
|
||||
public static readonly bool IsCustom;
|
||||
static EcsComponentCopy()
|
||||
{
|
||||
T def = default;
|
||||
if (def is IEcsComponentCopy<T> intrf)
|
||||
T raw = default;
|
||||
if (raw is IEcsComponentCopy<T> handler)
|
||||
{
|
||||
isHasHandler = true;
|
||||
instance = intrf;
|
||||
IsCustom = true;
|
||||
CustomHandler = handler;
|
||||
}
|
||||
else
|
||||
{
|
||||
isHasHandler = false;
|
||||
instance = new DummyHandler();
|
||||
IsCustom = false;
|
||||
CustomHandler = new DummyHandler();
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Copy(bool isCustom, IEcsComponentCopy<T> custom, ref T from, ref T to)
|
||||
{
|
||||
if (isCustom)
|
||||
{
|
||||
custom.Copy(ref from, ref to);
|
||||
}
|
||||
else
|
||||
{
|
||||
to = from;
|
||||
}
|
||||
}
|
||||
private sealed class DummyHandler : IEcsComponentCopy<T>
|
||||
|
||||
@ -172,6 +172,30 @@ namespace DCFApixels.DragonECS
|
||||
#if DEBUG || DRAGONECS_ENABLE_DEBUG_SERVICE
|
||||
OnPrint(tag, v);
|
||||
DebugService.CurrentThreadInstance.Print(tag, v);
|
||||
#endif
|
||||
}
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
[UnityEngine.HideInCallstack]
|
||||
#endif
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void PrintJson(object v)
|
||||
{
|
||||
#if DEBUG || DRAGONECS_ENABLE_DEBUG_SERVICE
|
||||
string json = JsonDebugger.ToJsonLog(v);
|
||||
OnPrint(string.Empty, json);
|
||||
DebugService.CurrentThreadInstance.Print(json);
|
||||
#endif
|
||||
}
|
||||
#if UNITY_2021_3_OR_NEWER
|
||||
[UnityEngine.HideInCallstack]
|
||||
#endif
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void PrintJson(string tag, object v)
|
||||
{
|
||||
#if DEBUG || DRAGONECS_ENABLE_DEBUG_SERVICE
|
||||
string json = JsonDebugger.ToJsonLog(v);
|
||||
OnPrint(tag, json);
|
||||
DebugService.CurrentThreadInstance.Print(tag, json);
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -285,7 +285,7 @@ namespace DCFApixels.DragonECS
|
||||
public static TypeMeta GetTypeMeta(object obj)
|
||||
{
|
||||
if (obj == null) { return TypeMeta.NullTypeMeta; }
|
||||
return TypeMeta.Get(GetTypeMetaSource(obj).GetType());
|
||||
return TypeMeta.Get(Type.GetTypeHandle(obj));
|
||||
}
|
||||
public static TypeMeta GetTypeMeta<T>()
|
||||
{
|
||||
@ -295,16 +295,9 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
return TypeMeta.Get(type);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TypeMetaProvider
|
||||
public static bool IsTypeMetaProvided(object obj)
|
||||
public static TypeMeta GetTypeMeta(RuntimeTypeHandle typeHandle)
|
||||
{
|
||||
return obj is IEcsTypeMetaProvider;
|
||||
}
|
||||
public static object GetTypeMetaSource(object obj)
|
||||
{
|
||||
return obj is IEcsTypeMetaProvider intr ? intr.MetaSource : obj;
|
||||
return TypeMeta.Get(typeHandle);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@ -318,7 +311,11 @@ namespace DCFApixels.DragonECS
|
||||
#endif
|
||||
return EcsDebugUtility.GetTypeMeta(self);
|
||||
}
|
||||
public static TypeMeta ToMeta(this Type self)
|
||||
public static TypeMeta GetMeta(this Type self)
|
||||
{
|
||||
return EcsDebugUtility.GetTypeMeta(self);
|
||||
}
|
||||
public static TypeMeta GetMeta(this RuntimeTypeHandle self)
|
||||
{
|
||||
return EcsDebugUtility.GetTypeMeta(self);
|
||||
}
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50bc53c3762bf6b4ea127004a89a894e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,7 +0,0 @@
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public interface IEcsTypeMetaProvider
|
||||
{
|
||||
object MetaSource { get; }
|
||||
}
|
||||
}
|
||||
304
src/DebugUtils/JsonDebugger.cs
Normal file
304
src/DebugUtils/JsonDebugger.cs
Normal file
@ -0,0 +1,304 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace DCFApixels.DragonECS.Core.Internal
|
||||
{
|
||||
internal static class JsonDebugger
|
||||
{
|
||||
private readonly static List<string> _indentsChache = new List<string>();
|
||||
internal static string ToJsonLog(object obj)
|
||||
{
|
||||
if (obj == null) { return "null"; }
|
||||
var sb = new StringBuilder();
|
||||
int linesCounter = 0;
|
||||
var visited = new Dictionary<object, int>();
|
||||
ToJsonLog(ref linesCounter, obj, sb, visited, 0, 2);
|
||||
string json = sb.ToString();
|
||||
return json;
|
||||
}
|
||||
private static string GetIndentString(int count)
|
||||
{
|
||||
int newSize = count + 1;
|
||||
while (newSize > _indentsChache.Count)
|
||||
{
|
||||
_indentsChache.Add(new string(' ', _indentsChache.Count));
|
||||
}
|
||||
return _indentsChache[count];
|
||||
}
|
||||
private static void NewLine(
|
||||
ref int linesCounter,
|
||||
StringBuilder sb,
|
||||
int indent,
|
||||
int indentStep)
|
||||
{
|
||||
sb.AppendLine();
|
||||
sb.Append(GetIndentString(indent * indentStep));
|
||||
linesCounter++;
|
||||
}
|
||||
private static void ToJsonLog(
|
||||
ref int linesCounter,
|
||||
object value,
|
||||
StringBuilder sb,
|
||||
Dictionary<object, int> visited,
|
||||
int indent,
|
||||
int indentStep)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
sb.Append("null");
|
||||
return;
|
||||
}
|
||||
|
||||
Type type = value.GetType();
|
||||
|
||||
if (value is IEnumerable<char> rawString)
|
||||
{
|
||||
sb.Append('"');
|
||||
string str = value as string;
|
||||
if (str == null)
|
||||
{
|
||||
str = rawString.ToString();
|
||||
}
|
||||
EscapeString(str, sb);
|
||||
sb.Append('"');
|
||||
return;
|
||||
}
|
||||
|
||||
if (type == typeof(float))
|
||||
{
|
||||
sb.Append(((float)value).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
return;
|
||||
}
|
||||
if(type == typeof(double))
|
||||
{
|
||||
sb.Append(((double)value).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
return;
|
||||
}
|
||||
if (type == typeof(decimal))
|
||||
{
|
||||
sb.Append(((decimal)value).ToString(System.Globalization.CultureInfo.InvariantCulture));
|
||||
return;
|
||||
}
|
||||
if (type == typeof(bool))
|
||||
{
|
||||
sb.Append((bool)value ? "true" : "false");
|
||||
return;
|
||||
}
|
||||
if (type.IsEnum)
|
||||
{
|
||||
if(type.TryGetAttribute(out FlagsAttribute _))
|
||||
{
|
||||
sb.Append(type.FullName);
|
||||
sb.Append('.');
|
||||
sb.Append(value.ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(value.ToString());
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
if (type == typeof(DateTime))
|
||||
{
|
||||
sb.Append('"');
|
||||
sb.Append(((DateTime)value).ToString("yyyy-MM-ddTHH:mm:ss.fffZ", System.Globalization.CultureInfo.InvariantCulture));
|
||||
sb.Append('"');
|
||||
return;
|
||||
}
|
||||
if (type == typeof(Guid))
|
||||
{
|
||||
sb.Append('"');
|
||||
sb.Append(value.ToString());
|
||||
sb.Append('"');
|
||||
return;
|
||||
}
|
||||
if (type.IsPrimitive)
|
||||
{
|
||||
sb.Append(value);
|
||||
return;
|
||||
}
|
||||
if (value is Exception e)
|
||||
{
|
||||
sb.Append('"');
|
||||
sb.Append(type.Name);
|
||||
sb.Append(':').Append(' ');
|
||||
sb.Append(e.Message);
|
||||
sb.Append('"');
|
||||
return;
|
||||
}
|
||||
if (value is Type t)
|
||||
{
|
||||
sb.Append('"');
|
||||
sb.Append(t.GetMeta().TypeName);
|
||||
sb.Append('"');
|
||||
return;
|
||||
}
|
||||
if (type.Namespace == typeof(FieldInfo).Namespace || type.IsPointer)
|
||||
{
|
||||
sb.Append('"');
|
||||
sb.Append(value.ToString());
|
||||
sb.Append('"');
|
||||
return;
|
||||
}
|
||||
|
||||
if (value is Delegate del)
|
||||
{
|
||||
var list = del.GetInvocationList();
|
||||
if (list.Length == 0)
|
||||
{
|
||||
sb.Append("null");
|
||||
return;
|
||||
}
|
||||
if (list.Length == 1)
|
||||
{
|
||||
sb.Append('"');
|
||||
sb.Append(del.Target.GetType().FullName);
|
||||
sb.Append('.');
|
||||
sb.Append(del.Method.Name);
|
||||
sb.Append('"');
|
||||
return;
|
||||
}
|
||||
ToJsonLog(ref linesCounter, list, sb, visited, indent, indentStep);
|
||||
return;
|
||||
}
|
||||
|
||||
if (type.IsValueType == false)
|
||||
{
|
||||
if (visited.TryGetValue(value, out var line))
|
||||
{
|
||||
sb.Append('#');
|
||||
sb.Append(type.Name);
|
||||
sb.Append('#');
|
||||
sb.Append(line);
|
||||
sb.Append('#');
|
||||
return;
|
||||
}
|
||||
visited.Add(value, linesCounter);
|
||||
}
|
||||
|
||||
// Collections
|
||||
IEnumerable enumerable = value as IEnumerable;
|
||||
if(enumerable != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
enumerable.GetEnumerator();
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
enumerable = null;
|
||||
}
|
||||
}
|
||||
if (enumerable != null)
|
||||
{
|
||||
sb.Append('[');
|
||||
bool first = true;
|
||||
|
||||
foreach (object item in enumerable)
|
||||
{
|
||||
if (!first) { sb.Append(','); } else { first = false; }
|
||||
NewLine(ref linesCounter, sb, indent + 1, indentStep);
|
||||
ToJsonLog(ref linesCounter, item, sb, visited, indent + 1, indentStep);
|
||||
}
|
||||
|
||||
// перенос строки если были элементы
|
||||
if (!first)
|
||||
{
|
||||
NewLine(ref linesCounter, sb, indent, indentStep);
|
||||
}
|
||||
sb.Append(']');
|
||||
}
|
||||
else // Object
|
||||
{
|
||||
sb.Append('{');
|
||||
{
|
||||
NewLine(ref linesCounter, sb, indent + 1, indentStep);
|
||||
sb.Append("\"Type\": ");
|
||||
|
||||
ToJsonLog(ref linesCounter, type, sb, visited, indent + 1, indentStep);
|
||||
}
|
||||
|
||||
// Fields
|
||||
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var field in fields)
|
||||
{
|
||||
if (field.IsStatic) { continue; }
|
||||
|
||||
NewLine(ref linesCounter, sb, indent + 1, indentStep);
|
||||
sb.Append('"');
|
||||
sb.Append(field.Name);
|
||||
sb.Append('"');
|
||||
sb.Append(':').Append(' ');
|
||||
|
||||
object fieldValue = field.GetValue(value);
|
||||
ToJsonLog(ref linesCounter, fieldValue, sb, visited, indent + 1, 2);
|
||||
}
|
||||
|
||||
// Properties
|
||||
var properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
foreach (var prop in properties)
|
||||
{
|
||||
if (prop.GetIndexParameters().Length > 0 ||
|
||||
prop.GetMethod == null ||
|
||||
prop.GetMethod.IsStatic)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
NewLine(ref linesCounter, sb, indent + 1, indentStep);
|
||||
sb.Append('"');
|
||||
sb.Append(prop.Name);
|
||||
sb.Append('"');
|
||||
sb.Append(':').Append(' ');
|
||||
|
||||
object propValue;
|
||||
try
|
||||
{
|
||||
propValue = prop.GetValue(value);
|
||||
}
|
||||
catch (Exception cathcedE)
|
||||
{
|
||||
propValue = cathcedE;
|
||||
}
|
||||
ToJsonLog(ref linesCounter, propValue, sb, visited, indent + 1, indentStep);
|
||||
}
|
||||
|
||||
NewLine(ref linesCounter, sb, indent, indentStep);
|
||||
sb.Append('}');
|
||||
}
|
||||
|
||||
//visited.Remove(value);
|
||||
}
|
||||
|
||||
private static void EscapeString(string s, StringBuilder sb)
|
||||
{
|
||||
foreach (char c in s)
|
||||
{
|
||||
switch (c)
|
||||
{
|
||||
case '"': sb.Append("\\\""); break;
|
||||
case '\\': sb.Append("\\\\"); break;
|
||||
case '\b': sb.Append("\\b"); break;
|
||||
case '\f': sb.Append("\\f"); break;
|
||||
case '\n': sb.Append("\\n"); break;
|
||||
case '\r': sb.Append("\\r"); break;
|
||||
case '\t': sb.Append("\\t"); break;
|
||||
default:
|
||||
if (char.IsControl(c))
|
||||
{
|
||||
sb.AppendFormat("\\u{0:x4}", (int)c);
|
||||
}
|
||||
else
|
||||
{
|
||||
sb.Append(c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a591de1858028504d819333121bfddd6
|
||||
guid: 0f3febd9a6cdfea4fb27039c5f66436f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@ -5,7 +5,7 @@ using System;
|
||||
|
||||
namespace DCFApixels.DragonECS.Core
|
||||
{
|
||||
public abstract class EcsMetaAttribute : Attribute { }
|
||||
public abstract class DragonMetaAttribute : Attribute { }
|
||||
|
||||
internal static class EcsMetaAttributeHalper
|
||||
{
|
||||
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb8cc656a6e80f843b8794af9f63faa8
|
||||
guid: fd6afd87df377e5408428ccec3c02685
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@ -23,7 +23,7 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
}
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class MetaColorAttribute : EcsMetaAttribute, IMetaColor
|
||||
public sealed class MetaColorAttribute : DragonMetaAttribute, IMetaColor
|
||||
{
|
||||
public readonly MetaColor color;
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ using System;
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class MetaDescriptionAttribute : EcsMetaAttribute
|
||||
public sealed class MetaDescriptionAttribute : DragonMetaAttribute
|
||||
{
|
||||
public readonly MetaDescription Data;
|
||||
public MetaDescriptionAttribute(string text)
|
||||
|
||||
@ -10,7 +10,7 @@ using System.Text.RegularExpressions;
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class MetaGroupAttribute : EcsMetaAttribute
|
||||
public sealed class MetaGroupAttribute : DragonMetaAttribute
|
||||
{
|
||||
public const char SEPARATOR = MetaGroup.SEPARATOR;
|
||||
public readonly string Name = string.Empty;
|
||||
@ -73,6 +73,10 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
return new MetaGroup(name);
|
||||
}
|
||||
public static MetaGroup FromName(params string[] path)
|
||||
{
|
||||
return FromName(string.Join(SEPARATOR, path));
|
||||
}
|
||||
public static MetaGroup FromNameSpace(Type type)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(type.Namespace))
|
||||
|
||||
@ -14,7 +14,7 @@ using System.Text.RegularExpressions;
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class MetaIDAttribute : EcsMetaAttribute
|
||||
public sealed class MetaIDAttribute : DragonMetaAttribute
|
||||
{
|
||||
public readonly string ID;
|
||||
public MetaIDAttribute(string id)
|
||||
|
||||
@ -7,7 +7,7 @@ using System;
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class MetaNameAttribute : EcsMetaAttribute
|
||||
public sealed class MetaNameAttribute : DragonMetaAttribute
|
||||
{
|
||||
public readonly string name;
|
||||
public readonly bool isHideGeneric;
|
||||
|
||||
31
src/DebugUtils/MetaAttributes/MetaProxyAttribute.cs
Normal file
31
src/DebugUtils/MetaAttributes/MetaProxyAttribute.cs
Normal file
@ -0,0 +1,31 @@
|
||||
#if DISABLE_DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
|
||||
public sealed class MetaProxyAttribute : DragonMetaAttribute
|
||||
{
|
||||
public Type Type;
|
||||
public bool ForDeclaringType;
|
||||
public MetaProxyAttribute(Type type, bool forDeclaringType = false)
|
||||
{
|
||||
Type = type;
|
||||
ForDeclaringType = forDeclaringType;
|
||||
}
|
||||
}
|
||||
public class MetaProxyBase
|
||||
{
|
||||
public static readonly MetaProxyBase EmptyProxy = new MetaProxyBase(typeof(void));
|
||||
public virtual string Name { get { return null; } }
|
||||
public virtual MetaColor? Color { get { return null; } }
|
||||
public virtual MetaDescription Description { get { return null; } }
|
||||
public virtual MetaGroup Group { get { return null; } }
|
||||
public virtual IEnumerable<string> Tags { get { return null; } }
|
||||
public MetaProxyBase(Type type) { }
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a642dc8905124247bf83c9d13d8fb13
|
||||
guid: 2ce533d3befbc0f4fb16b68a1bcce552
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@ -8,7 +8,7 @@ using System.Collections.Generic;
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class MetaTagsAttribute : EcsMetaAttribute
|
||||
public sealed class MetaTagsAttribute : DragonMetaAttribute
|
||||
{
|
||||
public const char SEPARATOR = ',';
|
||||
private readonly string[] _tags = Array.Empty<string>();
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
#if DISABLE_DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
#if DEBUG || !REFLECTION_DISABLED
|
||||
#define REFLECTION_ENABLED
|
||||
#endif
|
||||
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using DCFApixels.DragonECS.PoolsCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
#if DEBUG || !REFLECTION_DISABLED
|
||||
#if REFLECTION_ENABLED
|
||||
using System.Reflection;
|
||||
#endif
|
||||
|
||||
@ -46,11 +48,13 @@ namespace DCFApixels.DragonECS
|
||||
public static readonly TypeMeta NullTypeMeta;
|
||||
|
||||
private static readonly object _lock = new object();
|
||||
private static readonly Dictionary<Type, TypeMeta> _metaCache = new Dictionary<Type, TypeMeta>();
|
||||
private static readonly Dictionary<RuntimeTypeHandle, TypeMeta> _metaCache = new Dictionary<RuntimeTypeHandle, TypeMeta>();
|
||||
private static int _increment = 1;
|
||||
|
||||
private readonly int _uniqueID;
|
||||
internal readonly Type _type;
|
||||
private readonly MetaProxyBase _proxy;
|
||||
private readonly bool _isSelfProxy;
|
||||
|
||||
private bool _isCustomName;
|
||||
private bool _isCustomColor;
|
||||
@ -93,24 +97,68 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
_initFlags = InitFlag.All,
|
||||
};
|
||||
_metaCache.Add(typeof(void), NullTypeMeta);
|
||||
|
||||
_metaCache.Add(typeof(void).TypeHandle, NullTypeMeta);
|
||||
}
|
||||
public static TypeMeta Get(Type type)
|
||||
public static TypeMeta Get(Type type) { return Get(type.TypeHandle); }
|
||||
public static TypeMeta Get(RuntimeTypeHandle typeHandle)
|
||||
{
|
||||
lock (_lock) //TODO посмотреть можно ли тут убрать лок
|
||||
lock (_lock)
|
||||
{
|
||||
if (_metaCache.TryGetValue(type, out TypeMeta result) == false)
|
||||
if (_metaCache.TryGetValue(typeHandle, out TypeMeta result) == false)
|
||||
{
|
||||
result = new TypeMeta(type);
|
||||
_metaCache.Add(type, result);
|
||||
result = new TypeMeta(Type.GetTypeFromHandle(typeHandle));
|
||||
_metaCache.Add(typeHandle, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
private static Type FindDeclaringType(Type targetPureType, Type currentType)
|
||||
{
|
||||
if (currentType == typeof(object)) { return null; }
|
||||
var pure = currentType.GetPureType();
|
||||
if (pure == targetPureType)
|
||||
{
|
||||
return currentType;
|
||||
}
|
||||
return FindDeclaringType(targetPureType, currentType.BaseType);
|
||||
}
|
||||
private TypeMeta(Type type)
|
||||
{
|
||||
_uniqueID = _increment++;
|
||||
_type = type;
|
||||
_proxy = MetaProxyBase.EmptyProxy;
|
||||
|
||||
if (type.TryGetAttributeInherited<MetaProxyAttribute>(out var proxyAtr, out var declaringAtrType))
|
||||
{
|
||||
#if REFLECTION_ENABLED
|
||||
#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
|
||||
#pragma warning disable IL2055 // Either the type on which the MakeGenericType is called can't be statically determined, or the type parameters to be used for generic arguments can't be statically determined.
|
||||
#pragma warning disable IL2077 // Target parameter argument does not satisfy 'DynamicallyAccessedMembersAttribute' in call to target method. The source field does not have matching annotations.
|
||||
Type proxyType = proxyAtr.Type;
|
||||
if (proxyType.ContainsGenericParameters && proxyType.IsNested)
|
||||
{
|
||||
if (declaringAtrType != null && declaringAtrType.ContainsGenericParameters == false)
|
||||
{
|
||||
var args = declaringAtrType.GetGenericArguments();
|
||||
proxyType = proxyType.MakeGenericType(args);
|
||||
}
|
||||
}
|
||||
|
||||
if (proxyType.ContainsGenericParameters == false)
|
||||
{
|
||||
var proxy = Activator.CreateInstance(proxyType, proxyAtr.ForDeclaringType ? declaringAtrType : type) as MetaProxyBase;
|
||||
if (proxy != null)
|
||||
{
|
||||
_proxy = proxy;
|
||||
_isSelfProxy = declaringAtrType == type;
|
||||
}
|
||||
}
|
||||
#pragma warning restore IL2055
|
||||
#pragma warning restore IL3050
|
||||
#pragma warning restore IL2077
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -126,7 +174,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
if (_initFlags.HasFlag(InitFlag.Name) == false)
|
||||
{
|
||||
(_name, _isCustomName) = MetaGenerator.GetMetaName(_type);
|
||||
(_name, _isCustomName) = MetaGenerator.GetMetaName(this);
|
||||
_typeName = _isCustomName ? MetaGenerator.GetTypeName(_type) : _name;
|
||||
_initFlags |= InitFlag.Name;
|
||||
}
|
||||
@ -191,7 +239,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
if (_initFlags.HasFlag(InitFlag.Description) == false)
|
||||
{
|
||||
_description = MetaGenerator.GetDescription(_type);
|
||||
_description = MetaGenerator.GetDescription(this);
|
||||
_initFlags |= InitFlag.Description;
|
||||
}
|
||||
return _description;
|
||||
@ -206,7 +254,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
if (_initFlags.HasFlag(InitFlag.Group) == false)
|
||||
{
|
||||
_group = MetaGenerator.GetGroup(_type);
|
||||
_group = MetaGenerator.GetGroup(this);
|
||||
_initFlags |= InitFlag.Group;
|
||||
}
|
||||
return _group;
|
||||
@ -219,7 +267,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
if (_initFlags.HasFlag(InitFlag.Tags) == false)
|
||||
{
|
||||
_tags = MetaGenerator.GetTags(_type);
|
||||
_tags = MetaGenerator.GetTags(this);
|
||||
_initFlags |= InitFlag.Tags;
|
||||
_isHidden = _tags.Contains(MetaTags.HIDDEN);
|
||||
_isObsolete = _tags.Contains(MetaTags.OBSOLETE);
|
||||
@ -375,7 +423,7 @@ namespace DCFApixels.DragonECS
|
||||
lock (_lock)
|
||||
{
|
||||
_metaCache.Clear();
|
||||
_metaCache.Add(typeof(void), NullTypeMeta);
|
||||
_metaCache.Add(typeof(void).TypeHandle, NullTypeMeta);
|
||||
}
|
||||
}
|
||||
ITypeMeta ITypeMeta.BaseMeta
|
||||
@ -384,7 +432,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
private static bool CheckEcsMemener(Type checkedType)
|
||||
{
|
||||
#if DEBUG || !REFLECTION_DISABLED
|
||||
#if REFLECTION_ENABLED
|
||||
return checkedType.IsInterface == false && checkedType.IsAbstract == false && typeof(IEcsMember).IsAssignableFrom(checkedType);
|
||||
#else
|
||||
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(TypeMeta)}.{nameof(CheckEcsMemener)} method does not work.");
|
||||
@ -395,7 +443,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
if (IsHasCustomMeta(type))
|
||||
{
|
||||
meta = type.ToMeta();
|
||||
meta = type.GetMeta();
|
||||
return true;
|
||||
}
|
||||
meta = null;
|
||||
@ -403,16 +451,16 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
public static bool IsHasCustomMeta(Type type)
|
||||
{
|
||||
#if DEBUG || !REFLECTION_DISABLED
|
||||
return CheckEcsMemener(type) || Attribute.GetCustomAttributes(type, typeof(EcsMetaAttribute), false).Length > 0;
|
||||
#if REFLECTION_ENABLED
|
||||
return CheckEcsMemener(type) || Attribute.GetCustomAttributes(type, typeof(DragonMetaAttribute), false).Length > 0;
|
||||
#else
|
||||
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(TypeMeta)}.{nameof(IsHasMeta)} method does not work.");
|
||||
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(TypeMeta)}.{nameof(IsHasCustomMeta)} method does not work.");
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
public static bool IsHasMetaID(Type type)
|
||||
{
|
||||
#if DEBUG || !REFLECTION_DISABLED
|
||||
#if REFLECTION_ENABLED
|
||||
return TryGetCustomMeta(type, out TypeMeta meta) && meta.IsHasMetaID();
|
||||
#else
|
||||
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(TypeMeta)}.{nameof(IsHasMetaID)} method does not work.");
|
||||
@ -469,15 +517,6 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Obsolete
|
||||
[Obsolete("Use TryGetCustomMeta(type)")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public static bool IsHasMeta(Type type)
|
||||
{
|
||||
return IsHasCustomMeta(type);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region MetaGenerator
|
||||
private static class MetaGenerator
|
||||
{
|
||||
@ -488,15 +527,20 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
return EcsDebugUtility.GetGenericTypeName(type, GENERIC_NAME_DEPTH);
|
||||
}
|
||||
public static (string, bool) GetMetaName(Type type)
|
||||
public static (string, bool) GetMetaName(TypeMeta meta)
|
||||
{
|
||||
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
|
||||
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
|
||||
if (meta._isSelfProxy && meta._proxy.Name != null)
|
||||
{
|
||||
return (meta._proxy.Name, true);
|
||||
}
|
||||
var type = meta.Type;
|
||||
bool isCustom = type.TryGetAttribute(out MetaNameAttribute atr) && string.IsNullOrEmpty(atr.name) == false;
|
||||
if (isCustom)
|
||||
{
|
||||
if ((type.IsGenericType && atr.isHideGeneric == false) == false)
|
||||
{
|
||||
return (atr.name, isCustom);
|
||||
return (atr.name, true);
|
||||
}
|
||||
string genericParams = "";
|
||||
Type[] typeParameters = type.GetGenericArguments();
|
||||
@ -505,12 +549,16 @@ namespace DCFApixels.DragonECS
|
||||
string paramTypeName = EcsDebugUtility.GetGenericTypeName(typeParameters[i], GENERIC_NAME_DEPTH);
|
||||
genericParams += (i == 0 ? paramTypeName : $", {paramTypeName}");
|
||||
}
|
||||
return ($"{atr.name}<{genericParams}>", isCustom);
|
||||
return ($"{atr.name}<{genericParams}>", true);
|
||||
}
|
||||
return (EcsDebugUtility.GetGenericTypeName(type, GENERIC_NAME_DEPTH), isCustom);
|
||||
if (meta._proxy.Name != null)
|
||||
{
|
||||
return (meta._proxy.Name, true);
|
||||
}
|
||||
return (EcsDebugUtility.GetGenericTypeName(type, GENERIC_NAME_DEPTH), false);
|
||||
#else
|
||||
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetMetaName)} method does not work.");
|
||||
return (type.Name, false);
|
||||
return (meta.Type.Name, false);
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
@ -531,9 +579,21 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
public static (MetaColor, bool) GetColor(TypeMeta meta)
|
||||
{
|
||||
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
|
||||
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
|
||||
if (meta._isSelfProxy && meta._proxy.Color != null)
|
||||
{
|
||||
return (meta._proxy.Color.Value, true);
|
||||
}
|
||||
bool isCustom = meta.Type.TryGetAttribute(out MetaColorAttribute atr);
|
||||
return (isCustom ? atr.color : AutoColor(meta), isCustom);
|
||||
if (isCustom)
|
||||
{
|
||||
return (atr.color, true);
|
||||
}
|
||||
if (meta._proxy.Color != null)
|
||||
{
|
||||
return (meta._proxy.Color.Value, true);
|
||||
}
|
||||
return (AutoColor(meta), false);
|
||||
#else
|
||||
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetColor)} method does not work.");
|
||||
return (MetaColor.White, false);
|
||||
@ -542,17 +602,22 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region GetGroup
|
||||
public static MetaGroup GetGroup(Type type)
|
||||
public static MetaGroup GetGroup(TypeMeta meta)
|
||||
{
|
||||
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
|
||||
if (type.TryGetAttribute(out MetaGroupAttribute atr))
|
||||
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
|
||||
if (meta._isSelfProxy && meta._proxy.Group != null)
|
||||
{
|
||||
return meta._proxy.Group;
|
||||
}
|
||||
if (meta.Type.TryGetAttribute(out MetaGroupAttribute atr))
|
||||
{
|
||||
return MetaGroup.FromName(atr.Name);
|
||||
}
|
||||
else
|
||||
if (meta._proxy.Group != null)
|
||||
{
|
||||
return MetaGroup.FromNameSpace(type);
|
||||
return meta._proxy.Group;
|
||||
}
|
||||
return MetaGroup.FromNameSpace(meta.Type);
|
||||
#else
|
||||
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetGroup)} method does not work.");
|
||||
return MetaGroup.Empty;
|
||||
@ -561,11 +626,22 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region GetDescription
|
||||
public static MetaDescription GetDescription(Type type)
|
||||
public static MetaDescription GetDescription(TypeMeta meta)
|
||||
{
|
||||
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
|
||||
bool isCustom = type.TryGetAttribute(out MetaDescriptionAttribute atr);
|
||||
return isCustom ? atr.Data : MetaDescription.Empty;
|
||||
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
|
||||
if (meta._isSelfProxy && meta._proxy.Description != null)
|
||||
{
|
||||
return meta._proxy.Description;
|
||||
}
|
||||
if (meta.Type.TryGetAttribute(out MetaDescriptionAttribute atr))
|
||||
{
|
||||
return atr.Data;
|
||||
}
|
||||
if (meta._proxy.Description != null)
|
||||
{
|
||||
return meta._proxy.Description;
|
||||
}
|
||||
return MetaDescription.Empty;
|
||||
#else
|
||||
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetDescription)} method does not work.");
|
||||
return MetaDescription.Empty;
|
||||
@ -574,11 +650,22 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region GetTags
|
||||
public static IReadOnlyList<string> GetTags(Type type)
|
||||
public static IReadOnlyList<string> GetTags(TypeMeta meta)
|
||||
{
|
||||
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
|
||||
var atr = type.GetCustomAttribute<MetaTagsAttribute>();
|
||||
return atr != null ? atr.Tags : Array.Empty<string>();
|
||||
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
|
||||
if (meta._isSelfProxy && meta._proxy.Tags != null)
|
||||
{
|
||||
return meta._proxy.Tags.ToArray();
|
||||
}
|
||||
if (meta.Type.TryGetAttribute(out MetaTagsAttribute atr))
|
||||
{
|
||||
return atr.Tags;
|
||||
}
|
||||
if (meta._proxy.Tags != null)
|
||||
{
|
||||
return meta._proxy.Tags.ToArray();
|
||||
}
|
||||
return Array.Empty<string>();
|
||||
#else
|
||||
EcsDebug.PrintWarning($"Reflection is not available, the {nameof(MetaGenerator)}.{nameof(GetTags)} method does not work.");
|
||||
return Array.Empty<string>();
|
||||
@ -589,7 +676,7 @@ namespace DCFApixels.DragonECS
|
||||
#region GetMetaID
|
||||
public static string GetMetaID(Type type)
|
||||
{
|
||||
#if DEBUG || !REFLECTION_DISABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
|
||||
#if REFLECTION_ENABLED //в дебажных утилитах REFLECTION_DISABLED только в релизном билде работает
|
||||
var atr = type.GetCustomAttribute<MetaIDAttribute>();
|
||||
|
||||
if (atr == null)
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
#endif
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using DCFApixels.DragonECS.PoolsCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@ -418,52 +417,6 @@ namespace DCFApixels.DragonECS
|
||||
EcsMask IComponentMask.ToMask(EcsWorld world) { return _mask; }
|
||||
#endregion
|
||||
|
||||
#region Obsolete
|
||||
[Obsolete("Use EcsMask.GetIterator()")]
|
||||
public Iterator GetIterator()
|
||||
{
|
||||
return new Iterator(Mask.GetIterator(), _source.Entities);
|
||||
}
|
||||
[Obsolete("Use EcsMask.GetIterator().Iterate(span)")]
|
||||
public Iterator GetIteratorFor(EcsSpan span)
|
||||
{
|
||||
return new Iterator(Mask.GetIterator(), span);
|
||||
}
|
||||
[Obsolete("Use EcsMaskIterator")]
|
||||
public ref struct Iterator
|
||||
{
|
||||
public readonly short worldID;
|
||||
public readonly EcsMaskIterator.Enumerable iterator;
|
||||
|
||||
public Iterator(EcsMaskIterator iterator, EcsSpan span)
|
||||
{
|
||||
worldID = iterator.World.ID;
|
||||
this.iterator = iterator.Iterate(span);
|
||||
}
|
||||
|
||||
#region CopyTo
|
||||
public void CopyTo(EcsGroup group)
|
||||
{
|
||||
iterator.CopyTo(group);
|
||||
}
|
||||
public int CopyTo(ref int[] array)
|
||||
{
|
||||
return iterator.CopyTo(ref array);
|
||||
}
|
||||
public EcsSpan CopyToSpan(ref int[] array)
|
||||
{
|
||||
int count = CopyTo(ref array);
|
||||
return new EcsSpan(worldID, array, count);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public EcsMaskIterator.Enumerable.Enumerator GetEnumerator()
|
||||
{
|
||||
return iterator.GetEnumerator();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
public delegate void OnInitApectHandler(object aspect, Builder builder);
|
||||
public static event OnInitApectHandler OnInit = delegate { };
|
||||
|
||||
206
src/EcsMask.cs
206
src/EcsMask.cs
@ -5,7 +5,6 @@ using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
@ -34,6 +33,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
public readonly int ID;
|
||||
public readonly short WorldID;
|
||||
public readonly EcsWorld World;
|
||||
|
||||
internal readonly EcsStaticMask _staticMask;
|
||||
internal readonly EcsMaskChunck[] _incChunckMasks;
|
||||
@ -51,11 +51,6 @@ namespace DCFApixels.DragonECS
|
||||
private EcsMaskIterator _iterator;
|
||||
|
||||
#region Properties
|
||||
public EcsWorld World
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return EcsWorld.GetWorld(WorldID); }
|
||||
}
|
||||
/// <summary> Sorted set excluding constraints. </summary>
|
||||
public ReadOnlySpan<int> Incs
|
||||
{
|
||||
@ -74,6 +69,25 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _anys; }
|
||||
}
|
||||
|
||||
/// <summary> Sorted set including constraints presented as global type codes. </summary>
|
||||
public ReadOnlySpan<EcsTypeCode> IncTypeCodes
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return ToStatic().IncTypeCodes; }
|
||||
}
|
||||
/// <summary> Sorted set excluding constraints presented as global type codes. </summary>
|
||||
public ReadOnlySpan<EcsTypeCode> ExcTypeCodes
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return ToStatic().ExcTypeCodes; }
|
||||
}
|
||||
/// <summary> Sorted set any constraints presented as global type codes. </summary>
|
||||
public ReadOnlySpan<EcsTypeCode> AnyTypeCodes
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return ToStatic().AnyTypeCodes; }
|
||||
}
|
||||
public EcsMaskFlags Flags
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -116,6 +130,7 @@ namespace DCFApixels.DragonECS
|
||||
_staticMask = staticMask;
|
||||
ID = id;
|
||||
WorldID = worldID;
|
||||
World = EcsWorld.GetWorld(worldID);
|
||||
_flags = staticMask.Flags;
|
||||
|
||||
EcsWorld world = EcsWorld.GetWorld(worldID);
|
||||
@ -345,8 +360,6 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
internal EcsMask ConvertFromStatic(EcsStaticMask staticMask)
|
||||
{
|
||||
|
||||
|
||||
if (_staticMasks.TryGetValue(staticMask.ID, out EcsMask result) == false)
|
||||
{
|
||||
result = new EcsMask(staticMask, _staticMasks.Count, _world.ID);
|
||||
@ -367,24 +380,82 @@ namespace DCFApixels.DragonECS
|
||||
_world = world;
|
||||
_builder = EcsStaticMask.New();
|
||||
}
|
||||
|
||||
public Builder Inc() { return this; }
|
||||
public Builder Exc() { return this; }
|
||||
public Builder Any() { return this; }
|
||||
public Builder Inc<T>() { _builder.Inc<T>(); return this; }
|
||||
public Builder Exc<T>() { _builder.Exc<T>(); return this; }
|
||||
public Builder Any<T>() { _builder.Any<T>(); return this; }
|
||||
public Builder Inc(Type type) { _builder.Inc(type); return this; }
|
||||
public Builder Exc(Type type) { _builder.Exc(type); return this; }
|
||||
public Builder Any(Type type) { _builder.Any(type); return this; }
|
||||
public Builder Inc(params Type[] types) { _builder.Inc(types); return this; }
|
||||
public Builder Exc(params Type[] types) { _builder.Exc(types); return this; }
|
||||
public Builder Any(params Type[] types) { _builder.Any(types); return this; }
|
||||
public Builder Inc(ReadOnlySpan<Type> types) { _builder.Inc(types); return this; }
|
||||
public Builder Exc(ReadOnlySpan<Type> types) { _builder.Exc(types); return this; }
|
||||
public Builder Any(ReadOnlySpan<Type> types) { _builder.Any(types); return this; }
|
||||
public Builder Inc(IEnumerable<Type> types) { _builder.Inc(types); return this; }
|
||||
public Builder Exc(IEnumerable<Type> types) { _builder.Exc(types); return this; }
|
||||
public Builder Any(IEnumerable<Type> types) { _builder.Any(types); return this; }
|
||||
public Builder Inc(EcsTypeCode typeCode) { _builder.Inc(typeCode); return this; }
|
||||
public Builder Exc(EcsTypeCode typeCode) { _builder.Exc(typeCode); return this; }
|
||||
public Builder Any(EcsTypeCode typeCode) { _builder.Any(typeCode); return this; }
|
||||
public Builder Inc(params EcsTypeCode[] typeCodes) { _builder.Inc(typeCodes); return this; }
|
||||
public Builder Exc(params EcsTypeCode[] typeCodes) { _builder.Exc(typeCodes); return this; }
|
||||
public Builder Any(params EcsTypeCode[] typeCodes) { _builder.Any(typeCodes); return this; }
|
||||
public Builder Inc(ReadOnlySpan<EcsTypeCode> typeCodes) { _builder.Inc(typeCodes); return this; }
|
||||
public Builder Exc(ReadOnlySpan<EcsTypeCode> typeCodes) { _builder.Exc(typeCodes); return this; }
|
||||
public Builder Any(ReadOnlySpan<EcsTypeCode> typeCodes) { _builder.Any(typeCodes); return this; }
|
||||
public Builder Inc(IEnumerable<EcsTypeCode> typeCodes) { _builder.Inc(typeCodes); return this; }
|
||||
public Builder Exc(IEnumerable<EcsTypeCode> typeCodes) { _builder.Exc(typeCodes); return this; }
|
||||
public Builder Any(IEnumerable<EcsTypeCode> typeCodes) { _builder.Any(typeCodes); return this; }
|
||||
|
||||
public Builder Combine(EcsMask mask) { _builder.Combine(mask._staticMask); return this; }
|
||||
public Builder Except(EcsMask mask) { _builder.Except(mask._staticMask); return this; }
|
||||
|
||||
public EcsMask Build() { return _world.Get<WorldMaskComponent>().ConvertFromStatic(_builder.Build()); }
|
||||
public static implicit operator EcsMask(Builder a) { return a.Build(); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Debug utils
|
||||
private Type[] _incTypes_Debug;
|
||||
private Type[] _excTypes_Debug;
|
||||
private Type[] _anyTypes_Debug;
|
||||
public ReadOnlySpan<Type> GetIncTypes_Debug()
|
||||
{
|
||||
if(_incTypes_Debug == null)
|
||||
{
|
||||
_incTypes_Debug = GetTypes(IncTypeCodes);
|
||||
}
|
||||
return _incTypes_Debug;
|
||||
}
|
||||
public ReadOnlySpan<Type> GetExcTypes_Debug()
|
||||
{
|
||||
if(_excTypes_Debug == null)
|
||||
{
|
||||
_excTypes_Debug = GetTypes(ExcTypeCodes);
|
||||
}
|
||||
return _excTypes_Debug;
|
||||
}
|
||||
public ReadOnlySpan<Type> GetAnyTypes_Debug()
|
||||
{
|
||||
if (_anyTypes_Debug == null)
|
||||
{
|
||||
_anyTypes_Debug = GetTypes(AnyTypeCodes);
|
||||
}
|
||||
return _anyTypes_Debug;
|
||||
}
|
||||
private Type[] GetTypes(ReadOnlySpan<EcsTypeCode> typeCodes)
|
||||
{
|
||||
Type[] result = new Type[typeCodes.Length];
|
||||
for (int i = 0; i < typeCodes.Length; i++)
|
||||
{
|
||||
result[i] = EcsTypeCodeManager.FindTypeOfCode(typeCodes[i]).Type;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
private static string CreateLogString(short worldID, int[] incs, int[] excs, int[] anys)
|
||||
{
|
||||
#if DEBUG
|
||||
@ -402,15 +473,18 @@ namespace DCFApixels.DragonECS
|
||||
public readonly int ID;
|
||||
public readonly EcsWorld world;
|
||||
private readonly short _worldID;
|
||||
public readonly EcsMaskChunck[] includedChunkMasks;
|
||||
public readonly EcsMaskChunck[] excludedChunkMasks;
|
||||
public readonly EcsMaskChunck[] anyChunkMasks;
|
||||
public readonly int[] included;
|
||||
public readonly int[] excluded;
|
||||
public readonly int[] any;
|
||||
public readonly Type[] includedTypes;
|
||||
public readonly Type[] excludedTypes;
|
||||
public readonly Type[] anyTypes;
|
||||
public readonly EcsMaskChunck[] incsChunkMasks;
|
||||
public readonly EcsMaskChunck[] excsChunkMasks;
|
||||
public readonly EcsMaskChunck[] anysChunkMasks;
|
||||
public readonly int[] incs;
|
||||
public readonly int[] excs;
|
||||
public readonly int[] anys;
|
||||
public readonly Type[] incsTypes;
|
||||
public readonly Type[] excsTypes;
|
||||
public readonly Type[] anysTypes;
|
||||
public readonly IEcsPool[] incsPools;
|
||||
public readonly IEcsPool[] excsPools;
|
||||
public readonly IEcsPool[] anysPools;
|
||||
|
||||
public bool IsEmpty { get { return _source.IsEmpty; } }
|
||||
public bool IsBroken { get { return _source.IsBroken; } }
|
||||
@ -422,51 +496,26 @@ namespace DCFApixels.DragonECS
|
||||
ID = mask.ID;
|
||||
world = EcsWorld.GetWorld(mask.WorldID);
|
||||
_worldID = mask.WorldID;
|
||||
includedChunkMasks = mask._incChunckMasks;
|
||||
excludedChunkMasks = mask._excChunckMasks;
|
||||
anyChunkMasks = mask._anyChunckMasks;
|
||||
included = mask._incs;
|
||||
excluded = mask._excs;
|
||||
any = mask._anys;
|
||||
Type converter(int o) { return world.GetComponentType(o); }
|
||||
includedTypes = included.Select(converter).ToArray();
|
||||
excludedTypes = excluded.Select(converter).ToArray();
|
||||
anyTypes = any.Select(converter).ToArray();
|
||||
incsChunkMasks = mask._incChunckMasks;
|
||||
excsChunkMasks = mask._excChunckMasks;
|
||||
anysChunkMasks = mask._anyChunckMasks;
|
||||
incs = mask._incs;
|
||||
excs = mask._excs;
|
||||
anys = mask._anys;
|
||||
IEcsPool converterPool(int o) { return world.FindPoolInstance(o); }
|
||||
incsTypes = mask.GetIncTypes_Debug().ToArray();
|
||||
excsTypes = mask.GetExcTypes_Debug().ToArray();
|
||||
anysTypes = mask.GetAnyTypes_Debug().ToArray();
|
||||
incsPools = incs.Select(converterPool).ToArray();
|
||||
excsPools = excs.Select(converterPool).ToArray();
|
||||
anysPools = anys.Select(converterPool).ToArray();
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return CreateLogString(_worldID, included, excluded, any);
|
||||
return CreateLogString(_worldID, incs, excs, anys);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Obsolete
|
||||
/// <summary> Sorted set including constraints. </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use Incs")]
|
||||
public ReadOnlySpan<int> Inc
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _incs; }
|
||||
}
|
||||
/// <summary> Sorted set excluding constraints. </summary>
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use Excs")]
|
||||
public ReadOnlySpan<int> Exc
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _excs; }
|
||||
}
|
||||
public partial struct Builder
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Never)][Obsolete] public Builder Include<T>() { return Inc<T>(); }
|
||||
[EditorBrowsable(EditorBrowsableState.Never)][Obsolete] public Builder Exclude<T>() { return Exc<T>(); }
|
||||
[EditorBrowsable(EditorBrowsableState.Never)][Obsolete] public Builder Include(Type type) { return Inc(type); }
|
||||
[EditorBrowsable(EditorBrowsableState.Never)][Obsolete] public Builder Exclude(Type type) { return Exc(type); }
|
||||
[EditorBrowsable(EditorBrowsableState.Never)][Obsolete] public Builder Inc(int componentTypeID) { Inc(_world.GetComponentType(componentTypeID)); return this; }
|
||||
[EditorBrowsable(EditorBrowsableState.Never)][Obsolete] public Builder Exc(int componentTypeID) { Exc(_world.GetComponentType(componentTypeID)); return this; }
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
[Flags]
|
||||
@ -607,8 +656,8 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
private void Cleanup(bool disposing)
|
||||
{
|
||||
_bufferHandler.Dispose();
|
||||
_chunckBufferHandler.Dispose();
|
||||
_bufferHandler.DisposeAndReset();
|
||||
_chunckBufferHandler.DisposeAndReset();
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -636,8 +685,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
if (_sortIncChunckBuffer.Length > 1)
|
||||
{
|
||||
var comparer = new IncCountComparer(counts);
|
||||
UnsafeArraySortHalperX<int>.InsertionSort(sortIncBuffer.ptr, sortIncBuffer.Length, ref comparer);
|
||||
SortHalper.Sort(sortIncBuffer.AsSpan(), new IncCountComparer(counts));
|
||||
ConvertToChuncks(preSortingBuffer, sortIncBuffer, _sortIncChunckBuffer);
|
||||
}
|
||||
if (_sortIncChunckBuffer.Length > 0)
|
||||
@ -651,8 +699,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
if (_sortExcChunckBuffer.Length > 1)
|
||||
{
|
||||
ExcCountComparer comparer = new ExcCountComparer(counts);
|
||||
UnsafeArraySortHalperX<int>.InsertionSort(sortExcBuffer.ptr, sortExcBuffer.Length, ref comparer);
|
||||
SortHalper.Sort(sortExcBuffer.AsSpan(), new ExcCountComparer(counts));
|
||||
ConvertToChuncks(preSortingBuffer, sortExcBuffer, _sortExcChunckBuffer);
|
||||
}
|
||||
// Выражение IncCount < (AllEntitesCount - ExcCount) мало вероятно будет истинным.
|
||||
@ -662,8 +709,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
if (_sortAnyChunckBuffer.Length > 1)
|
||||
{
|
||||
ExcCountComparer comparer = new ExcCountComparer(counts);
|
||||
UnsafeArraySortHalperX<int>.InsertionSort(sortAnyBuffer.ptr, sortAnyBuffer.Length, ref comparer);
|
||||
SortHalper.Sort(sortAnyBuffer.AsSpan(), new ExcCountComparer(counts));
|
||||
ConvertToChuncks(preSortingBuffer, sortAnyBuffer, _sortAnyChunckBuffer);
|
||||
}
|
||||
// Any не влияет на maxEntites если есть Inc и сложно высчитывается если нет Inc
|
||||
@ -691,9 +737,13 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region IterateTo
|
||||
//TODO Перемеиноваться в CacheTo
|
||||
public EcsMaskFlags MaskFlags
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void IterateTo(EcsSpan source, EcsGroup group)
|
||||
get { return _maskFlags; }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void CacheTo(EcsSpan source, EcsGroup group)
|
||||
{
|
||||
switch (_maskFlags)
|
||||
{
|
||||
@ -701,7 +751,7 @@ namespace DCFApixels.DragonECS
|
||||
group.CopyFrom(source);
|
||||
break;
|
||||
case EcsMaskFlags.Inc:
|
||||
IterateOnlyInc(source).CopyTo(group);
|
||||
IterateOnlyInc(source).CacheTo(group);
|
||||
break;
|
||||
case EcsMaskFlags.Exc:
|
||||
case EcsMaskFlags.Any:
|
||||
@ -709,7 +759,7 @@ namespace DCFApixels.DragonECS
|
||||
case EcsMaskFlags.IncAny:
|
||||
case EcsMaskFlags.ExcAny:
|
||||
case EcsMaskFlags.IncExcAny:
|
||||
Iterate(source).CopyTo(group);
|
||||
Iterate(source).CacheTo(group);
|
||||
break;
|
||||
case EcsMaskFlags.Broken:
|
||||
group.Clear();
|
||||
@ -720,21 +770,21 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IterateTo(EcsSpan source, ref int[] array)
|
||||
public int CacheTo(EcsSpan source, ref int[] array)
|
||||
{
|
||||
switch (_maskFlags)
|
||||
{
|
||||
case EcsMaskFlags.Empty:
|
||||
return source.ToArray(ref array);
|
||||
case EcsMaskFlags.Inc:
|
||||
return IterateOnlyInc(source).CopyTo(ref array);
|
||||
return IterateOnlyInc(source).CacheTo(ref array);
|
||||
case EcsMaskFlags.Exc:
|
||||
case EcsMaskFlags.Any:
|
||||
case EcsMaskFlags.IncExc:
|
||||
case EcsMaskFlags.IncAny:
|
||||
case EcsMaskFlags.ExcAny:
|
||||
case EcsMaskFlags.IncExcAny:
|
||||
return Iterate(source).CopyTo(ref array);
|
||||
return Iterate(source).CacheTo(ref array);
|
||||
case EcsMaskFlags.Broken:
|
||||
return new EcsSpan(World.ID, Array.Empty<int>()).ToArray(ref array);
|
||||
default:
|
||||
@ -764,7 +814,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
#region CopyTo
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void CopyTo(EcsGroup group)
|
||||
public void CacheTo(EcsGroup group)
|
||||
{
|
||||
group.Clear();
|
||||
var enumerator = GetEnumerator();
|
||||
@ -774,7 +824,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int CopyTo(ref int[] array)
|
||||
public int CacheTo(ref int[] array)
|
||||
{
|
||||
int count = 0;
|
||||
var enumerator = GetEnumerator();
|
||||
@ -920,7 +970,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
#region CopyTo
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void CopyTo(EcsGroup group)
|
||||
public void CacheTo(EcsGroup group)
|
||||
{
|
||||
group.Clear();
|
||||
var enumerator = GetEnumerator();
|
||||
@ -930,7 +980,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int CopyTo(ref int[] array)
|
||||
public int CacheTo(ref int[] array)
|
||||
{
|
||||
int count = 0;
|
||||
var enumerator = GetEnumerator();
|
||||
@ -1077,7 +1127,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
internal readonly struct IncCountComparer : IStructComparer<int>
|
||||
internal readonly struct IncCountComparer : IComparer<int>
|
||||
{
|
||||
public readonly EcsWorld.PoolSlot[] counts;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -1096,7 +1146,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
internal readonly struct ExcCountComparer : IStructComparer<int>
|
||||
internal readonly struct ExcCountComparer : IComparer<int>
|
||||
{
|
||||
public readonly EcsWorld.PoolSlot[] counts;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
#endif
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using DCFApixels.DragonECS.RunnersCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
@ -122,7 +121,6 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
private void InsertAfterNode_Internal(int insertAfterIndex, IEcsProcess system, string layer, int sortOrder, bool isUnique)
|
||||
{
|
||||
//TODO нужно потестить
|
||||
if (isUnique && _uniqueSystemsSet.Add(system.GetType()) == false)
|
||||
{
|
||||
//EcsDebug.PrintWarning($"The pipeline already contains a unique instance of {system.GetType().Name}");
|
||||
@ -600,20 +598,6 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Obsolete
|
||||
[Obsolete("Use " + nameof(Injections))]
|
||||
public readonly InitInjectionList Injector;
|
||||
[Obsolete("Use LayersMap")]
|
||||
public class LayerList : LayersMap
|
||||
{
|
||||
//public LayerList(Builder source, string basicLayerName) : base(source, basicLayerName) { }
|
||||
//public LayerList(Builder source, string preBeginlayer, string beginlayer, string basicLayer, string endLayer, string postEndLayer) : base(source, preBeginlayer, beginlayer, basicLayer, endLayer, postEndLayer) { }
|
||||
public LayerList(IDependencyGraph<string> graph, Builder pipelineBuilder) : base(graph, pipelineBuilder)
|
||||
{
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#if DISABLE_DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using DCFApixels.DragonECS.RunnersCore;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#if DISABLE_DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using DCFApixels.DragonECS.RunnersCore;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
@ -17,7 +17,7 @@ namespace DCFApixels.DragonECS
|
||||
[MetaID("DragonECS_EF8A557C9201E6F04D4A76DC670BDE19")]
|
||||
public interface IEcsProcess : IEcsMember { }
|
||||
|
||||
namespace RunnersCore
|
||||
namespace Core
|
||||
{
|
||||
//добавить инъекцию в раннеры
|
||||
public abstract class EcsRunner
|
||||
@ -63,6 +63,23 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
public delegate void ActionWithData<in TProcess, T>(TProcess process, ref T Data);
|
||||
|
||||
#region MetaProxy
|
||||
protected class RunnerMetaProxy : MetaProxyBase
|
||||
{
|
||||
private TypeMeta _processMeta;
|
||||
public override MetaColor? Color => MetaColor.DragonRose;
|
||||
public override MetaDescription Description => new MetaDescription(EcsConsts.AUTHOR, $"Runner for {_processMeta.TypeName} process.");
|
||||
public override MetaGroup Group => MetaGroup.FromName(EcsConsts.PACK_GROUP, EcsConsts.PROCESSES_GROUP);
|
||||
public RunnerMetaProxy(Type type) : base(type)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
_processMeta = type.GetGenericArguments()[0].GetMeta();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
@ -80,9 +97,9 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)]
|
||||
[MetaDescription(EcsConsts.AUTHOR, "...")]
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaID("DragonECS_7DB3557C9201F85E0E1C17D7B19D9CEE")]
|
||||
[MetaProxy(typeof(RunnerMetaProxy), true)]
|
||||
public abstract class EcsRunner<TProcess> : EcsRunner, IEcsRunner, IEcsProcess
|
||||
where TProcess : IEcsProcess
|
||||
{
|
||||
@ -148,7 +165,7 @@ namespace DCFApixels.DragonECS
|
||||
#region Constructors
|
||||
public RunHelper(EcsRunner<TProcess> runner) : this(runner,
|
||||
#if DEBUG
|
||||
typeof(TProcess).ToMeta().Name)
|
||||
typeof(TProcess).GetMeta().Name)
|
||||
#else
|
||||
string.Empty)
|
||||
#endif
|
||||
@ -298,7 +315,7 @@ namespace DCFApixels.DragonECS
|
||||
#region Constructors
|
||||
public RunHelperWithFinally(EcsRunner<TProcess> runner) : this(runner,
|
||||
#if DEBUG
|
||||
typeof(TProcess).ToMeta().Name)
|
||||
typeof(TProcess).GetMeta().Name)
|
||||
#else
|
||||
string.Empty)
|
||||
#endif
|
||||
@ -476,8 +493,6 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
|
||||
//----
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -65,7 +65,7 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _excs; }
|
||||
}
|
||||
/// <summary> Sorted set excluding constraints presented as global type codes. </summary>
|
||||
/// <summary> Sorted set any constraints presented as global type codes. </summary>
|
||||
public ReadOnlySpan<EcsTypeCode> AnyTypeCodes
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -109,9 +109,15 @@ namespace DCFApixels.DragonECS
|
||||
public static Builder Inc(Type type) { return Builder.New().Inc(type); }
|
||||
public static Builder Exc(Type type) { return Builder.New().Exc(type); }
|
||||
public static Builder Any(Type type) { return Builder.New().Any(type); }
|
||||
public static Builder Inc(params Type[] types) { return Builder.New().Inc(types); }
|
||||
public static Builder Exc(params Type[] types) { return Builder.New().Exc(types); }
|
||||
public static Builder Any(params Type[] types) { return Builder.New().Any(types); }
|
||||
public static Builder Inc(EcsTypeCode typeCode) { return Builder.New().Inc(typeCode); }
|
||||
public static Builder Exc(EcsTypeCode typeCode) { return Builder.New().Exc(typeCode); }
|
||||
public static Builder Any(EcsTypeCode typeCode) { return Builder.New().Any(typeCode); }
|
||||
public static Builder Inc(params EcsTypeCode[] typeCodes) { return Builder.New().Inc(typeCodes); }
|
||||
public static Builder Exc(params EcsTypeCode[] typeCodes) { return Builder.New().Exc(typeCodes); }
|
||||
public static Builder Any(params EcsTypeCode[] typeCodes) { return Builder.New().Any(typeCodes); }
|
||||
private static EcsStaticMask CreateMask(Key key)
|
||||
{
|
||||
if (_ids.TryGetValue(key, out EcsStaticMask result) == false)
|
||||
@ -305,12 +311,24 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Inc/Exc/Combine/Except
|
||||
public Builder Inc() { return this; }
|
||||
public Builder Exc() { return this; }
|
||||
public Builder Any() { return this; }
|
||||
public Builder Inc<T>() { return Inc(EcsTypeCodeManager.Get<T>()); }
|
||||
public Builder Exc<T>() { return Exc(EcsTypeCodeManager.Get<T>()); }
|
||||
public Builder Any<T>() { return Any(EcsTypeCodeManager.Get<T>()); }
|
||||
public Builder Inc(Type type) { return Inc(EcsTypeCodeManager.Get(type)); }
|
||||
public Builder Exc(Type type) { return Exc(EcsTypeCodeManager.Get(type)); }
|
||||
public Builder Any(Type type) { return Any(EcsTypeCodeManager.Get(type)); }
|
||||
public Builder Inc(params Type[] types) { foreach (var type in types) { Inc(type); } return this; }
|
||||
public Builder Exc(params Type[] types) { foreach (var type in types) { Exc(type); } return this; }
|
||||
public Builder Any(params Type[] types) { foreach (var type in types) { Any(type); } return this; }
|
||||
public Builder Inc(ReadOnlySpan<Type> types) { foreach (var type in types) { Inc(type); } return this; }
|
||||
public Builder Exc(ReadOnlySpan<Type> types) { foreach (var type in types) { Exc(type); } return this; }
|
||||
public Builder Any(ReadOnlySpan<Type> types) { foreach (var type in types) { Any(type); } return this; }
|
||||
public Builder Inc(IEnumerable<Type> types) { foreach (var type in types) { Inc(type); } return this; }
|
||||
public Builder Exc(IEnumerable<Type> types) { foreach (var type in types) { Exc(type); } return this; }
|
||||
public Builder Any(IEnumerable<Type> types) { foreach (var type in types) { Any(type); } return this; }
|
||||
public Builder Inc(EcsTypeCode typeCode)
|
||||
{
|
||||
if (_version != _builder._version) { Throw.CantReuseBuilder(); }
|
||||
@ -329,6 +347,15 @@ namespace DCFApixels.DragonECS
|
||||
_builder.Any(typeCode);
|
||||
return this;
|
||||
}
|
||||
public Builder Inc(params EcsTypeCode[] typeCodes) { foreach (var typeCode in typeCodes) { Inc(typeCode); } return this; }
|
||||
public Builder Exc(params EcsTypeCode[] typeCodes) { foreach (var typeCode in typeCodes) { Exc(typeCode); } return this; }
|
||||
public Builder Any(params EcsTypeCode[] typeCodes) { foreach (var typeCode in typeCodes) { Any(typeCode); } return this; }
|
||||
public Builder Inc(ReadOnlySpan<EcsTypeCode> typeCodes) { foreach (var typeCode in typeCodes) { Inc(typeCode); } return this; }
|
||||
public Builder Exc(ReadOnlySpan<EcsTypeCode> typeCodes) { foreach (var typeCode in typeCodes) { Exc(typeCode); } return this; }
|
||||
public Builder Any(ReadOnlySpan<EcsTypeCode> typeCodes) { foreach (var typeCode in typeCodes) { Any(typeCode); } return this; }
|
||||
public Builder Inc(IEnumerable<EcsTypeCode> typeCodes) { foreach (var typeCode in typeCodes) { Inc(typeCode); } return this; }
|
||||
public Builder Exc(IEnumerable<EcsTypeCode> typeCodes) { foreach (var typeCode in typeCodes) { Exc(typeCode); } return this; }
|
||||
public Builder Any(IEnumerable<EcsTypeCode> typeCodes) { foreach (var typeCode in typeCodes) { Any(typeCode); } return this; }
|
||||
public Builder Combine(EcsStaticMask mask)
|
||||
{
|
||||
if (_version != _builder._version) { Throw.CantReuseBuilder(); }
|
||||
@ -354,6 +381,7 @@ namespace DCFApixels.DragonECS
|
||||
return result;
|
||||
}
|
||||
}
|
||||
public static implicit operator EcsStaticMask(Builder a) { return a.Build(); }
|
||||
public void Cancel()
|
||||
{
|
||||
if (_version != _builder._version) { Throw.CantReuseBuilder(); }
|
||||
|
||||
@ -2,7 +2,6 @@
|
||||
#undef DEBUG
|
||||
#endif
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.PoolsCore;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
|
||||
128
src/EcsWorld.cs
128
src/EcsWorld.cs
@ -4,7 +4,6 @@
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using DCFApixels.DragonECS.Core.Unchecked;
|
||||
using DCFApixels.DragonECS.PoolsCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
@ -71,6 +70,7 @@ namespace DCFApixels.DragonECS
|
||||
private int[] _delEntBuffer = Array.Empty<int>();
|
||||
private int _delEntBufferCount = 0;
|
||||
private int[] _emptyEntities = Array.Empty<int>();
|
||||
private int _emptyEntitiesLength = 0;
|
||||
private int _emptyEntitiesCount = 0;
|
||||
private bool _isEnableAutoReleaseDelEntBuffer = true;
|
||||
|
||||
@ -86,6 +86,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
private StructList<IEcsWorldEventListener> _listeners = new StructList<IEcsWorldEventListener>(2);
|
||||
private StructList<IEcsEntityEventListener> _entityListeners = new StructList<IEcsEntityEventListener>(2);
|
||||
private bool _hasAnyEntityListener = false;
|
||||
|
||||
#region Properties
|
||||
EcsWorld IEntityStorage.World
|
||||
@ -394,8 +395,11 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
slot.gen |= GEN_SLEEP_MASK;
|
||||
}
|
||||
if (_hasAnyEntityListener)
|
||||
{
|
||||
_entityListeners.InvokeOnNewEntity(entityID);
|
||||
MoveToEmptyEntities(entityID);
|
||||
}
|
||||
MoveToEmptyEntities(entityID, false);
|
||||
}
|
||||
|
||||
|
||||
@ -436,25 +440,92 @@ namespace DCFApixels.DragonECS
|
||||
_delEntBuffer[_delEntBufferCount++] = entityID;
|
||||
_entities[entityID].isUsed = false;
|
||||
_entitiesCount--;
|
||||
if (_hasAnyEntityListener)
|
||||
{
|
||||
_entityListeners.InvokeOnDelEntity(entityID);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void MoveToEmptyEntities(int entityID)
|
||||
{
|
||||
_emptyEntities[_emptyEntitiesCount++] = entityID;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool RemoveFromEmptyEntities(int entityID)
|
||||
private void MoveToEmptyEntities(int entityID, bool readyToRemove)
|
||||
{
|
||||
for (int i = _emptyEntitiesCount - 1; i >= 0; i--)
|
||||
if (readyToRemove)
|
||||
{
|
||||
if (_emptyEntities[i] == entityID)
|
||||
entityID |= int.MinValue;
|
||||
}
|
||||
_emptyEntities[_emptyEntitiesLength++] = entityID;
|
||||
_emptyEntitiesCount++;
|
||||
if (_emptyEntitiesLength == _emptyEntities.Length)
|
||||
{
|
||||
_emptyEntities[i] = _emptyEntities[--_emptyEntitiesCount];
|
||||
return true;
|
||||
ReleaseEmptyEntitiesBuffer_OnlyReadyToRemove();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void RemoveFromEmptyEntities(int entityID)
|
||||
{
|
||||
const int THRESHOLD = 16;
|
||||
_emptyEntitiesCount--;
|
||||
|
||||
if (_emptyEntitiesLength < THRESHOLD)
|
||||
{
|
||||
for (int i = _emptyEntitiesLength - 1; i >= 0; i--)
|
||||
{
|
||||
if ((_emptyEntities[i] & int.MaxValue) == entityID)
|
||||
{
|
||||
_emptyEntities[i] = _emptyEntities[--_emptyEntitiesLength];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if DRAGONECS_DEEP_DEBUG
|
||||
if (_emptyEntitiesCount < 0)
|
||||
{
|
||||
Throw.DeepDebugException();
|
||||
}
|
||||
#endif
|
||||
if (_emptyEntitiesCount == 0)
|
||||
{
|
||||
_emptyEntitiesCount = 0;
|
||||
_emptyEntitiesLength = 0;
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ReleaseEmptyEntitiesBuffer()
|
||||
{
|
||||
for (int i = 0; i < _emptyEntitiesLength; i++)
|
||||
{
|
||||
var entityID = _emptyEntities[i] & int.MaxValue;
|
||||
if (IsUsed(entityID) && _entities[entityID].componentsCount == 0)
|
||||
{
|
||||
DelEntity(entityID);
|
||||
}
|
||||
}
|
||||
_emptyEntitiesCount = 0;
|
||||
_emptyEntitiesLength = 0;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ReleaseEmptyEntitiesBuffer_OnlyReadyToRemove()
|
||||
{
|
||||
var newCount = 0;
|
||||
for (int i = 0; i < _emptyEntitiesLength; i++)
|
||||
{
|
||||
var entityID = _emptyEntities[i];
|
||||
bool isReady = entityID < 0;
|
||||
entityID &= int.MaxValue;
|
||||
|
||||
if (IsUsed(entityID) && _entities[entityID].componentsCount == 0)
|
||||
{
|
||||
if (isReady)
|
||||
{
|
||||
DelEntity(entityID);
|
||||
}
|
||||
else
|
||||
{
|
||||
_emptyEntities[newCount++] = entityID;
|
||||
}
|
||||
}
|
||||
}
|
||||
_emptyEntitiesCount = newCount;
|
||||
_emptyEntitiesLength = newCount;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -698,7 +769,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
else
|
||||
{
|
||||
poolIdsPtr = UnmanagedArrayUtility.New<int>(count);
|
||||
poolIdsPtr = MemoryAllocator.Alloc<int>(count).Ptr;
|
||||
}
|
||||
|
||||
UnsafeArray<int> ua = UnsafeArray<int>.Manual(poolIdsPtr, count);
|
||||
@ -711,7 +782,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
if (count >= BUFFER_THRESHOLD)
|
||||
{
|
||||
UnmanagedArrayUtility.Free(poolIdsPtr);
|
||||
MemoryAllocator.Free(poolIdsPtr);
|
||||
}
|
||||
|
||||
|
||||
@ -748,7 +819,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
else
|
||||
{
|
||||
poolIdsPtr = UnmanagedArrayUtility.New<int>(count);
|
||||
poolIdsPtr = MemoryAllocator.Alloc<int>(count).Ptr;
|
||||
}
|
||||
|
||||
GetComponentTypeIDsFor_Internal(fromEntityID, poolIdsPtr, count);
|
||||
@ -759,7 +830,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
if (count >= BUFFER_THRESHOLD)
|
||||
{
|
||||
UnmanagedArrayUtility.Free(poolIdsPtr);
|
||||
MemoryAllocator.Free(poolIdsPtr);
|
||||
}
|
||||
|
||||
//foreach (var pool in _pools)
|
||||
@ -890,16 +961,12 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
ReleaseDelEntityBuffer(-1);
|
||||
}
|
||||
public unsafe void ReleaseDelEntityBuffer(int count)
|
||||
public void ReleaseDelEntityBuffer(int count)
|
||||
{
|
||||
if (_emptyEntitiesCount <= 0 && _delEntBufferCount <= 0) { return; }
|
||||
if (_emptyEntitiesLength <= 0 && _delEntBufferCount <= 0) { return; }
|
||||
unchecked { _version++; }
|
||||
|
||||
for (int i = 0; i < _emptyEntitiesCount; i++)
|
||||
{
|
||||
TryDelEntity(_emptyEntities[i]);
|
||||
}
|
||||
_emptyEntitiesCount = 0;
|
||||
ReleaseEmptyEntitiesBuffer();
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
@ -1023,10 +1090,12 @@ namespace DCFApixels.DragonECS
|
||||
public void AddListener(IEcsEntityEventListener entityEventListener)
|
||||
{
|
||||
_entityListeners.Add(entityEventListener);
|
||||
_hasAnyEntityListener = _entityListeners.Count > 0;
|
||||
}
|
||||
public void RemoveListener(IEcsEntityEventListener entityEventListener)
|
||||
{
|
||||
_entityListeners.Remove(entityEventListener);
|
||||
_hasAnyEntityListener = _entityListeners.Count > 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -1082,7 +1151,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
else
|
||||
{
|
||||
poolIdsPtr = UnmanagedArrayUtility.New<int>(count);
|
||||
poolIdsPtr = MemoryAllocator.Alloc<int>(count).Ptr;
|
||||
}
|
||||
|
||||
GetComponentTypeIDsFor_Internal(entityID, poolIdsPtr, count);
|
||||
@ -1105,7 +1174,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
if (count >= BUFFER_THRESHOLD)
|
||||
{
|
||||
UnmanagedArrayUtility.Free(poolIdsPtr);
|
||||
MemoryAllocator.Free(poolIdsPtr);
|
||||
}
|
||||
}
|
||||
public ReadOnlySpan<object> GetComponentsFor(int entityID)
|
||||
@ -1352,6 +1421,7 @@ namespace DCFApixels.DragonECS
|
||||
public interface IEcsEntityEventListener
|
||||
{
|
||||
void OnNewEntity(int entityID);
|
||||
void OnMigrateEntity(int entityID);
|
||||
void OnDelEntity(int entityID);
|
||||
}
|
||||
internal static class WorldEventListExtensions
|
||||
@ -1389,6 +1459,14 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InvokeOnMigrateEntity(this ref StructList<IEcsEntityEventListener> self, int entityID)
|
||||
{
|
||||
for (int i = 0, iMax = self.Count; i < iMax; i++)
|
||||
{
|
||||
self[i].OnMigrateEntity(entityID);
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InvokeOnDelEntity(this ref StructList<IEcsEntityEventListener> self, int entityID)
|
||||
{
|
||||
for (int i = 0, iMax = self.Count; i < iMax; i++)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
#if DISABLE_DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using DCFApixels.DragonECS.PoolsCore;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
@ -127,7 +127,6 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
return _cmpTypeCode_2_CmpTypeIDs.Contains((int)EcsTypeCodeManager.Get(componentType));
|
||||
}
|
||||
//TODO пересмотреть нейминг или функцию
|
||||
public bool IsComponentTypeDeclared(int componentTypeID)
|
||||
{
|
||||
if (componentTypeID >= 0 && componentTypeID < _pools.Length)
|
||||
@ -287,6 +286,10 @@ namespace DCFApixels.DragonECS
|
||||
RemoveFromEmptyEntities(entityID);
|
||||
}
|
||||
_entityComponentMasks[(entityID << _entityComponentMaskLengthBitShift) + maskBit.chunkIndex] |= maskBit.mask;
|
||||
if (_hasAnyEntityListener)
|
||||
{
|
||||
_entityListeners.InvokeOnMigrateEntity(entityID);
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void UnregisterEntityComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit)
|
||||
@ -297,12 +300,15 @@ namespace DCFApixels.DragonECS
|
||||
slot.version++;
|
||||
var count = --_entities[entityID].componentsCount;
|
||||
_entityComponentMasks[(entityID << _entityComponentMaskLengthBitShift) + maskBit.chunkIndex] &= ~maskBit.mask;
|
||||
|
||||
if (count == 0 && IsUsed(entityID))
|
||||
{
|
||||
MoveToEmptyEntities(entityID);
|
||||
MoveToEmptyEntities(entityID, true);
|
||||
}
|
||||
CheckUnregisterValid(count, entityID);
|
||||
if (_hasAnyEntityListener)
|
||||
{
|
||||
_entityListeners.InvokeOnMigrateEntity(entityID);
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool TryRegisterEntityComponent(int entityID, int componentTypeID, EcsMaskChunck maskBit)
|
||||
@ -321,6 +327,10 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
RemoveFromEmptyEntities(entityID);
|
||||
}
|
||||
if (_hasAnyEntityListener)
|
||||
{
|
||||
_entityListeners.InvokeOnMigrateEntity(entityID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -341,9 +351,13 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
if (count == 0 && IsUsed(entityID))
|
||||
{
|
||||
MoveToEmptyEntities(entityID);
|
||||
MoveToEmptyEntities(entityID, true);
|
||||
}
|
||||
CheckUnregisterValid(count, entityID);
|
||||
if (_hasAnyEntityListener)
|
||||
{
|
||||
_entityListeners.InvokeOnMigrateEntity(entityID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
@ -5,7 +5,6 @@ using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
@ -26,12 +25,15 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
private static EcsWorld[] _worlds = Array.Empty<EcsWorld>();
|
||||
private static readonly IdDispenser _worldIdDispenser = new IdDispenser(4, 0, n => Array.Resize(ref _worlds, n));
|
||||
private static StructList<WorldComponentPoolAbstract> _allWorldComponentPools = new StructList<WorldComponentPoolAbstract>(64);
|
||||
private static readonly object _worldLock = new object();
|
||||
|
||||
private StructList<WorldComponentPoolAbstract> _worldComponentPools;
|
||||
private int _builtinWorldComponentsCount = 0;
|
||||
|
||||
public static int AllWorldsCount
|
||||
{
|
||||
get { return _worldIdDispenser.Count; }
|
||||
}
|
||||
static EcsWorld()
|
||||
{
|
||||
_worlds[NULL_WORLD_ID] = new NullWorld();
|
||||
@ -145,7 +147,7 @@ namespace DCFApixels.DragonECS
|
||||
private static short _count;
|
||||
private static short[] _recycledItems = new short[4];
|
||||
private static short _recycledItemsCount;
|
||||
private static readonly IEcsWorldComponent<T> _interface = EcsWorldComponentHandler<T>.instance;
|
||||
private static readonly IEcsWorldComponent<T> _interface = EcsWorldComponent<T>.CustomHandler;
|
||||
private static readonly Abstract _controller = new Abstract();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -292,6 +294,8 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region NullWorld
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)]
|
||||
private sealed class NullWorld : EcsWorld
|
||||
{
|
||||
internal NullWorld() : base(new EcsWorldConfig(4, 4, 4, 4, 4), null, 0) { }
|
||||
@ -321,27 +325,5 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Obsolete
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("Use EcsWorld.ID")]
|
||||
public short id
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return ID; }
|
||||
}
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("The GetPoolInstance(int componentTypeID) method will be removed in future updates, use FindPoolInstance(Type componentType)")]
|
||||
public IEcsPool GetPoolInstance(int componentTypeID)
|
||||
{
|
||||
return FindPoolInstance(componentTypeID);
|
||||
}
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[Obsolete("The GetPoolInstance(Type componentType) method will be removed in future updates, use FindPoolInstance(Type componentType)")]
|
||||
public IEcsPool GetPoolInstance(Type componentType)
|
||||
{
|
||||
return FindPoolInstance(componentType);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@
|
||||
#endif
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static DCFApixels.DragonECS.Core.Internal.MemoryAllocator;
|
||||
#if ENABLE_IL2CPP
|
||||
using Unity.IL2CPP.CompilerServices;
|
||||
#endif
|
||||
@ -13,13 +14,13 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
internal sealed class EcsWhereExecutor : MaskQueryExecutor
|
||||
internal sealed unsafe class EcsWhereExecutor : MaskQueryExecutor
|
||||
{
|
||||
private EcsMaskIterator _iterator;
|
||||
|
||||
private int[] _filteredAllEntities = new int[32];
|
||||
private HMem<int> _filteredAllEntities = Alloc<int>(32);
|
||||
private int _filteredAllEntitiesCount = 0;
|
||||
private int[] _filteredEntities = null;
|
||||
private HMem<int> _filteredEntities = default;
|
||||
private int _filteredEntitiesCount = 0;
|
||||
|
||||
private long _version;
|
||||
@ -54,6 +55,14 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
protected sealed override void OnDestroy()
|
||||
{
|
||||
if (_isDestroyed) { return; }
|
||||
if (_filteredAllEntities.IsCreated)
|
||||
{
|
||||
_filteredAllEntities.DisposeAndReset();
|
||||
}
|
||||
if (_filteredEntities.IsCreated)
|
||||
{
|
||||
_filteredEntities.DisposeAndReset();
|
||||
}
|
||||
_isDestroyed = true;
|
||||
_versionsChecker.Dispose();
|
||||
}
|
||||
@ -67,7 +76,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
if (_versionsChecker.CheckAndNext() == false)
|
||||
{
|
||||
_version++;
|
||||
_filteredAllEntitiesCount = _iterator.IterateTo(World.Entities, ref _filteredAllEntities);
|
||||
_filteredAllEntitiesCount = _iterator.CacheTo(World.Entities, ref _filteredAllEntities);
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -77,19 +86,19 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
if (span.IsNull) { Throw.ArgumentNull(nameof(span)); }
|
||||
if (span.WorldID != World.ID) { Throw.Quiery_ArgumentDifferentWorldsException(); }
|
||||
#endif
|
||||
if (_filteredEntities == null)
|
||||
if (_filteredEntities.IsCreated == false)
|
||||
{
|
||||
_filteredEntities = new int[32];
|
||||
_filteredEntities = Alloc<int>(32);
|
||||
}
|
||||
_filteredEntitiesCount = _iterator.IterateTo(span, ref _filteredEntities);
|
||||
_filteredEntitiesCount = _iterator.CacheTo(span, ref _filteredEntities);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsSpan Execute()
|
||||
public EcsUnsafeSpan Execute()
|
||||
{
|
||||
Execute_Iternal();
|
||||
#if DEBUG && DRAGONECS_DEEP_DEBUG
|
||||
var newSpan = new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount);
|
||||
var result = new EcsUnsafeSpan(World.ID, _filteredAllEntities.Ptr, _filteredAllEntitiesCount);
|
||||
using (EcsGroup group = EcsGroup.New(World))
|
||||
{
|
||||
foreach (var e in World.Entities)
|
||||
@ -100,20 +109,20 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
}
|
||||
}
|
||||
|
||||
if (group.SetEquals(newSpan) == false)
|
||||
if (group.SetEquals(result.ToSpan()) == false)
|
||||
{
|
||||
int[] array = new int[_filteredAllEntities.Length];
|
||||
var count = _iterator.IterateTo(World.Entities, ref array);
|
||||
var count = _iterator.CacheTo(World.Entities, ref array);
|
||||
|
||||
EcsDebug.PrintError(newSpan.ToString() + "\r\n" + group.ToSpan().ToString());
|
||||
EcsDebug.PrintError(result.ToString() + "\r\n" + group.ToSpan().ToString());
|
||||
Throw.DeepDebugException();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount);
|
||||
return new EcsUnsafeSpan(World.ID, _filteredAllEntities.Ptr, _filteredAllEntitiesCount); ;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsSpan ExecuteFor(EcsSpan span)
|
||||
public EcsUnsafeSpan ExecuteFor(EcsSpan span)
|
||||
{
|
||||
if (span.IsSourceEntities)
|
||||
{
|
||||
@ -121,8 +130,8 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
}
|
||||
ExecuteFor_Iternal(span);
|
||||
#if DEBUG && DRAGONECS_DEEP_DEBUG
|
||||
var newSpan = new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount);
|
||||
foreach (var e in newSpan)
|
||||
var result = new EcsUnsafeSpan(World.ID, _filteredEntities.Ptr, _filteredEntitiesCount);
|
||||
foreach (var e in result)
|
||||
{
|
||||
if (World.IsMatchesMask(Mask, e) == false)
|
||||
{
|
||||
@ -130,27 +139,28 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount);
|
||||
return new EcsUnsafeSpan(World.ID, _filteredEntities.Ptr, _filteredEntitiesCount); ;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsSpan Execute(Comparison<int> comparison)
|
||||
public EcsUnsafeSpan Execute(Comparison<int> comparison)
|
||||
{
|
||||
Execute_Iternal();
|
||||
ArraySortHalperX<int>.Sort(_filteredAllEntities, comparison, _filteredAllEntitiesCount);
|
||||
return new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount);
|
||||
SortHalper.Sort(_filteredAllEntities.AsSpan(_filteredAllEntitiesCount), comparison);
|
||||
return new EcsUnsafeSpan(World.ID, _filteredAllEntities.Ptr, _filteredAllEntitiesCount);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsSpan ExecuteFor(EcsSpan span, Comparison<int> comparison)
|
||||
public EcsUnsafeSpan ExecuteFor(EcsSpan source, Comparison<int> comparison)
|
||||
{
|
||||
if (span.IsSourceEntities)
|
||||
if (source.IsSourceEntities)
|
||||
{
|
||||
return Execute(comparison);
|
||||
}
|
||||
ExecuteFor_Iternal(span);
|
||||
ArraySortHalperX<int>.Sort(_filteredEntities, comparison, _filteredEntitiesCount);
|
||||
return new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount);
|
||||
ExecuteFor_Iternal(source);
|
||||
SortHalper.Sort(_filteredEntities.AsSpan(_filteredEntitiesCount), comparison);
|
||||
return new EcsUnsafeSpan(World.ID, _filteredEntities.Ptr, _filteredEntitiesCount);
|
||||
}
|
||||
public override EcsSpan Snapshot() { return Execute(); }
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -66,7 +66,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
if (_versionsChecker.CheckAndNext() == false)
|
||||
{
|
||||
_version++;
|
||||
_iterator.IterateTo(World.Entities, _filteredAllGroup);
|
||||
_iterator.CacheTo(World.Entities, _filteredAllGroup);
|
||||
#if DEBUG && DRAGONECS_DEEP_DEBUG
|
||||
if (_filteredGroup == null)
|
||||
{
|
||||
@ -98,7 +98,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
{
|
||||
_filteredGroup = EcsGroup.New(World);
|
||||
}
|
||||
_iterator.IterateTo(span, _filteredGroup);
|
||||
_iterator.CacheTo(span, _filteredGroup);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -117,6 +117,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
ExecuteFor_Iternal(span);
|
||||
return _filteredGroup;
|
||||
}
|
||||
public override EcsSpan Snapshot() { return Execute(); }
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -105,6 +105,7 @@ namespace DCFApixels.DragonECS.Core
|
||||
}
|
||||
protected abstract void OnInitialize();
|
||||
protected abstract void OnDestroy();
|
||||
public abstract EcsSpan Snapshot();
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
|
||||
@ -84,6 +84,76 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WhereUnsafe
|
||||
public static EcsUnsafeSpan WhereUnsafe<TCollection, TAspect>(this TCollection entities, out TAspect aspect)
|
||||
where TAspect : new()
|
||||
where TCollection : IEntityStorage
|
||||
{
|
||||
return entities.ToSpan().WhereUnsafe(out aspect);
|
||||
}
|
||||
public static EcsUnsafeSpan WhereUnsafe<TAspect>(this EcsReadonlyGroup group, out TAspect aspect)
|
||||
where TAspect : new()
|
||||
{
|
||||
return group.ToSpan().WhereUnsafe(out aspect);
|
||||
}
|
||||
public static EcsUnsafeSpan WhereUnsafe<TAspect>(this EcsSpan span, out TAspect aspect)
|
||||
where TAspect : new()
|
||||
{
|
||||
span.World.GetQueryCache(out EcsWhereExecutor executor, out aspect);
|
||||
return executor.ExecuteFor(span);
|
||||
}
|
||||
|
||||
public static EcsUnsafeSpan WhereUnsafe<TCollection>(this TCollection entities, IComponentMask mask)
|
||||
where TCollection : IEntityStorage
|
||||
{
|
||||
return entities.ToSpan().WhereUnsafe(mask);
|
||||
}
|
||||
public static EcsUnsafeSpan WhereUnsafe(this EcsReadonlyGroup group, IComponentMask mask)
|
||||
{
|
||||
return group.ToSpan().WhereUnsafe(mask);
|
||||
}
|
||||
public static EcsUnsafeSpan WhereUnsafe(this EcsSpan span, IComponentMask mask)
|
||||
{
|
||||
var executor = span.World.GetExecutorForMask<EcsWhereExecutor>(mask);
|
||||
return executor.ExecuteFor(span);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WhereUnsafe with sort
|
||||
public static EcsUnsafeSpan WhereUnsafe<TCollection, TAspect>(this TCollection entities, out TAspect aspect, Comparison<int> comparison)
|
||||
where TAspect : new()
|
||||
where TCollection : IEntityStorage
|
||||
{
|
||||
return entities.ToSpan().WhereUnsafe(out aspect, comparison);
|
||||
}
|
||||
public static EcsUnsafeSpan WhereUnsafe<TAspect>(this EcsReadonlyGroup group, out TAspect aspect, Comparison<int> comparison)
|
||||
where TAspect : new()
|
||||
{
|
||||
return group.ToSpan().WhereUnsafe(out aspect, comparison);
|
||||
}
|
||||
public static EcsUnsafeSpan WhereUnsafe<TAspect>(this EcsSpan span, out TAspect aspect, Comparison<int> comparison)
|
||||
where TAspect : new()
|
||||
{
|
||||
span.World.GetQueryCache(out EcsWhereExecutor executor, out aspect);
|
||||
return executor.ExecuteFor(span, comparison);
|
||||
}
|
||||
|
||||
public static EcsUnsafeSpan WhereUnsafe<TCollection>(this TCollection entities, IComponentMask mask, Comparison<int> comparison)
|
||||
where TCollection : IEntityStorage
|
||||
{
|
||||
return entities.ToSpan().WhereUnsafe(mask, comparison);
|
||||
}
|
||||
public static EcsUnsafeSpan WhereUnsafe(this EcsReadonlyGroup group, IComponentMask mask, Comparison<int> comparison)
|
||||
{
|
||||
return group.ToSpan().WhereUnsafe(mask, comparison);
|
||||
}
|
||||
public static EcsUnsafeSpan WhereUnsafe(this EcsSpan span, IComponentMask mask, Comparison<int> comparison)
|
||||
{
|
||||
var executor = span.World.GetExecutorForMask<EcsWhereExecutor>(mask);
|
||||
return executor.ExecuteFor(span);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region WhereToGroup
|
||||
public static EcsReadonlyGroup WhereToGroup<TCollection, TAspect>(this TCollection entities, out TAspect aspect)
|
||||
where TAspect : new()
|
||||
|
||||
@ -126,7 +126,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
return (T)Extract_Internal(typeof(T));
|
||||
}
|
||||
private object Extract_Internal(Type type)//TODO проверить
|
||||
private object Extract_Internal(Type type)
|
||||
{
|
||||
if (_nodes.TryGetValue(type, out InjectionNodeBase node))
|
||||
{
|
||||
@ -192,7 +192,7 @@ namespace DCFApixels.DragonECS
|
||||
FindMonoWorld(obj);
|
||||
_injections.Add(new Injection<T>(obj));
|
||||
}
|
||||
public void Extract<T>(ref T obj) // TODO проверить
|
||||
public void Extract<T>(ref T obj)
|
||||
{
|
||||
Type type = typeof(T);
|
||||
for (int i = _injections.Count - 1; i >= 0; i--)
|
||||
@ -272,7 +272,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
public void InjectTo(Injector injector, EcsPipeline pipeline)
|
||||
{
|
||||
var monoWorldProcess = pipeline.GetProcess<IMonoWorldInject>(); // TODO Проверить IMonoWorldInject
|
||||
var monoWorldProcess = pipeline.GetProcess<IMonoWorldInject>();
|
||||
foreach (var monoWorldSystem in monoWorldProcess)
|
||||
{
|
||||
monoWorldSystem.World = _monoWorld;
|
||||
|
||||
@ -1,8 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f5d8c72b3decc2b488a616932366ff0f
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,25 +1,97 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static DCFApixels.DragonECS.Core.Internal.MemoryAllocator;
|
||||
#if ENABLE_IL2CPP
|
||||
using Unity.IL2CPP.CompilerServices;
|
||||
#endif
|
||||
|
||||
namespace DCFApixels.DragonECS.Core.Internal
|
||||
{
|
||||
internal static class AllocatorUtility
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
internal static unsafe class AllocatorUtility
|
||||
{
|
||||
public static unsafe void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes)
|
||||
public static void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes)
|
||||
{
|
||||
ClearAllocatedMemory((byte*)ptr, startByte, lengthInBytes);
|
||||
}
|
||||
public static unsafe void ClearAllocatedMemory(byte* ptr, int startByte, int lengthInBytes)
|
||||
public static void ClearAllocatedMemory(byte* ptr, int startByte, int lengthInBytes)
|
||||
{
|
||||
#if ENABLE_DUMMY_SPAN
|
||||
lengthInBytes += startByte;
|
||||
for (int i = startByte; i < lengthInBytes; i++)
|
||||
{
|
||||
ptr[i] = 0;
|
||||
}
|
||||
#else
|
||||
Span<byte> memorySpan = new Span<byte>(ptr + startByte, lengthInBytes);
|
||||
memorySpan.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int CacheTo(this EcsMaskIterator it, EcsSpan source, ref HMem<int> array)
|
||||
{
|
||||
switch (it.MaskFlags)
|
||||
{
|
||||
case EcsMaskFlags.Empty:
|
||||
{
|
||||
if(array.Length < source.Count)
|
||||
{
|
||||
array = Realloc<int>(array, source.Count);
|
||||
}
|
||||
source.AsSystemSpan().CopyTo(array.AsSpan());
|
||||
return source.Count;
|
||||
}
|
||||
case EcsMaskFlags.Inc:
|
||||
{
|
||||
return it.IterateOnlyInc(source).CacheTo(ref array);
|
||||
}
|
||||
case EcsMaskFlags.Exc:
|
||||
case EcsMaskFlags.Any:
|
||||
case EcsMaskFlags.IncExc:
|
||||
case EcsMaskFlags.IncAny:
|
||||
case EcsMaskFlags.ExcAny:
|
||||
case EcsMaskFlags.IncExcAny:
|
||||
{
|
||||
return it.Iterate(source).CacheTo(ref array);
|
||||
}
|
||||
case EcsMaskFlags.Broken:
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
default:
|
||||
{
|
||||
Throw.UndefinedException();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int CacheTo(this EcsMaskIterator.OnlyIncEnumerable e, ref HMem<int> array)
|
||||
{
|
||||
int count = 0;
|
||||
var enumerator = e.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
if (array.Length <= count)
|
||||
{
|
||||
array = Realloc<int>(array, array.Length << 1);
|
||||
}
|
||||
array.Ptr[count++] = enumerator.Current;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int CacheTo(this EcsMaskIterator.Enumerable e, ref HMem<int> array)
|
||||
{
|
||||
int count = 0;
|
||||
var enumerator = e.GetEnumerator();
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
if (array.Length <= count)
|
||||
{
|
||||
array = Realloc<int>(array, array.Length << 1);
|
||||
}
|
||||
array.Ptr[count++] = enumerator.Current;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
#undef DEBUG
|
||||
#endif
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS.Core.Internal
|
||||
@ -9,6 +10,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
internal unsafe static class MemoryAllocator
|
||||
{
|
||||
#if DEBUG
|
||||
private static ulong _inrement = 0;
|
||||
private static IdDispenser _idDispenser;
|
||||
private static HandlerDebugInfo[] _debugInfos;
|
||||
#endif
|
||||
@ -24,32 +26,51 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
_debugInfos = new HandlerDebugInfo[32];
|
||||
#endif
|
||||
}
|
||||
private static HandlerDebugInfo[] CurrentHandlersList
|
||||
{
|
||||
get { return CreateCurrentHandlersList_Debug(); }
|
||||
}
|
||||
internal static HandlerDebugInfo[] CreateCurrentHandlersList_Debug()
|
||||
{
|
||||
#if DEBUG
|
||||
var result = new HandlerDebugInfo[_idDispenser.Count];
|
||||
int i = 0;
|
||||
foreach (var id in _idDispenser)
|
||||
{
|
||||
result[i++] = _debugInfos[id];
|
||||
}
|
||||
SortHalper.SortBy<HandlerDebugInfo, ulong>(result, o => o.increment);
|
||||
return result;
|
||||
#else
|
||||
return Array.Empty<HandlerDebugInfo>();
|
||||
#endif
|
||||
}
|
||||
|
||||
#region AllocAndInit
|
||||
public static Handler AllocAndInit<T>(int count) where T : unmanaged
|
||||
public static HMem<T> AllocAndInit<T>(int count) where T : unmanaged
|
||||
{
|
||||
return AllocAndInit_Internal(Marshal.SizeOf<T>() * count, typeof(T));
|
||||
return new HMem<T>(AllocAndInit_Internal(Marshal.SizeOf<T>() * count, typeof(T)), count);
|
||||
}
|
||||
public static Handler AllocAndInit(int byteLength)
|
||||
public static HMem<byte> AllocAndInit(int byteLength)
|
||||
{
|
||||
return AllocAndInit_Internal(byteLength, null);
|
||||
return new HMem<byte>(AllocAndInit_Internal(byteLength, null), byteLength);
|
||||
}
|
||||
public static Handler AllocAndInit_Internal(int byteLength, Type type)
|
||||
{
|
||||
Handler handler = Alloc_Internal(byteLength, type);
|
||||
AllocatorUtility.ClearAllocatedMemory(handler.Ptr, 0, byteLength);
|
||||
AllocatorUtility.ClearAllocatedMemory(handler.RawPtr, 0, byteLength);
|
||||
return handler;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Alloc
|
||||
public static Handler Alloc<T>(int count) where T : unmanaged
|
||||
public static HMem<T> Alloc<T>(int count) where T : unmanaged
|
||||
{
|
||||
return Alloc_Internal(Marshal.SizeOf<T>() * count, typeof(T));
|
||||
return new HMem<T>(Alloc_Internal(Marshal.SizeOf<T>() * count, typeof(T)), count);
|
||||
}
|
||||
public static Handler Alloc(int byteLength)
|
||||
public static HMem<byte> Alloc(int byteLength)
|
||||
{
|
||||
return Alloc_Internal(byteLength, null);
|
||||
return new HMem<byte>(Alloc_Internal(byteLength, null), byteLength); ;
|
||||
}
|
||||
public static Handler Alloc_Internal(int byteLength, Type type)
|
||||
{
|
||||
@ -75,6 +96,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
#if DRAGONECS_DEEP_DEBUG
|
||||
_debugInfos[id].stackTrace = new System.Diagnostics.StackTrace();
|
||||
#endif
|
||||
_debugInfos[id].increment = ++_inrement;
|
||||
_debugInfos[id].type = type;
|
||||
_debugInfos[id].handler = handler;
|
||||
#endif
|
||||
@ -84,69 +106,135 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
#endregion
|
||||
|
||||
#region ReallocAndInit
|
||||
public static Handler ReallocAndInit<T>(void* target, int oldCount, int newCount) where T : unmanaged
|
||||
public static HMem<T> ReallocAndInit<T>(T* target, int oldCount, int newCount) where T : unmanaged
|
||||
{
|
||||
return ReallocAndInit<T>(Handler.FromDataPtr(target), oldCount, newCount);
|
||||
}
|
||||
public static Handler ReallocAndInit(void* target, int oldByteLength, int newByteLength)
|
||||
public static HMem<byte> ReallocAndInit(void* target, int oldByteLength, int newByteLength)
|
||||
{
|
||||
return ReallocAndInit(Handler.FromDataPtr(target), oldByteLength, newByteLength);
|
||||
}
|
||||
public static Handler ReallocAndInit<T>(Handler target, int oldCount, int newCount) where T : unmanaged
|
||||
public static HMem<T> ReallocAndInit<T>(HMem<T> target, int newCount) where T : unmanaged
|
||||
{
|
||||
var size = Marshal.SizeOf<T>();
|
||||
return ReallocAndInit_Internal(target, size * oldCount, size * newCount, typeof(T));
|
||||
return new HMem<T>(ReallocAndInit_Internal(target, size * target.Length, size * newCount, typeof(T)), newCount);
|
||||
}
|
||||
public static Handler ReallocAndInit(Handler target, int oldByteLength, int newByteLength)
|
||||
public static HMem<T> ReallocAndInit<T>(Handler target, int oldCount, int newCount) where T : unmanaged
|
||||
{
|
||||
return ReallocAndInit_Internal(target, oldByteLength, newByteLength, null);
|
||||
var size = Marshal.SizeOf<T>();
|
||||
return new HMem<T>(ReallocAndInit_Internal(target, size * oldCount, size * newCount, typeof(T)), newCount);
|
||||
}
|
||||
public static HMem<byte> ReallocAndInit(Handler target, int oldByteLength, int newByteLength)
|
||||
{
|
||||
return new HMem<byte>(ReallocAndInit_Internal(target, oldByteLength, newByteLength, null), newByteLength);
|
||||
}
|
||||
private static Handler ReallocAndInit_Internal(Handler target, int oldByteLength, int newByteLength, Type newType)
|
||||
{
|
||||
Handler handler = Realloc_Internal(target, newByteLength, newType);
|
||||
AllocatorUtility.ClearAllocatedMemory(handler.Ptr, oldByteLength, newByteLength - oldByteLength);
|
||||
AllocatorUtility.ClearAllocatedMemory(handler.RawPtr, oldByteLength, newByteLength - oldByteLength);
|
||||
return handler;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Realloc
|
||||
public static Handler Realloc<T>(void* target, int newCount) where T : unmanaged
|
||||
public static HMem<T> Realloc<T>(T* target, int newCount) where T : unmanaged
|
||||
{
|
||||
return Realloc<T>(Handler.FromDataPtr(target), Marshal.SizeOf<T>() * newCount);
|
||||
}
|
||||
public static Handler Realloc(void* target, int newByteLength)
|
||||
public static HMem<byte> Realloc(void* target, int newByteLength)
|
||||
{
|
||||
return Realloc(Handler.FromDataPtr(target), newByteLength);
|
||||
return new HMem<byte>(Realloc(Handler.FromDataPtr(target), newByteLength), newByteLength);
|
||||
}
|
||||
public static Handler Realloc<T>(Handler target, int newCount) where T : unmanaged
|
||||
public static HMem<T> Realloc<T>(HMem<T> target, int newCount) where T : unmanaged
|
||||
{
|
||||
return Realloc_Internal(target, Marshal.SizeOf<T>() * newCount, typeof(T));
|
||||
return new HMem<T>(Realloc_Internal(target, Marshal.SizeOf<T>() * newCount, typeof(T)), newCount);
|
||||
}
|
||||
public static Handler Realloc(Handler target, int newByteLength)
|
||||
public static HMem<T> Realloc<T>(Handler target, int newCount) where T : unmanaged
|
||||
{
|
||||
return Realloc_Internal(target, newByteLength, null);
|
||||
return new HMem<T>(Realloc_Internal(target, Marshal.SizeOf<T>() * newCount, typeof(T)), newCount);
|
||||
}
|
||||
public static HMem<byte> Realloc(Handler target, int newByteLength)
|
||||
{
|
||||
return new HMem<byte>(Realloc_Internal(target, newByteLength, null), newByteLength);
|
||||
}
|
||||
private static Handler Realloc_Internal(Handler target, int newByteLength, Type newType)
|
||||
{
|
||||
newByteLength = newByteLength == 0 ? 1 : newByteLength;
|
||||
Meta* newHandledPtr = (Meta*)Marshal.ReAllocHGlobal((IntPtr)target.GetHandledPtr(), (IntPtr)newByteLength + sizeof(Meta));
|
||||
if (target.IsCreated == false)
|
||||
{
|
||||
return Alloc_Internal(newByteLength, newType);
|
||||
}
|
||||
//#if DEBUG
|
||||
// int id = 0;
|
||||
// lock (_idDispenser)
|
||||
// {
|
||||
// if (_debugInfos.Length <= _idDispenser.Count)
|
||||
// {
|
||||
// Array.Resize(ref _debugInfos, ArrayUtility.NextPow2(_idDispenser.Count));
|
||||
// }
|
||||
// id = _idDispenser.UseFree();
|
||||
// }
|
||||
//#endif
|
||||
#if DEBUG
|
||||
var id = target.GetHandledPtr()->ID;
|
||||
#endif
|
||||
|
||||
Meta* newHandledPtr = (Meta*)Marshal.ReAllocHGlobal(
|
||||
(IntPtr)target.GetHandledPtr(),
|
||||
(IntPtr)newByteLength + sizeof(Meta));
|
||||
Handler handler = Handler.FromHandledPtr(newHandledPtr);
|
||||
#if DEBUG
|
||||
newHandledPtr->ID = id;
|
||||
newHandledPtr->ByteLength = newByteLength;
|
||||
#if DRAGONECS_DEEP_DEBUG
|
||||
_debugInfos[newHandledPtr->ID].stackTrace = new System.Diagnostics.StackTrace();
|
||||
_debugInfos[id].stackTrace = new System.Diagnostics.StackTrace();
|
||||
#endif
|
||||
_debugInfos[newHandledPtr->ID].type = newType;
|
||||
_debugInfos[newHandledPtr->ID].handler = handler;
|
||||
_debugInfos[id].increment = ++_inrement;
|
||||
_debugInfos[id].type = newType;
|
||||
_debugInfos[id].handler = handler;
|
||||
#endif
|
||||
return handler;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Clone
|
||||
public static HMem<T> From<T>(HMem<T> source)
|
||||
where T : unmanaged
|
||||
{
|
||||
var result = Alloc<T>(source.Length);
|
||||
source.AsSpan().CopyTo(result.AsSpan());
|
||||
return result;
|
||||
}
|
||||
public static HMem<T> From<T>(T* ptr, int length)
|
||||
where T : unmanaged
|
||||
{
|
||||
return From<T>(new ReadOnlySpan<T>(ptr, length));
|
||||
}
|
||||
public static HMem<T> From<T>(T[] source)
|
||||
where T : unmanaged
|
||||
{
|
||||
return From(new ReadOnlySpan<T>(source));
|
||||
}
|
||||
public static HMem<T> From<T>(ReadOnlySpan<T> source)
|
||||
where T : unmanaged
|
||||
{
|
||||
var result = Alloc<T>(source.Length);
|
||||
source.CopyTo(result.AsSpan());
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Free
|
||||
public static void Free(Handler target)
|
||||
{
|
||||
Free_Internal(target.GetHandledPtr());
|
||||
}
|
||||
public static void FreeAndClear<T>(ref HMem<T> target)
|
||||
where T : unmanaged
|
||||
{
|
||||
Free_Internal(target.Handler.GetHandledPtr());
|
||||
target = default;
|
||||
}
|
||||
public static void FreeAndClear(ref Handler target)
|
||||
{
|
||||
Free_Internal(target.GetHandledPtr());
|
||||
@ -159,6 +247,10 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
private static void Free_Internal(Meta* handledPtr)
|
||||
{
|
||||
#if DEBUG
|
||||
if (handledPtr == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
lock (_idDispenser)
|
||||
{
|
||||
_idDispenser.Release(handledPtr->ID);
|
||||
@ -199,6 +291,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
#if DRAGONECS_DEEP_DEBUG
|
||||
public System.Diagnostics.StackTrace stackTrace;
|
||||
#endif
|
||||
public ulong increment;
|
||||
public Type type;
|
||||
public Handler handler;
|
||||
#endif
|
||||
@ -210,11 +303,97 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Handlers
|
||||
public readonly struct HMem<T> : IDisposable, IEquatable<HMem<T>>
|
||||
where T : unmanaged
|
||||
{
|
||||
public readonly T* Ptr;
|
||||
public readonly int Length;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal HMem(Handler handler, int length)
|
||||
{
|
||||
Ptr = handler.As<T>();
|
||||
Length = length;
|
||||
}
|
||||
|
||||
public bool IsCreated
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return Ptr != null; }
|
||||
}
|
||||
public IntPtr RawPtr
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return new IntPtr(Ptr); }
|
||||
}
|
||||
public Handler Handler
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return Handler.FromDataPtr(Ptr); }
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public HMem<U> As<U>() where U : unmanaged
|
||||
{
|
||||
if (IsCreated)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
long totalBytes = (long)Length * sizeof(T);
|
||||
long newLengthLong = totalBytes / sizeof(U);
|
||||
#if DEBUG
|
||||
if (totalBytes % sizeof(U) != 0)
|
||||
{
|
||||
throw new InvalidOperationException($"Cannot cast Memory<{typeof(T).Name}> to Memory<{typeof(U).Name}> because the size of the underlying memory ({totalBytes} bytes) is not a multiple of the size of {typeof(U).Name} ({sizeof(U)} bytes).");
|
||||
}
|
||||
if (newLengthLong > int.MaxValue)
|
||||
{
|
||||
throw new InvalidOperationException($"Resulting length ({newLengthLong}) exceeds int.MaxValue.");
|
||||
}
|
||||
#endif
|
||||
|
||||
return new HMem<U>(Handler, (int)newLengthLong);
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
Handler.Dispose();
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public override string ToString() { return Handler.DebuggerDisplay(); }
|
||||
#endif
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() { return RawPtr.GetHashCode(); }
|
||||
public override bool Equals(object obj) { return obj is Handler h && h == this; }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(HMem<T> other) { return other.Ptr == Ptr; }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(HMem<T> a, HMem<T> b) { return a.Ptr == b.Ptr; }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(HMem<T> a, HMem<T> b) { return a.Ptr != b.Ptr; }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<T> AsSpan() { return new Span<T>(Ptr, Length); }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Span<T> AsSpan(int length)
|
||||
{
|
||||
#if DEBUG
|
||||
if (length > Length) { Throw.UndefinedException(); }
|
||||
#endif
|
||||
return new Span<T>(Ptr, length);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator Handler(HMem<T> memory) { return memory.Handler; }
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
[System.Diagnostics.DebuggerDisplay("{DebuggerDisplay()}")]
|
||||
[System.Diagnostics.DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||
#endif
|
||||
public readonly struct Handler : IDisposable
|
||||
public readonly struct Handler : IDisposable, IEquatable<Handler>
|
||||
{
|
||||
public static readonly Handler Empty = new Handler();
|
||||
internal readonly Meta* Data; // Data[-1] is meta;
|
||||
@ -239,16 +418,39 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
#endif
|
||||
}
|
||||
|
||||
public bool IsEmpty { get { return Data == null; } }
|
||||
public IntPtr Ptr { get { return (IntPtr)Data; } }
|
||||
public T* As<T>() where T : unmanaged { return (T*)Ptr; }
|
||||
public bool IsCreated
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return Data != null; }
|
||||
}
|
||||
public IntPtr RawPtr
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return (IntPtr)Data; }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T* As<T>() where T : unmanaged { return (T*)RawPtr; }
|
||||
|
||||
void IDisposable.Dispose() { Free((void*)Ptr); }
|
||||
public void Dispose() { Free((void*)RawPtr); }
|
||||
|
||||
#if DEBUG
|
||||
public override string ToString() { return DebuggerDisplay(); }
|
||||
#endif
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() { return RawPtr.GetHashCode(); }
|
||||
public override bool Equals(object obj) { return obj is Handler h && h == this; }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(Handler other) { return other.Data == Data; }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(Handler a, Handler b) { return a.Data == b.Data; }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(Handler a, Handler b) { return a.Data != b.Data; }
|
||||
|
||||
#region Debugger
|
||||
#if DEBUG
|
||||
#pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.
|
||||
internal unsafe string DebuggerDisplay()
|
||||
internal string DebuggerDisplay()
|
||||
{
|
||||
if (Data == null)
|
||||
{
|
||||
@ -282,8 +484,9 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
private unsafe struct Union
|
||||
private struct Union
|
||||
{
|
||||
[FieldOffset(0)]
|
||||
public Array array;
|
||||
@ -303,13 +506,13 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
|
||||
public HandlerDebugInfo[] OtherHandlersInfo;
|
||||
|
||||
public unsafe DebuggerProxy(Handler handler)
|
||||
public DebuggerProxy(Handler handler)
|
||||
{
|
||||
IsAlive = handler.Ptr.ToPointer() != null;
|
||||
IsAlive = handler.RawPtr.ToPointer() != null;
|
||||
if (IsAlive == false) { return; }
|
||||
|
||||
Meta = handler.GetHandledPtr()[0];
|
||||
_data = (byte*)handler.Ptr;
|
||||
_data = (byte*)handler.RawPtr;
|
||||
DebugInfo = _debugInfos[Meta.ID];
|
||||
|
||||
if (DebugInfo.type == null)
|
||||
@ -333,13 +536,20 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
#endif
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal static class MemoryAllocatorHandlerExtensions
|
||||
{
|
||||
public static void Dispose(this ref MemoryAllocator.Handler self)
|
||||
public static void DisposeAndReset(this ref MemoryAllocator.Handler self)
|
||||
{
|
||||
MemoryAllocator.FreeAndClear(ref self);
|
||||
}
|
||||
public static void DisposeAndReset<T>(this ref MemoryAllocator.HMem<T> self)
|
||||
where T : unmanaged
|
||||
{
|
||||
self.Dispose();
|
||||
self = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -54,7 +54,7 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
{
|
||||
MemoryAllocator.Free(_ptr);
|
||||
}
|
||||
_ptr = MemoryAllocator.Alloc<byte>(byteSize).As<byte>();
|
||||
_ptr = MemoryAllocator.Alloc<byte>(byteSize).Ptr;
|
||||
_byteSize = byteSize;
|
||||
}
|
||||
return (T*)_ptr;
|
||||
|
||||
@ -1,272 +0,0 @@
|
||||
#if DISABLE_DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
#if ENABLE_IL2CPP
|
||||
using Unity.IL2CPP.CompilerServices;
|
||||
#endif
|
||||
|
||||
namespace DCFApixels.DragonECS.Core.Internal
|
||||
{
|
||||
internal interface IStructComparer<T> : IComparer<T>
|
||||
{
|
||||
// a > b = return > 0
|
||||
// int Compare(T a, T b);
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
internal static class ArraySortHalperX<T>
|
||||
{
|
||||
private const int IntrosortSizeThreshold = 16;
|
||||
|
||||
#region IStructComparer
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SwapIfGreater<TComparer>(T[] items, ref TComparer comparer, int i, int j) where TComparer : IStructComparer<T>
|
||||
{
|
||||
if (comparer.Compare(items[i], items[j]) > 0)
|
||||
{
|
||||
T key = items[i];
|
||||
items[i] = items[j];
|
||||
items[j] = key;
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InsertionSort<TComparer>(T[] items, ref TComparer comparer) where TComparer : IStructComparer<T>
|
||||
{
|
||||
for (int i = 0; i < items.Length - 1; i++)
|
||||
{
|
||||
T t = items[i + 1];
|
||||
|
||||
int j = i;
|
||||
while (j >= 0 && comparer.Compare(t, items[j]) < 0)
|
||||
{
|
||||
items[j + 1] = items[j];
|
||||
j--;
|
||||
}
|
||||
|
||||
items[j + 1] = t;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Sort<TComparer>(T[] items, ref TComparer comparer) where TComparer : IStructComparer<T>
|
||||
{
|
||||
int length = items.Length;
|
||||
if (length == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (length <= IntrosortSizeThreshold)
|
||||
{
|
||||
|
||||
if (length == 2)
|
||||
{
|
||||
SwapIfGreater(items, ref comparer, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (length == 3)
|
||||
{
|
||||
SwapIfGreater(items, ref comparer, 0, 1);
|
||||
SwapIfGreater(items, ref comparer, 0, 2);
|
||||
SwapIfGreater(items, ref comparer, 1, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
InsertionSort(items, ref comparer);
|
||||
return;
|
||||
}
|
||||
|
||||
IStructComparer<T> packed = comparer;
|
||||
Array.Sort(items, 0, items.Length, packed);
|
||||
comparer = (TComparer)packed;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Comparison
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SwapIfGreater(T[] items, Comparison<T> comparison, int i, int j)
|
||||
{
|
||||
if (comparison(items[i], items[j]) > 0)
|
||||
{
|
||||
T key = items[i];
|
||||
items[i] = items[j];
|
||||
items[j] = key;
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InsertionSort(T[] items, int length, Comparison<T> comparison)
|
||||
{
|
||||
for (int i = 0; i < length - 1; i++)
|
||||
{
|
||||
T t = items[i + 1];
|
||||
|
||||
int j = i;
|
||||
while (j >= 0 && comparison(t, items[j]) < 0)
|
||||
{
|
||||
items[j + 1] = items[j];
|
||||
j--;
|
||||
}
|
||||
|
||||
items[j + 1] = t;
|
||||
}
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
private class ComparisonHach : IComparer<T>
|
||||
{
|
||||
public static readonly ComparisonHach Instance = new ComparisonHach();
|
||||
public Comparison<T> comparison;
|
||||
private ComparisonHach() { }
|
||||
public int Compare(T x, T y) { return comparison(x, y); }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Sort(T[] items, Comparison<T> comparison)
|
||||
{
|
||||
Sort(items, comparison, items.Length);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Sort(T[] items, Comparison<T> comparison, int length)
|
||||
{
|
||||
if (length <= IntrosortSizeThreshold)
|
||||
{
|
||||
if (length == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (length == 2)
|
||||
{
|
||||
SwapIfGreater(items, comparison, 0, 1);
|
||||
return;
|
||||
}
|
||||
if (length == 3)
|
||||
{
|
||||
SwapIfGreater(items, comparison, 0, 1);
|
||||
SwapIfGreater(items, comparison, 0, 2);
|
||||
SwapIfGreater(items, comparison, 1, 2);
|
||||
return;
|
||||
}
|
||||
InsertionSort(items, length, comparison);
|
||||
return;
|
||||
}
|
||||
ComparisonHach.Instance.comparison = comparison;
|
||||
Array.Sort(items, 0, length, ComparisonHach.Instance);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
internal static unsafe class UnsafeArraySortHalperX<T> where T : unmanaged
|
||||
{
|
||||
private const int IntrosortSizeThreshold = 16;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SwapIfGreater<TComparer>(T* items, ref TComparer comparer, int i, int j) where TComparer : IStructComparer<T>
|
||||
{
|
||||
if (comparer.Compare(items[i], items[j]) > 0)
|
||||
{
|
||||
T key = items[i];
|
||||
items[i] = items[j];
|
||||
items[j] = key;
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InsertionSort_Unchecked<TComparer>(T* items, int length, ref TComparer comparer) where TComparer : IStructComparer<T>
|
||||
{
|
||||
for (int i = 0; i < length - 1; i++)
|
||||
{
|
||||
T t = items[i + 1];
|
||||
|
||||
int j = i;
|
||||
while (j >= 0 && comparer.Compare(t, items[j]) < 0)
|
||||
{
|
||||
items[j + 1] = items[j];
|
||||
j--;
|
||||
}
|
||||
|
||||
items[j + 1] = t;
|
||||
}
|
||||
}
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
//public static void OptimizedBubbleSort_Unchecked<TComparer>(T* items, int length, ref TComparer comparer) where TComparer : IComparerX<T>
|
||||
//{
|
||||
// for (int i = 0, n = length - 1; i < n; i++)
|
||||
// {
|
||||
// bool noSwaped = true;
|
||||
// for (int j = 0; j < n - i;)
|
||||
// {
|
||||
// ref T j0 = ref items[j++];
|
||||
// if(comparer.Compare(j0, items[j]) < 0)
|
||||
// {
|
||||
// T tmp = items[j];
|
||||
// items[j] = j0;
|
||||
// j0 = tmp;
|
||||
// noSwaped = false;
|
||||
// }
|
||||
// }
|
||||
// if (noSwaped)
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
public static void InsertionSort<TComparer>(T* items, int length, ref TComparer comparer) where TComparer : IStructComparer<T>
|
||||
{
|
||||
if (length == 1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (length == 2)
|
||||
{
|
||||
SwapIfGreater(items, ref comparer, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (length == 3)
|
||||
{
|
||||
SwapIfGreater(items, ref comparer, 0, 1);
|
||||
SwapIfGreater(items, ref comparer, 0, 2);
|
||||
SwapIfGreater(items, ref comparer, 1, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
InsertionSort_Unchecked(items, length, ref comparer);
|
||||
}
|
||||
//public static void OptimizedBubbleSort<TComparer>(T* items, int length, ref TComparer comparer) where TComparer : IComparerX<T>
|
||||
//{
|
||||
// if (length == 1)
|
||||
// {
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (length == 2)
|
||||
// {
|
||||
// SwapIfGreater(items, ref comparer, 0, 1);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// if (length == 3)
|
||||
// {
|
||||
// SwapIfGreater(items, ref comparer, 0, 1);
|
||||
// SwapIfGreater(items, ref comparer, 0, 2);
|
||||
// SwapIfGreater(items, ref comparer, 1, 2);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// OptimizedBubbleSort_Unchecked(items, length, ref comparer);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -6,7 +6,6 @@ using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS.Core.Internal
|
||||
{
|
||||
@ -270,114 +269,6 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
|
||||
}
|
||||
}
|
||||
internal static unsafe class UnmanagedArrayUtility
|
||||
{
|
||||
private static class MetaCache<T>
|
||||
{
|
||||
public readonly static int Size;
|
||||
static MetaCache()
|
||||
{
|
||||
T def = default;
|
||||
Size = Marshal.SizeOf(def);
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* New<T>(int capacity) where T : unmanaged
|
||||
{
|
||||
//Console.WriteLine($"{typeof(T).Name} - {Marshal.SizeOf<T>()} - {capacity} - {Marshal.SizeOf<T>() * capacity}");
|
||||
//return (T*)Marshal.AllocHGlobal(Marshal.SizeOf<T>() * capacity).ToPointer();
|
||||
return MemoryAllocator.Alloc<T>(capacity).As<T>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void New<T>(out T* ptr, int capacity) where T : unmanaged
|
||||
{
|
||||
//ptr = (T*)Marshal.AllocHGlobal(Marshal.SizeOf<T>() * capacity).ToPointer();
|
||||
ptr = MemoryAllocator.Alloc<T>(capacity).As<T>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* NewAndInit<T>(int capacity) where T : unmanaged
|
||||
{
|
||||
//int newSize = MetaCache<T>.Size * capacity;
|
||||
//byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer();
|
||||
//
|
||||
//for (int i = 0; i < newSize; i++)
|
||||
//{
|
||||
// *(newPointer + i) = 0;
|
||||
//}
|
||||
//
|
||||
//return (T*)newPointer;
|
||||
return MemoryAllocator.AllocAndInit<T>(capacity).As<T>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void NewAndInit<T>(out T* ptr, int capacity) where T : unmanaged
|
||||
{
|
||||
//int newSize = MetaCache<T>.Size * capacity;
|
||||
//byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer();
|
||||
//
|
||||
//for (int i = 0; i < newSize; i++)
|
||||
//{
|
||||
// *(newPointer + i) = 0;
|
||||
//}
|
||||
//
|
||||
//ptr = (T*)newPointer;
|
||||
ptr = MemoryAllocator.AllocAndInit<T>(capacity).As<T>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Free(void* pointer)
|
||||
{
|
||||
//Marshal.FreeHGlobal(new IntPtr(pointer));
|
||||
MemoryAllocator.Free(dataPtr: pointer);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Free<T>(ref T* pointer, ref int length) where T : unmanaged
|
||||
{
|
||||
//Marshal.FreeHGlobal(new IntPtr(pointer));
|
||||
MemoryAllocator.Free(dataPtr: pointer);
|
||||
pointer = null;
|
||||
length = 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* Clone<T>(T* sourcePtr, int length) where T : unmanaged
|
||||
{
|
||||
T* clone = New<T>(length);
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
clone[i] = sourcePtr[i];
|
||||
}
|
||||
return clone;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* Resize<T>(void* oldPointer, int newCount) where T : unmanaged
|
||||
{
|
||||
//return (T*)(Marshal.ReAllocHGlobal(
|
||||
// new IntPtr(oldPointer),
|
||||
// new IntPtr(MetaCache<T>.Size * newCount))).ToPointer();
|
||||
return MemoryAllocator.Realloc<T>(MemoryAllocator.Handler.FromDataPtr(oldPointer), newCount).As<T>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* ResizeAndInit<T>(void* oldPointer, int oldSize, int newSize) where T : unmanaged
|
||||
{
|
||||
//int sizeT = MetaCache<T>.Size;
|
||||
//T* result = (T*)Marshal.ReAllocHGlobal(
|
||||
// new IntPtr(oldPointer),
|
||||
// new IntPtr(sizeT * newSize)).ToPointer();
|
||||
//Init((byte*)result, sizeT * oldSize, sizeT * newSize);
|
||||
//return result;
|
||||
return MemoryAllocator.ReallocAndInit<T>(MemoryAllocator.Handler.FromDataPtr(oldPointer), oldSize, newSize).As<T>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void Init(byte* pointer, int startByteIndex, int endByteIndex)
|
||||
{
|
||||
for (int i = startByteIndex; i < endByteIndex; i++)
|
||||
{
|
||||
*(pointer + i) = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class CollectionUtility
|
||||
{
|
||||
|
||||
@ -9,6 +9,33 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
{
|
||||
internal static class ReflectionUtility
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Type GetPureType(this Type type)
|
||||
{
|
||||
if (type.IsGenericType)
|
||||
{
|
||||
return type.GetGenericTypeDefinition();
|
||||
}
|
||||
return type;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool TryGetAttributeInherited<T>(this Type self, out T attribute, out Type declaringAtrType) where T : Attribute
|
||||
{
|
||||
if (self == null || self == typeof(object))
|
||||
{
|
||||
attribute = null;
|
||||
declaringAtrType = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
attribute = self.GetCustomAttribute<T>();
|
||||
if (attribute == null)
|
||||
{
|
||||
return self.BaseType.TryGetAttributeInherited<T>(out attribute, out declaringAtrType);
|
||||
}
|
||||
declaringAtrType = self;
|
||||
return true;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool TryGetAttribute<T>(this MemberInfo self, out T attribute) where T : Attribute
|
||||
{
|
||||
|
||||
392
src/Internal/SortHalper.cs
Normal file
392
src/Internal/SortHalper.cs
Normal file
@ -0,0 +1,392 @@
|
||||
#if DISABLE_DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
#if ENABLE_IL2CPP
|
||||
using Unity.IL2CPP.CompilerServices;
|
||||
#endif
|
||||
|
||||
namespace DCFApixels.DragonECS.Core.Internal
|
||||
{
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
internal static unsafe class SortHalper
|
||||
{
|
||||
#region OrderBy
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void SortBy<T, TKey>(Span<T> span, Func<T, TKey> keySelector)
|
||||
where TKey : IComparable<TKey>
|
||||
{
|
||||
var c = new ComparisonWrapper<T>((a, b) => keySelector(a).CompareTo(keySelector(b)));
|
||||
SortHalper<T, ComparisonWrapper<T>>.Sort(span, ref c);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Span
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Sort<T>(Span<T> span)
|
||||
{
|
||||
var c = new ComparerWrapper<T>(Comparer<T>.Default);
|
||||
SortHalper<T, ComparerWrapper<T>>.Sort(span, ref c);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Sort<T>(Span<T> span, bool _ = false)
|
||||
where T : IComparable<T>
|
||||
{
|
||||
var c = new ComparableWrapper<T>();
|
||||
SortHalper<T, ComparableWrapper<T>>.Sort(span, ref c);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Sort<T>(Span<T> span, Comparer<T> comparer)
|
||||
{
|
||||
var c = new ComparerWrapper<T>(comparer);
|
||||
SortHalper<T, ComparerWrapper<T>>.Sort(span, ref c);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Sort<T>(Span<T> span, Comparison<T> comparison)
|
||||
{
|
||||
var c = new ComparisonWrapper<T>(comparison);
|
||||
SortHalper<T, ComparisonWrapper<T>>.Sort(span, ref c);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Sort<T, TComparer>(Span<T> span, ref TComparer comparer)
|
||||
where TComparer : struct, IComparer<T>
|
||||
{
|
||||
SortHalper<T, TComparer>.Sort(span, ref comparer);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Sort<T, TComparer>(Span<T> span, TComparer comparer)
|
||||
where TComparer : struct, IComparer<T>
|
||||
{
|
||||
SortHalper<T, TComparer>.Sort(span, ref comparer);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Utils
|
||||
// Таблица для De Bruijn-умножения (позиция старшего бита для чисел 0..31)
|
||||
private const int Log2DeBruijn32_Length = 32;
|
||||
private static readonly uint* Log2DeBruijn32 = MemoryAllocator.From(new uint[Log2DeBruijn32_Length]
|
||||
{
|
||||
0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30,
|
||||
8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31
|
||||
}).Ptr;
|
||||
|
||||
/// <summary>32-битный логарифм по основанию 2 (округление вниз). Для value = 0 возвращает 0.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Log2(uint value)
|
||||
{
|
||||
// Для нуля сразу возвращаем 0 (по договорённости, чтобы избежать исключения)
|
||||
if (value == 0) return 0;
|
||||
|
||||
// Заполняем все биты справа от старшего единицей: превращаем число в 2^n - 1
|
||||
value |= value >> 1;
|
||||
value |= value >> 2;
|
||||
value |= value >> 4;
|
||||
value |= value >> 8;
|
||||
value |= value >> 16;
|
||||
|
||||
// Умножение на константу De Bruijn и сдвиг для получения индекса в таблице
|
||||
return (int)Log2DeBruijn32[(value * 0x07C4ACDDu) >> 27];
|
||||
}
|
||||
|
||||
/// <summary>64-битный логарифм по основанию 2 (округление вниз). Для value = 0 возвращает 0.</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Log2(ulong value)
|
||||
{
|
||||
if (value == 0) { return 0; }
|
||||
|
||||
uint high = (uint)(value >> 32);
|
||||
if (high == 0)
|
||||
{
|
||||
return Log2((uint)value);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 32 + Log2(high);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
// a > b = return > 0
|
||||
// int Compare(T a, T b);
|
||||
|
||||
//IComparer<T> comparer
|
||||
//if (comparer == null)
|
||||
//{
|
||||
// comparer = Comparer<T>.Default;
|
||||
//}
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
internal static class SortHalper<T, TComparer>
|
||||
where TComparer : struct, IComparer<T>
|
||||
{
|
||||
private const int IntrosortSizeThreshold = 16;
|
||||
|
||||
#region Public
|
||||
public static void Sort(Span<T> keys, ref TComparer comparer)
|
||||
{
|
||||
// Add a try block here to detect IComparers (or their
|
||||
// underlying IComparables, etc) that are bogus.
|
||||
IntrospectiveSort(keys, ref comparer);
|
||||
}
|
||||
public static int BinarySearch(T[] array, int index, int length, T value, ref TComparer comparer)
|
||||
{
|
||||
Debug.Assert(array != null, "Check the arguments in the caller!");
|
||||
Debug.Assert(index >= 0 && length >= 0 && (array.Length - index >= length), "Check the arguments in the caller!");
|
||||
|
||||
int lo = index;
|
||||
int hi = index + length - 1;
|
||||
while (lo <= hi)
|
||||
{
|
||||
int i = lo + ((hi - lo) >> 1);
|
||||
int order = comparer.Compare(array[i], value);
|
||||
|
||||
if (order == 0) return i;
|
||||
if (order < 0)
|
||||
{
|
||||
lo = i + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
hi = i - 1;
|
||||
}
|
||||
}
|
||||
|
||||
return ~lo;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Internal
|
||||
internal static void IntrospectiveSort(Span<T> keys, ref TComparer comparer)
|
||||
{
|
||||
//Debug.Assert(comparer != null);
|
||||
if (keys.Length > 1)
|
||||
{
|
||||
IntroSort(keys, 2 * (SortHalper.Log2((uint)keys.Length) + 1), ref comparer);
|
||||
}
|
||||
}
|
||||
|
||||
// IntroSort is recursive; block it from being inlined into itself as
|
||||
// this is currenly not profitable.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private static void IntroSort(Span<T> keys, int depthLimit, ref TComparer comparer)
|
||||
{
|
||||
Debug.Assert(!keys.IsEmpty);
|
||||
Debug.Assert(depthLimit >= 0);
|
||||
//Debug.Assert(comparer != null);
|
||||
|
||||
int partitionSize = keys.Length;
|
||||
while (partitionSize > 1)
|
||||
{
|
||||
if (partitionSize <= IntrosortSizeThreshold)
|
||||
{
|
||||
|
||||
if (partitionSize == 2)
|
||||
{
|
||||
SwapIfGreater(keys, ref comparer, 0, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (partitionSize == 3)
|
||||
{
|
||||
SwapIfGreater(keys, ref comparer, 0, 1);
|
||||
SwapIfGreater(keys, ref comparer, 0, 2);
|
||||
SwapIfGreater(keys, ref comparer, 1, 2);
|
||||
return;
|
||||
}
|
||||
|
||||
InsertionSort(keys.Slice(0, partitionSize), ref comparer);
|
||||
return;
|
||||
}
|
||||
|
||||
if (depthLimit == 0)
|
||||
{
|
||||
HeapSort(keys.Slice(0, partitionSize), ref comparer);
|
||||
return;
|
||||
}
|
||||
depthLimit--;
|
||||
|
||||
int p = PickPivotAndPartition(keys.Slice(0, partitionSize), ref comparer);
|
||||
|
||||
// Note we've already partitioned around the pivot and do not have to move the pivot again.
|
||||
IntroSort(keys.Slice(p + 1, partitionSize - (p + 1)), depthLimit, ref comparer);
|
||||
partitionSize = p;
|
||||
}
|
||||
}
|
||||
|
||||
private static int PickPivotAndPartition(Span<T> keys, ref TComparer comparer)
|
||||
{
|
||||
Debug.Assert(keys.Length >= IntrosortSizeThreshold);
|
||||
//Debug.Assert(comparer != null);
|
||||
|
||||
int hi = keys.Length - 1;
|
||||
|
||||
// Compute median-of-three. But also partition them, since we've done the comparison.
|
||||
int middle = hi >> 1;
|
||||
|
||||
// Sort lo, mid and hi appropriately, then pick mid as the pivot.
|
||||
SwapIfGreater(keys, ref comparer, 0, middle); // swap the low with the mid point
|
||||
SwapIfGreater(keys, ref comparer, 0, hi); // swap the low with the high
|
||||
SwapIfGreater(keys, ref comparer, middle, hi); // swap the middle with the high
|
||||
|
||||
T pivot = keys[middle];
|
||||
Swap(keys, middle, hi - 1);
|
||||
int left = 0, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below.
|
||||
|
||||
while (left < right)
|
||||
{
|
||||
while (comparer.Compare(keys[++left], pivot) < 0) ;
|
||||
while (comparer.Compare(pivot, keys[--right]) < 0) ;
|
||||
|
||||
if (left >= right)
|
||||
break;
|
||||
|
||||
Swap(keys, left, right);
|
||||
}
|
||||
|
||||
// Put pivot in the right location.
|
||||
if (left != hi - 1)
|
||||
{
|
||||
Swap(keys, left, hi - 1);
|
||||
}
|
||||
return left;
|
||||
}
|
||||
|
||||
private static void HeapSort(Span<T> keys, ref TComparer comparer)
|
||||
{
|
||||
Debug.Assert(!keys.IsEmpty);
|
||||
//Debug.Assert(comparer != null);
|
||||
|
||||
int n = keys.Length;
|
||||
for (int i = n >> 1; i >= 1; i--)
|
||||
{
|
||||
DownHeap(keys, i, n, ref comparer);
|
||||
}
|
||||
|
||||
for (int i = n; i > 1; i--)
|
||||
{
|
||||
Swap(keys, 0, i - 1);
|
||||
DownHeap(keys, 1, i - 1, ref comparer);
|
||||
}
|
||||
}
|
||||
|
||||
private static void DownHeap(Span<T> keys, int i, int n, ref TComparer comparer)
|
||||
{
|
||||
//Debug.Assert(comparer != null);
|
||||
|
||||
T d = keys[i - 1];
|
||||
while (i <= n >> 1)
|
||||
{
|
||||
int child = 2 * i;
|
||||
if (child < n && comparer.Compare(keys[child - 1], keys[child]) < 0)
|
||||
{
|
||||
child++;
|
||||
}
|
||||
|
||||
if (!(comparer.Compare(d, keys[child - 1]) < 0))
|
||||
break;
|
||||
|
||||
keys[i - 1] = keys[child - 1];
|
||||
i = child;
|
||||
}
|
||||
|
||||
keys[i - 1] = d;
|
||||
}
|
||||
|
||||
private static void InsertionSort(Span<T> keys, ref TComparer comparer)
|
||||
{
|
||||
for (int i = 0; i < keys.Length - 1; i++)
|
||||
{
|
||||
T t = keys[i + 1];
|
||||
|
||||
int j = i;
|
||||
while (j >= 0 && comparer.Compare(t, keys[j]) < 0)
|
||||
{
|
||||
keys[j + 1] = keys[j];
|
||||
j--;
|
||||
}
|
||||
|
||||
keys[j + 1] = t;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Swaps
|
||||
private static void SwapIfGreater(Span<T> keys, ref TComparer comparer, int i, int j)
|
||||
{
|
||||
Debug.Assert(i != j);
|
||||
if (comparer.Compare(keys[i], keys[j]) > 0)
|
||||
{
|
||||
T key = keys[i];
|
||||
keys[i] = keys[j];
|
||||
keys[j] = key;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static void Swap(Span<T> a, int i, int j)
|
||||
{
|
||||
Debug.Assert(i != j);
|
||||
T t = a[i];
|
||||
a[i] = a[j];
|
||||
a[j] = t;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region Wrappers
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
internal readonly struct ComparableWrapper<T> : IComparer<T>
|
||||
where T : IComparable<T>
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Compare(T x, T y) { return x.CompareTo(y); }
|
||||
}
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
internal readonly struct ComparisonWrapper<T> : IComparer<T>
|
||||
{
|
||||
public readonly Comparison<T> Comparison;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ComparisonWrapper(Comparison<T> comparison)
|
||||
{
|
||||
Comparison = comparison;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Compare(T x, T y)
|
||||
{
|
||||
return Comparison(x, y);
|
||||
}
|
||||
}
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
internal readonly struct ComparerWrapper<T> : IComparer<T>
|
||||
{
|
||||
public readonly Comparer<T> Comparer;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ComparerWrapper(Comparer<T> comparer)
|
||||
{
|
||||
Comparer = comparer;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Compare(T x, T y)
|
||||
{
|
||||
return Comparer.Compare(x, y);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bfb00ad58c175a84388c316daf9b03ae
|
||||
guid: 7e60048b7c58cdf4ba044b7c832bd6a4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@ -38,13 +38,13 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UnsafeArray(int length)
|
||||
{
|
||||
UnmanagedArrayUtility.New(out ptr, length);
|
||||
ptr = MemoryAllocator.Alloc<T>(length).Ptr;
|
||||
Length = length;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UnsafeArray(int length, bool isInit)
|
||||
{
|
||||
UnmanagedArrayUtility.NewAndInit(out ptr, length);
|
||||
ptr = MemoryAllocator.AllocAndInit<T>(length).Ptr;
|
||||
Length = length;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -90,18 +90,20 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public UnsafeArray<T> Clone()
|
||||
{
|
||||
return new UnsafeArray<T>(UnmanagedArrayUtility.Clone(ptr, Length), Length);
|
||||
return new UnsafeArray<T>(MemoryAllocator.From(ptr, Length).Ptr, Length);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose()
|
||||
{
|
||||
UnmanagedArrayUtility.Free(ref ptr, ref Length);
|
||||
MemoryAllocator.Free(ptr);
|
||||
ptr = default;
|
||||
Length = default;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ReadonlyDispose()
|
||||
{
|
||||
UnmanagedArrayUtility.Free(ptr);
|
||||
MemoryAllocator.Free(ptr);
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
@ -114,6 +116,9 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
return CollectionUtility.AutoToString(elements, "ua");
|
||||
}
|
||||
|
||||
public Span<T> AsSpan() { return new Span<T>(ptr, Length); }
|
||||
public T[] ToArray() { return AsSpan().ToArray(); }
|
||||
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() { return GetEnumerator(); }
|
||||
IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
@ -3,7 +3,6 @@
|
||||
#endif
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using DCFApixels.DragonECS.PoolsCore;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -26,6 +25,7 @@ namespace DCFApixels.DragonECS
|
||||
/// <summary>Pool for IEcsComponent components</summary>
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)]
|
||||
@ -35,7 +35,8 @@ namespace DCFApixels.DragonECS
|
||||
public sealed class EcsPool<T> : IEcsPoolImplementation<T>, IEcsStructPool<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
|
||||
where T : struct, IEcsComponent
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private short _worldID;
|
||||
private EcsWorld _world;
|
||||
private int _componentTypeID;
|
||||
private EcsMaskChunck _maskBit;
|
||||
|
||||
@ -45,14 +46,14 @@ namespace DCFApixels.DragonECS
|
||||
private int[] _recycledItems;
|
||||
private int _recycledItemsCount = 0;
|
||||
|
||||
private readonly IEcsComponentLifecycle<T> _componentLifecycleHandler = EcsComponentLifecycleHandler<T>.instance;
|
||||
private readonly bool _isHasComponentLifecycleHandler = EcsComponentLifecycleHandler<T>.isHasHandler;
|
||||
private readonly IEcsComponentCopy<T> _componentCopyHandler = EcsComponentCopyHandler<T>.instance;
|
||||
private readonly bool _isHasComponentCopyHandler = EcsComponentCopyHandler<T>.isHasHandler;
|
||||
private readonly IEcsComponentLifecycle<T> _customLifecycle = EcsComponentLifecycle<T>.CustomHandler;
|
||||
private readonly bool _isCustomLifecycle = EcsComponentLifecycle<T>.IsCustom;
|
||||
private readonly IEcsComponentCopy<T> _customCopy = EcsComponentCopy<T>.CustomHandler;
|
||||
private readonly bool _isCustomCopy = EcsComponentCopy<T>.IsCustom;
|
||||
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
private StructList<IEcsPoolEventListener> _listeners = new StructList<IEcsPoolEventListener>(2);
|
||||
private int _listenersCachedCount = 0;
|
||||
private bool _hasAnyListener = false;
|
||||
#endif
|
||||
private bool _isLocked;
|
||||
|
||||
@ -77,7 +78,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
public EcsWorld World
|
||||
{
|
||||
get { return _source; }
|
||||
get { return _world; }
|
||||
}
|
||||
public bool IsReadOnly
|
||||
{
|
||||
@ -104,7 +105,8 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID)
|
||||
{
|
||||
_source = world;
|
||||
_world = world;
|
||||
_worldID = world.ID;
|
||||
_mediator = mediator;
|
||||
_componentTypeID = componentTypeID;
|
||||
_maskBit = EcsMaskChunck.FromID(componentTypeID);
|
||||
@ -128,13 +130,13 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
#if DEBUG
|
||||
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
|
||||
if (_source.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
|
||||
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
|
||||
if (_world.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
|
||||
if (itemIndex > 0) { EcsPoolThrowHelper.ThrowAlreadyHasComponent<T>(entityID); }
|
||||
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (itemIndex > 0) { return ref Get(entityID); }
|
||||
if (_isLocked | _source.IsUsed(entityID) == false) { return ref _items[0]; }
|
||||
if (_isLocked | _world.IsUsed(entityID) == false) { return ref _items[0]; }
|
||||
#endif
|
||||
if (_recycledItemsCount > 0)
|
||||
{
|
||||
@ -151,9 +153,9 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
ref T result = ref _items[itemIndex];
|
||||
EnableComponent(ref result);
|
||||
InvokeOnAdd(entityID, ref _items[itemIndex]);
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
_listeners.InvokeOnAddAndGet(entityID, _listenersCachedCount);
|
||||
if (_hasAnyListener) { _listeners.InvokeOnAddAndGet(entityID); }
|
||||
#endif
|
||||
return ref result;
|
||||
}
|
||||
@ -164,7 +166,7 @@ namespace DCFApixels.DragonECS
|
||||
if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
|
||||
#endif
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
_listeners.InvokeOnGet(entityID, _listenersCachedCount);
|
||||
if (_hasAnyListener) { _listeners.InvokeOnGet(entityID); }
|
||||
#endif
|
||||
return ref _items[_mapping[entityID]];
|
||||
}
|
||||
@ -179,7 +181,7 @@ namespace DCFApixels.DragonECS
|
||||
public ref T TryAddOrGet(int entityID)
|
||||
{
|
||||
#if DEBUG
|
||||
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
|
||||
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
|
||||
#endif
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
if (itemIndex <= 0)
|
||||
@ -203,13 +205,13 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
EnableComponent(ref _items[itemIndex]);
|
||||
InvokeOnAdd(entityID, ref _items[itemIndex]);
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
_listeners.InvokeOnAdd(entityID, _listenersCachedCount);
|
||||
if (_hasAnyListener) { _listeners.InvokeOnAdd(entityID); }
|
||||
#endif
|
||||
} //Add block end
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
_listeners.InvokeOnGet(entityID, _listenersCachedCount);
|
||||
if (_hasAnyListener) { _listeners.InvokeOnGet(entityID); }
|
||||
#endif
|
||||
return ref _items[itemIndex];
|
||||
}
|
||||
@ -222,14 +224,14 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
#if DEBUG
|
||||
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
|
||||
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
|
||||
if (itemIndex <= 0) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
|
||||
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (itemIndex <= 0) { return; }
|
||||
if (_isLocked) { return; }
|
||||
#endif
|
||||
DisableComponent(ref _items[itemIndex]);
|
||||
InvokeOnDel(entityID, ref _items[itemIndex]);
|
||||
if (_recycledItemsCount >= _recycledItems.Length)
|
||||
{
|
||||
Array.Resize(ref _recycledItems, ArrayUtility.NextPow2Safe(_recycledItemsCount));
|
||||
@ -239,7 +241,7 @@ namespace DCFApixels.DragonECS
|
||||
_itemsCount--;
|
||||
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
_listeners.InvokeOnDel(entityID, _listenersCachedCount);
|
||||
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
|
||||
#endif
|
||||
}
|
||||
public void TryDel(int entityID)
|
||||
@ -256,7 +258,7 @@ namespace DCFApixels.DragonECS
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (!Has(fromEntityID)) { return; }
|
||||
#endif
|
||||
CopyComponent(ref Get(fromEntityID), ref TryAddOrGet(toEntityID));
|
||||
EcsComponentCopy<T>.Copy(_isCustomCopy, _customCopy, ref Get(fromEntityID), ref TryAddOrGet(toEntityID));
|
||||
}
|
||||
public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
|
||||
{
|
||||
@ -265,7 +267,7 @@ namespace DCFApixels.DragonECS
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (!Has(fromEntityID)) { return; }
|
||||
#endif
|
||||
CopyComponent(ref Get(fromEntityID), ref toWorld.GetPool<T>().TryAddOrGet(toEntityID));
|
||||
EcsComponentCopy<T>.Copy(_isCustomCopy, _customCopy, ref Get(fromEntityID), ref toWorld.GetPool<T>().TryAddOrGet(toEntityID));
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
@ -277,15 +279,15 @@ namespace DCFApixels.DragonECS
|
||||
#endif
|
||||
_recycledItemsCount = 0; // ñïåðåäè ÷òîáû îáíóëÿëîñü, òàê êàê Del íå îáíóëÿåò
|
||||
if (_itemsCount <= 0) { return; }
|
||||
var span = _source.Where(out SingleAspect<T> _);
|
||||
var span = _world.Where(out SingleAspect<T> _);
|
||||
foreach (var entityID in span)
|
||||
{
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
DisableComponent(ref _items[itemIndex]);
|
||||
InvokeOnDel(entityID, ref _items[itemIndex]);
|
||||
itemIndex = 0;
|
||||
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
_listeners.InvokeOnDel(entityID, _listenersCachedCount);
|
||||
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
|
||||
#endif
|
||||
}
|
||||
_itemsCount = 0;
|
||||
@ -314,6 +316,26 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void InvokeOnAdd(int entityID, ref T component)
|
||||
{
|
||||
if (_isCustomLifecycle)
|
||||
{
|
||||
_customLifecycle.OnAdd(ref component, _worldID, entityID);
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void InvokeOnDel(int entityID, ref T component)
|
||||
{
|
||||
if (_isCustomLifecycle)
|
||||
{
|
||||
_customLifecycle.OnDel(ref component, _worldID, entityID);
|
||||
}
|
||||
else
|
||||
{
|
||||
component = default;
|
||||
}
|
||||
}
|
||||
void IEcsPool.AddEmpty(int entityID) { Add(entityID); }
|
||||
void IEcsPool.AddRaw(int entityID, object dataRaw)
|
||||
{
|
||||
@ -332,58 +354,19 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); }
|
||||
_listeners.Add(listener);
|
||||
_listenersCachedCount++;
|
||||
_hasAnyListener = _listeners.Count > 0;
|
||||
}
|
||||
public void RemoveListener(IEcsPoolEventListener listener)
|
||||
{
|
||||
if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); }
|
||||
if (_listeners.RemoveWithOrder(listener))
|
||||
{
|
||||
_listenersCachedCount--;
|
||||
_hasAnyListener = _listeners.Count > 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Enable/Disable/Copy
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void EnableComponent(ref T component)
|
||||
{
|
||||
if (_isHasComponentLifecycleHandler)
|
||||
{
|
||||
_componentLifecycleHandler.Enable(ref component);
|
||||
}
|
||||
else
|
||||
{
|
||||
component = default;
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void DisableComponent(ref T component)
|
||||
{
|
||||
if (_isHasComponentLifecycleHandler)
|
||||
{
|
||||
_componentLifecycleHandler.Disable(ref component);
|
||||
}
|
||||
else
|
||||
{
|
||||
component = default;
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void CopyComponent(ref T from, ref T to)
|
||||
{
|
||||
if (_isHasComponentCopyHandler)
|
||||
{
|
||||
_componentCopyHandler.Copy(ref from, ref to);
|
||||
}
|
||||
else
|
||||
{
|
||||
to = from;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IEnumerator - IntelliSense hack
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() { throw new NotImplementedException(); }
|
||||
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
|
||||
@ -406,12 +389,20 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
pool.TryAddOrGet(entityID) = component;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T Apply(short worldID, int entityID)
|
||||
{
|
||||
return ref EcsWorld.GetPoolInstance<EcsPool<T>>(worldID).TryAddOrGet(entityID);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
#endif
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public readonly struct ReadonlyEcsPool<T> : IEcsReadonlyPool //IEnumerable<T> - IntelliSense hack
|
||||
where T : struct, IEcsComponent
|
||||
@ -484,7 +475,6 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
public static class EcsPoolExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -518,29 +508,5 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
return self.AnyPool<EcsPool<TComponent>>();
|
||||
}
|
||||
|
||||
#region Obsolete
|
||||
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Inc) + "<T>()")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsPool<TComponent> Include<TComponent>(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent
|
||||
{
|
||||
return self.IncludePool<EcsPool<TComponent>>();
|
||||
}
|
||||
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Exc) + "<T>()")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsPool<TComponent> Exclude<TComponent>(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent
|
||||
{
|
||||
return self.ExcludePool<EcsPool<TComponent>>();
|
||||
}
|
||||
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Opt) + "<T>()")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsPool<TComponent> Optional<TComponent>(this EcsAspect.Builder self) where TComponent : struct, IEcsComponent
|
||||
{
|
||||
return self.OptionalPool<EcsPool<TComponent>>();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,19 @@
|
||||
#if DISABLE_DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
#define DRAGONECS_ENABLE_POOLS_EVENTS
|
||||
#else
|
||||
#undef DRAGONECS_ENABLE_POOLS_EVENTS
|
||||
#endif
|
||||
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using DCFApixels.DragonECS.PoolsCore;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS.PoolsCore
|
||||
namespace DCFApixels.DragonECS.Core
|
||||
{
|
||||
/// <summary> Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool<T>. </summary>
|
||||
public interface IEcsPoolImplementation : IEcsPool
|
||||
@ -62,7 +67,12 @@ namespace DCFApixels.DragonECS.PoolsCore
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowNullListener()
|
||||
{
|
||||
throw new ArgumentNullException("listener is null");
|
||||
throw new ArgumentNullException("Listener is null");
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowNullComponent()
|
||||
{
|
||||
throw new ArgumentNullException("Component is null");
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowPoolLocked()
|
||||
@ -286,72 +296,17 @@ namespace DCFApixels.DragonECS
|
||||
/// <summary>Called after deleting an entity from the pool</summary>
|
||||
void OnDel(int entityID);
|
||||
}
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
public static class PoolEventListExtensions
|
||||
{
|
||||
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InvokeOnAdd(this List<IEcsPoolEventListener> self, int entityID)
|
||||
{
|
||||
self.InvokeOnAdd(entityID, self.Count);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InvokeOnAdd(this List<IEcsPoolEventListener> self, int entityID, int cachedCount)
|
||||
{
|
||||
for (int i = 0; i < cachedCount; i++) { self[i].OnAdd(entityID); }
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InvokeOnAddAndGet(this List<IEcsPoolEventListener> self, int entityID)
|
||||
{
|
||||
self.InvokeOnAddAndGet(entityID, self.Count);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InvokeOnAddAndGet(this List<IEcsPoolEventListener> self, int entityID, int cachedCount)
|
||||
{
|
||||
for (int i = 0; i < cachedCount; i++)
|
||||
{
|
||||
self[i].OnAdd(entityID);
|
||||
self[i].OnGet(entityID);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InvokeOnGet(this List<IEcsPoolEventListener> self, int entityID)
|
||||
{
|
||||
self.InvokeOnGet(entityID, self.Count);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InvokeOnGet(this List<IEcsPoolEventListener> self, int entityID, int cachedCount)
|
||||
{
|
||||
for (int i = 1; i < cachedCount; i++) { self[i].OnGet(entityID); }
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InvokeOnDel(this List<IEcsPoolEventListener> self, int entityID)
|
||||
{
|
||||
self.InvokeOnDel(entityID, self.Count);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InvokeOnDel(this List<IEcsPoolEventListener> self, int entityID, int cachedCount)
|
||||
{
|
||||
for (int i = 0; i < cachedCount; i++) { self[i].OnDel(entityID); }
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void InvokeOnAdd(ref this StructList<IEcsPoolEventListener> self, int entityID)
|
||||
{
|
||||
for (int i = 0; i < self.Count; i++) { self[i].OnAdd(entityID); }
|
||||
}
|
||||
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void InvokeOnAdd(ref this StructList<IEcsPoolEventListener> self, int entityID, int cachedCount)
|
||||
{
|
||||
for (int i = 0; i < cachedCount; i++) { self[i].OnAdd(entityID); }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void InvokeOnAddAndGet(ref this StructList<IEcsPoolEventListener> self, int entityID)
|
||||
public static void InvokeOnAddAndGet(this List<IEcsPoolEventListener> self, int entityID)
|
||||
{
|
||||
for (int i = 0; i < self.Count; i++)
|
||||
{
|
||||
@ -359,36 +314,50 @@ namespace DCFApixels.DragonECS
|
||||
self[i].OnGet(entityID);
|
||||
}
|
||||
}
|
||||
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void InvokeOnAddAndGet(ref this StructList<IEcsPoolEventListener> self, int entityID, int cachedCount)
|
||||
public static void InvokeOnGet(this List<IEcsPoolEventListener> self, int entityID)
|
||||
{
|
||||
for (int i = 0; i < cachedCount; i++)
|
||||
for (int i = 1; i < self.Count; i++) { self[i].OnGet(entityID); }
|
||||
}
|
||||
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void InvokeOnDel(this List<IEcsPoolEventListener> self, int entityID)
|
||||
{
|
||||
for (int i = 0; i < self.Count; i++) { self[i].OnDel(entityID); }
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
|
||||
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void InvokeOnAdd(this StructList<IEcsPoolEventListener> self, int entityID)
|
||||
{
|
||||
for (int i = 0; i < self.Count; i++) { self[i].OnAdd(entityID); }
|
||||
}
|
||||
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void InvokeOnAddAndGet(this StructList<IEcsPoolEventListener> self, int entityID)
|
||||
{
|
||||
for (int i = 0; i < self.Count; i++)
|
||||
{
|
||||
self[i].OnAdd(entityID);
|
||||
self[i].OnGet(entityID);
|
||||
}
|
||||
}
|
||||
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void InvokeOnGet(ref this StructList<IEcsPoolEventListener> self, int entityID)
|
||||
internal static void InvokeOnGet(this StructList<IEcsPoolEventListener> self, int entityID)
|
||||
{
|
||||
for (int i = 1; i < self.Count; i++) { self[i].OnGet(entityID); }
|
||||
for (int i = 0; i < self.Count; i++) { self[i].OnGet(entityID); }
|
||||
}
|
||||
[Conditional("DRAGONECS_ENABLE_POOLS_EVENTS")]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void InvokeOnGet(ref this StructList<IEcsPoolEventListener> self, int entityID, int cachedCount)
|
||||
{
|
||||
for (int i = 1; i < cachedCount; i++) { self[i].OnGet(entityID); }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void InvokeOnDel(ref this StructList<IEcsPoolEventListener> self, int entityID)
|
||||
internal static void InvokeOnDel(this StructList<IEcsPoolEventListener> self, int entityID)
|
||||
{
|
||||
for (int i = 0; i < self.Count; i++) { self[i].OnDel(entityID); }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void InvokeOnDel(ref this StructList<IEcsPoolEventListener> self, int entityID, int cachedCount)
|
||||
{
|
||||
for (int i = 0; i < cachedCount; i++) { self[i].OnDel(entityID); }
|
||||
}
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
}
|
||||
@ -3,7 +3,6 @@
|
||||
#endif
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using DCFApixels.DragonECS.PoolsCore;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@ -26,6 +25,7 @@ namespace DCFApixels.DragonECS
|
||||
/// <summary> Pool for IEcsTagComponent components. </summary>
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)]
|
||||
@ -35,7 +35,7 @@ namespace DCFApixels.DragonECS
|
||||
public sealed class EcsTagPool<T> : IEcsPoolImplementation<T>, IEcsStructPool<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
|
||||
where T : struct, IEcsTagComponent
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private EcsWorld _world;
|
||||
private int _componentTypeID;
|
||||
private EcsMaskChunck _maskBit;
|
||||
|
||||
@ -44,7 +44,7 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
private StructList<IEcsPoolEventListener> _listeners = new StructList<IEcsPoolEventListener>(2);
|
||||
private int _listenersCachedCount = 0;
|
||||
private bool _hasAnyListener = false;
|
||||
#endif
|
||||
private bool _isLocked;
|
||||
|
||||
@ -78,7 +78,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
public EcsWorld World
|
||||
{
|
||||
get { return _source; }
|
||||
get { return _world; }
|
||||
}
|
||||
public bool IsReadOnly
|
||||
{
|
||||
@ -102,7 +102,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID)
|
||||
{
|
||||
_source = world;
|
||||
_world = world;
|
||||
_mediator = mediator;
|
||||
_componentTypeID = componentTypeID;
|
||||
_maskBit = EcsMaskChunck.FromID(componentTypeID);
|
||||
@ -116,17 +116,18 @@ namespace DCFApixels.DragonECS
|
||||
public void Add(int entityID)
|
||||
{
|
||||
#if DEBUG
|
||||
if (_source.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_source, entityID); }
|
||||
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
|
||||
if (_world.IsUsed(entityID) == false) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
|
||||
if (Has(entityID)) { EcsPoolThrowHelper.ThrowAlreadyHasComponent<T>(entityID); }
|
||||
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (Has(entityID) | _source.IsUsed(entityID) == false | _isLocked) { return; }
|
||||
if (Has(entityID) | _world.IsUsed(entityID) == false | _isLocked) { return; }
|
||||
#endif
|
||||
_count++;
|
||||
_mapping[entityID] = true;
|
||||
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
_listeners.InvokeOnAdd(entityID, _listenersCachedCount);
|
||||
if (_hasAnyListener) { _listeners.InvokeOnAdd(entityID); }
|
||||
#endif
|
||||
}
|
||||
public void TryAdd(int entityID)
|
||||
@ -144,6 +145,7 @@ namespace DCFApixels.DragonECS
|
||||
public void Del(int entityID)
|
||||
{
|
||||
#if DEBUG
|
||||
if (entityID == EcsConsts.NULL_ENTITY_ID) { EcsPoolThrowHelper.ThrowEntityIsNotAlive(_world, entityID); }
|
||||
if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
|
||||
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
@ -153,7 +155,7 @@ namespace DCFApixels.DragonECS
|
||||
_count--;
|
||||
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
_listeners.InvokeOnDel(entityID, _listenersCachedCount);
|
||||
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
|
||||
#endif
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -216,14 +218,14 @@ namespace DCFApixels.DragonECS
|
||||
if (_isLocked) { return; }
|
||||
#endif
|
||||
if (_count <= 0) { return; }
|
||||
var span = _source.Where(out SingleTagAspect<T> _);
|
||||
var span = _world.Where(out SingleTagAspect<T> _);
|
||||
_count = 0;
|
||||
foreach (var entityID in span)
|
||||
{
|
||||
_mapping[entityID] = false;
|
||||
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
_listeners.InvokeOnDel(entityID, _listenersCachedCount);
|
||||
if (_hasAnyListener) { _listeners.InvokeOnDel(entityID); }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
@ -293,14 +295,14 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); }
|
||||
_listeners.Add(listener);
|
||||
_listenersCachedCount++;
|
||||
_hasAnyListener = _listeners.Count > 0;
|
||||
}
|
||||
public void RemoveListener(IEcsPoolEventListener listener)
|
||||
{
|
||||
if (listener == null) { EcsPoolThrowHelper.ThrowNullListener(); }
|
||||
if (_listeners.RemoveWithOrder(listener))
|
||||
{
|
||||
_listenersCachedCount--;
|
||||
_hasAnyListener = _listeners.Count > 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -328,12 +330,22 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
pool.TryAdd(entityID);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T Apply(short worldID, int entityID)
|
||||
{
|
||||
var pool = EcsWorld.GetPoolInstance<EcsTagPool<T>>(worldID);
|
||||
pool.TryAdd(entityID);
|
||||
return ref pool._fakeComponent;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
#endif
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public readonly struct ReadonlyEcsTagPool<T> : IEcsReadonlyPool //IEnumerable<T> - IntelliSense hack
|
||||
where T : struct, IEcsTagComponent
|
||||
@ -407,7 +419,6 @@ namespace DCFApixels.DragonECS
|
||||
public static implicit operator ReadonlyEcsTagPool<T>(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance<EcsTagPool<T>>(); }
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class EcsTagPoolExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -441,68 +452,5 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
return self.OptionalPool<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
|
||||
#region Obsolete
|
||||
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Inc) + "<T>()")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> Include<TTagComponent>(this EcsAspect.Builder self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.IncludePool<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Exc) + "<T>()")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> Exclude<TTagComponent>(this EcsAspect.Builder self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.ExcludePool<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Opt) + "<T>()")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> Optional<TTagComponent>(this EcsAspect.Builder self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.OptionalPool<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
|
||||
//---------------------------------------------------
|
||||
|
||||
[Obsolete("Use " + nameof(EcsWorld) + "." + nameof(GetPool) + "<T>()")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> GetTagPool<TTagComponent>(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.GetPoolInstance<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
[Obsolete("Use " + nameof(EcsWorld) + "." + nameof(GetPoolUnchecked) + "<T>()")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> GetTagPoolUnchecked<TTagComponent>(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.GetPoolInstanceUnchecked<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
|
||||
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Inc) + "<T>()")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> IncludeTag<TTagComponent>(this EcsAspect.Builder self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.IncludePool<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Exc) + "<T>()")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> ExcludeTag<TTagComponent>(this EcsAspect.Builder self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.ExcludePool<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
[Obsolete("Use " + nameof(EcsAspect) + "." + nameof(EcsAspect.Builder) + "." + nameof(Opt) + "<T>()")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> OptionalTag<TTagComponent>(this EcsAspect.Builder self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.OptionalPool<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
560
src/Pools/EcsValuePool.cs
Normal file
560
src/Pools/EcsValuePool.cs
Normal file
@ -0,0 +1,560 @@
|
||||
#if DISABLE_DEBUG
|
||||
#undef DEBUG
|
||||
#endif
|
||||
using DCFApixels.DragonECS.Core;
|
||||
using DCFApixels.DragonECS.Core.Internal;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static DCFApixels.DragonECS.Core.Internal.MemoryAllocator;
|
||||
#if ENABLE_IL2CPP
|
||||
using Unity.IL2CPP.CompilerServices;
|
||||
#endif
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
/// <summary>Value type component</summary>
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)]
|
||||
[MetaDescription(EcsConsts.AUTHOR, "Value type component.")]
|
||||
[MetaID("DragonECS_B053D6FA9C01208AFD1922E6A1D57D83")]
|
||||
public interface IEcsValueComponent : IEcsComponentMember { }
|
||||
|
||||
/// <summary>Pool for IEcsValueComponent components</summary>
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
[Il2CppSetOption(Option.ArrayBoundsChecks, false)]
|
||||
#endif
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.POOLS_GROUP)]
|
||||
[MetaDescription(EcsConsts.AUTHOR, "Pool for IEcsValueComponent components.")]
|
||||
[MetaID("DragonECS_5097D6FA9C0109349197EEAC3A0D2858")]
|
||||
[DebuggerDisplay("Count: {Count} Type: {ComponentType}")]
|
||||
public sealed unsafe class EcsValuePool<T> : IEcsPoolImplementation<T>, IEcsStructPool<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
|
||||
where T : unmanaged, IEcsValueComponent
|
||||
{
|
||||
private EcsWorld _world;
|
||||
private short _worldID;
|
||||
private int _componentTypeID;
|
||||
private EcsMaskChunck _maskBit;
|
||||
|
||||
public int* _mapping; // index = entityID / value = itemIndex;/ value = 0 = no entityID.
|
||||
public T* _items; // dense; _items[0] - fake component.
|
||||
public int _itemsLength;
|
||||
public int _itemsCount;
|
||||
public int* _recycledItems;
|
||||
public int _recycledItemsLength;
|
||||
public int _recycledItemsCount;
|
||||
private readonly EcsValuePoolSharedStore* _sharedStore;
|
||||
|
||||
private HMem<int> _mappingHandler;
|
||||
private HMem<T> _itemsHandler;
|
||||
private HMem<int> _recycledItemsHandler;
|
||||
|
||||
private readonly IEcsComponentLifecycle<T> _customLifecycle = EcsComponentLifecycle<T>.CustomHandler;
|
||||
private readonly bool _isCustomLifecycle = EcsComponentLifecycle<T>.IsCustom;
|
||||
private readonly IEcsComponentCopy<T> _customCopy = EcsComponentCopy<T>.CustomHandler;
|
||||
private readonly bool _isCustomCopy = EcsComponentCopy<T>.IsCustom;
|
||||
|
||||
private bool _isLocked;
|
||||
|
||||
private EcsWorld.PoolsMediator _mediator;
|
||||
|
||||
#region Properites
|
||||
public int Count
|
||||
{
|
||||
get { return _itemsCount; }
|
||||
}
|
||||
public int Capacity
|
||||
{
|
||||
get { return _itemsLength; }
|
||||
}
|
||||
public int ComponentTypeID
|
||||
{
|
||||
get { return _componentTypeID; }
|
||||
}
|
||||
public Type ComponentType
|
||||
{
|
||||
get { return typeof(T); }
|
||||
}
|
||||
public EcsWorld World
|
||||
{
|
||||
get { return _world; }
|
||||
}
|
||||
public bool IsReadOnly
|
||||
{
|
||||
get { return false; }
|
||||
}
|
||||
public ref T this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return ref Get(index); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors/Init/Destroy
|
||||
public EcsValuePool()
|
||||
{
|
||||
if (_sharedStore == null)
|
||||
{
|
||||
_sharedStore = Alloc<EcsValuePoolSharedStore>(1).Ptr;
|
||||
}
|
||||
}
|
||||
public EcsValuePool(int capacity, int recycledCapacity = -1)
|
||||
{
|
||||
if (_sharedStore == null)
|
||||
{
|
||||
_sharedStore = Alloc<EcsValuePoolSharedStore>(1).Ptr;
|
||||
}
|
||||
capacity = ArrayUtility.NextPow2(capacity);
|
||||
if (recycledCapacity < 0)
|
||||
{
|
||||
recycledCapacity = capacity / 2;
|
||||
}
|
||||
_itemsLength = capacity;
|
||||
_itemsHandler = Alloc<T>(_itemsLength);
|
||||
_items = _itemsHandler.Ptr;
|
||||
_sharedStore->_items = _items;
|
||||
_recycledItemsLength = recycledCapacity;
|
||||
_recycledItemsHandler = Alloc<int>(_recycledItemsLength);
|
||||
_recycledItems = _recycledItemsHandler.Ptr;
|
||||
}
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID)
|
||||
{
|
||||
_world = world;
|
||||
_worldID = world.ID;
|
||||
_mediator = mediator;
|
||||
_componentTypeID = componentTypeID;
|
||||
_maskBit = EcsMaskChunck.FromID(componentTypeID);
|
||||
|
||||
_sharedStore->_worldID = _worldID;
|
||||
_sharedStore->_componentTypeID = _componentTypeID;
|
||||
|
||||
_mappingHandler = AllocAndInit<int>(world.Capacity);
|
||||
_mapping = _mappingHandler.Ptr;
|
||||
|
||||
_sharedStore->_mapping = _mapping;
|
||||
var worldConfig = world.Configs.GetWorldConfigOrDefault();
|
||||
if (_items == null)
|
||||
{
|
||||
_itemsLength = ArrayUtility.NextPow2(worldConfig.PoolComponentsCapacity);
|
||||
_itemsHandler = Alloc<T>(_itemsLength);
|
||||
_items = _itemsHandler.Ptr;
|
||||
_sharedStore->_items = _items;
|
||||
}
|
||||
if (_recycledItems == null)
|
||||
{
|
||||
_recycledItemsLength = worldConfig.PoolRecycledComponentsCapacity;
|
||||
_recycledItemsHandler = Alloc<int>(_recycledItemsLength);
|
||||
_recycledItems = _recycledItemsHandler.Ptr;
|
||||
}
|
||||
}
|
||||
void IEcsPoolImplementation.OnWorldDestroy() { }
|
||||
~EcsValuePool()
|
||||
{
|
||||
Free(_mappingHandler);
|
||||
Free(_itemsHandler);
|
||||
Free(_recycledItemsHandler);
|
||||
|
||||
Free(_sharedStore);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public NativeEcsValuePool<T> AsNative()
|
||||
{
|
||||
return new NativeEcsValuePool<T>(_sharedStore);
|
||||
}
|
||||
public ref T Add(int entityID)
|
||||
{
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
#if DEBUG
|
||||
if (entityID == EcsConsts.NULL_ENTITY_ID) { Throw.Ent_ThrowIsNotAlive(_world, entityID); }
|
||||
if (_world.IsUsed(entityID) == false) { Throw.Ent_ThrowIsNotAlive(_world, entityID); }
|
||||
if (itemIndex > 0) { EcsPoolThrowHelper.ThrowAlreadyHasComponent<T>(entityID); }
|
||||
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (itemIndex > 0) { return ref Get(entityID); }
|
||||
if (_isLocked | _world.IsUsed(entityID) == false) { return ref _items[0]; }
|
||||
#endif
|
||||
if (_recycledItemsCount > 0)
|
||||
{
|
||||
itemIndex = _recycledItems[--_recycledItemsCount];
|
||||
_itemsCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemIndex = ++_itemsCount;
|
||||
if (itemIndex >= _itemsLength)
|
||||
{
|
||||
_itemsLength = ArrayUtility.NextPow2(_itemsLength << 1);
|
||||
_itemsHandler = Realloc(_itemsHandler, _itemsLength);
|
||||
_items = _itemsHandler.Ptr;
|
||||
_sharedStore->_items = _items;
|
||||
}
|
||||
}
|
||||
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
ref T result = ref _items[itemIndex];
|
||||
EcsComponentLifecycle<T>.OnAdd(_isCustomLifecycle, _customLifecycle, ref result, _worldID, entityID);
|
||||
return ref result;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Get(int entityID)
|
||||
{
|
||||
#if DEBUG // не нужен STAB_MODE
|
||||
if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
|
||||
#endif
|
||||
return ref _items[_mapping[entityID]];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref readonly T Read(int entityID)
|
||||
{
|
||||
#if DEBUG // не нужен STAB_MODE
|
||||
if (!Has(entityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
|
||||
#endif
|
||||
return ref _items[_mapping[entityID]];
|
||||
}
|
||||
public ref T TryAddOrGet(int entityID)
|
||||
{
|
||||
#if DEBUG
|
||||
if (entityID == EcsConsts.NULL_ENTITY_ID) { Throw.Ent_ThrowIsNotAlive(_world, entityID); }
|
||||
#endif
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
if (itemIndex <= 0)
|
||||
{ //Add block
|
||||
#if DEBUG
|
||||
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (_isLocked) { return ref _items[0]; }
|
||||
#endif
|
||||
if (_recycledItemsCount > 0)
|
||||
{
|
||||
itemIndex = _recycledItems[--_recycledItemsCount];
|
||||
_itemsCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemIndex = ++_itemsCount;
|
||||
if (itemIndex >= _itemsLength)
|
||||
{
|
||||
_itemsLength = ArrayUtility.NextPow2(_itemsLength << 1);
|
||||
_itemsHandler = Realloc(_itemsHandler, _itemsLength);
|
||||
_items = _itemsHandler.Ptr;
|
||||
_sharedStore->_items = _items;
|
||||
}
|
||||
}
|
||||
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
EcsComponentLifecycle<T>.OnAdd(_isCustomLifecycle, _customLifecycle, ref _items[itemIndex], _worldID, entityID);
|
||||
} //Add block end
|
||||
return ref _items[itemIndex];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Has(int entityID)
|
||||
{
|
||||
return _mapping[entityID] != 0;
|
||||
}
|
||||
public void Del(int entityID)
|
||||
{
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
#if DEBUG
|
||||
if (entityID == EcsConsts.NULL_ENTITY_ID) { Throw.Ent_ThrowIsNotAlive(_world, entityID); }
|
||||
if (itemIndex <= 0) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(entityID); }
|
||||
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (itemIndex <= 0) { return; }
|
||||
if (_isLocked) { return; }
|
||||
#endif
|
||||
EcsComponentLifecycle<T>.OnDel( _isCustomLifecycle, _customLifecycle, ref ((T*)_items)[itemIndex], _worldID, entityID);
|
||||
if (_recycledItemsCount >= _recycledItemsLength)
|
||||
{
|
||||
_recycledItemsLength = ArrayUtility.NextPow2(_recycledItemsLength << 1);
|
||||
_recycledItemsHandler = Realloc(_recycledItemsHandler, _recycledItemsLength);
|
||||
_recycledItems = _recycledItemsHandler.Ptr;
|
||||
}
|
||||
_recycledItems[_recycledItemsCount++] = itemIndex;
|
||||
itemIndex = 0;
|
||||
_itemsCount--;
|
||||
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
}
|
||||
public void TryDel(int entityID)
|
||||
{
|
||||
if (Has(entityID))
|
||||
{
|
||||
Del(entityID);
|
||||
}
|
||||
}
|
||||
public void Copy(int fromEntityID, int toEntityID)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!Has(fromEntityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(fromEntityID); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (!Has(fromEntityID)) { return; }
|
||||
#endif
|
||||
EcsComponentCopy<T>.Copy(_isCustomCopy, _customCopy, ref Get(fromEntityID), ref TryAddOrGet(toEntityID));
|
||||
}
|
||||
public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
|
||||
{
|
||||
#if DEBUG
|
||||
if (!Has(fromEntityID)) { EcsPoolThrowHelper.ThrowNotHaveComponent<T>(fromEntityID); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (!Has(fromEntityID)) { return; }
|
||||
#endif
|
||||
EcsComponentCopy<T>.Copy(_isCustomCopy, _customCopy, ref Get(fromEntityID), ref toWorld.GetPool<T>().TryAddOrGet(toEntityID));
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
{
|
||||
#if DEBUG
|
||||
if (_isLocked) { EcsPoolThrowHelper.ThrowPoolLocked(); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (_isLocked) { return; }
|
||||
#endif
|
||||
_recycledItemsCount = 0; // спереди чтобы обнулялось, так как Del не обнуляет
|
||||
if (_itemsCount <= 0) { return; }
|
||||
var span = _world.Where(out SinglePoolAspect<EcsValuePool<T>> _);
|
||||
foreach (var entityID in span)
|
||||
{
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
EcsComponentLifecycle<T>.OnDel(_isCustomLifecycle, _customLifecycle, ref ((T*)_items)[itemIndex], _worldID, entityID);
|
||||
itemIndex = 0;
|
||||
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
}
|
||||
_itemsCount = 0;
|
||||
_recycledItemsCount = 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Callbacks
|
||||
void IEcsPoolImplementation.OnWorldResize(int newSize)
|
||||
{
|
||||
_mappingHandler = ReallocAndInit<int>(_mappingHandler, newSize);
|
||||
_mapping = _mappingHandler.Ptr;
|
||||
|
||||
_sharedStore->_mapping = _mapping;
|
||||
}
|
||||
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
|
||||
{
|
||||
if (_itemsCount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
foreach (var entityID in buffer)
|
||||
{
|
||||
TryDel(entityID);
|
||||
}
|
||||
}
|
||||
void IEcsPoolImplementation.OnLockedChanged_Debug(bool locked) { _isLocked = locked; }
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
void IEcsPool.AddEmpty(int entityID) { Add(entityID); }
|
||||
void IEcsPool.AddRaw(int entityID, object dataRaw)
|
||||
{
|
||||
Add(entityID) = dataRaw == null ? default : (T)dataRaw;
|
||||
}
|
||||
object IEcsReadonlyPool.GetRaw(int entityID) { return Read(entityID); }
|
||||
void IEcsPool.SetRaw(int entityID, object dataRaw)
|
||||
{
|
||||
Get(entityID) = dataRaw == null ? default : (T)dataRaw;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Listeners
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
public void AddListener(IEcsPoolEventListener listener)
|
||||
{
|
||||
EcsDebug.PrintWarning($"the {nameof(AddListener)} method is not supported for the {nameof(EcsValuePool<T>)}.");
|
||||
}
|
||||
public void RemoveListener(IEcsPoolEventListener listener)
|
||||
{
|
||||
EcsDebug.PrintWarning($"the {nameof(RemoveListener)} method is not supported for the {nameof(EcsValuePool<T>)}.");
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region IEnumerator - IntelliSense hack
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() { throw new NotImplementedException(); }
|
||||
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
|
||||
#endregion
|
||||
|
||||
#region Convertors
|
||||
public static implicit operator EcsValuePool<T>(IncludeMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
|
||||
public static implicit operator EcsValuePool<T>(ExcludeMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
|
||||
public static implicit operator EcsValuePool<T>(AnyMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
|
||||
public static implicit operator EcsValuePool<T>(OptionalMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
|
||||
public static implicit operator EcsValuePool<T>(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
|
||||
#endregion
|
||||
|
||||
#region Apply
|
||||
public static void Apply(ref T component, int entityID, short worldID)
|
||||
{
|
||||
EcsWorld.GetPoolInstance<EcsValuePool<T>>(worldID).TryAddOrGet(entityID) = component;
|
||||
}
|
||||
public static void Apply(ref T component, int entityID, EcsValuePool<T> pool)
|
||||
{
|
||||
pool.TryAddOrGet(entityID) = component;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T Apply(short worldID, int entityID)
|
||||
{
|
||||
return ref EcsWorld.GetPoolInstance<EcsValuePool<T>>(worldID).TryAddOrGet(entityID);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#if ENABLE_IL2CPP
|
||||
[Il2CppSetOption(Option.NullChecks, false)]
|
||||
#endif
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaColor(MetaColor.DragonRose)]
|
||||
[MetaGroup(EcsConsts.PACK_GROUP, EcsConsts.OTHER_GROUP)]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public readonly struct ReadonlyEcsValuePool<T> : IEcsReadonlyPool //IEnumerable<T> - IntelliSense hack
|
||||
where T : unmanaged, IEcsValueComponent
|
||||
{
|
||||
private readonly EcsValuePool<T> _pool;
|
||||
|
||||
#region Properties
|
||||
public int ComponentTypeID
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _pool.ComponentTypeID; }
|
||||
}
|
||||
public Type ComponentType
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _pool.ComponentType; }
|
||||
}
|
||||
public EcsWorld World
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _pool.World; }
|
||||
}
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _pool.Count; }
|
||||
}
|
||||
public bool IsReadOnly
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _pool.IsReadOnly; }
|
||||
}
|
||||
public ref readonly T this[int entityID]
|
||||
{
|
||||
get { return ref _pool.Read(entityID); }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
internal ReadonlyEcsValuePool(EcsValuePool<T> pool)
|
||||
{
|
||||
_pool = pool;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Has(int entityID) { return _pool.Has(entityID); }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref readonly T Get(int entityID) { return ref _pool.Read(entityID); }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref readonly T Read(int entityID) { return ref _pool.Read(entityID); }
|
||||
object IEcsReadonlyPool.GetRaw(int entityID) { return _pool.Read(entityID); }
|
||||
|
||||
#if !DRAGONECS_DISABLE_POOLS_EVENTS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddListener(IEcsPoolEventListener listener) { _pool.AddListener(listener); }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RemoveListener(IEcsPoolEventListener listener) { _pool.AddListener(listener); }
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Convertors
|
||||
public static implicit operator ReadonlyEcsValuePool<T>(EcsValuePool<T> a) { return new ReadonlyEcsValuePool<T>(a); }
|
||||
public static implicit operator ReadonlyEcsValuePool<T>(IncludeMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
|
||||
public static implicit operator ReadonlyEcsValuePool<T>(ExcludeMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
|
||||
public static implicit operator ReadonlyEcsValuePool<T>(AnyMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
|
||||
public static implicit operator ReadonlyEcsValuePool<T>(OptionalMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
|
||||
public static implicit operator ReadonlyEcsValuePool<T>(EcsWorld.GetPoolInstanceMarker a) { return a.GetInstance<EcsValuePool<T>>(); }
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal unsafe struct EcsValuePoolSharedStore
|
||||
{
|
||||
public short _worldID;
|
||||
public int _componentTypeID;
|
||||
public int* _mapping;
|
||||
public void* _items;
|
||||
}
|
||||
public readonly unsafe struct NativeEcsValuePool<T>
|
||||
where T : unmanaged, IEcsValueComponent
|
||||
{
|
||||
private readonly EcsValuePoolSharedStore* _store;
|
||||
public short WorldID
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _store->_worldID; }
|
||||
}
|
||||
public int ComponentTypeID
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return _store->_componentTypeID; }
|
||||
}
|
||||
public ref T this[int entityID]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get { return ref Get(entityID); }
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal NativeEcsValuePool(EcsValuePoolSharedStore* store)
|
||||
{
|
||||
_store = store;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Has(int entityID) { return _store->_mapping[entityID] != 0; }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Get(int entityID) { return ref ((T*)_store->_items)[_store->_mapping[entityID]]; }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref readonly T Read(int entityID) { return ref Get(entityID); }
|
||||
}
|
||||
|
||||
public static class EcsValuePoolExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsValuePool<TComponent> GetPool<TComponent>(this EcsWorld self) where TComponent : unmanaged, IEcsValueComponent
|
||||
{
|
||||
return self.GetPoolInstance<EcsValuePool<TComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsValuePool<TComponent> GetPoolUnchecked<TComponent>(this EcsWorld self) where TComponent : unmanaged, IEcsValueComponent
|
||||
{
|
||||
return self.GetPoolInstanceUnchecked<EcsValuePool<TComponent>>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsValuePool<TComponent> Inc<TComponent>(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent
|
||||
{
|
||||
return self.IncludePool<EcsValuePool<TComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsValuePool<TComponent> Exc<TComponent>(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent
|
||||
{
|
||||
return self.ExcludePool<EcsValuePool<TComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsValuePool<TComponent> Opt<TComponent>(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent
|
||||
{
|
||||
return self.OptionalPool<EcsValuePool<TComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsValuePool<TComponent> Any<TComponent>(this EcsAspect.Builder self) where TComponent : unmanaged, IEcsValueComponent
|
||||
{
|
||||
return self.AnyPool<EcsValuePool<TComponent>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/Pools/EcsValuePool.cs.meta
Normal file
11
src/Pools/EcsValuePool.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ffb282acdd5b51e4da44dc5cef296d50
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -37,7 +37,7 @@ namespace DCFApixels.DragonECS
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new InvalidOperationException($"Using component {componentType.ToMeta().TypeName} is not allowed in the {worldType.ToMeta().TypeName} world.");
|
||||
throw new InvalidOperationException($"Using component {componentType.GetMeta().TypeName} is not allowed in the {worldType.GetMeta().TypeName} world.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -139,12 +139,12 @@ namespace DCFApixels.DragonECS.Core.Internal
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void Quiery_ArgumentDifferentWorldsException()
|
||||
{
|
||||
throw new ArgumentException("The groups belong to different worlds.");
|
||||
ArgumentDifferentWorldsException();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void ArgumentDifferentWorldsException()
|
||||
{
|
||||
throw new ArgumentException("The groups belong to different worlds.");
|
||||
throw new ArgumentException("World ID mismatch: the expected and actual world identifiers do not match.");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
|
||||
@ -252,74 +252,5 @@ namespace DCFApixels.DragonECS.Core
|
||||
return _graph.Sort();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Obsolete
|
||||
[Obsolete("Use " + nameof(LayersMap) + ".Add(layer).Before(targetLayer).Back;")]
|
||||
public EcsPipeline.Builder Insert(string targetLayer, string newLayer)
|
||||
{
|
||||
Add(newLayer).Before(targetLayer);
|
||||
return _pipelineBuilder;
|
||||
}
|
||||
[Obsolete("Use " + nameof(LayersMap) + ".Add(layer).After(targetLayer).Back;")]
|
||||
public EcsPipeline.Builder InsertAfter(string targetLayer, string newLayer)
|
||||
{
|
||||
Add(newLayer).After(targetLayer);
|
||||
return _pipelineBuilder;
|
||||
}
|
||||
[Obsolete("Use " + nameof(LayersMap) + ".Move(layer).Before(targetLayer).Back;")]
|
||||
public EcsPipeline.Builder Move(string targetLayer, string newLayer)
|
||||
{
|
||||
Move(newLayer).Before(targetLayer);
|
||||
return _pipelineBuilder;
|
||||
}
|
||||
[Obsolete("Use " + nameof(LayersMap) + ".Move(layer).After(targetLayer).Back;")]
|
||||
public EcsPipeline.Builder MoveAfter(string targetLayer, string newLayer)
|
||||
{
|
||||
Move(newLayer).After(targetLayer);
|
||||
return _pipelineBuilder;
|
||||
}
|
||||
[Obsolete("Use " + nameof(LayersMap) + ".Add(layers).Before(targetLayer).Back;")]
|
||||
public EcsPipeline.Builder Insert(string targetLayer, params string[] newLayers)
|
||||
{
|
||||
Add(newLayers).Before(targetLayer);
|
||||
return _pipelineBuilder;
|
||||
}
|
||||
[Obsolete("Use " + nameof(LayersMap) + ".Add(layers).After(targetLayer).Back;")]
|
||||
public EcsPipeline.Builder InsertAfter(string targetLayer, params string[] newLayers)
|
||||
{
|
||||
Add(newLayers).After(targetLayer);
|
||||
return _pipelineBuilder;
|
||||
}
|
||||
[Obsolete("Use " + nameof(LayersMap) + ".Move(layers).Before(targetLayer).Back;")]
|
||||
public EcsPipeline.Builder Move(string targetLayer, params string[] movingLayers)
|
||||
{
|
||||
Move(movingLayers).Before(targetLayer);
|
||||
return _pipelineBuilder;
|
||||
}
|
||||
[Obsolete("Use " + nameof(LayersMap) + ".Move(layers).After(targetLayer).Back;")]
|
||||
public EcsPipeline.Builder MoveAfter(string targetLayer, params string[] movingLayers)
|
||||
{
|
||||
Move(movingLayers).After(targetLayer);
|
||||
return _pipelineBuilder;
|
||||
}
|
||||
|
||||
[Obsolete]
|
||||
public object this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
int i = 0;
|
||||
foreach (var item in this)
|
||||
{
|
||||
if (i == index)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,202 +0,0 @@
|
||||
#if ENABLE_DUMMY_SPAN
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute;
|
||||
using EditorBrowsableState = System.ComponentModel.EditorBrowsableState;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
internal static class ThrowHelper
|
||||
{
|
||||
public static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException();
|
||||
public static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException();
|
||||
public static void ThrowInvalidOperationException() => throw new InvalidOperationException();
|
||||
}
|
||||
[DebuggerDisplay("{ToString(),raw}")]
|
||||
public readonly ref struct ReadOnlySpan<T>
|
||||
{
|
||||
public static ReadOnlySpan<T> Empty => new ReadOnlySpan<T>(null);
|
||||
|
||||
internal readonly T[] _array;
|
||||
private readonly int _start;
|
||||
private readonly int _length;
|
||||
|
||||
#region Properties
|
||||
public int Length => _length;
|
||||
public bool IsEmpty => _length == 0;
|
||||
public ref readonly T this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
if ((uint)index >= (uint)_length || (uint)index < 0)
|
||||
ThrowHelper.ThrowIndexOutOfRangeException();
|
||||
return ref _array[index + _start];
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan(T[] array)
|
||||
{
|
||||
_array = array ?? Array.Empty<T>();
|
||||
_start = 0;
|
||||
_length = array.Length;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan(T[] array, int start, int length)
|
||||
{
|
||||
if (array == null)
|
||||
{
|
||||
if (start != 0 || length != 0)
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||
_array = Array.Empty<T>();
|
||||
_start = 0;
|
||||
_length = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start))
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||
|
||||
_array = array;
|
||||
_start = start;
|
||||
_length = length;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
#pragma warning disable CS0809 // Óñòàðåâøèé ÷ëåí ïåðåîïðåäåëÿåò íåóñòàðåâøèé ÷ëåí
|
||||
[Obsolete("Equals() on ReadOnlySpan will always throw an exception. Use the equality operator instead.")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public override bool Equals(object obj) => throw new NotSupportedException();
|
||||
[Obsolete("GetHashCode() on ReadOnlySpan will always throw an exception.")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public override int GetHashCode() => throw new NotSupportedException();
|
||||
#pragma warning restore CS0809 // Óñòàðåâøèé ÷ëåí ïåðåîïðåäåëÿåò íåóñòàðåâøèé ÷ëåí
|
||||
public override string ToString()
|
||||
{
|
||||
//if (typeof(T) == typeof(char))
|
||||
// return new string(new ReadOnlySpan<char>(ref Unsafe.As<T, char>(ref _reference), _length));
|
||||
return $"System.ReadOnlySpan<{typeof(T).Name}>[{_length}]";
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region operators
|
||||
public static bool operator !=(ReadOnlySpan<T> left, ReadOnlySpan<T> right) => !(left == right);
|
||||
|
||||
public static implicit operator ReadOnlySpan<T>(T[] array) => new ReadOnlySpan<T>(array);
|
||||
|
||||
public static implicit operator ReadOnlySpan<T>(ArraySegment<T> segment) => new ReadOnlySpan<T>(segment.Array, segment.Offset, segment.Count);
|
||||
public static bool operator ==(ReadOnlySpan<T> left, ReadOnlySpan<T> right) => left._length == right._length && left._array == right._array;
|
||||
#endregion
|
||||
|
||||
#region Enumerator
|
||||
public Enumerator GetEnumerator() => new Enumerator(this);
|
||||
public ref struct Enumerator
|
||||
{
|
||||
private readonly ReadOnlySpan<T> _span;
|
||||
private int _index;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal Enumerator(ReadOnlySpan<T> span)
|
||||
{
|
||||
_span = span;
|
||||
_index = span._start - 1;
|
||||
}
|
||||
public ref readonly T Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => ref _span[_index];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
int index = _index + 1;
|
||||
if (index < _span.Length)
|
||||
{
|
||||
_index = index;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public ref readonly T GetPinnableReference()
|
||||
{
|
||||
if (_length != 0) ThrowHelper.ThrowInvalidOperationException();
|
||||
return ref _array[0];
|
||||
}
|
||||
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
//public void CopyTo(Span<T> destination)
|
||||
//{
|
||||
// if ((uint)_length <= (uint)destination.Length)
|
||||
// {
|
||||
// Buffer.Memmove(ref destination._reference, ref _reference, (uint)_length);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// ThrowHelper.ThrowArgumentException_DestinationTooShort();
|
||||
// }
|
||||
//}
|
||||
|
||||
//public bool TryCopyTo(Span<T> destination)
|
||||
//{
|
||||
// bool retVal = false;
|
||||
// if ((uint)_length <= (uint)destination.Length)
|
||||
// {
|
||||
// Buffer.Memmove(ref destination._reference, ref _reference, (uint)_length);
|
||||
// retVal = true;
|
||||
// }
|
||||
// return retVal;
|
||||
//}
|
||||
public void CopyTo(T[] array)
|
||||
{
|
||||
if (_length > array.Length)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
for (int i = 0; i < _length; i++)
|
||||
{
|
||||
array[i] = _array[i];
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan<T> Slice(int start)
|
||||
{
|
||||
if ((uint)start > (uint)_length)
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||
|
||||
return new ReadOnlySpan<T>(_array, _start + start, _length - start);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan<T> Slice(int start, int length)
|
||||
{
|
||||
if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start))
|
||||
ThrowHelper.ThrowArgumentOutOfRangeException();
|
||||
|
||||
return new ReadOnlySpan<T>(_array, _start + start, length);
|
||||
}
|
||||
|
||||
public T[] ToArray()
|
||||
{
|
||||
if (_length == 0)
|
||||
return Array.Empty<T>();
|
||||
var result = new T[_length];
|
||||
Array.Copy(_array, _start, result, 0, _length);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@ -2,7 +2,7 @@
|
||||
#undef DEBUG
|
||||
#endif
|
||||
|
||||
namespace DCFApixels.DragonECS.UncheckedCore
|
||||
namespace DCFApixels.DragonECS.Core.Unchecked
|
||||
{
|
||||
public readonly struct EntitiesMatrix
|
||||
{
|
||||
|
||||
@ -5,73 +5,6 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS.UncheckedCore
|
||||
{
|
||||
[Obsolete("Use DCFApixels.DragonECS.Core.UncheckedUtility")]
|
||||
public static class UncheckedCoreUtility
|
||||
{
|
||||
#region CreateEntLong
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static entlong CreateEntLong(int entityID, short gen, short worldID)
|
||||
{
|
||||
return new entlong(entityID, gen, worldID);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static entlong CreateEntLong(long entityGenWorld)
|
||||
{
|
||||
return new entlong(entityGenWorld);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CreateSpan
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsSpan CreateSpan(short worldID, ReadOnlySpan<int> entitesArray)
|
||||
{
|
||||
return new EcsSpan(worldID, entitesArray);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsSpan CreateSpan(short worldID, int[] entitesArray, int startIndex, int length)
|
||||
{
|
||||
return new EcsSpan(worldID, entitesArray, startIndex, length);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsSpan CreateSpan(short worldID, int[] entitesArray, int length)
|
||||
{
|
||||
return new EcsSpan(worldID, entitesArray, length);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsSpan CreateSpan(short worldID, int[] entitesArray)
|
||||
{
|
||||
return new EcsSpan(worldID, entitesArray);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsSpan CreateEmptySpan(short worldID)
|
||||
{
|
||||
return new EcsSpan(worldID, Array.Empty<int>());
|
||||
}
|
||||
public static bool CheckSpanValideDebug(EcsSpan span)
|
||||
{
|
||||
HashSet<int> set = new HashSet<int>();
|
||||
foreach (var e in span)
|
||||
{
|
||||
if (set.Add(e) == false)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region EcsGroup
|
||||
public static EcsGroup GetSourceInstance(EcsReadonlyGroup group)
|
||||
{
|
||||
return group.GetSource_Internal();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
namespace DCFApixels.DragonECS.Core.Unchecked
|
||||
{
|
||||
public static class UncheckedUtility
|
||||
@ -129,6 +62,14 @@ namespace DCFApixels.DragonECS.Core.Unchecked
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Unsafe Span
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe EcsUnsafeSpan CreateUnsafeSpan(short worldID, int* ptr, int length)
|
||||
{
|
||||
return new EcsUnsafeSpan(worldID, ptr, length);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region EcsGroup
|
||||
public static EcsGroup GetSourceInstance(EcsReadonlyGroup group)
|
||||
{
|
||||
|
||||
136
src/entlong.cs
136
src/entlong.cs
@ -116,6 +116,48 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public entlong(EcsWorld world, int id) : this()
|
||||
{
|
||||
if (world == null)
|
||||
{
|
||||
_id = 0;
|
||||
_gen = 0;
|
||||
_world = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_id = id;
|
||||
_gen = world.GetGen(id);
|
||||
_world = world.ID;
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public entlong(int id, EcsWorld world) : this(world, id) { }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public entlong(EcsWorld world, entlong entity) : this()
|
||||
{
|
||||
#if DEBUG
|
||||
if (world.ID != entity.WorldID) { Throw.ArgumentDifferentWorldsException(); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (world.ID != entity.WorldID) { world = null; }
|
||||
#endif
|
||||
if (world == null)
|
||||
{
|
||||
_id = 0;
|
||||
_gen = 0;
|
||||
_world = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_id = entity._id;
|
||||
_gen = entity._gen;
|
||||
_world = entity._world;
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public entlong(entlong entity, EcsWorld world) : this(world, entity) { }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public entlong(int id, short gen, short world) : this()
|
||||
{
|
||||
@ -128,12 +170,6 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
_full = full;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static unsafe entlong NewUnsafe(long id, long gen, long world)
|
||||
{
|
||||
long x = id << 48 | gen << 32 | id;
|
||||
return *(entlong*)&x;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Unpacking Try
|
||||
@ -155,6 +191,13 @@ namespace DCFApixels.DragonECS
|
||||
worldID = _world;
|
||||
return IsAlive;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryGetGen(out short gen)
|
||||
{
|
||||
gen = _gen;
|
||||
return IsAlive;
|
||||
}
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryUnpack(out int id)
|
||||
@ -192,17 +235,24 @@ namespace DCFApixels.DragonECS
|
||||
id = _id;
|
||||
return IsAlive;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryUnpack(EcsWorld world)
|
||||
{
|
||||
if (world.ID != _world) { return false; }
|
||||
return world.IsAlive(_id, _gen);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryUnpack(EcsWorld world, out int id)
|
||||
{
|
||||
if (world.ID != _world) { Throw.ArgumentDifferentWorldsException(); }
|
||||
if (world.ID != _world) { id = EcsConsts.NULL_ENTITY_ID; return false; }
|
||||
id = _id;
|
||||
return world.IsAlive(_id, _gen);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryUnpack(EcsWorld world, out int id, out short gen)
|
||||
{
|
||||
if (world.ID != _world) { Throw.ArgumentDifferentWorldsException(); }
|
||||
if (world.ID != _world) { gen = 0; id = EcsConsts.NULL_ENTITY_ID; return false; }
|
||||
gen = _gen;
|
||||
id = _id;
|
||||
return world.IsAlive(_id, _gen);
|
||||
@ -210,14 +260,14 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryUnpack(EcsMask mask, out int id)
|
||||
{
|
||||
if (mask.WorldID != _world) { Throw.ArgumentDifferentWorldsException(); }
|
||||
if (mask.WorldID != _world) { id = EcsConsts.NULL_ENTITY_ID; return false; }
|
||||
id = _id;
|
||||
return mask.World.IsAlive(_id, _gen) && mask.World.IsMatchesMask(mask, _id);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryUnpack(EcsMask mask, out int id, out short gen)
|
||||
{
|
||||
if (mask.WorldID != _world) { Throw.ArgumentDifferentWorldsException(); }
|
||||
if (mask.WorldID != _world) { gen = 0; id = EcsConsts.NULL_ENTITY_ID; return false; }
|
||||
gen = _gen;
|
||||
id = _id;
|
||||
return mask.World.IsAlive(_id, _gen) && mask.World.IsMatchesMask(mask, _id);
|
||||
@ -225,14 +275,14 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryUnpack(EcsAspect aspect, out int id)
|
||||
{
|
||||
if (aspect.World.ID != _world) { Throw.ArgumentDifferentWorldsException(); }
|
||||
if (aspect.World.ID != _world) { id = EcsConsts.NULL_ENTITY_ID; return false; }
|
||||
id = _id;
|
||||
return aspect.World.IsAlive(_id, _gen) && aspect.IsMatches(_id);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool TryUnpack(EcsAspect aspect, out int id, out short gen)
|
||||
{
|
||||
if (aspect.World.ID != _world) { Throw.ArgumentDifferentWorldsException(); }
|
||||
if (aspect.World.ID != _world) { gen = 0; id = EcsConsts.NULL_ENTITY_ID; return false; }
|
||||
gen = _gen;
|
||||
id = _id;
|
||||
return aspect.World.IsAlive(_id, _gen) && aspect.IsMatches(_id);
|
||||
@ -323,9 +373,39 @@ namespace DCFApixels.DragonECS
|
||||
id = _id;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Deconstruct(out int id, out short gen, out short worldID) { Unpack(out id, out gen, out worldID); }
|
||||
public void Deconstruct(out int id, out short gen, out short worldID)
|
||||
{
|
||||
#if DEBUG
|
||||
if (IsAlive == false) { Throw.Ent_ThrowIsNotAlive(this); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (IsAlive == false)
|
||||
{
|
||||
worldID = EcsConsts.NULL_WORLD_ID;
|
||||
gen = default;
|
||||
id = EcsConsts.NULL_ENTITY_ID;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
worldID = _world;
|
||||
gen = _gen;
|
||||
id = _id;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Deconstruct(out int id, out EcsWorld world) { Unpack(out id, out world); }
|
||||
public void Deconstruct(out int id, out EcsWorld world)
|
||||
{
|
||||
#if DEBUG
|
||||
if (IsAlive == false) { Throw.Ent_ThrowIsNotAlive(this); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (IsAlive == false)
|
||||
{
|
||||
world = EcsWorld.GetWorld(EcsConsts.NULL_WORLD_ID);
|
||||
id = EcsConsts.NULL_ENTITY_ID;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
world = EcsWorld.GetWorld(_world);
|
||||
id = _id;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Unpacking Unchecked
|
||||
@ -384,16 +464,10 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator entlong((EcsWorld world, int entityID) a) { return Combine_Internal(a.entityID, a.world); }
|
||||
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
//public static implicit operator entlong((entlong entity, EcsWorld world) a) { return Combine_Internal(a.entity._id, a.world); }
|
||||
//[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
//public static implicit operator entlong((EcsWorld world, entlong entity) a) { return Combine_Internal(a.entity._id, a.world); }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static entlong Combine_Internal(int entityID, EcsWorld world)
|
||||
{
|
||||
return world == null ? new entlong(entityID, 0, 0) : world.GetEntityLong(entityID);
|
||||
}
|
||||
public static implicit operator entlong((entlong entity, EcsWorld world) a) { return Combine_Internal(a.entity, a.world); }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator entlong((EcsWorld world, entlong entity) a) { return Combine_Internal(a.entity, a.world); }
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator long(entlong a) { return a._full; }
|
||||
@ -405,6 +479,21 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
#region Other
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static entlong Combine_Internal(int entityID, EcsWorld world)
|
||||
{
|
||||
return world == null ? new entlong(entityID, 0, 0) : world.GetEntityLong(entityID);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private static entlong Combine_Internal(entlong entity, EcsWorld world)
|
||||
{
|
||||
#if DEBUG
|
||||
if (world.ID != entity.WorldID) { Throw.ArgumentDifferentWorldsException(); }
|
||||
#elif DRAGONECS_STABILITY_MODE
|
||||
if (world.ID != entity.WorldID) { return default; }
|
||||
#endif
|
||||
return entity;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private EcsWorld GetWorld_Internal()
|
||||
{
|
||||
#if DRAGONECS_STABILITY_MODE
|
||||
@ -412,6 +501,7 @@ namespace DCFApixels.DragonECS
|
||||
#endif
|
||||
return EcsWorld.GetWorld(_world);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() { return unchecked((int)_full) ^ (int)(_full >> 32); }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
|
||||
Loading…
Reference in New Issue
Block a user