mirror of
https://github.com/DCFApixels/DragonECS.git
synced 2026-04-22 01:45:55 +08:00
Merge branch 'new_dev'
This commit is contained in:
commit
c3f38232c5
292
README-RU.md
292
README-RU.md
@ -13,41 +13,49 @@
|
||||
| Languages: | [Русский](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md) | [English(WIP)](https://github.com/DCFApixels/DragonECS) |
|
||||
| :--- | :--- | :--- |
|
||||
|
||||
Данный [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Фреймворк нацелен на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Без генерации кода и зависимостей.
|
||||
> [!Warning]
|
||||
Данный [ECS](https://en.wikipedia.org/wiki/Entity_component_system) Фреймворк нацелен на максимальную удобность, модульность, расширяемость и производительность динамического изменения сущностей. Без генерации кода и зависимостей. Вднохновлен [LeoEcs](https://github.com/Leopotam/ecslite).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> И с Новым Годом
|
||||
|
||||
> [!WARNING]
|
||||
> Проект в стадии разработки. API может меняться.
|
||||
> Readme еще не завершен
|
||||
|
||||
## Оглавление
|
||||
* [Установка](#Установка)
|
||||
* [Unity-модуль](#Unity-модуль)
|
||||
* [В виде иходников](#В-виде-иходников)
|
||||
* [Версионирование](#Версионирование)
|
||||
* [Основные концепции](#Основные-концепции)
|
||||
* [Entity](#Entity)
|
||||
* [Component](#Component)
|
||||
* [System](#System)
|
||||
* [Концепции фреймворка](#Концепции-фреймворка)
|
||||
* [Пайплайн](#Пайплайн)
|
||||
* [Построение](#Построение)
|
||||
* [Внедрение зависимостей](#Внедрение-зависимостей)
|
||||
* [Модули](#Модули)
|
||||
* [Слои](#Слои)
|
||||
* [Процессы](#Процессы)
|
||||
* [Мир](#Мир)
|
||||
* [Пул](#Пул)
|
||||
* [Группа](#Группа)
|
||||
* [Аспект](#Аспект)
|
||||
* [Запрос](#Запрос)
|
||||
* [Корень ECS](#Корень-ECS)
|
||||
* [Пример для Unity](#Пример-для-Unity)
|
||||
* [Общий пример](#Общий-пример)
|
||||
* [Debug](#Debug)
|
||||
* [Debug-Атрибуты](#Debug-Атрибуты)
|
||||
* [EcsDebug](#EcsDebug)
|
||||
* [Расширения](#Расширения)
|
||||
* [FAQ](#FAQ)
|
||||
* [Обратная связь](#Обратная-связь)
|
||||
- [DragonECS - C# Entity Component System Framework](#dragonecs---c-entity-component-system-framework)
|
||||
- [Оглавление](#оглавление)
|
||||
- [Установка](#установка)
|
||||
- [Версионирование](#версионирование)
|
||||
- [Основные концепции](#основные-концепции)
|
||||
- [Entity](#entity)
|
||||
- [Component](#component)
|
||||
- [System](#system)
|
||||
- [Концепции фреймворка](#концепции-фреймворка)
|
||||
- [Пайплайн](#пайплайн)
|
||||
- [Построение](#построение)
|
||||
- [Внедрение зависимостей](#внедрение-зависимостей)
|
||||
- [Модули](#модули)
|
||||
- [Слои](#слои)
|
||||
- [Процессы](#процессы)
|
||||
- [Мир](#мир)
|
||||
- [Компоненты мира](#компоненты-мира)
|
||||
- [Пул](#пул)
|
||||
- [Аспект](#аспект)
|
||||
- [Запросы](#запросы)
|
||||
- [Группа](#группа)
|
||||
- [Корень ECS](#корень-ecs)
|
||||
- [Пример для Unity](#пример-для-unity)
|
||||
- [Общий пример](#общий-пример)
|
||||
- [Гибридность](#гибридность)
|
||||
- [Debug](#debug)
|
||||
- [Атрибуты](#атрибуты)
|
||||
- [EcsDebug](#ecsdebug)
|
||||
- [Профилирование](#профилирование)
|
||||
- [Расширения](#расширения)
|
||||
- [FAQ](#faq)
|
||||
- ['ReadOnlySpan\<\>' could not be found](#readonlyspan-could-not-be-found)
|
||||
- [Обратная связь](#обратная-связь)
|
||||
|
||||
</br>
|
||||
|
||||
@ -72,7 +80,7 @@ https://github.com/DCFApixels/DragonECS.git
|
||||
* `entlong` - долговременный идентификатор, содержит в себе полный набор информации для однозначной идентификации;
|
||||
``` csharp
|
||||
// Создание новой сущности в мире.
|
||||
int entityID = _world.NewEmptyEntity();
|
||||
int entityID = _world.NewEntity();
|
||||
|
||||
// Удаление сущности.
|
||||
_world.DelEntity(entityID);
|
||||
@ -119,7 +127,7 @@ struct PlayerTag : IEcsTagComponent {}
|
||||
Встроенные виды компонентов:
|
||||
* `IEcsComponent` - Компоненты с данными.
|
||||
* `IEcsTagComponent` - Компоненты-теги. Без данных.
|
||||
> Компоненты-теги хоть и не имеют данных, само наличие или отсутствие компонента-тега у сущности уже несет информацию и может применяться для определения типа сущности.
|
||||
* `IEcsHybridComponent` - Гибридные компоненты. Испольщуются для реализации [гибридности](#Гибридность).
|
||||
|
||||
## System
|
||||
**Системы** - это основная логика, тут задается поведение сущностей. Существуют в виде пользовательских классов, реализующих как минимум один из интерфейсов процессов. Основные процессы:
|
||||
@ -127,16 +135,16 @@ struct PlayerTag : IEcsTagComponent {}
|
||||
class SomeSystem : IEcsPreInitProcess, IEcsInitProcess, IEcsRunProcess, IEcsDestroyProcess
|
||||
{
|
||||
// Будет вызван один раз в момент работы EcsPipeline.Init() и до срабатывания IEcsInitProcess.Init()
|
||||
public void PreInit (EcsPipeline pipeline) { }
|
||||
public void PreInit () { }
|
||||
|
||||
// Будет вызван один раз в момент работы EcsPipeline.Init() и после срабатывания IEcsPreInitProcess.PreInit()
|
||||
public void Init (EcsPipeline pipeline) { }
|
||||
public void Init () { }
|
||||
|
||||
// Будет вызван один раз в момент работы EcsPipeline.Run().
|
||||
public void Run (EcsPipeline pipeline) { }
|
||||
public void Run () { }
|
||||
|
||||
// Будет вызван один раз в момент работы EcsPipeline.Destroy()
|
||||
public void Destroy (EcsPipeline pipeline) { }
|
||||
public void Destroy () { }
|
||||
}
|
||||
```
|
||||
> Для реализации дополнительных процессов перейдите к разделу [Процессы](#Процессы).
|
||||
@ -163,12 +171,45 @@ pipeline.Init(); // Инициализация пайплайна
|
||||
> Для одновременного построения и инициализации есть метод Builder.BuildAndInit();
|
||||
### Внедрение зависимостей
|
||||
Внедрение зависимостей - это процесс который запускается вместе с инициализацией пайплайна и внедряет данные переданные в Builder.
|
||||
|
||||
> [!WARNING]
|
||||
> Внедрение идет параллельно с PreInit, поэтому в PreInit инъекция - не гарантируется.
|
||||
> [!WARNING]
|
||||
> Экземпляр EcsPipeline автоматически внедряется до еще до PreInit.
|
||||
``` c#
|
||||
SomeData _someData;
|
||||
//...
|
||||
EcsPipelone pipeline = EcsPipeline.New()
|
||||
//...
|
||||
.Inject(_someData) // Внедрит в системы экземпляр _someData
|
||||
//...
|
||||
.BuildAndInit();
|
||||
|
||||
//...
|
||||
|
||||
class SomeSystem : IInject<SomeData>, IEcsRunProcess
|
||||
{
|
||||
// Для внедрения используется интерфейс IInject<T> и его метод Inject(T obj)
|
||||
SomeData _someData
|
||||
public void Inject(SomeData obj) => _someData = obj;
|
||||
|
||||
public void PreInit ()
|
||||
{
|
||||
// тут возможно еще не внедрен _someData
|
||||
}
|
||||
public void Init ()
|
||||
{
|
||||
// тут можно пользовать _someData
|
||||
}
|
||||
public void Run ()
|
||||
{
|
||||
// тут можно пользовать _someData
|
||||
}
|
||||
public void Destroy ()
|
||||
{
|
||||
// тут можно пользовать _someData
|
||||
}
|
||||
}
|
||||
```
|
||||
### Модули
|
||||
Группы систем реализующие общую фичу можно объединять в модули, и просто добавлять модули в Pipeline.
|
||||
@ -198,13 +239,14 @@ const string SOME_LAYER = nameof(SOME_LAYER);
|
||||
EcsPipelone pipeline = EcsPipeline.New()
|
||||
//...
|
||||
.Layers.Insert(EcsConsts.END_LAYER, SOME_LAYER) // Вставляет новый слой перед конечным слоем EcsConsts.END_LAYER
|
||||
.Add(New SomeSystem(), SOME_LAYER) // Система SomeSystem будет вставлена в слой SOME_LAYER
|
||||
//...
|
||||
.BuildAndInit();
|
||||
```
|
||||
Встроенные слои расположены в следующем порядке:
|
||||
* `EcsConst.PRE_BEGIN_LAYER`
|
||||
* `EcsConst.BEGIN_LAYER`
|
||||
* `EcsConst.BASIC_LAYER` (Если при добавблении системы не казать слой, то она будет доавблена сюда)
|
||||
* `EcsConst.BASIC_LAYER` (Если при добавблении системы не указать слой, то она будет доавблена сюда)
|
||||
* `EcsConst.END_LAYER`
|
||||
* `EcsConst.POST_END_LAYER`
|
||||
|
||||
@ -224,8 +266,9 @@ EcsPipelone pipeline = EcsPipeline.New()
|
||||
<details>
|
||||
<summary>Пользовательские процессы</summary>
|
||||
|
||||
Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от EcsRunner<TInterface>. Пример:
|
||||
Для добавления нового процесса создайте интерфейс наследованный от `IEcsProcess` и создайте раннер для него. Раннер это класс реализующий интерфейс запускаемого процесса и наследуемый от EcsRunner<TInterface>. А после к интерфейсу добавте атрибут `BindWithEcsRunner` для связи. Пример:
|
||||
```c#
|
||||
[BindWithEcsRunner(typeof(DoSomethingProcessRunner))]
|
||||
interface IDoSomethingProcess : IEcsProcess
|
||||
{
|
||||
void Do();
|
||||
@ -317,10 +360,11 @@ public struct WorldComponent : IEcsWorldComponent<WorldComponent>
|
||||
|
||||
> Компоненты можно применять для создания расширений в связке с методами расширений.
|
||||
## Пул
|
||||
Является контейнером для компонентов, предоставляет методы для добавления/чтения/редактирования/удаления компонентов на сущности. Есть несколько видов пулов, для разных целей
|
||||
* `EcsPool` - универсальный пул, хранит struct-компоненты реализующие интерфейс IEcsComponent;
|
||||
* `EcsTagPool` - подходит для хранения пустых компонентов-тегов, в сравнении с EcsPool имеет лучше оптимизацию памяти и действий с пулом, хранит в себе struct-компоненты реализующие IEcsTagComponent;
|
||||
|
||||
Является контейнером для компонентов, предоставляет методы для добавления/чтения/редактирования/удаления компонентов на сущности. Есть несколько видов пулов, для разных целей:
|
||||
* `EcsPool` - универсальный пул, хранит struct-компоненты реализующие интерфейс `IEcsComponent`;
|
||||
* `EcsTagPool` - подходит для хранения пустых компонентов-тегов, в сравнении с `EcsPool` имеет лучше оптимизацию памяти и скорости, хранит struct-компоненты `IEcsTagComponent`;
|
||||
* `EcsHybridPool` - пул для гибридных компонентов. Испольщуются для реализации [гибридности](#Гибридность), хранит struct-компоненты `IEcsHybridComponent`;
|
||||
|
||||
Пулы имеют 5 основных метода и их разновидности:
|
||||
``` csharp
|
||||
// Один из способов получить пул из мира.
|
||||
@ -434,14 +478,7 @@ for (int i = 0; i < group.Count; i++)
|
||||
//...
|
||||
}
|
||||
```
|
||||
Так как группы это множества, они содержат операции над множествами. Каждый метод имеет 2 варианта, с записью результата в groupA, либо с возвращением новой группы:
|
||||
|
||||
<details>
|
||||
<summary> Визуализация методов</summary>
|
||||
|
||||

|
||||
|
||||
</details>
|
||||
Так как группы это множества, они содержат методы аналогичные `ISet<T>`. Редактирующие методы имеет 2 варианта, с записью результата в groupA, либо с возвращением новой группы:
|
||||
|
||||
``` c#
|
||||
// Объединение groupA и groupB
|
||||
@ -563,40 +600,163 @@ public class EcsRoot
|
||||
}
|
||||
}
|
||||
```
|
||||
## Гибридность
|
||||
Для смешивания архитектурных подходов классического OOP и ECS используется специальный пул `EcsHybridPool<T>`. Принцип работы этого пула несколько отличается от других и он упрощает поддержу наследования и полиморфизма.
|
||||
|
||||
<details>
|
||||
<summary>Как это работает?</summary>
|
||||
|
||||
При добавлении элемента в пул, пул сканирует его иерархию наследования и реализуемые интерфейсы в поиске типов у которых есть интерфес `IEcsHybridComponent` и автоматически добавляет компонент в соответсвующие этим типам пулы. Таким же образом происходит удаление. Сканирвоание просиходит не для типа T а для типа экземпляра, поэтому в примере ниже строчка в `_world.GetPool<ITransform>().Add(entity, _rigidbody);` добавляет не только в пул `EcsHybridPool<ITransform>` но и в остальные.
|
||||
|
||||
</details>
|
||||
|
||||
Пример использования:
|
||||
``` csharp
|
||||
public interface ITransform : IEcsHybridComponent
|
||||
{
|
||||
Vector3 Position { get; set; }
|
||||
// ...
|
||||
}
|
||||
public class Transform : ITransform
|
||||
{
|
||||
public Vector3 Position { get; set; }
|
||||
// ...
|
||||
}
|
||||
public class Rigidbody : Transform
|
||||
{
|
||||
public Vector3 Position { get; set; }
|
||||
public float Mass { get; set; }
|
||||
// ...
|
||||
}
|
||||
public class Camera : ITransform
|
||||
{
|
||||
Vector3 Position { get; set; }
|
||||
// ...
|
||||
}
|
||||
public TransformAspect : EcsAspect
|
||||
{
|
||||
public EcsHybridPool<Transform> transforms;
|
||||
public Aspect(Builder b)
|
||||
{
|
||||
transforms = b.Include<Transform>();
|
||||
}
|
||||
}
|
||||
// ...
|
||||
|
||||
EcsWorld _world;
|
||||
Rigidbody _rigidbody;
|
||||
// ...
|
||||
|
||||
// Создадим пустую сущность.
|
||||
int entity = _world.NewEmptyEntity();
|
||||
// Получаем пул EcsHybridPool<ITransform> и добавляем в него для сущности компонент _rigidbody.
|
||||
// Если вместо ITransform подставить Transform или Rigidbody, то результат будет одинаковый
|
||||
_world.GetPool<ITransform>().Add(entity, _rigidbody);
|
||||
// ...
|
||||
|
||||
//Все эти строчки вернут экземпляр _rigidbody.
|
||||
ITransform iTransform = _world.GetPool<ITransform>().Get(entity);
|
||||
Transform transform = _world.GetPool<Transform>().Get(entity);
|
||||
Rigidbody rigidbody = _world.GetPool<Rigidbody>().Get(entity);
|
||||
//Исключение - отсутсвует компонент. Camera не является наследником или наследуемым классом для _rigidbody.
|
||||
Camera camera = _world.GetPool<Camera>().Get(entity);
|
||||
|
||||
//Вернет True. Поэтому фишка гибридных пулов будет работать и в запросах сущностей
|
||||
bool isMatches = _world.GetAspect<TransformAspect>().IsMatches(entity);
|
||||
|
||||
//Все эти строчки вернут True.
|
||||
bool isITransform = _world.GetPool<ITransform>().Has(entity);
|
||||
bool isTransform = _world.GetPool<Transform>().Has(entity);
|
||||
bool isRigidbody = _world.GetPool<Rigidbody>().Has(entity);
|
||||
//Эта строчка вернет False.
|
||||
bool isCamera = _world.GetPool<Camera>().Has(entity);
|
||||
// ...
|
||||
|
||||
// Удалим у сущности компонент.
|
||||
_world.GetPool<ITransform>().Del(entity);
|
||||
// ...
|
||||
//Все эти строчки вернут False.
|
||||
bool isITransform = _world.GetPool<ITransform>().Has(entity);
|
||||
bool isTransform = _world.GetPool<Transform>().Has(entity);
|
||||
bool isRigidbody = _world.GetPool<Rigidbody>().Has(entity);
|
||||
bool isCamera = _world.GetPool<Camera>().Has(entity);
|
||||
// ...
|
||||
```
|
||||
|
||||
</br>
|
||||
|
||||
# Debug
|
||||
Фреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды.
|
||||
## Debug-Атрибуты
|
||||
В чистом виде дебаг-атрибуты не имеют применения, но используются в интеграциях с движками для задания отображения в отладочных инструментах и редакторах.
|
||||
Фреймворк предоставляет дополнительные инструменты для отладки и логирования, не зависящие от среды. Так же многие типы имеют свой DebuggerProxy для более информативного отображения в IDE.
|
||||
## Атрибуты
|
||||
В чистом виде мета-атрибуты не имеют применения, но могут быть использованы для генерации автоматической документации и используются в интеграциях с движками для задания отображения в отладочных инструментах и редакторах.
|
||||
``` c#
|
||||
using DCFApixels.DragonECS;
|
||||
|
||||
// Задает пользовательское название типа, по умолчанию используется имя типа.
|
||||
[DebugName("SomeComponent")]
|
||||
|
||||
[MetaName("SomeComponent")]
|
||||
|
||||
// Используется для группировки типов.
|
||||
[MetaGroup("Abilities/Passive/")]
|
||||
|
||||
// Задает цвет типа в системе rgb, где каждый канал принимает значение от 0 до 255, по умолчанию белый.
|
||||
[DebugColor(DebugColor.Red)] // или [DebugColor(255, 0, 0)]
|
||||
[MetaColor(MetaColor.Red)] // или [DebugColor(255, 0, 0)]
|
||||
|
||||
// Добавляет описание типу.
|
||||
[DebugDescription("The quick brown fox jumps over the lazy dog")]
|
||||
[MetaDescription("The quick brown fox jumps over the lazy dog")]
|
||||
|
||||
// Скрывает тип.
|
||||
[DebugHide]
|
||||
// Добавляет строковые теги.
|
||||
[MetaTags(...)] // [MetaTags(MetaTags.HIDDEN))] чтобы скрыть в редакторе
|
||||
public struct Component { }
|
||||
```
|
||||
## EcsDebug
|
||||
Имеет набор методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы являются посредниками между системами отладки среды и EcsDebug. Это позволяет не изменяя отладочный код проекта, переносить его на другие движки, достаточно только реализовать специальный Debug-сервис.
|
||||
Имеет набор методов для отладки и логирования. Реализован как статический класс вызывающий методы Debug-сервисов. Debug-сервисы - это посредники между системами отладки среды и EcsDebug. Это позволяет не изменяя отладочный код проекта, переносить проект на другие движки, достаточно только реализовать специальный Debug-сервис.
|
||||
|
||||
По умолчанию используется `DefaultDebugService` который выводит логи в консоль. Для реализации пользовательского создайте класс наследуемый от `DebugService` и реализуйте абстрактные члены класса.
|
||||
# Расширения
|
||||
* [Автоматическое внедрение зависимостей](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||
* [Поддержка классической C# многопоточности](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||
* Интеграция с движком Unity (Work in progress)
|
||||
|
||||
``` csharp
|
||||
// Логирование.
|
||||
EcsDebug.Print("Message");
|
||||
|
||||
// Логирование с тегом.
|
||||
EcsDebug.Print("Tag", "Message");
|
||||
|
||||
// Прерывание игры.
|
||||
EcsDebug.Break();
|
||||
|
||||
// Установка другого Debug-Сервиса.
|
||||
EcsDebug.Set<OtherDebugService>();
|
||||
```
|
||||
|
||||
## Профилирование
|
||||
``` csharp
|
||||
// Создание маркера с именем SomeMarker.
|
||||
private static readonly EcsProfilerMarker marker = new EcsProfilerMarker("SomeMarker");
|
||||
|
||||
...
|
||||
|
||||
marker.Begin();
|
||||
// Код для которого замеряется скорость.
|
||||
marker.End();
|
||||
|
||||
// или
|
||||
|
||||
using (marker.Auto())
|
||||
{
|
||||
// Код для которого замеряется скорость.
|
||||
}
|
||||
```
|
||||
|
||||
</br>
|
||||
|
||||
# Расширения
|
||||
* [Автоматическое внедрение зависимостей](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||
* [Поддержка классической C# многопоточности](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||
* Отношения (Work in progress)
|
||||
* Интеграция с движком Unity (Work in progress)
|
||||
<!--* Твое расширение? Если разрабатываешь свои расширения для DragonECS, дай знать и они будут добавлены сюда-->
|
||||
|
||||
</br>
|
||||
|
||||
# FAQ
|
||||
## 'ReadOnlySpan<>' could not be found
|
||||
В версии юнити 2020.1.х в консоли может выпадать ошибка:
|
||||
@ -610,4 +770,4 @@ The type or namespace name 'ReadOnlySpan<>' could not be found (are you missing
|
||||
# Обратная связь
|
||||
Discord для дискуссий [https://discord.gg/kqmJjExuCf](https://discord.gg/kqmJjExuCf)
|
||||
|
||||
</br></br>
|
||||
</br></br></br>
|
||||
|
||||
11
README.md
11
README.md
@ -13,8 +13,14 @@ inspired by [LeoEcs](https://github.com/Leopotam/ecslite)
|
||||
| Languages: | [Русский](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md) | [English(WIP)](https://github.com/DCFApixels/DragonECS) |
|
||||
| :--- | :--- | :--- |
|
||||
|
||||
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
|
||||
> **NOTICE:** The project is a work in progress, API may change.
|
||||
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](https://github.com/Leopotam/ecslite).
|
||||
|
||||
> [!IMPORTANT]
|
||||
> And a Happy New Year.
|
||||
|
||||
> [!WARNING]
|
||||
> The project is a work in progress, API may change.
|
||||
>
|
||||
> While the English version of the README is incomplete, you can view the [Russian version](https://github.com/DCFApixels/DragonECS/blob/main/README-RU.md).
|
||||
|
||||
# Versioning
|
||||
@ -22,6 +28,7 @@ DragonECS uses this versioning semantics: [Open](https://gist.github.com/DCFApix
|
||||
# Extensions
|
||||
* [Dependency autoinjections](https://github.com/DCFApixels/DragonECS-AutoInjections)
|
||||
* [Classic C# multithreading support](https://github.com/DCFApixels/DragonECS-ClassicThreads)
|
||||
* Relations (Work in progress)
|
||||
* Unity integration (Work in progress)
|
||||
|
||||
# Feedback
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
"displayName": "DragonECS",
|
||||
"description": "C# Entity Component System Framework",
|
||||
"unity": "2020.3",
|
||||
"version": "0.7.8",
|
||||
"version": "0.8.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/DCFApixels/DragonECS.git"
|
||||
|
||||
@ -121,20 +121,20 @@
|
||||
var combined = self.GetAspect<CombinedAspect<A0, A1>>();
|
||||
a0 = combined.a0;
|
||||
a1 = combined.a1;
|
||||
return self.WhereFor<CombinedAspect<A0, A1>>(sourceGroup);
|
||||
return self.WhereToGroupFor<CombinedAspect<A0, A1>>(sourceGroup);
|
||||
}
|
||||
|
||||
public static EcsReadonlyGroup Where<A0, A1>(this EcsWorld self)
|
||||
where A0 : EcsAspect
|
||||
where A1 : EcsAspect
|
||||
{
|
||||
return self.Where<CombinedAspect<A0, A1>>();
|
||||
return self.WhereToGroup<CombinedAspect<A0, A1>>();
|
||||
}
|
||||
public static EcsReadonlyGroup WhereFor<A0, A1>(this EcsWorld self, EcsReadonlyGroup sourceGroup)
|
||||
where A0 : EcsAspect
|
||||
where A1 : EcsAspect
|
||||
{
|
||||
return self.WhereFor<CombinedAspect<A0, A1>>(sourceGroup);
|
||||
return self.WhereToGroupFor<CombinedAspect<A0, A1>>(sourceGroup);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -155,7 +155,7 @@
|
||||
a0 = combined.a0;
|
||||
a1 = combined.a1;
|
||||
a2 = combined.a2;
|
||||
return self.WhereFor<CombinedAspect<A0, A1, A2>>(sourceGroup);
|
||||
return self.WhereToGroupFor<CombinedAspect<A0, A1, A2>>(sourceGroup);
|
||||
}
|
||||
|
||||
public static EcsReadonlyGroup Where<A0, A1, A2>(this EcsWorld self)
|
||||
@ -163,14 +163,14 @@
|
||||
where A1 : EcsAspect
|
||||
where A2 : EcsAspect
|
||||
{
|
||||
return self.Where<CombinedAspect<A0, A1, A2>>();
|
||||
return self.WhereToGroup<CombinedAspect<A0, A1, A2>>();
|
||||
}
|
||||
public static EcsReadonlyGroup WhereFor<A0, A1, A2>(this EcsWorld self, EcsReadonlyGroup sourceGroup)
|
||||
where A0 : EcsAspect
|
||||
where A1 : EcsAspect
|
||||
where A2 : EcsAspect
|
||||
{
|
||||
return self.WhereFor<CombinedAspect<A0, A1, A2>>(sourceGroup);
|
||||
return self.WhereToGroupFor<CombinedAspect<A0, A1, A2>>(sourceGroup);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -194,7 +194,7 @@
|
||||
a1 = combined.a1;
|
||||
a2 = combined.a2;
|
||||
a3 = combined.a3;
|
||||
return self.WhereFor<CombinedAspect<A0, A1, A2, A3>>(sourceGroup);
|
||||
return self.WhereToGroupFor<CombinedAspect<A0, A1, A2, A3>>(sourceGroup);
|
||||
}
|
||||
|
||||
public static EcsReadonlyGroup Where<A0, A1, A2, A3>(this EcsWorld self)
|
||||
@ -203,7 +203,7 @@
|
||||
where A2 : EcsAspect
|
||||
where A3 : EcsAspect
|
||||
{
|
||||
return self.Where<CombinedAspect<A0, A1, A2, A3>>();
|
||||
return self.WhereToGroup<CombinedAspect<A0, A1, A2, A3>>();
|
||||
}
|
||||
public static EcsReadonlyGroup WhereFor<A0, A1, A2, A3>(this EcsWorld self, EcsReadonlyGroup sourceGroup)
|
||||
where A0 : EcsAspect
|
||||
@ -211,7 +211,7 @@
|
||||
where A2 : EcsAspect
|
||||
where A3 : EcsAspect
|
||||
{
|
||||
return self.WhereFor<CombinedAspect<A0, A1, A2, A3>>(sourceGroup);
|
||||
return self.WhereToGroupFor<CombinedAspect<A0, A1, A2, A3>>(sourceGroup);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -238,7 +238,7 @@
|
||||
a2 = combined.a2;
|
||||
a3 = combined.a3;
|
||||
a4 = combined.a4;
|
||||
return self.WhereFor<CombinedAspect<A0, A1, A2, A3, A4>>(sourceGroup);
|
||||
return self.WhereToGroupFor<CombinedAspect<A0, A1, A2, A3, A4>>(sourceGroup);
|
||||
}
|
||||
|
||||
|
||||
@ -249,7 +249,7 @@
|
||||
where A3 : EcsAspect
|
||||
where A4 : EcsAspect
|
||||
{
|
||||
return self.Where<CombinedAspect<A0, A1, A2, A3, A4>>();
|
||||
return self.WhereToGroup<CombinedAspect<A0, A1, A2, A3, A4>>();
|
||||
}
|
||||
public static EcsReadonlyGroup WhereFor<A0, A1, A2, A3, A4>(this EcsWorld self, EcsReadonlyGroup sourceGroup)
|
||||
where A0 : EcsAspect
|
||||
@ -258,7 +258,7 @@
|
||||
where A3 : EcsAspect
|
||||
where A4 : EcsAspect
|
||||
{
|
||||
return self.WhereFor<CombinedAspect<A0, A1, A2, A3, A4>>(sourceGroup);
|
||||
return self.WhereToGroupFor<CombinedAspect<A0, A1, A2, A3, A4>>(sourceGroup);
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -288,7 +288,7 @@
|
||||
a3 = combined.a3;
|
||||
a4 = combined.a4;
|
||||
a5 = combined.a5;
|
||||
return self.WhereFor<CombinedAspect<A0, A1, A2, A3, A4, A5>>(sourceGroup);
|
||||
return self.WhereToGroupFor<CombinedAspect<A0, A1, A2, A3, A4, A5>>(sourceGroup);
|
||||
}
|
||||
|
||||
|
||||
@ -300,7 +300,7 @@
|
||||
where A4 : EcsAspect
|
||||
where A5 : EcsAspect
|
||||
{
|
||||
return self.Where<CombinedAspect<A0, A1, A2, A3, A4, A5>>();
|
||||
return self.WhereToGroup<CombinedAspect<A0, A1, A2, A3, A4, A5>>();
|
||||
}
|
||||
public static EcsReadonlyGroup WhereFor<A0, A1, A2, A3, A4, A5>(this EcsWorld self, EcsReadonlyGroup sourceGroup)
|
||||
where A0 : EcsAspect
|
||||
@ -310,7 +310,7 @@
|
||||
where A4 : EcsAspect
|
||||
where A5 : EcsAspect
|
||||
{
|
||||
return self.WhereFor<CombinedAspect<A0, A1, A2, A3, A4, A5>>(sourceGroup);
|
||||
return self.WhereToGroupFor<CombinedAspect<A0, A1, A2, A3, A4, A5>>(sourceGroup);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
@ -1,32 +1,45 @@
|
||||
#pragma warning disable CS0162 // Обнаружен недостижимый код
|
||||
using DCFApixels.DragonECS.Internal;
|
||||
using DCFApixels.DragonECS.RunnersCore;
|
||||
using System;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
#region Interfaces
|
||||
[MetaName(nameof(PreInit))]
|
||||
[MetaColor(MetaColor.Orange)]
|
||||
[BindWithEcsRunner(typeof(EcsPreInitProcessRunner))]
|
||||
public interface IEcsPreInitProcess : IEcsProcess
|
||||
{
|
||||
void PreInit(EcsPipeline pipeline);
|
||||
void PreInit();
|
||||
}
|
||||
[MetaName(nameof(Init))]
|
||||
[MetaColor(MetaColor.Orange)]
|
||||
[BindWithEcsRunner(typeof(EcsInitProcessRunner))]
|
||||
public interface IEcsInitProcess : IEcsProcess
|
||||
{
|
||||
void Init(EcsPipeline pipeline);
|
||||
void Init();
|
||||
}
|
||||
[MetaName(nameof(Run))]
|
||||
[MetaColor(MetaColor.Orange)]
|
||||
[BindWithEcsRunner(typeof(EcsRunProcessRunner))]
|
||||
public interface IEcsRunProcess : IEcsProcess
|
||||
{
|
||||
void Run(EcsPipeline pipeline);
|
||||
void Run();
|
||||
}
|
||||
[MetaName(nameof(Destroy))]
|
||||
[MetaColor(MetaColor.Orange)]
|
||||
[BindWithEcsRunner(typeof(EcsDestroyProcessRunner))]
|
||||
public interface IEcsDestroyProcess : IEcsProcess
|
||||
{
|
||||
void Destroy(EcsPipeline pipeline);
|
||||
void Destroy();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Internal
|
||||
{
|
||||
[DebugColor(DebugColor.Orange)]
|
||||
[MetaColor(MetaColor.Orange)]
|
||||
public sealed class EcsPreInitProcessRunner : EcsRunner<IEcsPreInitProcess>, IEcsPreInitProcess
|
||||
{
|
||||
#if DEBUG && !DISABLE_DEBUG
|
||||
@ -40,33 +53,33 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
#endif
|
||||
public void PreInit(EcsPipeline pipeline)
|
||||
public void PreInit()
|
||||
{
|
||||
#if DEBUG && !DISABLE_DEBUG
|
||||
for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++)
|
||||
{
|
||||
_markers[i].Begin();
|
||||
try
|
||||
{
|
||||
_markers[i].Begin();
|
||||
targets[i].PreInit(pipeline);
|
||||
_markers[i].End();
|
||||
targets[i].PreInit();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
#if DISABLE_CATH_EXCEPTIONS
|
||||
throw;
|
||||
throw e;
|
||||
#endif
|
||||
EcsDebug.PrintError(e);
|
||||
}
|
||||
_markers[i].End();
|
||||
}
|
||||
#else
|
||||
foreach (var item in targets)
|
||||
{
|
||||
try { item.PreInit(pipeline); }
|
||||
try { item.PreInit(); }
|
||||
catch (Exception e)
|
||||
{
|
||||
#if DISABLE_CATH_EXCEPTIONS
|
||||
throw;
|
||||
throw e;
|
||||
#endif
|
||||
EcsDebug.PrintError(e);
|
||||
}
|
||||
@ -74,7 +87,7 @@ namespace DCFApixels.DragonECS
|
||||
#endif
|
||||
}
|
||||
}
|
||||
[DebugColor(DebugColor.Orange)]
|
||||
[MetaColor(MetaColor.Orange)]
|
||||
public sealed class EcsInitProcessRunner : EcsRunner<IEcsInitProcess>, IEcsInitProcess
|
||||
{
|
||||
#if DEBUG && !DISABLE_DEBUG
|
||||
@ -88,33 +101,33 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
#endif
|
||||
public void Init(EcsPipeline pipeline)
|
||||
public void Init()
|
||||
{
|
||||
#if DEBUG && !DISABLE_DEBUG
|
||||
for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++)
|
||||
{
|
||||
_markers[i].Begin();
|
||||
try
|
||||
{
|
||||
_markers[i].Begin();
|
||||
targets[i].Init(pipeline);
|
||||
_markers[i].End();
|
||||
targets[i].Init();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
#if DISABLE_CATH_EXCEPTIONS
|
||||
throw;
|
||||
throw e;
|
||||
#endif
|
||||
EcsDebug.PrintError(e);
|
||||
}
|
||||
_markers[i].End();
|
||||
}
|
||||
#else
|
||||
foreach (var item in targets)
|
||||
{
|
||||
try { item.Init(pipeline); }
|
||||
try { item.Init(); }
|
||||
catch (Exception e)
|
||||
{
|
||||
#if DISABLE_CATH_EXCEPTIONS
|
||||
throw;
|
||||
throw e;
|
||||
#endif
|
||||
EcsDebug.PrintError(e);
|
||||
}
|
||||
@ -122,7 +135,7 @@ namespace DCFApixels.DragonECS
|
||||
#endif
|
||||
}
|
||||
}
|
||||
[DebugColor(DebugColor.Orange)]
|
||||
[MetaColor(MetaColor.Orange)]
|
||||
public sealed class EcsRunProcessRunner : EcsRunner<IEcsRunProcess>, IEcsRunProcess
|
||||
{
|
||||
#if DEBUG && !DISABLE_DEBUG
|
||||
@ -136,33 +149,33 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
#endif
|
||||
public void Run(EcsPipeline pipeline)
|
||||
public void Run()
|
||||
{
|
||||
#if DEBUG && !DISABLE_DEBUG
|
||||
for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++)
|
||||
{
|
||||
_markers[i].Begin();
|
||||
try
|
||||
{
|
||||
_markers[i].Begin();
|
||||
targets[i].Run(pipeline);
|
||||
_markers[i].End();
|
||||
targets[i].Run();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
#if DISABLE_CATH_EXCEPTIONS
|
||||
throw;
|
||||
throw e;
|
||||
#endif
|
||||
EcsDebug.PrintError(e);
|
||||
}
|
||||
_markers[i].End();
|
||||
}
|
||||
#else
|
||||
foreach (var item in targets)
|
||||
{
|
||||
try { item.Run(pipeline); }
|
||||
try { item.Run(); }
|
||||
catch (Exception e)
|
||||
{
|
||||
#if DISABLE_CATH_EXCEPTIONS
|
||||
throw;
|
||||
throw e;
|
||||
#endif
|
||||
EcsDebug.PrintError(e);
|
||||
}
|
||||
@ -170,7 +183,7 @@ namespace DCFApixels.DragonECS
|
||||
#endif
|
||||
}
|
||||
}
|
||||
[DebugColor(DebugColor.Orange)]
|
||||
[MetaColor(MetaColor.Orange)]
|
||||
public sealed class EcsDestroyProcessRunner : EcsRunner<IEcsDestroyProcess>, IEcsDestroyProcess
|
||||
{
|
||||
#if DEBUG && !DISABLE_DEBUG
|
||||
@ -180,37 +193,37 @@ namespace DCFApixels.DragonECS
|
||||
_markers = new EcsProfilerMarker[targets.Length];
|
||||
for (int i = 0; i < targets.Length; i++)
|
||||
{
|
||||
_markers[i] = new EcsProfilerMarker($"{targets[i].GetType().Name}.{nameof(Destroy)}");
|
||||
_markers[i] = new EcsProfilerMarker($"{targets[i].GetType().Name}.{nameof(IEcsDestroyProcess.Destroy)}");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
public void Destroy(EcsPipeline pipeline)
|
||||
void IEcsDestroyProcess.Destroy()
|
||||
{
|
||||
#if DEBUG && !DISABLE_DEBUG
|
||||
for (int i = 0; i < targets.Length && targets.Length <= _markers.Length; i++)
|
||||
{
|
||||
_markers[i].Begin();
|
||||
try
|
||||
{
|
||||
_markers[i].Begin();
|
||||
targets[i].Destroy(pipeline);
|
||||
_markers[i].End();
|
||||
targets[i].Destroy();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
#if DISABLE_CATH_EXCEPTIONS
|
||||
throw;
|
||||
throw e;
|
||||
#endif
|
||||
EcsDebug.PrintError(e);
|
||||
}
|
||||
_markers[i].End();
|
||||
}
|
||||
#else
|
||||
foreach (var item in targets)
|
||||
{
|
||||
try { item.Destroy(pipeline); }
|
||||
try { item.Destroy(); }
|
||||
catch (Exception e)
|
||||
{
|
||||
#if DISABLE_CATH_EXCEPTIONS
|
||||
throw;
|
||||
throw e;
|
||||
#endif
|
||||
EcsDebug.PrintError(e);
|
||||
}
|
||||
|
||||
@ -2,18 +2,23 @@
|
||||
using DCFApixels.DragonECS.RunnersCore;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[MetaName(nameof(PreInject))]
|
||||
[BindWithEcsRunner(typeof(EcsPreInjectRunner))]
|
||||
public interface IEcsPreInject : IEcsProcess
|
||||
{
|
||||
void PreInject(object obj);
|
||||
}
|
||||
[MetaName(nameof(Inject))]
|
||||
[BindWithEcsRunner(typeof(EcsInjectRunner<>))]
|
||||
public interface IEcsInject<T> : IEcsProcess
|
||||
{
|
||||
void Inject(T obj);
|
||||
}
|
||||
[MetaName("PreInitInject")]
|
||||
[BindWithEcsRunner(typeof(EcsPreInitInjectProcessRunner))]
|
||||
public interface IEcsPreInitInjectProcess : IEcsProcess
|
||||
{
|
||||
void OnPreInitInjectionBefore();
|
||||
@ -45,7 +50,8 @@ namespace DCFApixels.DragonECS
|
||||
_injectSystems = null;
|
||||
}
|
||||
}
|
||||
[DebugHide, DebugColor(DebugColor.Gray)]
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaColor(MetaColor.Gray)]
|
||||
public sealed class EcsPreInjectRunner : EcsRunner<IEcsPreInject>, IEcsPreInject
|
||||
{
|
||||
void IEcsPreInject.PreInject(object obj)
|
||||
@ -53,18 +59,17 @@ namespace DCFApixels.DragonECS
|
||||
foreach (var item in targets) item.PreInject(obj);
|
||||
}
|
||||
}
|
||||
[DebugHide, DebugColor(DebugColor.Gray)]
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaColor(MetaColor.Gray)]
|
||||
public sealed class EcsInjectRunner<T> : EcsRunner<IEcsInject<T>>, IEcsInject<T>
|
||||
{
|
||||
private EcsBaseTypeInjectRunner _baseTypeInjectRunner;
|
||||
void IEcsInject<T>.Inject(T obj)
|
||||
{
|
||||
if (obj == null) ThrowArgumentNullException();
|
||||
if (obj == null) Throw.ArgumentNull();
|
||||
_baseTypeInjectRunner.Inject(obj);
|
||||
foreach (var item in targets) item.Inject(obj);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void ThrowArgumentNullException() => throw new ArgumentNullException();
|
||||
protected override void OnSetup()
|
||||
{
|
||||
Type baseType = typeof(T).BaseType;
|
||||
@ -78,20 +83,21 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
public abstract void Inject(object obj);
|
||||
}
|
||||
internal class EcsBaseTypeInjectRunner<T> : EcsBaseTypeInjectRunner
|
||||
internal sealed class EcsBaseTypeInjectRunner<T> : EcsBaseTypeInjectRunner
|
||||
{
|
||||
private IEcsInject<T> _runner;
|
||||
public EcsBaseTypeInjectRunner(EcsPipeline pipeline) => _runner = pipeline.GetRunner<IEcsInject<T>>();
|
||||
public override void Inject(object obj) => _runner.Inject((T)obj);
|
||||
public sealed override void Inject(object obj) => _runner.Inject((T)obj);
|
||||
}
|
||||
internal class EcsObjectTypePreInjectRunner : EcsBaseTypeInjectRunner
|
||||
internal sealed class EcsObjectTypePreInjectRunner : EcsBaseTypeInjectRunner
|
||||
{
|
||||
private IEcsPreInject _runner;
|
||||
public EcsObjectTypePreInjectRunner(EcsPipeline pipeline) => _runner = pipeline.GetRunner<IEcsPreInject>();
|
||||
public override void Inject(object obj) => _runner.PreInject(obj);
|
||||
public sealed override void Inject(object obj) => _runner.PreInject(obj);
|
||||
}
|
||||
|
||||
[DebugHide, DebugColor(DebugColor.Gray)]
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaColor(MetaColor.Gray)]
|
||||
public sealed class EcsPreInitInjectProcessRunner : EcsRunner<IEcsPreInitInjectProcess>, IEcsPreInitInjectProcess
|
||||
{
|
||||
public void OnPreInitInjectionAfter()
|
||||
@ -103,34 +109,40 @@ namespace DCFApixels.DragonECS
|
||||
foreach (var item in targets) item.OnPreInitInjectionBefore();
|
||||
}
|
||||
}
|
||||
public class InjectSystemBase { }
|
||||
[DebugHide, DebugColor(DebugColor.Gray)]
|
||||
public class InjectSystem<T> : InjectSystemBase, IEcsPreInitProcess, IEcsInject<PreInitInjectController>, IEcsPreInitInjectProcess
|
||||
public abstract class InjectSystemBase { }
|
||||
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaColor(MetaColor.Gray)]
|
||||
public class InjectSystem<T> : InjectSystemBase, IEcsInject<EcsPipeline>, IEcsPreInitProcess, IEcsInject<PreInitInjectController>, IEcsPreInitInjectProcess
|
||||
{
|
||||
private T _injectedData;
|
||||
private EcsPipeline _pipeline;
|
||||
void IEcsInject<EcsPipeline>.Inject(EcsPipeline obj) => _pipeline = obj;
|
||||
private PreInitInjectController _injectController;
|
||||
void IEcsInject<PreInitInjectController>.Inject(PreInitInjectController obj) => _injectController = obj;
|
||||
|
||||
private T _injectedData;
|
||||
|
||||
public InjectSystem(T injectedData)
|
||||
{
|
||||
if (injectedData == null) throw new ArgumentNullException();
|
||||
if (injectedData == null) Throw.ArgumentNull();
|
||||
_injectedData = injectedData;
|
||||
}
|
||||
public void PreInit(EcsPipeline pipeline)
|
||||
public void PreInit()
|
||||
{
|
||||
if (_injectedData == null) return;
|
||||
if (_injectController == null)
|
||||
{
|
||||
_injectController = new PreInitInjectController(pipeline);
|
||||
var injectMapRunner = pipeline.GetRunner<IEcsInject<PreInitInjectController>>();
|
||||
pipeline.GetRunner<IEcsPreInitInjectProcess>().OnPreInitInjectionBefore();
|
||||
_injectController = new PreInitInjectController(_pipeline);
|
||||
var injectMapRunner = _pipeline.GetRunner<IEcsInject<PreInitInjectController>>();
|
||||
_pipeline.GetRunner<IEcsPreInitInjectProcess>().OnPreInitInjectionBefore();
|
||||
injectMapRunner.Inject(_injectController);
|
||||
}
|
||||
var injectRunnerGeneric = pipeline.GetRunner<IEcsInject<T>>();
|
||||
var injectRunnerGeneric = _pipeline.GetRunner<IEcsInject<T>>();
|
||||
injectRunnerGeneric.Inject(_injectedData);
|
||||
if (_injectController.OnInject())
|
||||
{
|
||||
_injectController.Destroy();
|
||||
var injectCallbacksRunner = pipeline.GetRunner<IEcsPreInitInjectProcess>();
|
||||
var injectCallbacksRunner = _pipeline.GetRunner<IEcsPreInitInjectProcess>();
|
||||
injectCallbacksRunner.OnPreInitInjectionAfter();
|
||||
EcsRunner.Destroy(injectCallbacksRunner);
|
||||
}
|
||||
@ -149,8 +161,11 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
public static EcsPipeline.Builder Inject<T>(this EcsPipeline.Builder self, T data)
|
||||
{
|
||||
if (data == null) throw new ArgumentNullException();
|
||||
return self.Add(new InjectSystem<T>(data));
|
||||
if (data == null) Throw.ArgumentNull();
|
||||
self.Add(new InjectSystem<T>(data));
|
||||
if (data is IEcsModule module)
|
||||
self.AddModule(module);
|
||||
return self;
|
||||
}
|
||||
public static EcsPipeline.Builder Inject<A, B>(this EcsPipeline.Builder self, A a, B b)
|
||||
{
|
||||
|
||||
@ -5,42 +5,49 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
[DebugHide, DebugColor(DebugColor.Black)]
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaColor(MetaColor.Black)]
|
||||
public class SystemsLayerMarkerSystem : IEcsProcess
|
||||
{
|
||||
public readonly string name;
|
||||
public SystemsLayerMarkerSystem(string name) => this.name = name;
|
||||
}
|
||||
[DebugHide, DebugColor(DebugColor.Grey)]
|
||||
public class DeleteEmptyEntitesSystem : IEcsRunProcess, IEcsInject<EcsWorld>
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaColor(MetaColor.Grey)]
|
||||
public class EndFrameSystem : IEcsRunProcess, IEcsInject<EcsWorld>
|
||||
{
|
||||
private List<EcsWorld> _worlds = new List<EcsWorld>();
|
||||
private readonly List<EcsWorld> _worlds = new List<EcsWorld>();
|
||||
public void Inject(EcsWorld obj) => _worlds.Add(obj);
|
||||
public void Run(EcsPipeline pipeline)
|
||||
public void Run()
|
||||
{
|
||||
foreach (var world in _worlds)
|
||||
{
|
||||
world.DeleteEmptyEntites();
|
||||
world.ReleaseDelEntityBufferAll();
|
||||
}
|
||||
}
|
||||
}
|
||||
[DebugHide, DebugColor(DebugColor.Grey)]
|
||||
[MetaTags(MetaTags.HIDDEN)]
|
||||
[MetaColor(MetaColor.Grey)]
|
||||
public class DeleteOneFrameComponentSystem<TComponent> : IEcsRunProcess, IEcsInject<EcsWorld>
|
||||
where TComponent : struct, IEcsComponent
|
||||
{
|
||||
public EcsPipeline pipeline { get; set; }
|
||||
private sealed class Aspect : EcsAspect
|
||||
{
|
||||
public EcsPool<TComponent> pool;
|
||||
public Aspect(Builder b) => pool = b.Include<TComponent>();
|
||||
}
|
||||
List<EcsWorld> _worlds = new List<EcsWorld>();
|
||||
private readonly List<EcsWorld> _worlds = new List<EcsWorld>();
|
||||
public void Inject(EcsWorld obj) => _worlds.Add(obj);
|
||||
public void Run(EcsPipeline pipeline)
|
||||
public void Run()
|
||||
{
|
||||
for (int i = 0, iMax = _worlds.Count; i < iMax; i++)
|
||||
{
|
||||
EcsWorld world = _worlds[i];
|
||||
if (world.IsComponentTypeDeclared<TComponent>())
|
||||
{
|
||||
foreach (var e in world.Where(out Aspect a))
|
||||
foreach (var e in world.WhereToGroup(out Aspect a))
|
||||
a.pool.Del(e);
|
||||
}
|
||||
}
|
||||
|
||||
8
src/Collections.meta
Normal file
8
src/Collections.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2e026d1a6d4fd884ea7324b6097703c5
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
812
src/Collections/EcsGroup.cs
Normal file
812
src/Collections/EcsGroup.cs
Normal file
@ -0,0 +1,812 @@
|
||||
using DCFApixels.DragonECS.Internal;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
//_dense заполняется с индекса 1
|
||||
//в операциях изменяющих состояние группы нельзя итерироваться по this, либо осторожно учитывать этот момент
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)]
|
||||
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||
public readonly ref struct EcsReadonlyGroup
|
||||
{
|
||||
private readonly EcsGroup _source;
|
||||
|
||||
#region Constructors
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsReadonlyGroup(EcsGroup source) => _source = source;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public bool IsNull => _source == null;
|
||||
public int WorldID
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.World.id;
|
||||
}
|
||||
public EcsWorld World
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.World;
|
||||
}
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.Count;
|
||||
}
|
||||
public int CapacityDense
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.CapacityDense;
|
||||
}
|
||||
public int CapacitySparce
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.CapacitySparce;
|
||||
}
|
||||
public bool IsReleazed
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.IsReleased;
|
||||
}
|
||||
public int this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source[index];
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Has(int entityID) => _source.Has(entityID);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOf(int entityID) => _source.IndexOf(entityID);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsGroup.Enumerator GetEnumerator() => _source.GetEnumerator();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsGroup Clone() => _source.Clone();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int[] Bake() => _source.Bake();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Bake(ref int[] entities) => _source.Bake(ref entities);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Bake(List<int> entities) => _source.Bake(entities);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsSpan ToSpan() => _source.ToSpan();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsSpan ToSpan(int start, int length) => _source.ToSpan(start, length);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsGroup.LongsIterator GetLongs() => _source.GetLongs();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int First() => _source.First();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Last() => _source.Last();
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool SetEquals(EcsReadonlyGroup group) => _source.SetEquals(group._source);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool SetEquals(EcsGroup group) => _source.SetEquals(group);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool SetEquals(EcsSpan span) => _source.SetEquals(span);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Overlaps(EcsReadonlyGroup group) => _source.Overlaps(group._source);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Overlaps(EcsGroup group) => _source.Overlaps(group);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Overlaps(EcsSpan span) => _source.Overlaps(span);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsSubsetOf(EcsReadonlyGroup group) => _source.IsSubsetOf(group._source);
|
||||
public bool IsSubsetOf(EcsGroup group) => _source.IsSubsetOf(group);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsSupersetOf(EcsReadonlyGroup group) => _source.IsSupersetOf(group._source);
|
||||
public bool IsSupersetOf(EcsGroup group) => _source.IsSupersetOf(group);
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
public override string ToString() => _source != null ? _source.ToString() : "NULL";
|
||||
#endregion
|
||||
|
||||
#region Internal
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal EcsGroup GetGroupInternal() => _source;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator EcsSpan(EcsReadonlyGroup a) => a.ToSpan();
|
||||
internal class DebuggerProxy : EcsGroup.DebuggerProxy
|
||||
{
|
||||
public DebuggerProxy(EcsReadonlyGroup group) : base(group._source) { }
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||
public unsafe class EcsGroup : IDisposable, IEnumerable<int>
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private int[] _dense;
|
||||
private int[] _sparse;
|
||||
private int _count;
|
||||
internal bool _isReleased = true;
|
||||
|
||||
#region Properties
|
||||
public int WorldID
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.id;
|
||||
}
|
||||
public EcsWorld World
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source;
|
||||
}
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _count;
|
||||
}
|
||||
public int CapacityDense
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _dense.Length;
|
||||
}
|
||||
public int CapacitySparce
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _sparse.Length;
|
||||
}
|
||||
public EcsReadonlyGroup Readonly
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new EcsReadonlyGroup(this);
|
||||
}
|
||||
public bool IsReleased
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _isReleased;
|
||||
}
|
||||
public int this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (index < 0 || index >= Count) Throw.ArgumentOutOfRange();
|
||||
#endif
|
||||
return _dense[++index];
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constrcutors/Dispose
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsGroup New(EcsWorld world)
|
||||
{
|
||||
return world.GetFreeGroup();
|
||||
}
|
||||
internal EcsGroup(EcsWorld world, int denseCapacity = 64)
|
||||
{
|
||||
_source = world;
|
||||
_source.RegisterGroup(this);
|
||||
_dense = new int[denseCapacity];
|
||||
_sparse = new int[world.Capacity];
|
||||
|
||||
_count = 0;
|
||||
}
|
||||
public void Dispose() => _source.ReleaseGroup(this);
|
||||
#endregion
|
||||
|
||||
#region Has/IndexOf
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Has(int entityID)
|
||||
{
|
||||
return _sparse[entityID] > 0;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOf(int entityID)
|
||||
{
|
||||
return _sparse[entityID];
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Add/Remove
|
||||
public void AddUnchecked(int entityID) => AddInternal(entityID);
|
||||
public bool Add(int entityID)
|
||||
{
|
||||
if (Has(entityID))
|
||||
return false;
|
||||
AddInternal(entityID);
|
||||
return true;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void AddInternal(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (Has(entityID)) Throw.Group_AlreadyContains(entityID);
|
||||
#endif
|
||||
if (++_count >= _dense.Length)
|
||||
Array.Resize(ref _dense, _dense.Length << 1);
|
||||
_dense[_count] = entityID;
|
||||
_sparse[entityID] = _count;
|
||||
}
|
||||
|
||||
public void RemoveUnchecked(int entityID) => RemoveInternal(entityID);
|
||||
public bool Remove(int entityID)
|
||||
{
|
||||
if (!Has(entityID))
|
||||
return false;
|
||||
RemoveInternal(entityID);
|
||||
return true;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void RemoveInternal(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) Throw.Group_DoesNotContain(entityID);
|
||||
#endif
|
||||
_dense[_sparse[entityID]] = _dense[_count];
|
||||
_sparse[_dense[_count--]] = _sparse[entityID];
|
||||
_sparse[entityID] = 0;
|
||||
}
|
||||
|
||||
public void RemoveUnusedEntityIDs()
|
||||
{
|
||||
foreach (var e in this)
|
||||
{
|
||||
if (!_source.IsUsed(e))
|
||||
RemoveInternal(e);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Clear
|
||||
public void Clear()
|
||||
{
|
||||
_count = 0;
|
||||
//массив _dense нет смысла очищать
|
||||
for (int i = 0; i < _sparse.Length; i++)
|
||||
_sparse[i] = 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CopyFrom/Clone/Bake/ToSpan
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void CopyFrom(EcsReadonlyGroup group) => CopyFrom(group.GetGroupInternal());
|
||||
public void CopyFrom(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (group.World != _source) throw new ArgumentException("groupFilter.WorldIndex != WorldIndex");
|
||||
#endif
|
||||
if (_count > 0)
|
||||
Clear();
|
||||
foreach (var item in group)
|
||||
AddInternal(item);
|
||||
}
|
||||
public EcsGroup Clone()
|
||||
{
|
||||
EcsGroup result = _source.GetFreeGroup();
|
||||
result.CopyFrom(this);
|
||||
return result;
|
||||
}
|
||||
public int[] Bake()
|
||||
{
|
||||
int[] result = new int[_count];
|
||||
Array.Copy(_dense, 1, result, 0, _count);
|
||||
return result;
|
||||
}
|
||||
public int Bake(ref int[] entities)
|
||||
{
|
||||
if (entities.Length < _count)
|
||||
entities = new int[_count];
|
||||
Array.Copy(_dense, 1, entities, 0, _count);
|
||||
return _count;
|
||||
}
|
||||
public void Bake(List<int> entities)
|
||||
{
|
||||
entities.Clear();
|
||||
foreach (var e in this)
|
||||
entities.Add(e);
|
||||
}
|
||||
public EcsSpan ToSpan()
|
||||
{
|
||||
return new EcsSpan(WorldID, _dense);
|
||||
}
|
||||
public EcsSpan ToSpan(int start, int length)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (start + length > _count) Throw.ArgumentOutOfRange();
|
||||
#endif
|
||||
return new EcsSpan(WorldID, _dense, start, length);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Set operations
|
||||
|
||||
#region UnionWith
|
||||
/// <summary>as Union sets</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void UnionWith(EcsReadonlyGroup group) => UnionWith(group.GetGroupInternal());
|
||||
/// <summary>as Union sets</summary>
|
||||
public void UnionWith(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
foreach (var item in group)
|
||||
if (!Has(item))
|
||||
AddInternal(item);
|
||||
}
|
||||
public void UnionWith(EcsSpan span)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source.id != span.WorldID) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
foreach (var item in span)
|
||||
{
|
||||
if (!Has(item))
|
||||
AddInternal(item);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ExceptWith
|
||||
/// <summary>as Except sets</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ExceptWith(EcsReadonlyGroup group) => ExceptWith(group.GetGroupInternal());
|
||||
/// <summary>as Except sets</summary>
|
||||
public void ExceptWith(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
//if (group.Count > Count) // вариант 1. есть итерация по this
|
||||
//{
|
||||
// foreach (var item in this)
|
||||
// if (group.Has(item))
|
||||
// RemoveInternal(item);
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// foreach (var item in group)
|
||||
// if (Has(item))
|
||||
// RemoveInternal(item);
|
||||
//}
|
||||
|
||||
//foreach (var item in group) // вариант 2
|
||||
// if (Has(item))
|
||||
// RemoveInternal(item);
|
||||
|
||||
if (group.Count > Count)
|
||||
{
|
||||
for (int i = _count; i > 0; i--)//итерация в обратном порядке исключает ошибки при удалении элементов
|
||||
{
|
||||
int item = _dense[i];
|
||||
if (group.Has(item))
|
||||
RemoveInternal(item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in group)
|
||||
{
|
||||
if (Has(item))
|
||||
RemoveInternal(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void ExceptWith(EcsSpan span)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source.id != span.WorldID) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
foreach (var item in span)
|
||||
{
|
||||
if (Has(item))
|
||||
RemoveInternal(item);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IntersectWith
|
||||
/// <summary>as Intersect sets</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void IntersectWith(EcsReadonlyGroup group) => IntersectWith(group.GetGroupInternal());
|
||||
/// <summary>as Intersect sets</summary>
|
||||
public void IntersectWith(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (World != group.World) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
for (int i = _count; i > 0; i--)//итерация в обратном порядке исключает ошибки при удалении элементов
|
||||
{
|
||||
int item = _dense[i];
|
||||
if (!group.Has(item))
|
||||
RemoveInternal(item);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SymmetricExceptWith
|
||||
/// <summary>as Symmetric Except sets</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SymmetricExceptWith(EcsReadonlyGroup group) => SymmetricExceptWith(group.GetGroupInternal());
|
||||
/// <summary>as Symmetric Except sets</summary>
|
||||
public void SymmetricExceptWith(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
foreach (var item in group)
|
||||
if (Has(item))
|
||||
RemoveInternal(item);
|
||||
else
|
||||
AddInternal(item);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Inverse
|
||||
public void Inverse()
|
||||
{
|
||||
foreach (var item in _source.Entities)
|
||||
if (Has(item))
|
||||
RemoveInternal(item);
|
||||
else
|
||||
AddInternal(item);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region SetEquals
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool SetEquals(EcsReadonlyGroup group) => SetEquals(group.GetGroupInternal());
|
||||
public bool SetEquals(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
if (group.Count != Count)
|
||||
return false;
|
||||
foreach (var item in group)
|
||||
if (!Has(item))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public bool SetEquals(EcsSpan span)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source.id != span.WorldID) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
if (span.Length != Count)
|
||||
return false;
|
||||
foreach (var item in span)
|
||||
if (!Has(item))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Overlaps
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Overlaps(EcsReadonlyGroup group) => Overlaps(group.GetGroupInternal());
|
||||
public bool Overlaps(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
if(group.Count > Count)
|
||||
{
|
||||
foreach (var item in this)
|
||||
if (group.Has(item))
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var item in group)
|
||||
if (Has(item))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
public bool Overlaps(EcsSpan span)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source.id != span.WorldID) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
foreach (var item in span)
|
||||
if (Has(item))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IsSubsetOf
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsSubsetOf(EcsReadonlyGroup group) => IsSubsetOf(group.GetGroupInternal());
|
||||
public bool IsSubsetOf(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
if (group.Count < Count)
|
||||
return false;
|
||||
foreach (var item in this)
|
||||
if (!group.Has(item))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IsSupersetOf
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsSupersetOf(EcsReadonlyGroup group) => IsSupersetOf(group.GetGroupInternal());
|
||||
public bool IsSupersetOf(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source != group.World) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
if (group.Count > Count)
|
||||
return false;
|
||||
foreach (var item in group)
|
||||
if (!Has(item))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Static Set operations
|
||||
/// <summary>as Intersect sets</summary>
|
||||
/// <returns>new group from pool</returns>
|
||||
public static EcsGroup Union(EcsGroup a, EcsGroup b)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (a._source != b._source) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
EcsGroup result = a._source.GetFreeGroup();
|
||||
foreach (var item in a)
|
||||
result.AddInternal(item);
|
||||
foreach (var item in b)
|
||||
result.Add(item);
|
||||
return result;
|
||||
}
|
||||
/// <summary>as Except sets</summary>
|
||||
/// <returns>new group from pool</returns>
|
||||
public static EcsGroup Except(EcsGroup a, EcsGroup b)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (a._source != b._source) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
EcsGroup result = a._source.GetFreeGroup();
|
||||
foreach (var item in a)
|
||||
if (!b.Has(item))
|
||||
result.AddInternal(item);
|
||||
return result;
|
||||
}
|
||||
/// <summary>as Intersect sets</summary>
|
||||
/// <returns>new group from pool</returns>
|
||||
public static EcsGroup Intersect(EcsGroup a, EcsGroup b)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (a._source != b._source) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
EcsGroup result = a._source.GetFreeGroup();
|
||||
foreach (var item in a)
|
||||
if (b.Has(item))
|
||||
result.AddInternal(item);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>as Symmetric Except sets</summary>
|
||||
/// <returns>new group from pool</returns>
|
||||
public static EcsGroup SymmetricExcept(EcsGroup a, EcsGroup b)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (a._source != b._source) Throw.Group_ArgumentDifferentWorldsException();
|
||||
#endif
|
||||
EcsGroup result = a._source.GetFreeGroup();
|
||||
foreach (var item in a)
|
||||
if (!b.Has(item))
|
||||
result.AddInternal(item);
|
||||
foreach (var item in b)
|
||||
if (!a.Has(item))
|
||||
result.AddInternal(item);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static EcsGroup Inverse(EcsGroup a)
|
||||
{
|
||||
EcsGroup result = a._source.GetFreeGroup();
|
||||
foreach (var item in a._source.Entities)
|
||||
if (!a.Has(item))
|
||||
result.AddInternal(item);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Enumerator
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator GetEnumerator() => new Enumerator(this);
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator();
|
||||
public LongsIterator GetLongs() => new LongsIterator(this);
|
||||
public struct Enumerator : IEnumerator<int>
|
||||
{
|
||||
private readonly int[] _dense;
|
||||
private uint _index;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator(EcsGroup group)
|
||||
{
|
||||
_dense = group._dense;
|
||||
_index = (uint)(group._count > _dense.Length ? _dense.Length : group._count) + 1;
|
||||
}
|
||||
public int Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _dense[_index];
|
||||
}
|
||||
object IEnumerator.Current => Current;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext() => --_index > 0; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose() { }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset() { }
|
||||
}
|
||||
public readonly struct LongsIterator : IEnumerable<entlong>
|
||||
{
|
||||
private readonly EcsGroup _group;
|
||||
public LongsIterator(EcsGroup group) => _group = group;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator GetEnumerator() => new Enumerator(_group);
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < _group._count; i++)
|
||||
yield return _group.World.GetEntityLong(_group._dense[i]);
|
||||
}
|
||||
IEnumerator<entlong> IEnumerable<entlong>.GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < _group._count; i++)
|
||||
yield return _group.World.GetEntityLong(_group._dense[i]);
|
||||
}
|
||||
public struct Enumerator : IEnumerator<entlong>
|
||||
{
|
||||
private readonly EcsWorld world;
|
||||
private readonly int[] _dense;
|
||||
private uint _index;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator(EcsGroup group)
|
||||
{
|
||||
world = group.World;
|
||||
_dense = group._dense;
|
||||
_index = (uint)(group._count > _dense.Length ? _dense.Length : group._count) + 1;
|
||||
}
|
||||
public entlong Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => world.GetEntityLong(_dense[_index]);
|
||||
}
|
||||
object IEnumerator.Current => Current;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext() => --_index > 0; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Dispose() { }
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Reset() { }
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
public override string ToString()
|
||||
{
|
||||
return $"group{{{string.Join(", ", _dense.Skip(1).Take(_count))}}}";
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int First()
|
||||
{
|
||||
return _dense[1];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Last()
|
||||
{
|
||||
return _dense[_count];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void OnWorldResize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _sparse, newSize);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator EcsReadonlyGroup(EcsGroup a) => a.Readonly;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator EcsSpan(EcsGroup a) => a.ToSpan();
|
||||
internal class DebuggerProxy
|
||||
{
|
||||
private EcsGroup _group;
|
||||
public EcsWorld World => _group.World;
|
||||
public bool IsReleased => _group.IsReleased;
|
||||
public entlong[] Entities
|
||||
{
|
||||
get
|
||||
{
|
||||
entlong[] result = new entlong[_group.Count];
|
||||
int i = 0;
|
||||
foreach (var e in _group)
|
||||
result[i++] = _group.World.GetEntityLong(e);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
public int Count => _group.Count;
|
||||
public int CapacityDense => _group.CapacityDense;
|
||||
public int CapacitySparce => _group.CapacitySparce;
|
||||
public override string ToString() => _group.ToString();
|
||||
public DebuggerProxy(EcsGroup group) => _group = group;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
#if false
|
||||
public static class EcsGroupAliases
|
||||
{
|
||||
/// <summary>Alias for UnionWith</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Add(this EcsGroup self, EcsGroup group)
|
||||
{
|
||||
self.UnionWith(group);
|
||||
}
|
||||
/// <summary>Alias for UnionWith</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Add(this EcsGroup self, EcsReadonlyGroup group)
|
||||
{
|
||||
self.UnionWith(group);
|
||||
}
|
||||
/// <summary>Alias for ExceptWith</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Remove(this EcsGroup self, EcsGroup group)
|
||||
{
|
||||
self.ExceptWith(group);
|
||||
}
|
||||
/// <summary>Alias for ExceptWith</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Remove(this EcsGroup self, EcsReadonlyGroup group)
|
||||
{
|
||||
self.ExceptWith(group);
|
||||
}
|
||||
/// <summary>Alias for SymmetricExceptWith</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Xor(this EcsGroup self, EcsGroup group)
|
||||
{
|
||||
self.SymmetricExceptWith(group);
|
||||
}
|
||||
/// <summary>Alias for SymmetricExceptWith</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Xor(this EcsGroup self, EcsReadonlyGroup group)
|
||||
{
|
||||
self.SymmetricExceptWith(group);
|
||||
}
|
||||
/// <summary>Alias for IntersectWith</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void And(this EcsGroup self, EcsGroup group)
|
||||
{
|
||||
self.IntersectWith(group);
|
||||
}
|
||||
/// <summary>Alias for IntersectWith</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void And(this EcsGroup self, EcsReadonlyGroup group)
|
||||
{
|
||||
self.IntersectWith(group);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
125
src/Collections/EcsSpan.cs
Normal file
125
src/Collections/EcsSpan.cs
Normal file
@ -0,0 +1,125 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public readonly ref struct EcsSpan
|
||||
{
|
||||
private readonly int _worldID;
|
||||
private readonly ReadOnlySpan<int> _values;
|
||||
|
||||
#region Properties
|
||||
public int WorldID
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _worldID;
|
||||
}
|
||||
public EcsWorld World
|
||||
{
|
||||
get => EcsWorld.GetWorld(_worldID);
|
||||
}
|
||||
public int Length
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _values.Length;
|
||||
}
|
||||
public readonly int this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _values[index];
|
||||
}
|
||||
public bool IsNull
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _values.IsEmpty;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal EcsSpan(int worldID, ReadOnlySpan<int> span)
|
||||
{
|
||||
_worldID = worldID;
|
||||
_values = span;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal EcsSpan(int worldID, int[] array)
|
||||
{
|
||||
_worldID = worldID;
|
||||
_values = array;
|
||||
}
|
||||
internal EcsSpan(int worldID, int[] array, int length)
|
||||
{
|
||||
_worldID = worldID;
|
||||
_values = new ReadOnlySpan<int>(array, 0, length);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal EcsSpan(int worldID, int[] array, int start, int length)
|
||||
{
|
||||
_worldID = worldID;
|
||||
_values = new ReadOnlySpan<int>(array, start, length);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methdos
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int[] Bake()
|
||||
{
|
||||
int[] result = new int[_values.Length];
|
||||
_values.CopyTo(result);
|
||||
return result;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Bake(ref int[] entities)
|
||||
{
|
||||
if (entities.Length < _values.Length)
|
||||
Array.Resize(ref entities, _values.Length);
|
||||
int[] result = new int[_values.Length];
|
||||
_values.CopyTo(result);
|
||||
return _values.Length;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Bake(List<int> entities)
|
||||
{
|
||||
entities.Clear();
|
||||
foreach (var e in _values)
|
||||
{
|
||||
entities.Add(e);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
#pragma warning disable CS0809 // Устаревший член переопределяет неустаревший член
|
||||
[Obsolete("Equals() on EcsSpan 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 EcsSpan will always throw an exception.")]
|
||||
[EditorBrowsable(EditorBrowsableState.Never)]
|
||||
public override int GetHashCode() => throw new NotSupportedException();
|
||||
#pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член
|
||||
public override string ToString() => _values.ToString();
|
||||
#endregion
|
||||
|
||||
#region operators
|
||||
public static bool operator ==(EcsSpan left, EcsSpan right) => left._values == right._values;
|
||||
public static bool operator !=(EcsSpan left, EcsSpan right) => left._values != right._values;
|
||||
#endregion
|
||||
|
||||
#region Enumerator
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan<int>.Enumerator GetEnumerator() => _values.GetEnumerator();
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsSpan Slice(int start) => new EcsSpan(_worldID, _values.Slice(start));
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsSpan Slice(int start, int length) => new EcsSpan(_worldID, _values.Slice(start, length));
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int[] ToArray() => _values.ToArray();
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5e06cf4352ab9414293b145eb27daba7
|
||||
guid: 55c6215b2c0f45849b191532a01e1dfe
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
@ -8,11 +8,14 @@
|
||||
public const string DEBUG_PREFIX = "[DEBUG] ";
|
||||
public const string DEBUG_WARNING_TAG = "WARNING";
|
||||
public const string DEBUG_ERROR_TAG = "ERROR";
|
||||
public const string DEBUG_PASS_TAG = "PASS";
|
||||
|
||||
public const string PRE_BEGIN_LAYER = nameof(PRE_BEGIN_LAYER);
|
||||
public const string BEGIN_LAYER = nameof(BEGIN_LAYER);
|
||||
public const string BASIC_LAYER = nameof(BASIC_LAYER);
|
||||
public const string END_LAYER = nameof(END_LAYER);
|
||||
public const string POST_END_LAYER = nameof(POST_END_LAYER);
|
||||
|
||||
public const string META_HIDDEN_TAG = "HiddenInDebagging";
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,94 +0,0 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class DebugColorAttribute : Attribute
|
||||
{
|
||||
private Color color;
|
||||
public byte r => color.r;
|
||||
public byte g => color.g;
|
||||
public byte b => color.b;
|
||||
public DebugColorAttribute(byte r, byte g, byte b) => color = new Color(r, g, b);
|
||||
public DebugColorAttribute(DebugColor color) => this.color = new Color((int)color);
|
||||
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 4)]
|
||||
internal readonly struct Color
|
||||
{
|
||||
[FieldOffset(0)] public readonly int full;
|
||||
[FieldOffset(3)] public readonly byte r;
|
||||
[FieldOffset(2)] public readonly byte g;
|
||||
[FieldOffset(1)] public readonly byte b;
|
||||
public Color(byte r, byte g, byte b) : this()
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
}
|
||||
public Color(int full) : this() => this.full = full;
|
||||
public (byte, byte, byte) ToTuple() => (r, g, b);
|
||||
|
||||
public Color UpContrastColor()
|
||||
{
|
||||
byte minChannel = Math.Min(Math.Min(r, g), b);
|
||||
byte maxChannel = Math.Max(Math.Max(r, g), b);
|
||||
if (maxChannel == minChannel)
|
||||
return default;
|
||||
float factor = 255f / (maxChannel - minChannel);
|
||||
return new Color((byte)((r - minChannel) * factor), (byte)((g - minChannel) * factor), (byte)((b - minChannel) * factor));
|
||||
}
|
||||
|
||||
public static Color operator /(Color a, float b)
|
||||
{
|
||||
return new Color((byte)(a.r / b), (byte)(a.g / b), (byte)(a.b / b));
|
||||
}
|
||||
}
|
||||
}
|
||||
public enum DebugColor
|
||||
{
|
||||
/// <summary> Red. RGB is (255, 0, 0)</summary>
|
||||
Red = (255 << 24) + (000 << 16) + (000 << 8),
|
||||
/// <summary> Green. RGB is (0, 255, 0)</summary>
|
||||
Green = (000 << 24) + (255 << 16) + (000 << 8),
|
||||
/// <summary> Blue. RGB is (0, 0, 255)</summary>
|
||||
Blue = (000 << 24) + (000 << 16) + (255 << 8),
|
||||
|
||||
/// <summary> Yellow. RGB is (255, 255, 0)</summary>
|
||||
Yellow = (255 << 24) + (255 << 16) + (000 << 8),
|
||||
/// <summary> Cyan. RGB is (0, 255, 255)</summary>
|
||||
Cyan = (000 << 24) + (255 << 16) + (255 << 8),
|
||||
/// <summary> Magenta. RGB is (255, 0, 255)</summary>
|
||||
Magenta = (255 << 24) + (000 << 16) + (255 << 8),
|
||||
|
||||
/// <summary> Yellow. RGB is (255, 165, 0)</summary>
|
||||
Orange = (255 << 24) + (165 << 16) + (000 << 8),
|
||||
/// <summary> Yellow. RGB is (255, 69, 0)</summary>
|
||||
OrangeRed = (255 << 24) + (69 << 16) + (000 << 8),
|
||||
/// <summary> Lime. RGB is (125, 255, 0)</summary>
|
||||
Lime = (125 << 24) + (255 << 16) + (000 << 8),
|
||||
/// <summary> Lime. RGB is (127, 255, 212)</summary>
|
||||
Aquamarine = (127 << 24) + (255 << 16) + (212 << 8),
|
||||
/// <summary> Lime. RGB is (218, 165, 32)</summary>
|
||||
Goldenrod = (218 << 24) + (165 << 16) + (32 << 8),
|
||||
/// <summary> Yellow. RGB is (255, 105, 180)</summary>
|
||||
DeepPink = (255 << 24) + (105 << 16) + (180 << 8),
|
||||
/// <summary> Yellow. RGB is (220, 20, 60)</summary>
|
||||
Crimson = (220 << 24) + (20 << 16) + (60 << 8),
|
||||
/// <summary> Yellow. RGB is (138, 43, 226)</summary>
|
||||
BlueViolet = (138 << 24) + (43 << 16) + (226 << 8),
|
||||
/// <summary> Yellow. RGB is (255, 3, 62)</summary>
|
||||
AmericanRose = (255 << 24) + (3 << 16) + (62 << 8),
|
||||
|
||||
/// <summary> Grey/Gray. RGB is (127, 127, 127)</summary>
|
||||
Gray = (127 << 24) + (127 << 16) + (127 << 8),
|
||||
/// <summary> Grey/Gray. RGB is (127, 127, 127)</summary>
|
||||
Grey = Gray,
|
||||
/// <summary> Grey/Gray. RGB is (192, 192, 192)</summary>
|
||||
Silver = (192 << 24) + (192 << 16) + (192 << 8),
|
||||
/// <summary> White. RGB is (255, 255, 255)</summary>
|
||||
White = -1,
|
||||
/// <summary> Black. RGB is (0, 0, 0)</summary>
|
||||
Black = 0,
|
||||
}
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class DebugDescriptionAttribute : Attribute
|
||||
{
|
||||
public readonly string description;
|
||||
public DebugDescriptionAttribute(string description) => this.description = description;
|
||||
}
|
||||
}
|
||||
@ -1,7 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class DebugHideAttribute : Attribute { }
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class DebugNameAttribute : Attribute
|
||||
{
|
||||
public readonly string name;
|
||||
public DebugNameAttribute(string name) => this.name = name;
|
||||
}
|
||||
}
|
||||
@ -31,12 +31,23 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
public static class EcsDebug
|
||||
{
|
||||
public const string WARNING_TAG = EcsConsts.DEBUG_WARNING_TAG;
|
||||
public const string ERROR_TAG = EcsConsts.DEBUG_ERROR_TAG;
|
||||
public const string PASS_TAG = EcsConsts.DEBUG_PASS_TAG;
|
||||
|
||||
public static void Set<T>() where T : DebugService, new() => DebugService.Set<T>();
|
||||
public static void Set(DebugService service) => DebugService.Set(service);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void PrintWarning(object v) => Print(EcsConsts.DEBUG_WARNING_TAG, v);
|
||||
public static void PrintError(object v) => Print(EcsConsts.DEBUG_ERROR_TAG, v);
|
||||
public static void PrintPass(object v) => Print(EcsConsts.DEBUG_PASS_TAG, v);
|
||||
public static void Print()
|
||||
{
|
||||
#if !DISABLE_DRAGONECS_DEBUGGER
|
||||
DebugService.Instance.Print("");
|
||||
#endif
|
||||
}
|
||||
public static void Print(object v)
|
||||
{
|
||||
#if !DISABLE_DRAGONECS_DEBUGGER
|
||||
@ -50,6 +61,14 @@ namespace DCFApixels.DragonECS
|
||||
DebugService.Instance.Print(tag, v);
|
||||
#endif
|
||||
}
|
||||
public static void Break()
|
||||
{
|
||||
{
|
||||
#if !DISABLE_DRAGONECS_DEBUGGER
|
||||
DebugService.Instance.Break();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class DebugService
|
||||
@ -80,7 +99,7 @@ namespace DCFApixels.DragonECS
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Print(object v) => Print(null, v);
|
||||
public abstract void Print(string tag, object v);
|
||||
|
||||
public abstract void Break();
|
||||
public int RegisterMark(string name)
|
||||
{
|
||||
int id;
|
||||
@ -114,14 +133,46 @@ namespace DCFApixels.DragonECS
|
||||
private string[] _stopwatchsNames;
|
||||
public DefaultDebugService()
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.White;
|
||||
Console.BackgroundColor = ConsoleColor.Black;
|
||||
#if !DISABLE_DRAGONECS_DEBUGGER
|
||||
_stopwatchs = new Stopwatch[64];
|
||||
_stopwatchsNames = new string[64];
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void Print(string tag, object v)
|
||||
{
|
||||
Console.WriteLine($"[{tag}] {v}");
|
||||
if (string.IsNullOrEmpty(tag))
|
||||
{
|
||||
Console.WriteLine(v);
|
||||
}
|
||||
else
|
||||
{
|
||||
var color = Console.ForegroundColor;
|
||||
switch (tag)
|
||||
{
|
||||
case EcsDebug.ERROR_TAG:
|
||||
Console.ForegroundColor = ConsoleColor.Red;
|
||||
break;
|
||||
case EcsDebug.WARNING_TAG:
|
||||
Console.ForegroundColor = ConsoleColor.Yellow;
|
||||
break;
|
||||
case EcsDebug.PASS_TAG:
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
break;
|
||||
}
|
||||
Console.WriteLine($"[{tag}] {v}");
|
||||
Console.ForegroundColor = color;
|
||||
}
|
||||
}
|
||||
public override void Break()
|
||||
{
|
||||
var color = Console.ForegroundColor;
|
||||
Console.ForegroundColor = ConsoleColor.Cyan;
|
||||
Console.WriteLine("Press Enter to сontinue.");
|
||||
Console.ReadKey();
|
||||
Console.ForegroundColor = color;
|
||||
}
|
||||
public override void ProfilerMarkBegin(int id)
|
||||
{
|
||||
@ -129,10 +180,13 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
public override void ProfilerMarkEnd(int id)
|
||||
{
|
||||
var color = Console.ForegroundColor;
|
||||
Console.ForegroundColor = ConsoleColor.DarkGray;
|
||||
_stopwatchs[id].Stop();
|
||||
var time = _stopwatchs[id].Elapsed;
|
||||
_stopwatchs[id].Reset();
|
||||
Print("ProfilerMark", _stopwatchsNames[id] + " s:" + time.TotalSeconds);
|
||||
Console.ForegroundColor = color;
|
||||
}
|
||||
protected override void OnDelProfilerMark(int id)
|
||||
{
|
||||
|
||||
@ -1,11 +1,15 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public static class EcsDebugUtility
|
||||
{
|
||||
private const BindingFlags RFL_FLAGS = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
|
||||
|
||||
#region GetGenericTypeName
|
||||
public static string GetGenericTypeFullName<T>(int maxDepth = 2) => GetGenericTypeFullName(typeof(T), maxDepth);
|
||||
public static string GetGenericTypeFullName(Type type, int maxDepth = 2) => GetGenericTypeNameInternal(type, maxDepth, true);
|
||||
@ -45,7 +49,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
private static string AutoToString(object target, Type type, bool isWriteName)
|
||||
{
|
||||
var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
var fields = type.GetFields(RFL_FLAGS);
|
||||
string[] values = new string[fields.Length];
|
||||
for (int i = 0; i < fields.Length; i++)
|
||||
values[i] = (fields[i].GetValue(target) ?? "NULL").ToString();
|
||||
@ -57,12 +61,24 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region GetName
|
||||
public static string GetName<T>() => GetName(typeof(T));
|
||||
public static string GetName(Type type) => type.TryGetCustomAttribute(out DebugNameAttribute atr) ? atr.name : GetGenericTypeName(type);
|
||||
public static bool TryGetCustomName<T>(out string name) => TryGetCustomName(typeof(T), out name);
|
||||
public static string GetName(object obj, int maxGenericDepth = 2)
|
||||
{
|
||||
return obj is IEcsMetaProvider intr ?
|
||||
GetName(intr.MetaSource, maxGenericDepth) :
|
||||
GetName(type: obj.GetType(), maxGenericDepth);
|
||||
}
|
||||
public static string GetName<T>(int maxGenericDepth = 2) => GetName(typeof(T), maxGenericDepth);
|
||||
public static string GetName(Type type, int maxGenericDepth = 2) => type.TryGetCustomAttribute(out MetaNameAttribute atr) ? atr.name : GetGenericTypeName(type, maxGenericDepth);
|
||||
public static bool TryGetCustomName(object obj, out string name)
|
||||
{
|
||||
return obj is IEcsMetaProvider intr ?
|
||||
TryGetCustomName(intr.MetaSource, out name) :
|
||||
TryGetCustomName(type: obj.GetType(), out name);
|
||||
}
|
||||
public static bool TryGetCustomName<T>(out string name) => TryGetCustomName(type: typeof(T), out name);
|
||||
public static bool TryGetCustomName(Type type, out string name)
|
||||
{
|
||||
if (type.TryGetCustomAttribute(out DebugNameAttribute atr))
|
||||
if (type.TryGetCustomAttribute(out MetaNameAttribute atr))
|
||||
{
|
||||
name = atr.name;
|
||||
return true;
|
||||
@ -72,13 +88,53 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GetGroup
|
||||
public static MetaGroup GetGroup(object obj)
|
||||
{
|
||||
return obj is IEcsMetaProvider intr ?
|
||||
GetGroup(intr.MetaSource) :
|
||||
GetGroup(type: obj.GetType());
|
||||
}
|
||||
public static MetaGroup GetGroup<T>() => GetGroup(typeof(T));
|
||||
public static MetaGroup GetGroup(Type type) => type.TryGetCustomAttribute(out MetaGroupAttribute atr) ? atr.GetData() : MetaGroup.Empty;
|
||||
public static bool TryGetGroup(object obj, out MetaGroup group)
|
||||
{
|
||||
return obj is IEcsMetaProvider intr ?
|
||||
TryGetGroup(intr.MetaSource, out group) :
|
||||
TryGetGroup(type: obj.GetType(), out group);
|
||||
}
|
||||
public static bool TryGetGroup<T>(out MetaGroup text) => TryGetGroup(typeof(T), out text);
|
||||
public static bool TryGetGroup(Type type, out MetaGroup group)
|
||||
{
|
||||
if (type.TryGetCustomAttribute(out MetaGroupAttribute atr))
|
||||
{
|
||||
group = atr.GetData();
|
||||
return true;
|
||||
}
|
||||
group = MetaGroup.Empty;
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GetDescription
|
||||
public static string GetDescription(object obj)
|
||||
{
|
||||
return obj is IEcsMetaProvider intr ?
|
||||
GetDescription(intr.MetaSource) :
|
||||
GetDescription(type: obj.GetType());
|
||||
}
|
||||
public static string GetDescription<T>() => GetDescription(typeof(T));
|
||||
public static string GetDescription(Type type) => type.TryGetCustomAttribute(out DebugDescriptionAttribute atr) ? atr.description : string.Empty;
|
||||
public static string GetDescription(Type type) => type.TryGetCustomAttribute(out MetaDescriptionAttribute atr) ? atr.description : string.Empty;
|
||||
public static bool TryGetDescription(object obj, out string text)
|
||||
{
|
||||
return obj is IEcsMetaProvider intr ?
|
||||
TryGetDescription(intr.MetaSource, out text) :
|
||||
TryGetDescription(type: obj.GetType(), out text);
|
||||
}
|
||||
public static bool TryGetDescription<T>(out string text) => TryGetDescription(typeof(T), out text);
|
||||
public static bool TryGetDescription(Type type, out string text)
|
||||
{
|
||||
if (type.TryGetCustomAttribute(out DebugDescriptionAttribute atr))
|
||||
if (type.TryGetCustomAttribute(out MetaDescriptionAttribute atr))
|
||||
{
|
||||
text = atr.description;
|
||||
return true;
|
||||
@ -89,12 +145,12 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region GetColor
|
||||
private static Random random = new Random();
|
||||
private static Random random = new Random(100100100);
|
||||
private static Dictionary<string, WordColor> _words = new Dictionary<string, WordColor>();
|
||||
private class WordColor
|
||||
{
|
||||
public int wordsCount;
|
||||
public DebugColorAttribute.Color color;
|
||||
public MetaColor color;
|
||||
}
|
||||
private class NameColor
|
||||
{
|
||||
@ -107,7 +163,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
color = new WordColor();
|
||||
_words.Add(word, color);
|
||||
color.color = new DebugColorAttribute.Color((byte)random.Next(), (byte)random.Next(), (byte)random.Next()).UpContrastColor() / 2;
|
||||
color.color = new MetaColor((byte)random.Next(), (byte)random.Next(), (byte)random.Next()).UpContrastColor() / 2;
|
||||
}
|
||||
color.wordsCount++;
|
||||
colors.Add(color);
|
||||
@ -122,7 +178,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
return result;
|
||||
}
|
||||
public DebugColorAttribute.Color CalcColor()
|
||||
public MetaColor CalcColor()
|
||||
{
|
||||
float r = 0, g = 0, b = 0;
|
||||
int totalWordsCount = CalcTotalWordsColor();
|
||||
@ -134,11 +190,11 @@ namespace DCFApixels.DragonECS
|
||||
g += m * color.color.g;
|
||||
b += m * color.color.b;
|
||||
}
|
||||
return new DebugColorAttribute.Color((byte)r, (byte)g, (byte)b);
|
||||
return new MetaColor((byte)r, (byte)g, (byte)b);
|
||||
}
|
||||
}
|
||||
private static Dictionary<Type, NameColor> _names = new Dictionary<Type, NameColor>();
|
||||
private static DebugColorAttribute.Color CalcNameColorFor(Type type)
|
||||
private static MetaColor CalcNameColorFor(Type type)
|
||||
{
|
||||
Type targetType = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
|
||||
if (!_names.TryGetValue(targetType, out NameColor nameColor))
|
||||
@ -148,7 +204,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
return nameColor.CalcColor();
|
||||
}
|
||||
public static List<string> SplitString(string s)
|
||||
private static List<string> SplitString(string s)
|
||||
{
|
||||
string subs;
|
||||
List<string> words = new List<string>();
|
||||
@ -169,47 +225,152 @@ namespace DCFApixels.DragonECS
|
||||
return words;
|
||||
}
|
||||
|
||||
public static (byte, byte, byte) GetColorRGB<T>() => GetColorRGB(typeof(T));
|
||||
public static (byte, byte, byte) GetColorRGB(Type type)
|
||||
public static MetaColor GetColor(object obj)
|
||||
{
|
||||
var atr = type.GetCustomAttribute<DebugColorAttribute>();
|
||||
return atr != null ? (atr.r, atr.g, atr.b)
|
||||
return obj is IEcsMetaProvider intr ?
|
||||
GetColor(intr.MetaSource) :
|
||||
GetColor(type: obj.GetType());
|
||||
}
|
||||
public static MetaColor GetColor<T>() => GetColor(typeof(T));
|
||||
public static MetaColor GetColor(Type type)
|
||||
{
|
||||
var atr = type.GetCustomAttribute<MetaColorAttribute>();
|
||||
return atr != null ? atr.color
|
||||
#if DEBUG //optimization for release build
|
||||
: CalcNameColorFor(type).ToTuple();
|
||||
#else
|
||||
: ((byte)0, (byte)0, (byte)0);
|
||||
: CalcNameColorFor(type);
|
||||
#else
|
||||
: MetaColor.BlackColor;
|
||||
#endif
|
||||
}
|
||||
public static bool TryGetColorRGB<T>(out (byte, byte, byte) color) => TryGetColorRGB(typeof(T), out color);
|
||||
public static bool TryGetColorRGB(Type type, out (byte, byte, byte) color)
|
||||
public static bool TryGetColor(object obj, out MetaColor color)
|
||||
{
|
||||
var atr = type.GetCustomAttribute<DebugColorAttribute>();
|
||||
return obj is IEcsMetaProvider intr ?
|
||||
TryGetColor(intr.MetaSource, out color) :
|
||||
TryGetColor(type: obj.GetType(), out color);
|
||||
}
|
||||
public static bool TryGetColor<T>(out MetaColor color) => TryGetColor(typeof(T), out color);
|
||||
public static bool TryGetColor(Type type, out MetaColor color)
|
||||
{
|
||||
var atr = type.GetCustomAttribute<MetaColorAttribute>();
|
||||
if (atr != null)
|
||||
{
|
||||
color = (atr.r, atr.g, atr.b);
|
||||
color = atr.color;
|
||||
return true;
|
||||
}
|
||||
color = ((byte)0, (byte)0, (byte)0);
|
||||
color = MetaColor.BlackColor;
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GetTags
|
||||
public static IReadOnlyCollection<string> GetTags(object obj)
|
||||
{
|
||||
return obj is IEcsMetaProvider intr ?
|
||||
GetTags(intr.MetaSource) :
|
||||
GetTags(type: obj.GetType());
|
||||
}
|
||||
public static IReadOnlyCollection<string> GetTags<T>() => GetTags(typeof(T));
|
||||
public static IReadOnlyCollection<string> GetTags(Type type)
|
||||
{
|
||||
var atr = type.GetCustomAttribute<MetaTagsAttribute>();
|
||||
return atr != null ? atr.Tags : Array.Empty<string>();
|
||||
}
|
||||
|
||||
public static bool TryGetTags(object obj, out IReadOnlyCollection<string> tags)
|
||||
{
|
||||
return obj is IEcsMetaProvider intr ?
|
||||
TryGetTags(intr.MetaSource, out tags) :
|
||||
TryGetTags(type: obj.GetType(), out tags);
|
||||
}
|
||||
public static bool TryGetTags<T>(out IReadOnlyCollection<string> tags) => TryGetTags(typeof(T), out tags);
|
||||
public static bool TryGetTags(Type type, out IReadOnlyCollection<string> tags)
|
||||
{
|
||||
var atr = type.GetCustomAttribute<MetaTagsAttribute>();
|
||||
if (atr != null)
|
||||
{
|
||||
tags = atr.Tags;
|
||||
return true;
|
||||
}
|
||||
tags = Array.Empty<string>();
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IsHidden
|
||||
public static bool IsHidden(object obj)
|
||||
{
|
||||
return obj is IEcsMetaProvider intr ?
|
||||
IsHidden(intr.MetaSource) :
|
||||
IsHidden(type: obj.GetType());
|
||||
}
|
||||
public static bool IsHidden<T>() => IsHidden(typeof(T));
|
||||
public static bool IsHidden(Type type) => type.TryGetCustomAttribute(out DebugHideAttribute _);
|
||||
public static bool IsHidden(Type type) => type.TryGetCustomAttribute(out MetaTagsAttribute atr) && atr.Tags.Contains(MetaTags.HIDDEN);
|
||||
#endregion
|
||||
|
||||
#region MetaSource
|
||||
public static bool IsMetaSourceProvided(object obj)
|
||||
{
|
||||
return obj is IEcsMetaProvider;
|
||||
}
|
||||
public static object GetMetaSource(object obj)
|
||||
{
|
||||
return obj is IEcsMetaProvider intr ? intr.MetaSource : obj;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region GenerateTypeDebugData
|
||||
public static TypeMetaData GenerateTypeDebugData(object obj)
|
||||
{
|
||||
return obj is IEcsMetaProvider intr ?
|
||||
GenerateTypeDebugData(intr.MetaSource) :
|
||||
GenerateTypeDebugData(type: obj.GetType());
|
||||
}
|
||||
public static TypeMetaData GenerateTypeDebugData<T>() => GenerateTypeDebugData(typeof(T));
|
||||
public static TypeMetaData GenerateTypeDebugData(Type type)
|
||||
{
|
||||
return new TypeMetaData(
|
||||
type,
|
||||
GetName(type),
|
||||
GetGroup(type),
|
||||
GetColor(type),
|
||||
GetDescription(type),
|
||||
GetTags(type).ToArray());
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ReflectionExtensions
|
||||
internal static bool TryGetCustomAttribute<T>(this Type self, out T attribute) where T : Attribute
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool TryGetCustomAttribute<T>(this Type self, out T attribute) where T : Attribute
|
||||
{
|
||||
attribute = self.GetCustomAttribute<T>();
|
||||
return attribute != null;
|
||||
}
|
||||
internal static bool TryGetCustomAttribute<T>(this MemberInfo self, out T attribute) where T : Attribute
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool TryGetCustomAttribute<T>(this MemberInfo self, out T attribute) where T : Attribute
|
||||
{
|
||||
attribute = self.GetCustomAttribute<T>();
|
||||
return attribute != null;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class TypeMetaData
|
||||
{
|
||||
public readonly Type type;
|
||||
public readonly string name;
|
||||
public readonly MetaGroup group;
|
||||
public readonly MetaColor color;
|
||||
public readonly string description;
|
||||
public readonly string[] tags;
|
||||
public TypeMetaData(Type type, string name, MetaGroup group, MetaColor color, string description, string[] tags)
|
||||
{
|
||||
this.type = type;
|
||||
this.name = name;
|
||||
this.group = group;
|
||||
this.color = color;
|
||||
this.description = description;
|
||||
this.tags = tags;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
8
src/Debug/Interfaces.meta
Normal file
8
src/Debug/Interfaces.meta
Normal file
@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 50bc53c3762bf6b4ea127004a89a894e
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7
src/Debug/Interfaces/IEcsMetaProvider.cs
Normal file
7
src/Debug/Interfaces/IEcsMetaProvider.cs
Normal file
@ -0,0 +1,7 @@
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public interface IEcsMetaProvider
|
||||
{
|
||||
object MetaSource { get; }
|
||||
}
|
||||
}
|
||||
11
src/Debug/Interfaces/IEcsMetaProvider.cs.meta
Normal file
11
src/Debug/Interfaces/IEcsMetaProvider.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5379b4d037441ed4cb4171648c1453d4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
109
src/Debug/MetaAttributes/MetaColorAttribute.cs
Normal file
109
src/Debug/MetaAttributes/MetaColorAttribute.cs
Normal file
@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class MetaColorAttribute : Attribute
|
||||
{
|
||||
public readonly MetaColor color;
|
||||
public byte R => color.r;
|
||||
public byte G => color.g;
|
||||
public byte B => color.b;
|
||||
public float FloatR => R / (float)byte.MaxValue;
|
||||
public float FloatG => G / (float)byte.MaxValue;
|
||||
public float FloatB => B / (float)byte.MaxValue;
|
||||
public MetaColorAttribute(byte r, byte g, byte b) => color = new MetaColor(r, g, b, 255);
|
||||
public MetaColorAttribute(int colorCode) => color = new MetaColor(colorCode, true);
|
||||
}
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 1, Size = 4)]
|
||||
public readonly struct MetaColor
|
||||
{
|
||||
public static readonly MetaColor BlackColor = new MetaColor(Black);
|
||||
/// <summary> color code Red. RGB is (255, 0, 0)</summary>
|
||||
public const int Red = (255 << 24) | (000 << 16) | (000 << 8) | 255;
|
||||
/// <summary> color code Green. RGB is (0, 255, 0)</summary>
|
||||
public const int Green = (000 << 24) | (255 << 16) | (000 << 8) | 255;
|
||||
/// <summary> color code Blue. RGB is (0, 0, 255)</summary>
|
||||
public const int Blue = (000 << 24) | (000 << 16) | (255 << 8) | 255;
|
||||
|
||||
/// <summary> color code Yellow. RGB is (255, 255, 0)</summary>
|
||||
public const int Yellow = (255 << 24) | (255 << 16) | (000 << 8) | 255;
|
||||
/// <summary> color code Cyan. RGB is (0, 255, 255)</summary>
|
||||
public const int Cyan = (000 << 24) | (255 << 16) | (255 << 8) | 255;
|
||||
/// <summary> color code Magenta. RGB is (255, 0, 255)</summary>
|
||||
public const int Magenta = (255 << 24) | (000 << 16) | (255 << 8) | 255;
|
||||
|
||||
/// <summary> color code Orange. RGB is (255, 165, 0)</summary>
|
||||
public const int Orange = (255 << 24) | (165 << 16) | (000 << 8) | 255;
|
||||
/// <summary> color code OrangeRed. RGB is (255, 69, 0)</summary>
|
||||
public const int OrangeRed = (255 << 24) | (69 << 16) | (000 << 8) | 255;
|
||||
/// <summary> color code Lime. RGB is (125, 255, 0)</summary>
|
||||
public const int Lime = (125 << 24) | (255 << 16) | (000 << 8) | 255;
|
||||
/// <summary> color code Aquamarine. RGB is (127, 255, 212)</summary>
|
||||
public const int Aquamarine = (127 << 24) | (255 << 16) | (212 << 8) | 255;
|
||||
/// <summary> color code Goldenrod. RGB is (218, 165, 32)</summary>
|
||||
public const int Goldenrod = (218 << 24) | (165 << 16) | (32 << 8) | 255;
|
||||
/// <summary> color code DeepPink. RGB is (255, 105, 180)</summary>
|
||||
public const int DeepPink = (255 << 24) | (105 << 16) | (180 << 8) | 255;
|
||||
/// <summary> color code Crimson. RGB is (220, 20, 60)</summary>
|
||||
public const int Crimson = (220 << 24) | (20 << 16) | (60 << 8) | 255;
|
||||
/// <summary> color code BlueViolet. RGB is (138, 43, 226)</summary>
|
||||
public const int BlueViolet = (138 << 24) | (43 << 16) | (226 << 8) | 255;
|
||||
/// <summary> color code AmericanRose. RGB is (255, 3, 62)</summary>
|
||||
public const int AmericanRose = (255 << 24) | (3 << 16) | (62 << 8) | 255;
|
||||
|
||||
/// <summary> color code Grey/Gray. RGB is (127, 127, 127)</summary>
|
||||
public const int Gray = (127 << 24) | (127 << 16) | (127 << 8) | 255;
|
||||
/// <summary> color code Grey/Gray. RGB is (127, 127, 127)</summary>
|
||||
public const int Grey = Gray;
|
||||
/// <summary> color code Silver. RGB is (192, 192, 192)</summary>
|
||||
public const int Silver = (192 << 24) | (192 << 16) | (192 << 8) | 255;
|
||||
/// <summary> color code White. RGB is (255, 255, 255)</summary>
|
||||
public const int White = -1;
|
||||
/// <summary> color code Black. RGB is (0, 0, 0)</summary>
|
||||
public const int Black = 0;
|
||||
|
||||
[FieldOffset(0)] public readonly int colorCode;
|
||||
[FieldOffset(3)] public readonly byte r;
|
||||
[FieldOffset(2)] public readonly byte g;
|
||||
[FieldOffset(1)] public readonly byte b;
|
||||
[FieldOffset(0)] public readonly byte a;
|
||||
public float FloatR => r / (float)byte.MaxValue;
|
||||
public float FloatG => g / (float)byte.MaxValue;
|
||||
public float FloatB => b / (float)byte.MaxValue;
|
||||
public float FloatA => a / (float)byte.MaxValue;
|
||||
public MetaColor(byte r, byte g, byte b) : this()
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
a = 255;
|
||||
}
|
||||
public MetaColor(byte r, byte g, byte b, byte a) : this()
|
||||
{
|
||||
this.r = r;
|
||||
this.g = g;
|
||||
this.b = b;
|
||||
this.a = a;
|
||||
}
|
||||
public MetaColor(int colorCode) : this() => this.colorCode = colorCode;
|
||||
public MetaColor(int colorCode, bool withoutAlpha) : this() => this.colorCode = withoutAlpha ? colorCode | 255 : colorCode;
|
||||
public (byte, byte, byte) ToTupleRGB() => (r, g, b);
|
||||
public (byte, byte, byte, byte) ToTupleRGBA() => (r, g, b, a);
|
||||
|
||||
public MetaColor UpContrastColor()
|
||||
{
|
||||
byte minChannel = Math.Min(Math.Min(r, g), b);
|
||||
byte maxChannel = Math.Max(Math.Max(r, g), b);
|
||||
if (maxChannel == minChannel)
|
||||
return default;
|
||||
float factor = 255f / (maxChannel - minChannel);
|
||||
return new MetaColor((byte)((r - minChannel) * factor), (byte)((g - minChannel) * factor), (byte)((b - minChannel) * factor));
|
||||
}
|
||||
public static MetaColor operator /(MetaColor a, float b)
|
||||
{
|
||||
return new MetaColor((byte)(a.r / b), (byte)(a.g / b), (byte)(a.b / b));
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/Debug/MetaAttributes/MetaDescriptionAttribute.cs
Normal file
11
src/Debug/MetaAttributes/MetaDescriptionAttribute.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class MetaDescriptionAttribute : Attribute
|
||||
{
|
||||
public readonly string description;
|
||||
public MetaDescriptionAttribute(string description) => this.description = description;
|
||||
}
|
||||
}
|
||||
33
src/Debug/MetaAttributes/MetaGroupAttribute.cs
Normal file
33
src/Debug/MetaAttributes/MetaGroupAttribute.cs
Normal file
@ -0,0 +1,33 @@
|
||||
using System;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class MetaGroupAttribute : Attribute
|
||||
{
|
||||
public static readonly MetaGroupAttribute Empty = new MetaGroupAttribute("");
|
||||
public readonly string name;
|
||||
public readonly string rootCategory;
|
||||
public MetaGroupAttribute(string name)
|
||||
{
|
||||
name = Regex.Replace(name, @"^[/|\\]+|[/|\\]+$", "");
|
||||
rootCategory = Regex.Match(name, @"^(.*?)[/\\]").Groups[1].Value;
|
||||
this.name = name;
|
||||
}
|
||||
public string[] SplitCategories()
|
||||
{
|
||||
return Regex.Split(name, @"[/|\\]");
|
||||
}
|
||||
public MetaGroup GetData() => new MetaGroup(this);
|
||||
}
|
||||
public readonly struct MetaGroup
|
||||
{
|
||||
public static readonly MetaGroup Empty = new MetaGroup(MetaGroupAttribute.Empty);
|
||||
private readonly MetaGroupAttribute _source;
|
||||
public string Name => _source.name;
|
||||
public string RootCategory => _source.rootCategory;
|
||||
public MetaGroup(MetaGroupAttribute source) => _source = source;
|
||||
public string[] SplitCategories() => _source.SplitCategories();
|
||||
}
|
||||
}
|
||||
11
src/Debug/MetaAttributes/MetaGroupAttribute.cs.meta
Normal file
11
src/Debug/MetaAttributes/MetaGroupAttribute.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c447392c75f8b4a42a2e5c3eb49e5b82
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
11
src/Debug/MetaAttributes/MetaNameAttribute.cs
Normal file
11
src/Debug/MetaAttributes/MetaNameAttribute.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using System;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class MetaNameAttribute : Attribute
|
||||
{
|
||||
public readonly string name;
|
||||
public MetaNameAttribute(string name) => this.name = name;
|
||||
}
|
||||
}
|
||||
24
src/Debug/MetaAttributes/MetaTagsAttribute.cs
Normal file
24
src/Debug/MetaAttributes/MetaTagsAttribute.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class | AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class MetaTagsAttribute : Attribute
|
||||
{
|
||||
private readonly string[] _tags;
|
||||
public IReadOnlyCollection<string> Tags => _tags;
|
||||
|
||||
[Obsolete("With empty parameters, this attribute makes no sense.", true)]
|
||||
public MetaTagsAttribute() { }
|
||||
public MetaTagsAttribute(params string[] tags)
|
||||
{
|
||||
_tags = tags.Where(o => !string.IsNullOrEmpty(o)).ToArray();
|
||||
}
|
||||
}
|
||||
public readonly ref struct MetaTags
|
||||
{
|
||||
public const string HIDDEN = EcsConsts.META_HIDDEN_TAG;
|
||||
}
|
||||
}
|
||||
203
src/EcsAspect.cs
203
src/EcsAspect.cs
@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using DCFApixels.DragonECS.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
@ -11,9 +11,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
public abstract class EcsAspect
|
||||
{
|
||||
[EditorBrowsable(EditorBrowsableState.Always)]
|
||||
internal EcsWorld source;
|
||||
[EditorBrowsable(EditorBrowsableState.Always)]
|
||||
internal EcsMask mask;
|
||||
private bool _isInit;
|
||||
|
||||
@ -85,7 +83,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
int id = _world.GetComponentID(type);
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{type.Name} already in constraints list.");
|
||||
if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type);
|
||||
#endif
|
||||
_inc.Add(id);
|
||||
}
|
||||
@ -93,7 +91,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
int id = _world.GetComponentID(type);
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_inc.Contains(id) || _exc.Contains(id)) throw new EcsFrameworkException($"{type.Name} already in constraints list.");
|
||||
if (_inc.Contains(id) || _exc.Contains(id)) Throw.ConstraintIsAlreadyContainedInMask(type);
|
||||
#endif
|
||||
_exc.Add(id);
|
||||
}
|
||||
@ -125,10 +123,10 @@ namespace DCFApixels.DragonECS
|
||||
foreach (var item in _combined)
|
||||
{
|
||||
EcsMask submask = item.aspect.mask;
|
||||
maskInc.ExceptWith(submask._exc);//удаляю конфликтующие ограничения
|
||||
maskExc.ExceptWith(submask._inc);//удаляю конфликтующие ограничения
|
||||
maskInc.UnionWith(submask._inc);
|
||||
maskExc.UnionWith(submask._exc);
|
||||
maskInc.ExceptWith(submask.exc);//удаляю конфликтующие ограничения
|
||||
maskExc.ExceptWith(submask.inc);//удаляю конфликтующие ограничения
|
||||
maskInc.UnionWith(submask.inc);
|
||||
maskExc.UnionWith(submask.exc);
|
||||
}
|
||||
maskInc.ExceptWith(_exc);//удаляю конфликтующие ограничения
|
||||
maskExc.ExceptWith(_inc);//удаляю конфликтующие ограничения
|
||||
@ -141,12 +139,29 @@ namespace DCFApixels.DragonECS
|
||||
maskExc = _exc;
|
||||
}
|
||||
|
||||
Dictionary<int, int> r = new Dictionary<int, int>();
|
||||
foreach (var id in maskInc)
|
||||
{
|
||||
var bit = EcsMaskBit.FromID(id);
|
||||
if (!r.TryAdd(bit.chankIndex, bit.mask))
|
||||
r[bit.chankIndex] = r[bit.chankIndex] | bit.mask;
|
||||
}
|
||||
EcsMaskBit[] incMasks = r.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray();
|
||||
r.Clear();
|
||||
foreach (var id in maskExc)
|
||||
{
|
||||
var bit = EcsMaskBit.FromID(id);
|
||||
if (!r.TryAdd(bit.chankIndex, bit.mask))
|
||||
r[bit.chankIndex] = r[bit.chankIndex] | bit.mask;
|
||||
}
|
||||
EcsMaskBit[] excMasks = r.Select(o => new EcsMaskBit(o.Key, o.Value)).ToArray();
|
||||
|
||||
var inc = maskInc.ToArray();
|
||||
Array.Sort(inc);
|
||||
var exc = maskExc.ToArray();
|
||||
Array.Sort(exc);
|
||||
|
||||
mask = new EcsMask(_world.WorldTypeID, inc, exc);
|
||||
mask = new EcsMask(_world.id, inc, exc, incMasks, excMasks);
|
||||
_world = null;
|
||||
_inc = null;
|
||||
_exc = null;
|
||||
@ -173,16 +188,16 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
return new EcsAspectIterator(this, source.Entities);
|
||||
}
|
||||
public EcsAspectIterator GetIteratorFor(EcsReadonlyGroup sourceGroup)
|
||||
public EcsAspectIterator GetIteratorFor(EcsSpan span)
|
||||
{
|
||||
return new EcsAspectIterator(this, sourceGroup);
|
||||
return new EcsAspectIterator(this, span);
|
||||
}
|
||||
#endregion
|
||||
|
||||
private struct Combined
|
||||
private readonly struct Combined
|
||||
{
|
||||
public EcsAspect aspect;
|
||||
public int order;
|
||||
public readonly EcsAspect aspect;
|
||||
public readonly int order;
|
||||
public Combined(EcsAspect aspect, int order)
|
||||
{
|
||||
this.aspect = aspect;
|
||||
@ -201,27 +216,56 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Mask
|
||||
public readonly struct EcsMaskBit
|
||||
{
|
||||
private const int BITS = 32;
|
||||
private const int DIV_SHIFT = 5;
|
||||
private const int MOD_MASK = BITS - 1;
|
||||
|
||||
public readonly int chankIndex;
|
||||
public readonly int mask;
|
||||
public EcsMaskBit(int chankIndex, int mask)
|
||||
{
|
||||
this.chankIndex = chankIndex;
|
||||
this.mask = mask;
|
||||
}
|
||||
public static EcsMaskBit FromID(int id)
|
||||
{
|
||||
return new EcsMaskBit(id >> DIV_SHIFT, 1 << (id & MOD_MASK)); //аналогично new EcsMaskBit(id / BITS, 1 << (id % BITS)) но быстрее
|
||||
}
|
||||
public override string ToString()
|
||||
{
|
||||
return $"bit({chankIndex}, {mask})";
|
||||
}
|
||||
}
|
||||
|
||||
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||
public sealed class EcsMask
|
||||
{
|
||||
internal readonly int _worldTypeID;
|
||||
internal readonly int worldID;
|
||||
internal readonly EcsMaskBit[] incChunckMasks;
|
||||
internal readonly EcsMaskBit[] excChunckMasks;
|
||||
internal readonly int[] inc;
|
||||
internal readonly int[] exc;
|
||||
public int WorldID => worldID;
|
||||
/// <summary>Including constraints</summary>
|
||||
internal readonly int[] _inc;
|
||||
public ReadOnlySpan<int> Inc => inc;
|
||||
/// <summary>Excluding constraints</summary>
|
||||
internal readonly int[] _exc;
|
||||
internal EcsMask(int worldTypeID, int[] inc, int[] exc)
|
||||
public ReadOnlySpan<int> Exc => exc;
|
||||
internal EcsMask(int worldID, int[] inc, int[] exc, EcsMaskBit[] incChunckMasks, EcsMaskBit[] excChunckMasks)
|
||||
{
|
||||
#if DEBUG
|
||||
if (worldTypeID == 0) throw new ArgumentException();
|
||||
CheckConstraints(inc, exc);
|
||||
#endif
|
||||
_worldTypeID = worldTypeID;
|
||||
_inc = inc;
|
||||
_exc = exc;
|
||||
this.inc = inc;
|
||||
this.exc = exc;
|
||||
this.worldID = worldID;
|
||||
this.incChunckMasks = incChunckMasks;
|
||||
this.excChunckMasks = excChunckMasks;
|
||||
}
|
||||
|
||||
#region Object
|
||||
public override string ToString() => CreateLogString(_worldTypeID, _inc, _exc);
|
||||
public override string ToString() => CreateLogString(worldID, inc, exc);
|
||||
#endregion
|
||||
|
||||
#region Debug utils
|
||||
@ -249,10 +293,10 @@ namespace DCFApixels.DragonECS
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
private static string CreateLogString(int worldTypeID, int[] inc, int[] exc)
|
||||
private static string CreateLogString(int worldID, int[] inc, int[] exc)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG)
|
||||
string converter(int o) => EcsDebugUtility.GetGenericTypeName(WorldMetaStorage.GetComponentType(worldTypeID, o), 1);
|
||||
string converter(int o) => EcsDebugUtility.GetGenericTypeName(EcsWorld.GetWorld(worldID).AllPools[o].ComponentType, 1);
|
||||
return $"Inc({string.Join(", ", inc.Select(converter))}) Exc({string.Join(", ", exc.Select(converter))})";
|
||||
#else
|
||||
return $"Inc({string.Join(", ", inc)}) Exc({string.Join(", ", exc)})"; // Release optimization
|
||||
@ -260,31 +304,28 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
internal class DebuggerProxy
|
||||
{
|
||||
public readonly Type worldType;
|
||||
public readonly int worldTypeID;
|
||||
public readonly EcsWorld world;
|
||||
public readonly int worldID;
|
||||
public readonly EcsMaskBit[] includedChunkMasks;
|
||||
public readonly EcsMaskBit[] excludedChunkMasks;
|
||||
public readonly int[] included;
|
||||
public readonly int[] excluded;
|
||||
public readonly Type[] includedTypes;
|
||||
public readonly Type[] excludedTypes;
|
||||
|
||||
public DebuggerProxy(EcsMask mask)
|
||||
{
|
||||
worldType = WorldMetaStorage.GetWorldType(mask._worldTypeID);
|
||||
worldTypeID = mask._worldTypeID;
|
||||
included = mask._inc;
|
||||
excluded = mask._exc;
|
||||
Type converter(int o) => WorldMetaStorage.GetComponentType(worldTypeID, o);
|
||||
world = EcsWorld.GetWorld(mask.worldID);
|
||||
worldID = mask.worldID;
|
||||
includedChunkMasks = mask.incChunckMasks;
|
||||
excludedChunkMasks = mask.excChunckMasks;
|
||||
included = mask.inc;
|
||||
excluded = mask.exc;
|
||||
Type converter(int o) => world.GetComponentType(o);
|
||||
includedTypes = included.Select(converter).ToArray();
|
||||
excludedTypes = excluded.Select(converter).ToArray();
|
||||
}
|
||||
public override string ToString() => CreateLogString(worldTypeID, included, excluded);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ThrowHelper
|
||||
internal static class ThrowHelper
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowArgumentDifferentWorldsException() => throw new ArgumentException("The groups belong to different worlds.");
|
||||
public override string ToString() => CreateLogString(worldID, included, excluded);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@ -293,14 +334,16 @@ namespace DCFApixels.DragonECS
|
||||
#region Iterator
|
||||
public ref struct EcsAspectIterator
|
||||
{
|
||||
public readonly int worldID;
|
||||
public readonly EcsMask mask;
|
||||
private EcsReadonlyGroup _sourceGroup;
|
||||
private EcsSpan _span;
|
||||
private Enumerator _enumerator;
|
||||
|
||||
public EcsAspectIterator(EcsAspect aspect, EcsReadonlyGroup sourceGroup)
|
||||
public EcsAspectIterator(EcsAspect aspect, EcsSpan span)
|
||||
{
|
||||
mask = aspect.mask;
|
||||
_sourceGroup = sourceGroup;
|
||||
worldID = aspect.World.id;
|
||||
mask = aspect.mask;
|
||||
_span = span;
|
||||
_enumerator = default;
|
||||
}
|
||||
|
||||
@ -320,6 +363,30 @@ namespace DCFApixels.DragonECS
|
||||
while (enumerator.MoveNext())
|
||||
group.AddInternal(enumerator.Current);
|
||||
}
|
||||
public int CopyTo(ref int[] array)
|
||||
{
|
||||
var enumerator = GetEnumerator();
|
||||
int count = 0;
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
if(array.Length <= count)
|
||||
Array.Resize(ref array, array.Length << 1);
|
||||
array[count++] = enumerator.Current;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
public EcsSpan CopyToSpan(ref int[] array)
|
||||
{
|
||||
var enumerator = GetEnumerator();
|
||||
int count = 0;
|
||||
while (enumerator.MoveNext())
|
||||
{
|
||||
if (array.Length <= count)
|
||||
Array.Resize(ref array, array.Length << 1);
|
||||
array[count++] = enumerator.Current;
|
||||
}
|
||||
return new EcsSpan(worldID, array, count);
|
||||
}
|
||||
|
||||
#region object
|
||||
public override string ToString()
|
||||
@ -336,38 +403,48 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
#region Enumerator
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator GetEnumerator() => new Enumerator(_sourceGroup, mask);
|
||||
public Enumerator GetEnumerator() => new Enumerator(_span, mask);
|
||||
|
||||
public ref struct Enumerator
|
||||
{
|
||||
private EcsGroup.Enumerator _sourceGroup;
|
||||
private readonly int[] _inc;
|
||||
private readonly int[] _exc;
|
||||
private readonly IEcsPoolImplementation[] _pools;
|
||||
private ReadOnlySpan<int>.Enumerator _span;
|
||||
private readonly EcsMaskBit[] _inc;
|
||||
private readonly EcsMaskBit[] _exc;
|
||||
private readonly int[][] _entitiesComponentMasks;
|
||||
|
||||
public Enumerator(EcsReadonlyGroup sourceGroup, EcsMask mask)
|
||||
public Enumerator(EcsSpan span, EcsMask mask)
|
||||
{
|
||||
_sourceGroup = sourceGroup.GetEnumerator();
|
||||
_inc = mask._inc;
|
||||
_exc = mask._exc;
|
||||
_pools = sourceGroup.World._pools;
|
||||
_span = span.GetEnumerator();
|
||||
_inc = mask.incChunckMasks;
|
||||
_exc = mask.excChunckMasks;
|
||||
_entitiesComponentMasks = span.World._entitiesComponentMasks;
|
||||
}
|
||||
public int Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _sourceGroup.Current;
|
||||
get => _span.Current;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext()
|
||||
{
|
||||
while (_sourceGroup.MoveNext())
|
||||
while (_span.MoveNext())
|
||||
{
|
||||
int e = _sourceGroup.Current;
|
||||
int e = _span.Current;
|
||||
EcsMaskBit bit;
|
||||
for (int i = 0, iMax = _inc.Length; i < iMax; i++)
|
||||
if (!_pools[_inc[i]].Has(e)) goto next;
|
||||
{
|
||||
bit = _inc[i];
|
||||
//if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) == 0) goto skip;
|
||||
if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != bit.mask) goto skip;
|
||||
}
|
||||
for (int i = 0, iMax = _exc.Length; i < iMax; i++)
|
||||
if (_pools[_exc[i]].Has(e)) goto next;
|
||||
{
|
||||
bit = _exc[i];
|
||||
//if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) != 0) goto skip;
|
||||
if ((_entitiesComponentMasks[e][bit.chankIndex] & bit.mask) == bit.mask) goto skip;
|
||||
}
|
||||
return true;
|
||||
next: continue;
|
||||
skip: continue;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
566
src/EcsGroup.cs
566
src/EcsGroup.cs
@ -1,566 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
using static DCFApixels.DragonECS.EcsGroup.ThrowHelper;
|
||||
#endif
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 0, Size = 8)]
|
||||
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||
public readonly ref struct EcsReadonlyGroup
|
||||
{
|
||||
private readonly EcsGroup _source;
|
||||
|
||||
#region Constructors
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsReadonlyGroup(EcsGroup source) => _source = source;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public bool IsNull => _source == null;
|
||||
public EcsWorld World
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.World;
|
||||
}
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.Count;
|
||||
}
|
||||
public int CapacityDense
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.CapacityDense;
|
||||
}
|
||||
public int CapacitySparce
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.CapacitySparce;
|
||||
}
|
||||
public bool IsReleazed
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.IsReleased;
|
||||
}
|
||||
public int this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source[index];
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Has(int entityID) => _source.Has(entityID);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOf(int entityID) => _source.IndexOf(entityID);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsGroup.Enumerator GetEnumerator() => _source.GetEnumerator();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsGroup Clone() => _source.Clone();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int[] Bake() => _source.Bake();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Bake(ref int[] entities) => _source.Bake(ref entities);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Bake(List<int> entities) => _source.Bake(entities);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ReadOnlySpan<int> ToSpan() => _source.ToSpan();
|
||||
public ReadOnlySpan<int> ToSpan(int start, int length) => _source.ToSpan(start, length);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int First() => _source.First();
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Last() => _source.Last();
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
public override string ToString() => _source != null ? _source.ToString() : "NULL";
|
||||
public override int GetHashCode() => _source.GetHashCode();
|
||||
public override bool Equals(object obj) => obj is EcsGroup group && group == this;
|
||||
public bool Equals(EcsReadonlyGroup other) => _source == other._source;
|
||||
#endregion
|
||||
|
||||
#region Internal
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal EcsGroup GetGroupInternal() => _source;
|
||||
|
||||
#endregion
|
||||
|
||||
#region operators
|
||||
public static bool operator ==(EcsReadonlyGroup a, EcsReadonlyGroup b) => a.Equals(b);
|
||||
public static bool operator ==(EcsReadonlyGroup a, EcsGroup b) => a.Equals(b);
|
||||
public static bool operator !=(EcsReadonlyGroup a, EcsReadonlyGroup b) => !a.Equals(b);
|
||||
public static bool operator !=(EcsReadonlyGroup a, EcsGroup b) => !a.Equals(b);
|
||||
#endregion
|
||||
|
||||
#region DebuggerProxy
|
||||
internal class DebuggerProxy : EcsGroup.DebuggerProxy
|
||||
{
|
||||
public DebuggerProxy(EcsReadonlyGroup group) : base(group._source) { }
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||
public unsafe class EcsGroup : IDisposable, IEquatable<EcsGroup>, IEnumerable<int>
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private int[] _dense;
|
||||
private int[] _sparse;
|
||||
private int _count;
|
||||
internal bool _isReleased = true;
|
||||
|
||||
#region Properties
|
||||
public EcsWorld World => _source;
|
||||
public int Count
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _count;
|
||||
}
|
||||
public int CapacityDense
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _dense.Length;
|
||||
}
|
||||
public int CapacitySparce
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _sparse.Length;
|
||||
}
|
||||
public EcsReadonlyGroup Readonly
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => new EcsReadonlyGroup(this);
|
||||
}
|
||||
public bool IsReleased
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _isReleased;
|
||||
}
|
||||
public int this[int index]
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (index < 0 || index >= Count) ThrowArgumentOutOfRange();
|
||||
#endif
|
||||
return _dense[++index];
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constrcutors/Dispose
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsGroup New(EcsWorld world)
|
||||
{
|
||||
return world.GetFreeGroup();
|
||||
}
|
||||
internal EcsGroup(EcsWorld world, int denseCapacity = 64)
|
||||
{
|
||||
_source = world;
|
||||
_source.RegisterGroup(this);
|
||||
_dense = new int[denseCapacity];
|
||||
_sparse = new int[world.Capacity];
|
||||
|
||||
_count = 0;
|
||||
}
|
||||
public void Dispose() => _source.ReleaseGroup(this);
|
||||
#endregion
|
||||
|
||||
#region Has/IndexOf
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Has(int entityID)
|
||||
{
|
||||
return _sparse[entityID] > 0;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int IndexOf(int entityID)
|
||||
{
|
||||
return _sparse[entityID];
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Add/Remove
|
||||
public void UncheckedAdd(int entityID) => AddInternal(entityID);
|
||||
public void Add(int entityID)
|
||||
{
|
||||
if (Has(entityID)) return;
|
||||
AddInternal(entityID);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void AddInternal(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (Has(entityID)) ThrowAlreadyContains(entityID);
|
||||
#endif
|
||||
if (++_count >= _dense.Length)
|
||||
Array.Resize(ref _dense, _dense.Length << 1);
|
||||
_dense[_count] = entityID;
|
||||
_sparse[entityID] = _count;
|
||||
}
|
||||
|
||||
public void UncheckedRemove(int entityID) => RemoveInternal(entityID);
|
||||
public void Remove(int entityID)
|
||||
{
|
||||
if (!Has(entityID)) return;
|
||||
RemoveInternal(entityID);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void RemoveInternal(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) ThrowDoesNotContain(entityID);
|
||||
#endif
|
||||
_dense[_sparse[entityID]] = _dense[_count];
|
||||
_sparse[_dense[_count--]] = _sparse[entityID];
|
||||
_sparse[entityID] = 0;
|
||||
}
|
||||
|
||||
public void RemoveUnusedEntityIDs()
|
||||
{
|
||||
foreach (var e in this)
|
||||
{
|
||||
if (!_source.IsUsed(e))
|
||||
RemoveInternal(e);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Clear
|
||||
public void Clear()
|
||||
{
|
||||
_count = 0;
|
||||
//массив _dense нет смысла очищать
|
||||
for (int i = 0; i < _sparse.Length; i++)
|
||||
_sparse[i] = 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region CopyFrom/Clone/Bake/ToSpan
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void CopyFrom(EcsReadonlyGroup group) => CopyFrom(group.GetGroupInternal());
|
||||
public void CopyFrom(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (group.World != _source) throw new ArgumentException("groupFilter.WorldIndex != WorldIndex");
|
||||
#endif
|
||||
if (_count > 0)
|
||||
Clear();
|
||||
foreach (var item in group)
|
||||
AddInternal(item);
|
||||
}
|
||||
public EcsGroup Clone()
|
||||
{
|
||||
EcsGroup result = _source.GetFreeGroup();
|
||||
result.CopyFrom(this);
|
||||
return result;
|
||||
}
|
||||
public int[] Bake()
|
||||
{
|
||||
int[] result = new int[_count];
|
||||
Array.Copy(_dense, 1, result, 0, _count);
|
||||
return result;
|
||||
}
|
||||
public int Bake(ref int[] entities)
|
||||
{
|
||||
if (entities.Length < _count)
|
||||
entities = new int[_count];
|
||||
Array.Copy(_dense, 1, entities, 0, _count);
|
||||
return _count;
|
||||
}
|
||||
public void Bake(List<int> entities)
|
||||
{
|
||||
entities.Clear();
|
||||
foreach (var e in this)
|
||||
entities.Add(e);
|
||||
}
|
||||
public ReadOnlySpan<int> ToSpan() => new ReadOnlySpan<int>(_dense, 0, _count);
|
||||
public ReadOnlySpan<int> ToSpan(int start, int length)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (start + length > _count) ThrowArgumentOutOfRangeException();
|
||||
#endif
|
||||
return new ReadOnlySpan<int>(_dense, start, length);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Set operations
|
||||
/// <summary>as Union sets</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void UnionWith(EcsReadonlyGroup group) => UnionWith(group.GetGroupInternal());
|
||||
/// <summary>as Union sets</summary>
|
||||
public void UnionWith(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source != group.World) ThrowArgumentDifferentWorldsException();
|
||||
#endif
|
||||
foreach (var item in group)
|
||||
if (!Has(item))
|
||||
AddInternal(item);
|
||||
}
|
||||
|
||||
/// <summary>as Except sets</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void ExceptWith(EcsReadonlyGroup group) => ExceptWith(group.GetGroupInternal());
|
||||
/// <summary>as Except sets</summary>
|
||||
public void ExceptWith(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source != group.World) ThrowArgumentDifferentWorldsException();
|
||||
#endif
|
||||
foreach (var item in this)
|
||||
if (group.Has(item))
|
||||
RemoveInternal(item);
|
||||
}
|
||||
|
||||
/// <summary>as Intersect sets</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void IntersectWith(EcsReadonlyGroup group) => IntersectWith(group.GetGroupInternal());
|
||||
/// <summary>as Intersect sets</summary>
|
||||
public void IntersectWith(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (World != group.World) ThrowArgumentDifferentWorldsException();
|
||||
#endif
|
||||
foreach (var item in this)
|
||||
if (!group.Has(item))
|
||||
RemoveInternal(item);
|
||||
}
|
||||
|
||||
/// <summary>as Symmetric Except sets</summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void SymmetricExceptWith(EcsReadonlyGroup group) => SymmetricExceptWith(group.GetGroupInternal());
|
||||
/// <summary>as Symmetric Except sets</summary>
|
||||
public void SymmetricExceptWith(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (_source != group.World) ThrowArgumentDifferentWorldsException();
|
||||
#endif
|
||||
foreach (var item in group)
|
||||
if (Has(item))
|
||||
RemoveInternal(item);
|
||||
else
|
||||
AddInternal(item);
|
||||
}
|
||||
public void Inverse()
|
||||
{
|
||||
foreach (var item in _source.Entities)
|
||||
if (Has(item))
|
||||
RemoveInternal(item);
|
||||
else
|
||||
AddInternal(item);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Static Set operations
|
||||
/// <summary>as Intersect sets</summary>
|
||||
/// <returns>new group from pool</returns>
|
||||
public static EcsGroup Union(EcsGroup a, EcsGroup b)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (a._source != b._source) ThrowArgumentDifferentWorldsException();
|
||||
#endif
|
||||
EcsGroup result = a._source.GetFreeGroup();
|
||||
foreach (var item in a)
|
||||
result.AddInternal(item);
|
||||
foreach (var item in b)
|
||||
result.Add(item);
|
||||
return result;
|
||||
}
|
||||
/// <summary>as Except sets</summary>
|
||||
/// <returns>new group from pool</returns>
|
||||
public static EcsGroup Except(EcsGroup a, EcsGroup b)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (a._source != b._source) ThrowArgumentDifferentWorldsException();
|
||||
#endif
|
||||
EcsGroup result = a._source.GetFreeGroup();
|
||||
foreach (var item in a)
|
||||
if (!b.Has(item))
|
||||
result.AddInternal(item);
|
||||
return result;
|
||||
}
|
||||
/// <summary>as Intersect sets</summary>
|
||||
/// <returns>new group from pool</returns>
|
||||
public static EcsGroup Intersect(EcsGroup a, EcsGroup b)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (a._source != b._source) ThrowArgumentDifferentWorldsException();
|
||||
#endif
|
||||
EcsGroup result = a._source.GetFreeGroup();
|
||||
foreach (var item in a)
|
||||
if (b.Has(item))
|
||||
result.AddInternal(item);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>as Symmetric Except sets</summary>
|
||||
/// <returns>new group from pool</returns>
|
||||
public static EcsGroup SymmetricExcept(EcsGroup a, EcsGroup b)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (a._source != b._source) ThrowArgumentDifferentWorldsException();
|
||||
#endif
|
||||
EcsGroup result = a._source.GetFreeGroup();
|
||||
foreach (var item in a)
|
||||
if (!b.Has(item))
|
||||
result.AddInternal(item);
|
||||
foreach (var item in b)
|
||||
if (!a.Has(item))
|
||||
result.AddInternal(item);
|
||||
return result;
|
||||
}
|
||||
|
||||
public static EcsGroup Inverse(EcsGroup a)
|
||||
{
|
||||
EcsGroup result = a._source.GetFreeGroup();
|
||||
foreach (var item in a._source.Entities)
|
||||
if (!a.Has(item))
|
||||
result.AddInternal(item);
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Enumerator
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator GetEnumerator()
|
||||
{
|
||||
return new Enumerator(this);
|
||||
}
|
||||
IEnumerator IEnumerable.GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < _count; i++)
|
||||
yield return _dense[i];
|
||||
}
|
||||
IEnumerator<int> IEnumerable<int>.GetEnumerator()
|
||||
{
|
||||
for (int i = 0; i < _count; i++)
|
||||
yield return _dense[i];
|
||||
}
|
||||
public ref struct Enumerator
|
||||
{
|
||||
private readonly int[] _dense;
|
||||
private readonly int _count;
|
||||
private int _index;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Enumerator(EcsGroup group)
|
||||
{
|
||||
_dense = group._dense;
|
||||
_count = group._count;
|
||||
_index = 0;
|
||||
}
|
||||
public int Current
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _dense[_index];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool MoveNext() => ++_index <= _count && _count < _dense.Length; // <= потму что отсчет начинается с индекса 1 //_count < _dense.Length дает среде понять что проверки на выход за границы не нужны
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
public override string ToString() => string.Join(", ", _dense.Cast<string>(), 0, _count);
|
||||
public override bool Equals(object obj) => obj is EcsGroup group && Equals(group);
|
||||
public bool Equals(EcsReadonlyGroup other) => Equals(other.GetGroupInternal());
|
||||
public bool Equals(EcsGroup other)
|
||||
{
|
||||
if (other is null || other.Count != Count)
|
||||
return false;
|
||||
foreach (var e in other)
|
||||
if (!Has(e))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
public override int GetHashCode()
|
||||
{
|
||||
int hash = 0;
|
||||
foreach (var item in this)
|
||||
hash ^= 1 << (item % 32); //реализация от балды, так как не нужен, но фишка в том что хеш не учитывает порядок сущьностей, что явлется правильным поведением.
|
||||
return hash;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region OtherMethods
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int First() => this[0];
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int Last() => this[_count - 1];
|
||||
|
||||
#endregion
|
||||
|
||||
#region operators
|
||||
private static bool StaticEquals(EcsGroup a, EcsReadonlyGroup b) => StaticEquals(a, b.GetGroupInternal());
|
||||
private static bool StaticEquals(EcsGroup a, EcsGroup b)
|
||||
{
|
||||
if (a is null) return false;
|
||||
return a.Equals(b);
|
||||
}
|
||||
public static bool operator ==(EcsGroup a, EcsGroup b) => StaticEquals(a, b);
|
||||
public static bool operator ==(EcsGroup a, EcsReadonlyGroup b) => StaticEquals(a, b);
|
||||
public static bool operator !=(EcsGroup a, EcsGroup b) => !StaticEquals(a, b);
|
||||
public static bool operator !=(EcsGroup a, EcsReadonlyGroup b) => !StaticEquals(a, b);
|
||||
#endregion
|
||||
|
||||
#region OnWorldResize
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void OnWorldResize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _sparse, newSize);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ThrowHalper
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
internal static class ThrowHelper
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowAlreadyContains(int entityID) => throw new EcsFrameworkException($"This group already contains entity {entityID}.");
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowArgumentOutOfRange() => throw new ArgumentOutOfRangeException($"index is less than 0 or is equal to or greater than Count.");
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowDoesNotContain(int entityID) => throw new EcsFrameworkException($"This group does not contain entity {entityID}.");
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException();
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowArgumentDifferentWorldsException() => throw new ArgumentException("The groups belong to different worlds.");
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region DebuggerProxy
|
||||
internal class DebuggerProxy
|
||||
{
|
||||
private EcsGroup _group;
|
||||
public EcsWorld World => _group.World;
|
||||
public bool IsReleased => _group.IsReleased;
|
||||
public entlong[] Entities
|
||||
{
|
||||
get
|
||||
{
|
||||
entlong[] result = new entlong[_group.Count];
|
||||
int i = 0;
|
||||
foreach (var e in _group)
|
||||
result[i++] = _group.World.GetEntityLong(e);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
public int Count => _group.Count;
|
||||
public int CapacityDense => _group.CapacityDense;
|
||||
public int CapacitySparce => _group.CapacitySparce;
|
||||
|
||||
public override string ToString() => _group.ToString();
|
||||
public DebuggerProxy(EcsGroup group) => _group = group;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@ -12,14 +12,14 @@ namespace DCFApixels.DragonECS
|
||||
public sealed class EcsPipeline
|
||||
{
|
||||
private IEcsProcess[] _allSystems;
|
||||
private Dictionary<Type, IEcsRunner> _runners;
|
||||
private Dictionary<Type, IEcsRunner> _runners = new Dictionary<Type, IEcsRunner>();
|
||||
private IEcsRunProcess _runRunnerCache;
|
||||
|
||||
private ReadOnlyCollection<IEcsProcess> _allSystemsSealed;
|
||||
private ReadOnlyDictionary<Type, IEcsRunner> _allRunnersSealed;
|
||||
|
||||
private bool _isInit;
|
||||
private bool _isDestoryed;
|
||||
private bool _isInit = false;
|
||||
private bool _isDestoryed = false;
|
||||
|
||||
#region Properties
|
||||
public ReadOnlyCollection<IEcsProcess> AllSystems => _allSystemsSealed;
|
||||
@ -32,13 +32,8 @@ namespace DCFApixels.DragonECS
|
||||
private EcsPipeline(IEcsProcess[] systems)
|
||||
{
|
||||
_allSystems = systems;
|
||||
_runners = new Dictionary<Type, IEcsRunner>();
|
||||
|
||||
_allSystemsSealed = new ReadOnlyCollection<IEcsProcess>(_allSystems);
|
||||
_allRunnersSealed = new ReadOnlyDictionary<Type, IEcsRunner>(_runners);
|
||||
|
||||
_isInit = false;
|
||||
_isDestoryed = false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
@ -64,7 +59,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
if (_isInit == true)
|
||||
{
|
||||
EcsDebug.Print("[Warning]", $"This {nameof(EcsPipeline)} has already been initialized");
|
||||
EcsDebug.PrintWarning($"This {nameof(EcsPipeline)} has already been initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
@ -72,60 +67,41 @@ namespace DCFApixels.DragonECS
|
||||
ecsPipelineInjectRunner.Inject(this);
|
||||
EcsRunner.Destroy(ecsPipelineInjectRunner);
|
||||
var preInitRunner = GetRunner<IEcsPreInitProcess>();
|
||||
preInitRunner.PreInit(this);
|
||||
preInitRunner.PreInit();
|
||||
EcsRunner.Destroy(preInitRunner);
|
||||
var initRunner = GetRunner<IEcsInitProcess>();
|
||||
initRunner.Init(this);
|
||||
initRunner.Init();
|
||||
EcsRunner.Destroy(initRunner);
|
||||
|
||||
_runRunnerCache = GetRunner<IEcsRunProcess>();
|
||||
_isInit = true;
|
||||
GC.Collect();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Run()
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
CheckBeforeInitForMethod(nameof(Run));
|
||||
CheckAfterDestroyForMethod(nameof(Run));
|
||||
if (!_isInit) Throw.Pipeline_MethodCalledBeforeInitialisation(nameof(Run));
|
||||
if (_isDestoryed) Throw.Pipeline_MethodCalledAfterDestruction(nameof(Run));
|
||||
#endif
|
||||
_runRunnerCache.Run(this);
|
||||
_runRunnerCache.Run();
|
||||
}
|
||||
public void Destroy()
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
CheckBeforeInitForMethod(nameof(Run));
|
||||
if (!_isInit) Throw.Pipeline_MethodCalledBeforeInitialisation(nameof(Destroy));
|
||||
#endif
|
||||
if (_isDestoryed == true)
|
||||
if (_isDestoryed)
|
||||
{
|
||||
EcsDebug.Print("[Warning]", $"This {nameof(EcsPipeline)} has already been destroyed");
|
||||
EcsDebug.PrintWarning($"This {nameof(EcsPipeline)} has already been destroyed");
|
||||
return;
|
||||
}
|
||||
_isDestoryed = true;
|
||||
GetRunner<IEcsDestroyProcess>().Destroy(this);
|
||||
GetRunner<IEcsDestroyProcess>().Destroy();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region StateChecks
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
private void CheckBeforeInitForMethod(string methodName)
|
||||
{
|
||||
if (!_isInit)
|
||||
throw new MethodAccessException($"It is forbidden to call {methodName}, before initialization {nameof(EcsPipeline)}");
|
||||
}
|
||||
private void CheckAfterInitForMethod(string methodName)
|
||||
{
|
||||
if (_isInit)
|
||||
throw new MethodAccessException($"It is forbidden to call {methodName}, after initialization {nameof(EcsPipeline)}");
|
||||
}
|
||||
private void CheckAfterDestroyForMethod(string methodName)
|
||||
{
|
||||
if (_isDestoryed)
|
||||
throw new MethodAccessException($"It is forbidden to call {methodName}, after destroying {nameof(EcsPipeline)}");
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
#region Builder
|
||||
public static Builder New() => new Builder();
|
||||
public class Builder
|
||||
@ -184,7 +160,7 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
public EcsPipeline Build()
|
||||
{
|
||||
Add(new DeleteEmptyEntitesSystem(), EcsConsts.POST_END_LAYER);
|
||||
Add(new EndFrameSystem(), EcsConsts.POST_END_LAYER);
|
||||
List<IEcsProcess> result = new List<IEcsProcess>(32);
|
||||
List<IEcsProcess> basicBlockList = _systems[_basicLayer];
|
||||
foreach (var item in _systems)
|
||||
|
||||
241
src/EcsRunner.cs
241
src/EcsRunner.cs
@ -5,7 +5,7 @@ using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.RegularExpressions;
|
||||
using static DCFApixels.DragonECS.EcsDebugUtility;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
@ -22,6 +22,31 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
public EcsRunnerFilterAttribute(object filter) : this(null, filter) { }
|
||||
}
|
||||
#if UNITY_2020_3_OR_NEWER
|
||||
[UnityEngine.Scripting.RequireDerived, UnityEngine.Scripting.Preserve]
|
||||
#endif
|
||||
[AttributeUsage(AttributeTargets.Interface, Inherited = false, AllowMultiple = false)]
|
||||
public sealed class BindWithEcsRunnerAttribute : Attribute
|
||||
{
|
||||
private static readonly Type _baseType = typeof(EcsRunner<>);
|
||||
public readonly Type runnerType;
|
||||
public BindWithEcsRunnerAttribute(Type runnerType)
|
||||
{
|
||||
if (runnerType == null)
|
||||
throw new ArgumentNullException();
|
||||
if (!CheckSubclass(runnerType))
|
||||
throw new ArgumentException();
|
||||
this.runnerType = runnerType;
|
||||
}
|
||||
private bool CheckSubclass(Type type)
|
||||
{
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == _baseType)
|
||||
return true;
|
||||
if (type.BaseType != null)
|
||||
return CheckSubclass(type.BaseType);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public interface IEcsProcess { }
|
||||
|
||||
@ -39,86 +64,6 @@ namespace DCFApixels.DragonECS
|
||||
void Destroy();
|
||||
}
|
||||
|
||||
internal static class EcsRunnerActivator
|
||||
{
|
||||
private static Dictionary<Type, Type> _runnerHandlerTypes; //interface base type/Runner handler type pairs;
|
||||
static EcsRunnerActivator()
|
||||
{
|
||||
List<Exception> delayedExceptions = new List<Exception>();
|
||||
Type runnerBaseType = typeof(EcsRunner<>);
|
||||
List<Type> runnerHandlerTypes = new List<Type>();
|
||||
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
runnerHandlerTypes.AddRange(assembly.GetTypes()
|
||||
.Where(type => type.BaseType != null && type.BaseType.IsGenericType && runnerBaseType == type.BaseType.GetGenericTypeDefinition()));
|
||||
}
|
||||
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
for (int i = 0; i < runnerHandlerTypes.Count; i++)
|
||||
{
|
||||
var e = CheckRunnerValide(runnerHandlerTypes[i]);
|
||||
if (e != null)
|
||||
{
|
||||
runnerHandlerTypes.RemoveAt(i--);
|
||||
delayedExceptions.Add(e);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
_runnerHandlerTypes = new Dictionary<Type, Type>();
|
||||
foreach (var item in runnerHandlerTypes)
|
||||
{
|
||||
Type interfaceType = item.BaseType.GenericTypeArguments[0];
|
||||
_runnerHandlerTypes.Add(interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType, item);
|
||||
}
|
||||
if (delayedExceptions.Count > 0)
|
||||
{
|
||||
foreach (var item in delayedExceptions) EcsDebug.Print(EcsConsts.DEBUG_ERROR_TAG, item.Message);
|
||||
throw delayedExceptions[0];
|
||||
}
|
||||
}
|
||||
|
||||
private static Exception CheckRunnerValide(Type type) //TODO доработать проверку валидности реалиазации ранера
|
||||
{
|
||||
Type baseType = type.BaseType;
|
||||
Type baseTypeArgument = baseType.GenericTypeArguments[0];
|
||||
|
||||
if (type.ReflectedType != null)
|
||||
{
|
||||
return new EcsRunnerImplementationException($"{GetGenericTypeFullName(type, 1)}.ReflectedType must be Null, but equal to {GetGenericTypeFullName(type.ReflectedType, 1)}.");
|
||||
}
|
||||
if (!baseTypeArgument.IsInterface)
|
||||
{
|
||||
return new EcsRunnerImplementationException($"Argument T of class EcsRunner<T>, can only be an inetrface. The {GetGenericTypeFullName(baseTypeArgument, 1)} type is not an interface.");
|
||||
}
|
||||
|
||||
var interfaces = type.GetInterfaces();
|
||||
|
||||
if (!interfaces.Any(o => o == baseTypeArgument))
|
||||
{
|
||||
return new EcsRunnerImplementationException($"Runner {GetGenericTypeFullName(type, 1)} does not implement interface {GetGenericTypeFullName(baseTypeArgument, 1)}.");
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void InitFor<TInterface>() where TInterface : IEcsProcess
|
||||
{
|
||||
Type interfaceType = typeof(TInterface);
|
||||
|
||||
if (!_runnerHandlerTypes.TryGetValue(interfaceType.IsGenericType ? interfaceType.GetGenericTypeDefinition() : interfaceType, out Type runnerType))
|
||||
{
|
||||
throw new EcsRunnerImplementationException($"There is no implementation of a runner for the {GetGenericTypeFullName<TInterface>(1)} interface.");
|
||||
}
|
||||
if (interfaceType.IsGenericType)
|
||||
{
|
||||
Type[] genericTypes = interfaceType.GetGenericArguments();
|
||||
runnerType = runnerType.MakeGenericType(genericTypes);
|
||||
}
|
||||
EcsRunner<TInterface>.Register(runnerType);
|
||||
}
|
||||
}
|
||||
#if UNITY_2020_3_OR_NEWER
|
||||
[UnityEngine.Scripting.RequireDerived, UnityEngine.Scripting.Preserve]
|
||||
#endif
|
||||
@ -185,9 +130,58 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Instantiate
|
||||
private static void CheckRunnerValide(Type type) //TODO доработать проверку валидности реалиазации ранера
|
||||
{
|
||||
Type targetInterface = typeof(TInterface);
|
||||
if (type.IsAbstract)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
Type GetRunnerBaseType(Type type)
|
||||
{
|
||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(EcsRunner<>))
|
||||
return type;
|
||||
if (type.BaseType != null)
|
||||
return GetRunnerBaseType(type.BaseType);
|
||||
return null;
|
||||
}
|
||||
Type baseType = GetRunnerBaseType(type);
|
||||
Type baseTypeArgument = baseType.GenericTypeArguments[0];
|
||||
|
||||
if (baseTypeArgument != targetInterface)
|
||||
{
|
||||
throw new Exception();
|
||||
}
|
||||
|
||||
if (!type.GetInterfaces().Any(o => o == targetInterface))
|
||||
{
|
||||
throw new EcsRunnerImplementationException($"Runner {GetGenericTypeFullName(type, 1)} does not implement interface {GetGenericTypeFullName(baseTypeArgument, 1)}.");
|
||||
}
|
||||
}
|
||||
|
||||
private static TInterface Instantiate(EcsPipeline source, TInterface[] targets, bool isHasFilter, object filter)
|
||||
{
|
||||
if (_subclass == null) EcsRunnerActivator.InitFor<TInterface>();
|
||||
if (_subclass == null)
|
||||
{
|
||||
Type interfaceType = typeof(TInterface);
|
||||
if (interfaceType.TryGetCustomAttribute(out BindWithEcsRunnerAttribute atr))
|
||||
{
|
||||
Type runnerType = atr.runnerType;
|
||||
if (interfaceType.IsGenericType)
|
||||
{
|
||||
Type[] genericTypes = interfaceType.GetGenericArguments();
|
||||
runnerType = runnerType.MakeGenericType(genericTypes);
|
||||
}
|
||||
CheckRunnerValide(runnerType);
|
||||
_subclass = runnerType;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new EcsFrameworkException("Процесс не связан с раннером, используйте атрибуут BindWithEcsRunner(Type runnerType)");
|
||||
}
|
||||
}
|
||||
|
||||
var instance = (EcsRunner<TInterface>)Activator.CreateInstance(_subclass);
|
||||
return (TInterface)(IEcsProcess)instance.Set(source, targets, isHasFilter, filter);
|
||||
}
|
||||
@ -245,7 +239,7 @@ namespace DCFApixels.DragonECS
|
||||
_filter = null;
|
||||
OnDestroy();
|
||||
}
|
||||
protected virtual void OnSetup() { }
|
||||
protected virtual void OnSetup() { } //rename to OnInitialize
|
||||
protected virtual void OnDestroy() { }
|
||||
}
|
||||
}
|
||||
@ -263,4 +257,85 @@ namespace DCFApixels.DragonECS
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
public static class EcsProcessUtility
|
||||
{
|
||||
private struct ProcessInterface
|
||||
{
|
||||
public Type interfaceType;
|
||||
public string processName;
|
||||
public ProcessInterface(Type interfaceType, string processName)
|
||||
{
|
||||
this.interfaceType = interfaceType;
|
||||
this.processName = processName;
|
||||
}
|
||||
}
|
||||
private static Dictionary<Type, ProcessInterface> _processes = new Dictionary<Type, ProcessInterface>();
|
||||
private static HashSet<Type> _systems = new HashSet<Type>();
|
||||
|
||||
static EcsProcessUtility()
|
||||
{
|
||||
Type processBasicInterface = typeof(IEcsProcess);
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
var types = assembly.GetTypes();
|
||||
foreach (var type in types)
|
||||
{
|
||||
if (type.GetInterface(nameof(IEcsProcess)) != null || type == processBasicInterface)
|
||||
{
|
||||
if (type.IsInterface)
|
||||
{
|
||||
string name = type.Name;
|
||||
if (name[0] == 'I' && name.Length > 1 && char.IsUpper(name[1]))
|
||||
name = name.Substring(1);
|
||||
name = Regex.Replace(name, @"\bEcs|Process\b", "");
|
||||
if (Regex.IsMatch(name, "`\\w{1,}$"))
|
||||
{
|
||||
var s = name.Split("`");
|
||||
name = s[0] + $"<{s[1]}>";
|
||||
}
|
||||
_processes.Add(type, new ProcessInterface(type, name));
|
||||
}
|
||||
else
|
||||
{
|
||||
_systems.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region Systems
|
||||
public static bool IsSystem(Type type) => _systems.Contains(type);
|
||||
public static bool IsEcsSystem(this Type type) => _systems.Contains(type);
|
||||
#endregion
|
||||
|
||||
#region Process
|
||||
public static bool IsProcessInterface(Type type)
|
||||
{
|
||||
if (type.IsGenericType) type = type.GetGenericTypeDefinition();
|
||||
return _processes.ContainsKey(type);
|
||||
}
|
||||
public static bool IsEcsProcessInterface(this Type type) => IsProcessInterface(type);
|
||||
|
||||
public static string GetProcessInterfaceName(Type type)
|
||||
{
|
||||
if (type.IsGenericType) type = type.GetGenericTypeDefinition();
|
||||
return _processes[type].processName;
|
||||
}
|
||||
public static bool TryGetProcessInterfaceName(Type type, out string name)
|
||||
{
|
||||
if (type.IsGenericType) type = type.GetGenericTypeDefinition();
|
||||
bool result = _processes.TryGetValue(type, out ProcessInterface data);
|
||||
name = data.processName;
|
||||
return result;
|
||||
}
|
||||
|
||||
public static IEnumerable<Type> GetEcsProcessInterfaces(this Type self)
|
||||
{
|
||||
return self.GetInterfaces().Where(o => o.IsEcsProcessInterface());
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
50
src/EcsWorld.cache.cs
Normal file
50
src/EcsWorld.cache.cs
Normal file
@ -0,0 +1,50 @@
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public abstract partial class EcsWorld
|
||||
{
|
||||
internal readonly struct PoolCache<T> : IEcsWorldComponent<PoolCache<T>>
|
||||
where T : IEcsPoolImplementation, new()
|
||||
{
|
||||
public readonly T instance;
|
||||
public PoolCache(T instance) => this.instance = instance;
|
||||
void IEcsWorldComponent<PoolCache<T>>.Init(ref PoolCache<T> component, EcsWorld world)
|
||||
{
|
||||
component = new PoolCache<T>(world.CreatePool<T>());
|
||||
}
|
||||
void IEcsWorldComponent<PoolCache<T>>.OnDestroy(ref PoolCache<T> component, EcsWorld world)
|
||||
{
|
||||
component = default;
|
||||
}
|
||||
}
|
||||
internal readonly struct AspectCache<T> : IEcsWorldComponent<AspectCache<T>>
|
||||
where T : EcsAspect
|
||||
{
|
||||
public readonly T instance;
|
||||
public AspectCache(T instance) => this.instance = instance;
|
||||
void IEcsWorldComponent<AspectCache<T>>.Init(ref AspectCache<T> component, EcsWorld world)
|
||||
{
|
||||
component = new AspectCache<T>(EcsAspect.Builder.Build<T>(world));
|
||||
}
|
||||
void IEcsWorldComponent<AspectCache<T>>.OnDestroy(ref AspectCache<T> component, EcsWorld world)
|
||||
{
|
||||
component = default;
|
||||
}
|
||||
}
|
||||
internal readonly struct ExcecutorCache<T> : IEcsWorldComponent<ExcecutorCache<T>>
|
||||
where T : EcsQueryExecutor, new()
|
||||
{
|
||||
public readonly T instance;
|
||||
public ExcecutorCache(T instance) => this.instance = instance;
|
||||
void IEcsWorldComponent<ExcecutorCache<T>>.Init(ref ExcecutorCache<T> component, EcsWorld world)
|
||||
{
|
||||
T instance = new T();
|
||||
instance.Initialize(world);
|
||||
component = new ExcecutorCache<T>(instance);
|
||||
}
|
||||
void IEcsWorldComponent<ExcecutorCache<T>>.OnDestroy(ref ExcecutorCache<T> component, EcsWorld world)
|
||||
{
|
||||
component = default;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/EcsWorld.cache.cs.meta
Normal file
11
src/EcsWorld.cache.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5cae52accc835594f95c8d972e40c57e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
538
src/EcsWorld.cs
538
src/EcsWorld.cs
@ -3,107 +3,14 @@ using DCFApixels.DragonECS.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 4)]
|
||||
public struct EcsWorldCmp<T> where T : struct
|
||||
{
|
||||
private int _worldID;
|
||||
public EcsWorldCmp(int worldID) => _worldID = worldID;
|
||||
public EcsWorld World => EcsWorld.GetWorld(_worldID);
|
||||
public ref T Value => ref EcsWorld.GetData<T>(_worldID);
|
||||
}
|
||||
public abstract partial class EcsWorld
|
||||
{
|
||||
private const short GEN_BITS = 0x7fff;
|
||||
private const short DEATH_GEN_BIT = short.MinValue;
|
||||
private const int DEL_ENT_BUFFER_SIZE_OFFSET = 2;
|
||||
|
||||
private static EcsWorld[] Worlds = new EcsWorld[4];
|
||||
private static IntDispenser _worldIdDispenser = new IntDispenser(0);
|
||||
|
||||
private static List<DataReleaser> _dataReleaseres = new List<DataReleaser>();
|
||||
|
||||
static EcsWorld()
|
||||
{
|
||||
Worlds[0] = new EcsNullWorld();
|
||||
}
|
||||
private static void ReleaseData(int worldID)
|
||||
{
|
||||
for (int i = 0, iMax = _dataReleaseres.Count; i < iMax; i++)
|
||||
_dataReleaseres[i].Release(worldID);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsWorld GetWorld(int worldID) => Worlds[worldID];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T GetData<T>(int worldID) => ref WorldComponentPool<T>.GetForWorld(worldID);
|
||||
|
||||
private abstract class DataReleaser
|
||||
{
|
||||
public abstract void Release(int worldID);
|
||||
}
|
||||
private static class WorldComponentPool<T>
|
||||
{
|
||||
private static T[] _items = new T[4];
|
||||
private static int[] _mapping = new int[4];
|
||||
private static int _count;
|
||||
private static int[] _recycledItems = new int[4];
|
||||
private static int _recycledItemsCount;
|
||||
private static IEcsWorldComponent<T> _interface = EcsWorldComponentHandler<T>.instance;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T Get(int itemIndex) => ref _items[itemIndex];
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T GetForWorld(int worldID) => ref _items[GetItemIndex(worldID)];
|
||||
public static int GetItemIndex(int worldID)
|
||||
{
|
||||
if (_mapping.Length < Worlds.Length)
|
||||
Array.Resize(ref _mapping, Worlds.Length);
|
||||
|
||||
ref int itemIndex = ref _mapping[worldID];
|
||||
if (itemIndex <= 0)
|
||||
{
|
||||
if (_recycledItemsCount > 0)
|
||||
{
|
||||
_count++;
|
||||
itemIndex = _recycledItems[--_recycledItemsCount];
|
||||
}
|
||||
else
|
||||
{
|
||||
itemIndex = ++_count;
|
||||
}
|
||||
_interface.Init(ref _items[itemIndex], Worlds[worldID]);
|
||||
_dataReleaseres.Add(new Releaser());
|
||||
}
|
||||
return itemIndex;
|
||||
}
|
||||
private static void Release(int worldID)
|
||||
{
|
||||
ref int itemIndex = ref _mapping[worldID];
|
||||
if (itemIndex != 0)
|
||||
{
|
||||
_interface.OnDestroy(ref _items[itemIndex], Worlds[worldID]);
|
||||
_recycledItems[_recycledItemsCount++] = itemIndex;
|
||||
}
|
||||
}
|
||||
private sealed class Releaser : DataReleaser
|
||||
{
|
||||
public sealed override void Release(int worldID)
|
||||
{
|
||||
WorldComponentPool<T>.Release(worldID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public abstract partial class EcsWorld
|
||||
{
|
||||
public readonly short id;
|
||||
|
||||
private Type _worldType;
|
||||
private int _worldTypeID;
|
||||
private bool _isDestroyed;
|
||||
|
||||
private IntDispenser _entityDispenser;
|
||||
private int _entitiesCount;
|
||||
@ -114,12 +21,9 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
private int[] _delEntBuffer;
|
||||
private int _delEntBufferCount;
|
||||
|
||||
internal IEcsPoolImplementation[] _pools;
|
||||
private EcsNullPool _nullPool = EcsNullPool.instance;
|
||||
|
||||
private EcsAspect[] _aspects;
|
||||
private EcsQueryExecutor[] _executors;
|
||||
private int _delEntBufferMinCount;
|
||||
private int _freeSpace;
|
||||
private bool _isEnableReleaseDelEntBuffer = true;
|
||||
|
||||
private List<WeakReference<EcsGroup>> _groups = new List<WeakReference<EcsGroup>>();
|
||||
private Stack<EcsGroup> _groupsPool = new Stack<EcsGroup>(64);
|
||||
@ -127,10 +31,18 @@ namespace DCFApixels.DragonECS
|
||||
private List<IEcsWorldEventListener> _listeners = new List<IEcsWorldEventListener>();
|
||||
private List<IEcsEntityEventListener> _entityListeners = new List<IEcsEntityEventListener>();
|
||||
|
||||
internal int[][] _entitiesComponentMasks;
|
||||
|
||||
private readonly PoolsMediator _poolsMediator;
|
||||
|
||||
#region Properties
|
||||
public int WorldTypeID => _worldTypeID;
|
||||
public bool IsDestroyed => _isDestroyed;
|
||||
public int Count => _entitiesCount;
|
||||
public int Capacity => _entitesCapacity; //_denseEntities.Length;
|
||||
|
||||
public int DelEntBufferCount => _delEntBufferCount;
|
||||
public bool IsEnableReleaseDelEntBuffer => _isEnableReleaseDelEntBuffer;
|
||||
|
||||
public EcsReadonlyGroup Entities => _allEntites.Readonly;
|
||||
public ReadOnlySpan<IEcsPoolImplementation> AllPools => _pools;// new ReadOnlySpan<IEcsPoolImplementation>(pools, 0, _poolsCount);
|
||||
#endregion
|
||||
@ -139,19 +51,18 @@ namespace DCFApixels.DragonECS
|
||||
public EcsWorld() : this(true) { }
|
||||
internal EcsWorld(bool isIndexable)
|
||||
{
|
||||
_poolsMediator = new PoolsMediator(this);
|
||||
|
||||
_entitesCapacity = 512;
|
||||
|
||||
if (isIndexable)
|
||||
{
|
||||
id = (short)_worldIdDispenser.GetFree();
|
||||
id = (short)_worldIdDispenser.UseFree();
|
||||
if (id >= Worlds.Length)
|
||||
Array.Resize(ref Worlds, Worlds.Length << 1);
|
||||
Worlds[id] = this;
|
||||
}
|
||||
|
||||
_worldType = this.GetType();
|
||||
_worldTypeID = WorldMetaStorage.GetWorldID(_worldType);
|
||||
|
||||
_entityDispenser = new IntDispenser(0);
|
||||
_pools = new IEcsPoolImplementation[512];
|
||||
ArrayUtility.Fill(_pools, _nullPool);
|
||||
@ -161,12 +72,15 @@ namespace DCFApixels.DragonECS
|
||||
|
||||
ArrayUtility.Fill(_gens, DEATH_GEN_BIT);
|
||||
_delEntBufferCount = 0;
|
||||
_delEntBuffer = new int[_entitesCapacity >> DEL_ENT_BUFFER_SIZE_OFFSET];
|
||||
//_delEntBuffer = new int[_entitesCapacity >> DEL_ENT_BUFFER_SIZE_OFFSET];
|
||||
_delEntBuffer = new int[_entitesCapacity];
|
||||
_entitiesComponentMasks = new int[_entitesCapacity][];
|
||||
for (int i = 0; i < _entitesCapacity; i++)
|
||||
_entitiesComponentMasks[i] = new int[_pools.Length / 32 + 1];
|
||||
|
||||
_delEntBufferMinCount = Math.Max(_delEntBuffer.Length >> DEL_ENT_BUFFER_SIZE_OFFSET, DEL_ENT_BUFFER_MIN_SIZE);
|
||||
|
||||
_allEntites = GetFreeGroup();
|
||||
|
||||
_aspects = new EcsAspect[128];
|
||||
_executors = new EcsQueryExecutor[128];
|
||||
}
|
||||
public void Destroy()
|
||||
{
|
||||
@ -174,131 +88,149 @@ namespace DCFApixels.DragonECS
|
||||
_gens = null;
|
||||
_pools = null;
|
||||
_nullPool = null;
|
||||
_aspects = null;
|
||||
_executors = null;
|
||||
Worlds[id] = null;
|
||||
ReleaseData(id);
|
||||
_worldIdDispenser.Release(id);
|
||||
_isDestroyed = true;
|
||||
_poolIds = null;
|
||||
_componentIds = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region ComponentInfo
|
||||
public int GetComponentID<T>() => WorldMetaStorage.GetComponentID<T>(_worldTypeID);
|
||||
public int GetComponentID(Type type) => WorldMetaStorage.GetComponentID(type, _worldTypeID);
|
||||
public Type GetComponentType(int componentID) => WorldMetaStorage.GetComponentType(_worldTypeID, componentID);
|
||||
public bool IsComponentTypeDeclared<T>() => IsComponentTypeDeclared(typeof(T));
|
||||
public bool IsComponentTypeDeclared(Type type) => WorldMetaStorage.IsComponentTypeDeclared(_worldTypeID, type);
|
||||
#endregion
|
||||
|
||||
#region Getters
|
||||
#if UNITY_2020_3_OR_NEWER
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
#endif
|
||||
public TPool GetPool<TPool>() where TPool : IEcsPoolImplementation, new()
|
||||
{
|
||||
int index = WorldMetaStorage.GetPoolID<TPool>(_worldTypeID);
|
||||
if (index >= _pools.Length)
|
||||
{
|
||||
int oldCapacity = _pools.Length;
|
||||
Array.Resize(ref _pools, _pools.Length << 1);
|
||||
ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length);
|
||||
}
|
||||
if (_pools[index] == _nullPool)
|
||||
{
|
||||
var pool = new TPool();
|
||||
_pools[index] = pool;
|
||||
pool.OnInit(this, index);
|
||||
}
|
||||
return (TPool)_pools[index];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TAspect GetAspect<TAspect>() where TAspect : EcsAspect
|
||||
{
|
||||
int index = WorldMetaStorage.GetAspectID<TAspect>(_worldTypeID);
|
||||
if (index >= _aspects.Length)
|
||||
Array.Resize(ref _aspects, _aspects.Length << 1);
|
||||
if (_aspects[index] == null)
|
||||
_aspects[index] = EcsAspect.Builder.Build<TAspect>(this);
|
||||
return (TAspect)_aspects[index];
|
||||
return Get<AspectCache<TAspect>>().instance;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TExecutor GetExecutor<TExecutor>() where TExecutor : EcsQueryExecutor, new()
|
||||
{
|
||||
int index = WorldMetaStorage.GetExecutorID<TExecutor>(_worldTypeID);
|
||||
if (index >= _executors.Length)
|
||||
Array.Resize(ref _executors, _executors.Length << 1);
|
||||
var result = _executors[index];
|
||||
if (result == null)
|
||||
{
|
||||
result = new TExecutor();
|
||||
_executors[index] = result;
|
||||
result.Initialize(this);
|
||||
}
|
||||
return (TExecutor)result;
|
||||
return Get<ExcecutorCache<TExecutor>>().instance;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T Get<T>() where T : struct
|
||||
{
|
||||
return ref WorldComponentPool<T>.GetForWorld(id);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref T GetUnchecked<T>() where T : struct
|
||||
{
|
||||
return ref WorldComponentPool<T>.GetForWorldUnchecked(id);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T Get<T>(int worldID) where T : struct
|
||||
{
|
||||
return ref WorldComponentPool<T>.GetForWorld(worldID);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T GetUnchecked<T>(int worldID) where T : struct
|
||||
{
|
||||
return ref WorldComponentPool<T>.GetForWorldUnchecked(worldID);
|
||||
}
|
||||
public ref T Get<T>() where T : struct => ref WorldComponentPool<T>.GetForWorld(id);
|
||||
#endregion
|
||||
|
||||
#region Where Query
|
||||
public EcsReadonlyGroup WhereFor<TAspect>(EcsReadonlyGroup sourceGroup, out TAspect aspect) where TAspect : EcsAspect
|
||||
public EcsReadonlyGroup WhereToGroupFor<TAspect>(EcsSpan span, out TAspect aspect) where TAspect : EcsAspect
|
||||
{
|
||||
if(_isEnableReleaseDelEntBuffer)
|
||||
{
|
||||
ReleaseDelEntityBufferAll();
|
||||
}
|
||||
var executor = GetExecutor<EcsWhereExecutor<TAspect>>();
|
||||
aspect = executor.Aspect;
|
||||
return executor.ExecuteFor(sourceGroup);
|
||||
return executor.ExecuteFor(span);
|
||||
}
|
||||
public EcsReadonlyGroup WhereFor<TAspect>(EcsReadonlyGroup sourceGroup) where TAspect : EcsAspect
|
||||
public EcsReadonlyGroup WhereToGroupFor<TAspect>(EcsSpan span) where TAspect : EcsAspect
|
||||
{
|
||||
return GetExecutor<EcsWhereExecutor<TAspect>>().ExecuteFor(sourceGroup);
|
||||
if (_isEnableReleaseDelEntBuffer)
|
||||
{
|
||||
ReleaseDelEntityBufferAll();
|
||||
}
|
||||
return GetExecutor<EcsWhereExecutor<TAspect>>().ExecuteFor(span);
|
||||
}
|
||||
public EcsReadonlyGroup Where<TAspect>(out TAspect aspect) where TAspect : EcsAspect
|
||||
public EcsReadonlyGroup WhereToGroup<TAspect>(out TAspect aspect) where TAspect : EcsAspect
|
||||
{
|
||||
if (_isEnableReleaseDelEntBuffer)
|
||||
{
|
||||
ReleaseDelEntityBufferAll();
|
||||
}
|
||||
var executor = GetExecutor<EcsWhereExecutor<TAspect>>();
|
||||
aspect = executor.Aspect;
|
||||
return executor.Execute();
|
||||
}
|
||||
public EcsReadonlyGroup Where<TAspect>() where TAspect : EcsAspect
|
||||
public EcsReadonlyGroup WhereToGroup<TAspect>() where TAspect : EcsAspect
|
||||
{
|
||||
if (_isEnableReleaseDelEntBuffer)
|
||||
{
|
||||
ReleaseDelEntityBufferAll();
|
||||
}
|
||||
return GetExecutor<EcsWhereExecutor<TAspect>>().Execute();
|
||||
}
|
||||
|
||||
public EcsSpan WhereFor<TAspect>(EcsSpan span, out TAspect aspect) where TAspect : EcsAspect
|
||||
{
|
||||
if (_isEnableReleaseDelEntBuffer)
|
||||
{
|
||||
ReleaseDelEntityBufferAll();
|
||||
}
|
||||
var executor = GetExecutor<EcsWhereSpanExecutor<TAspect>>();
|
||||
aspect = executor.Aspect;
|
||||
return executor.ExecuteFor(span);
|
||||
}
|
||||
public EcsSpan WhereFor<TAspect>(EcsSpan span) where TAspect : EcsAspect
|
||||
{
|
||||
if (_isEnableReleaseDelEntBuffer)
|
||||
{
|
||||
ReleaseDelEntityBufferAll();
|
||||
}
|
||||
return GetExecutor<EcsWhereSpanExecutor<TAspect>>().ExecuteFor(span);
|
||||
}
|
||||
public EcsSpan Where<TAspect>(out TAspect aspect) where TAspect : EcsAspect
|
||||
{
|
||||
if (_isEnableReleaseDelEntBuffer)
|
||||
{
|
||||
ReleaseDelEntityBufferAll();
|
||||
}
|
||||
var executor = GetExecutor<EcsWhereSpanExecutor<TAspect>>();
|
||||
aspect = executor.Aspect;
|
||||
return executor.Execute();
|
||||
}
|
||||
public EcsSpan Where<TAspect>() where TAspect : EcsAspect
|
||||
{
|
||||
if (_isEnableReleaseDelEntBuffer)
|
||||
{
|
||||
ReleaseDelEntityBufferAll();
|
||||
}
|
||||
return GetExecutor<EcsWhereSpanExecutor<TAspect>>().Execute();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Entity
|
||||
public int NewEmptyEntity()
|
||||
public int NewEntity()
|
||||
{
|
||||
//if (_isEnableReleaseDelEntBuffer && _freeSpace <= 1 && _delEntBufferCount > _delEntBufferMinCount)
|
||||
// ReleaseDelEntityBufferAll();
|
||||
|
||||
int entityID = _entityDispenser.GetFree();
|
||||
_freeSpace--;
|
||||
_entitiesCount++;
|
||||
|
||||
if (_gens.Length <= entityID)
|
||||
{
|
||||
Array.Resize(ref _gens, _gens.Length << 1);
|
||||
Array.Resize(ref _componentCounts, _gens.Length);
|
||||
ArrayUtility.Fill(_gens, DEATH_GEN_BIT, _entitesCapacity);
|
||||
_entitesCapacity = _gens.Length;
|
||||
Upsize();
|
||||
|
||||
for (int i = 0; i < _groups.Count; i++)
|
||||
{
|
||||
if (_groups[i].TryGetTarget(out EcsGroup group))
|
||||
{
|
||||
group.OnWorldResize(_gens.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
int last = _groups.Count - 1;
|
||||
_groups[i--] = _groups[last];
|
||||
_groups.RemoveAt(last);
|
||||
}
|
||||
}
|
||||
foreach (var item in _pools)
|
||||
item.OnWorldResize(_gens.Length);
|
||||
|
||||
_listeners.InvokeOnWorldResize(_gens.Length);
|
||||
}
|
||||
_gens[entityID] &= GEN_BITS;
|
||||
_allEntites.Add(entityID);
|
||||
_entityListeners.InvokeOnNewEntity(entityID);
|
||||
return entityID;
|
||||
}
|
||||
public entlong NewEmptyEntityLong()
|
||||
public entlong NewEntityLong()
|
||||
{
|
||||
int e = NewEmptyEntity();
|
||||
int e = NewEntity();
|
||||
return GetEntityLong(e);
|
||||
}
|
||||
public void DelEntity(int entityID)
|
||||
@ -308,12 +240,15 @@ namespace DCFApixels.DragonECS
|
||||
_gens[entityID] |= DEATH_GEN_BIT;
|
||||
_entitiesCount--;
|
||||
_entityListeners.InvokeOnDelEntity(entityID);
|
||||
|
||||
if (_delEntBufferCount >= _delEntBuffer.Length)
|
||||
ReleaseDelEntityBuffer();
|
||||
//if (_delEntBufferCount >= _delEntBuffer.Length)
|
||||
// ReleaseDelEntityBufferAll();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public entlong GetEntityLong(int entityID) => new entlong(entityID, _gens[entityID], id); //TODO придумать получше имя метода
|
||||
public unsafe entlong GetEntityLong(int entityID)
|
||||
{
|
||||
long x = (long)id << 48 | (long)_gens[entityID] << 32 | (long)entityID;
|
||||
return *(entlong*)&x;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsAlive(int entityID, short gen) => _gens[entityID] == gen;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
@ -326,36 +261,30 @@ namespace DCFApixels.DragonECS
|
||||
public bool IsMatchesMask(EcsMask mask, int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (mask._worldTypeID != _worldTypeID)
|
||||
if (mask.worldID != id)
|
||||
throw new EcsFrameworkException("The types of the target world of the mask and this world are different.");
|
||||
#endif
|
||||
for (int i = 0, iMax = mask._inc.Length; i < iMax; i++)
|
||||
for (int i = 0, iMax = mask.incChunckMasks.Length; i < iMax; i++)
|
||||
{
|
||||
if (!_pools[mask._inc[i]].Has(entityID))
|
||||
if (!_pools[mask.inc[i]].Has(entityID))
|
||||
return false;
|
||||
}
|
||||
for (int i = 0, iMax = mask._exc.Length; i < iMax; i++)
|
||||
for (int i = 0, iMax = mask.excChunckMasks.Length; i < iMax; i++)
|
||||
{
|
||||
if (_pools[mask._exc[i]].Has(entityID))
|
||||
if (_pools[mask.exc[i]].Has(entityID))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public void ReleaseDelEntityBuffer()
|
||||
{
|
||||
ReadOnlySpan<int> buffser = new ReadOnlySpan<int>(_delEntBuffer, 0, _delEntBufferCount);
|
||||
foreach (var pool in _pools)
|
||||
pool.OnReleaseDelEntityBuffer(buffser);
|
||||
_listeners.InvokeOnReleaseDelEntityBuffer(buffser);
|
||||
for (int i = 0; i < _delEntBufferCount; i++)
|
||||
_entityDispenser.Release(_delEntBuffer[i]);
|
||||
_delEntBufferCount = 0;
|
||||
}
|
||||
|
||||
public void DeleteEmptyEntites()
|
||||
{
|
||||
foreach (var e in _allEntites)
|
||||
{
|
||||
if (_componentCounts[e] <= 0) DelEntity(e);
|
||||
if (_componentCounts[e] <= 0)
|
||||
{
|
||||
DelEntity(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,15 +293,30 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
foreach (var pool in _pools)
|
||||
{
|
||||
if (pool.Has(fromEntityID)) pool.Copy(fromEntityID, toEntityID);
|
||||
if (pool.Has(fromEntityID))
|
||||
pool.Copy(fromEntityID, toEntityID);
|
||||
}
|
||||
}
|
||||
public void CopyEntity(int fromEntityID, EcsWorld toWorld, int toEntityID)
|
||||
{
|
||||
foreach (var pool in _pools)
|
||||
{
|
||||
if (pool.Has(fromEntityID))
|
||||
pool.Copy(fromEntityID, toWorld, toEntityID);
|
||||
}
|
||||
}
|
||||
public int CloneEntity(int fromEntityID)
|
||||
{
|
||||
int newEntity = NewEmptyEntity();
|
||||
int newEntity = NewEntity();
|
||||
CopyEntity(fromEntityID, newEntity);
|
||||
return newEntity;
|
||||
}
|
||||
public int CloneEntity(int fromEntityID, EcsWorld toWorld)
|
||||
{
|
||||
int newEntity = NewEntity();
|
||||
CopyEntity(fromEntityID, toWorld, newEntity);
|
||||
return newEntity;
|
||||
}
|
||||
public void CloneEntity(int fromEntityID, int toEntityID)
|
||||
{
|
||||
CopyEntity(fromEntityID, toEntityID);
|
||||
@ -382,24 +326,128 @@ namespace DCFApixels.DragonECS
|
||||
pool.Del(toEntityID);
|
||||
}
|
||||
}
|
||||
//public void CloneEntity(int fromEntityID, EcsWorld toWorld, int toEntityID)
|
||||
#endregion
|
||||
|
||||
#region Components Increment
|
||||
#region Pools mediation
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void IncrementEntityComponentCount(int entityID) => _componentCounts[entityID]++;
|
||||
private void RegisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit)
|
||||
{
|
||||
_componentCounts[entityID]++;
|
||||
_entitiesComponentMasks[entityID][maskBit.chankIndex] |= maskBit.mask;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal void DecrementEntityComponentCount(int entityID)
|
||||
private void UnregisterEntityComponent(int entityID, int componentTypeID, EcsMaskBit maskBit)
|
||||
{
|
||||
var count = --_componentCounts[entityID];
|
||||
if (count == 0 && _allEntites.Has(entityID)) DelEntity(entityID);
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (count < 0) throw new EcsFrameworkException("нарушен баланс инкремента/декремента компонентов");
|
||||
_entitiesComponentMasks[entityID][maskBit.chankIndex] &= ~maskBit.mask;
|
||||
|
||||
if (count == 0 && _allEntites.Has(entityID))
|
||||
DelEntity(entityID);
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (count < 0) Throw.World_InvalidIncrementComponentsBalance();
|
||||
#endif
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool HasEntityComponent(int entityID, EcsMaskBit maskBit)
|
||||
{
|
||||
return (_entitiesComponentMasks[entityID][maskBit.chankIndex] & maskBit.mask) != maskBit.mask;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region DelEntBuffer
|
||||
public AutoReleaseDelEntBufferLonkUnloker DisableAutoReleaseDelEntBuffer()
|
||||
{
|
||||
_isEnableReleaseDelEntBuffer = false;
|
||||
return new AutoReleaseDelEntBufferLonkUnloker(this);
|
||||
}
|
||||
public void EnableAutoReleaseDelEntBuffer()
|
||||
{
|
||||
_isEnableReleaseDelEntBuffer = true;
|
||||
}
|
||||
public readonly struct AutoReleaseDelEntBufferLonkUnloker : IDisposable
|
||||
{
|
||||
private readonly EcsWorld _source;
|
||||
public AutoReleaseDelEntBufferLonkUnloker(EcsWorld source)
|
||||
{
|
||||
_source = source;
|
||||
}
|
||||
public void Dispose()
|
||||
{
|
||||
_source.EnableAutoReleaseDelEntBuffer();
|
||||
}
|
||||
}
|
||||
public void ReleaseDelEntityBufferAll()
|
||||
{
|
||||
ReleaseDelEntityBuffer(-1);
|
||||
}
|
||||
public void ReleaseDelEntityBuffer(int count)
|
||||
{
|
||||
if (_delEntBufferCount <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (count < 0)
|
||||
{
|
||||
count = _delEntBufferCount;
|
||||
}
|
||||
else if (count > _delEntBufferCount)
|
||||
{
|
||||
count = _delEntBufferCount;
|
||||
}
|
||||
_delEntBufferCount -= count;
|
||||
ReadOnlySpan<int> buffser = new ReadOnlySpan<int>(_delEntBuffer, _delEntBufferCount, count);
|
||||
for (int i = 0; i < _poolsCount; i++)
|
||||
{
|
||||
_pools[i].OnReleaseDelEntityBuffer(buffser);
|
||||
}
|
||||
_listeners.InvokeOnReleaseDelEntityBuffer(buffser);
|
||||
for (int i = 0; i < buffser.Length; i++)
|
||||
{
|
||||
_entityDispenser.Release(buffser[i]);
|
||||
}
|
||||
_freeSpace += count;// _entitesCapacity - _entitiesCount;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Upsize
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
private void Upsize()
|
||||
{
|
||||
Array.Resize(ref _gens, _gens.Length << 1);
|
||||
Array.Resize(ref _componentCounts, _gens.Length);
|
||||
Array.Resize(ref _delEntBuffer, _gens.Length);
|
||||
Array.Resize(ref _entitiesComponentMasks, _gens.Length);
|
||||
for (int i = _entitesCapacity; i < _gens.Length; i++)
|
||||
_entitiesComponentMasks[i] = new int[_pools.Length / 32 + 1];
|
||||
|
||||
_delEntBufferMinCount = Math.Max(_delEntBuffer.Length >> DEL_ENT_BUFFER_SIZE_OFFSET, DEL_ENT_BUFFER_MIN_SIZE);
|
||||
ArrayUtility.Fill(_gens, DEATH_GEN_BIT, _entitesCapacity);
|
||||
_entitesCapacity = _gens.Length;
|
||||
|
||||
for (int i = 0; i < _groups.Count; i++)
|
||||
{
|
||||
if (_groups[i].TryGetTarget(out EcsGroup group))
|
||||
{
|
||||
group.OnWorldResize(_gens.Length);
|
||||
}
|
||||
else
|
||||
{
|
||||
int last = _groups.Count - 1;
|
||||
_groups[i--] = _groups[last];
|
||||
_groups.RemoveAt(last);
|
||||
}
|
||||
}
|
||||
foreach (var item in _pools)
|
||||
item.OnWorldResize(_gens.Length);
|
||||
|
||||
_listeners.InvokeOnWorldResize(_gens.Length);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Groups Pool
|
||||
internal void RegisterGroup(EcsGroup group)
|
||||
{
|
||||
@ -414,8 +462,7 @@ namespace DCFApixels.DragonECS
|
||||
internal void ReleaseGroup(EcsGroup group)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || !DISABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (group.World != this)
|
||||
throw new ArgumentException("groupFilter.WorldIndex != this");
|
||||
if (group.World != this) Throw.World_GroupDoesNotBelongWorld();
|
||||
#endif
|
||||
group._isReleased = true;
|
||||
group.Clear();
|
||||
@ -447,18 +494,69 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
list.Clear();
|
||||
var itemsCount = GetComponentsCount(entityID);
|
||||
|
||||
for (var i = 0; i < _pools.Length; i++)
|
||||
for (var i = 0; i < _poolsCount; i++)
|
||||
{
|
||||
if (_pools[i].Has(entityID))
|
||||
{
|
||||
itemsCount--;
|
||||
list.Add(_pools[i].GetRaw(entityID));
|
||||
if (itemsCount <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
public void GetComponentTypes(int entityID, HashSet<Type> typeSet)
|
||||
{
|
||||
typeSet.Clear();
|
||||
var itemsCount = GetComponentsCount(entityID);
|
||||
for (var i = 0; i < _poolsCount; i++)
|
||||
{
|
||||
if (_pools[i].Has(entityID))
|
||||
{
|
||||
itemsCount--;
|
||||
typeSet.Add(_pools[i].ComponentType);
|
||||
if (itemsCount <= 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region PoolsMediator
|
||||
public readonly struct PoolsMediator
|
||||
{
|
||||
private readonly EcsWorld _world;
|
||||
internal PoolsMediator(EcsWorld world)
|
||||
{
|
||||
if (world == null)
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
if (world._poolsMediator._world != null)
|
||||
{
|
||||
throw new MethodAccessException();
|
||||
}
|
||||
_world = world;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void RegisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit)
|
||||
{
|
||||
_world.RegisterEntityComponent(entityID, componentTypeID, maskBit);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void UnregisterComponent(int entityID, int componentTypeID, EcsMaskBit maskBit)
|
||||
{
|
||||
_world.UnregisterEntityComponent(entityID, componentTypeID, maskBit);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool HasComponent(int entityID, EcsMaskBit maskBit)
|
||||
{
|
||||
return _world.HasEntityComponent(entityID, maskBit);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
internal sealed class EcsNullWorld : EcsWorld { }
|
||||
|
||||
#region Callbacks Interface
|
||||
public interface IEcsWorldEventListener
|
||||
{
|
||||
|
||||
111
src/EcsWorld.pools.cs
Normal file
111
src/EcsWorld.pools.cs
Normal file
@ -0,0 +1,111 @@
|
||||
using DCFApixels.DragonECS.Internal;
|
||||
using DCFApixels.DragonECS.Utils;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public abstract partial class EcsWorld
|
||||
{
|
||||
private SparseArray<int> _poolIds = new SparseArray<int>();
|
||||
private SparseArray<int> _componentIds = new SparseArray<int>();
|
||||
private int _poolsCount;
|
||||
internal IEcsPoolImplementation[] _pools;
|
||||
private EcsNullPool _nullPool = EcsNullPool.instance;
|
||||
|
||||
#region ComponentInfo
|
||||
public int GetComponentID<T>() => DeclareComponentType(EcsTypeCode.Get<T>());
|
||||
public int GetComponentID(Type type) => DeclareComponentType(EcsTypeCode.Get(type));
|
||||
public bool IsComponentTypeDeclared<T>() => _componentIds.Contains(EcsTypeCode.Get<T>());
|
||||
public bool IsComponentTypeDeclared(Type type) => _componentIds.Contains(EcsTypeCode.Get(type));
|
||||
public Type GetComponentType(int componentID) => _pools[componentID].ComponentType;
|
||||
#endregion
|
||||
|
||||
#region Getters
|
||||
|
||||
#if UNITY_2020_3_OR_NEWER
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
#endif
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TPool GetPool<TPool>() where TPool : IEcsPoolImplementation, new()
|
||||
{
|
||||
return Get<PoolCache<TPool>>().instance;
|
||||
}
|
||||
#if UNITY_2020_3_OR_NEWER
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
#endif
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TPool GetPoolUnchecked<TPool>() where TPool : IEcsPoolImplementation, new()
|
||||
{
|
||||
return GetUnchecked<PoolCache<TPool>>().instance;
|
||||
}
|
||||
#if UNITY_2020_3_OR_NEWER
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
#endif
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TPool GetPool<TPool>(int worldID) where TPool : IEcsPoolImplementation, new()
|
||||
{
|
||||
return Get<PoolCache<TPool>>(worldID).instance;
|
||||
}
|
||||
#if UNITY_2020_3_OR_NEWER
|
||||
[UnityEngine.Scripting.Preserve]
|
||||
#endif
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static TPool UncheckedGetPool<TPool>(int worldID) where TPool : IEcsPoolImplementation, new()
|
||||
{
|
||||
return GetUnchecked<PoolCache<TPool>>(worldID).instance;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Declare/Create
|
||||
private int DeclareComponentType(int typeCode)
|
||||
{
|
||||
if (!_componentIds.TryGetValue(typeCode, out int componentId))
|
||||
{
|
||||
componentId = _poolsCount++;
|
||||
_componentIds.Add(typeCode, componentId);
|
||||
}
|
||||
return componentId;
|
||||
}
|
||||
private TPool CreatePool<TPool>() where TPool : IEcsPoolImplementation, new()
|
||||
{
|
||||
int poolTypeCode = EcsTypeCode.Get<TPool>();
|
||||
if (_poolIds.Contains(poolTypeCode))
|
||||
throw new EcsFrameworkException("The pool has already been created.");
|
||||
|
||||
Type componentType = typeof(TPool).GetInterfaces().First(o => o.IsGenericType && o.GetGenericTypeDefinition() == typeof(IEcsPoolImplementation<>)).GetGenericArguments()[0];
|
||||
int componentTypeCode = EcsTypeCode.Get(componentType);
|
||||
|
||||
if (_componentIds.TryGetValue(componentTypeCode, out int componentTypeID))
|
||||
{
|
||||
_poolIds[poolTypeCode] = componentTypeID;
|
||||
}
|
||||
else
|
||||
{
|
||||
componentTypeID = _poolsCount++;
|
||||
_poolIds[poolTypeCode] = componentTypeID;
|
||||
_componentIds[componentTypeCode] = componentTypeID;
|
||||
}
|
||||
|
||||
if (_poolsCount >= _pools.Length)
|
||||
{
|
||||
int oldCapacity = _pools.Length;
|
||||
Array.Resize(ref _pools, _pools.Length << 1);
|
||||
ArrayUtility.Fill(_pools, _nullPool, oldCapacity, oldCapacity - _pools.Length);
|
||||
|
||||
for (int i = 0; i < _entitesCapacity; i++)
|
||||
Array.Resize(ref _entitiesComponentMasks[i], _pools.Length / 32 + 1);
|
||||
}
|
||||
|
||||
if (_pools[componentTypeID] == _nullPool)
|
||||
{
|
||||
var pool = new TPool();
|
||||
_pools[componentTypeID] = pool;
|
||||
pool.OnInit(this, _poolsMediator, componentTypeID);
|
||||
}
|
||||
return (TPool)_pools[componentTypeID];
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
11
src/EcsWorld.pools.cs.meta
Normal file
11
src/EcsWorld.pools.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2832746be4142a847b513bab5c276ba7
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
130
src/EcsWorld.static.cs
Normal file
130
src/EcsWorld.static.cs
Normal file
@ -0,0 +1,130 @@
|
||||
using DCFApixels.DragonECS.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4, Size = 4)]
|
||||
public struct EcsWorldCmp<T> where T : struct
|
||||
{
|
||||
private int _worldID;
|
||||
public EcsWorldCmp(int worldID) => _worldID = worldID;
|
||||
public EcsWorld World => EcsWorld.GetWorld(_worldID);
|
||||
public ref T Value => ref EcsWorld.GetData<T>(_worldID);
|
||||
}
|
||||
public abstract partial class EcsWorld
|
||||
{
|
||||
private const short GEN_BITS = 0x7fff;
|
||||
private const short DEATH_GEN_BIT = short.MinValue;
|
||||
private const int DEL_ENT_BUFFER_SIZE_OFFSET = 5;
|
||||
private const int DEL_ENT_BUFFER_MIN_SIZE = 64;
|
||||
|
||||
private static EcsWorld[] Worlds = new EcsWorld[4];
|
||||
private static IdDispenser _worldIdDispenser = new IdDispenser(0);
|
||||
|
||||
private static List<DataReleaser> _dataReleaseres = new List<DataReleaser>();
|
||||
|
||||
static EcsWorld()
|
||||
{
|
||||
Worlds[0] = new EcsNullWorld();
|
||||
}
|
||||
private static void ReleaseData(int worldID)
|
||||
{
|
||||
for (int i = 0, iMax = _dataReleaseres.Count; i < iMax; i++)
|
||||
_dataReleaseres[i].Release(worldID);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsWorld GetWorld(int worldID) => Worlds[worldID];
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T GetData<T>(int worldID) => ref WorldComponentPool<T>.GetForWorld(worldID);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T UncheckedGetData<T>(int worldID) => ref WorldComponentPool<T>.GetForWorldUnchecked(worldID);
|
||||
|
||||
private abstract class DataReleaser
|
||||
{
|
||||
public abstract void Release(int worldID);
|
||||
}
|
||||
private static class WorldComponentPool<T>
|
||||
{
|
||||
private static T[] _items = new T[4];
|
||||
private static short[] _mapping = new short[4];
|
||||
private static short _count;
|
||||
private static short[] _recycledItems = new short[4];
|
||||
private static short _recycledItemsCount;
|
||||
private static IEcsWorldComponent<T> _interface = EcsWorldComponentHandler<T>.instance;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T Get(int itemIndex)
|
||||
{
|
||||
return ref _items[itemIndex];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T GetForWorld(int worldID)
|
||||
{
|
||||
int index = GetItemIndex(worldID);
|
||||
return ref _items[index];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ref T GetForWorldUnchecked(int worldID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG)
|
||||
if (_mapping[worldID] <= 0)
|
||||
throw new Exception();
|
||||
#endif
|
||||
return ref _items[_mapping[worldID]];
|
||||
}
|
||||
public static int GetItemIndex(int worldID)
|
||||
{
|
||||
if (_mapping.Length < Worlds.Length)
|
||||
{
|
||||
Array.Resize(ref _mapping, Worlds.Length);
|
||||
|
||||
}
|
||||
|
||||
ref short itemIndex = ref _mapping[worldID];
|
||||
if (itemIndex <= 0)
|
||||
{
|
||||
if (_recycledItemsCount > 0)
|
||||
{
|
||||
_count++;
|
||||
itemIndex = _recycledItems[--_recycledItemsCount];
|
||||
}
|
||||
else
|
||||
{
|
||||
itemIndex = ++_count;
|
||||
}
|
||||
if(_items.Length <= itemIndex)
|
||||
{
|
||||
Array.Resize(ref _items, _items.Length << 1);
|
||||
}
|
||||
_interface.Init(ref _items[itemIndex], Worlds[worldID]);
|
||||
_dataReleaseres.Add(new Releaser());
|
||||
}
|
||||
return itemIndex;
|
||||
}
|
||||
private static void Release(int worldID)
|
||||
{
|
||||
ref short itemIndex = ref _mapping[worldID];
|
||||
if (itemIndex != 0)
|
||||
{
|
||||
_interface.OnDestroy(ref _items[itemIndex], Worlds[worldID]);
|
||||
_recycledItems[_recycledItemsCount++] = itemIndex;
|
||||
}
|
||||
}
|
||||
private sealed class Releaser : DataReleaser
|
||||
{
|
||||
public sealed override void Release(int worldID)
|
||||
{
|
||||
WorldComponentPool<T>.Release(worldID);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
internal sealed class EcsNullWorld : EcsWorld
|
||||
{
|
||||
internal EcsNullWorld() : base(false) { }
|
||||
}
|
||||
}
|
||||
11
src/EcsWorld.static.cs.meta
Normal file
11
src/EcsWorld.static.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30c8fd4d7c5aeae4486e16024b4f50cc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,16 +1,32 @@
|
||||
namespace DCFApixels.DragonECS
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public abstract class EcsQueryExecutor
|
||||
{
|
||||
private EcsWorld _world;
|
||||
public EcsWorld World => _world;
|
||||
private EcsWorld _source;
|
||||
public int WorldID
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source.id;
|
||||
}
|
||||
public EcsWorld World
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _source;
|
||||
}
|
||||
public abstract long Version { get; }
|
||||
internal void Initialize(EcsWorld world)
|
||||
{
|
||||
_world = world;
|
||||
_source = world;
|
||||
OnInitialize();
|
||||
}
|
||||
internal void Destroy() => OnDestroy();
|
||||
internal void Destroy()
|
||||
{
|
||||
OnDestroy();
|
||||
_source = null;
|
||||
}
|
||||
protected abstract void OnInitialize();
|
||||
protected abstract void OnDestroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,19 +1,29 @@
|
||||
namespace DCFApixels.DragonECS
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public sealed class EcsWhereExecutor<TAspect> : EcsQueryExecutor where TAspect : EcsAspect
|
||||
{
|
||||
private TAspect _aspect;
|
||||
private EcsGroup _filteredGroup;
|
||||
|
||||
private long _executeVersion;
|
||||
private long _version;
|
||||
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
private EcsProfilerMarker _executeWhere = new EcsProfilerMarker("Where");
|
||||
private readonly EcsProfilerMarker _executeMarker = new EcsProfilerMarker("Where");
|
||||
#endif
|
||||
|
||||
#region Properties
|
||||
public TAspect Aspect => _aspect;
|
||||
internal long ExecuteVersion => _executeVersion;
|
||||
public TAspect Aspect
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _aspect;
|
||||
}
|
||||
public sealed override long Version
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _version;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region OnInitialize/OnDestroy
|
||||
@ -29,19 +39,80 @@
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsReadonlyGroup Execute() => ExecuteFor(_aspect.World.Entities);
|
||||
public EcsReadonlyGroup ExecuteFor(EcsReadonlyGroup sourceGroup)
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsReadonlyGroup ExecuteFor(EcsSpan span)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
_executeWhere.Begin();
|
||||
if (sourceGroup.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения.
|
||||
_executeMarker.Begin();
|
||||
if (span.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения.
|
||||
if (span.WorldID != WorldID) throw new System.ArgumentException();//TODO составить текст исключения.
|
||||
#endif
|
||||
_aspect.GetIteratorFor(sourceGroup).CopyTo(_filteredGroup);
|
||||
unchecked { _version++; }
|
||||
_aspect.GetIteratorFor(span).CopyTo(_filteredGroup);
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
_executeWhere.End();
|
||||
_executeMarker.End();
|
||||
#endif
|
||||
return _filteredGroup.Readonly;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public sealed class EcsWhereSpanExecutor<TAspect> : EcsQueryExecutor where TAspect : EcsAspect
|
||||
{
|
||||
private TAspect _aspect;
|
||||
private int[] _filteredEntities;
|
||||
|
||||
private long _version;
|
||||
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
private readonly EcsProfilerMarker _executeMarker = new EcsProfilerMarker("Where");
|
||||
#endif
|
||||
|
||||
#region Properties
|
||||
public TAspect Aspect
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _aspect;
|
||||
}
|
||||
public sealed override long Version
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => _version;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region OnInitialize/OnDestroy
|
||||
protected sealed override void OnInitialize()
|
||||
{
|
||||
_aspect = World.GetAspect<TAspect>();
|
||||
_filteredEntities = new int[32];
|
||||
}
|
||||
protected sealed override void OnDestroy() { }
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsSpan Execute() => ExecuteFor(_aspect.World.Entities);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public EcsSpan ExecuteFor(EcsSpan span)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
_executeMarker.Begin();
|
||||
if (span.IsNull) throw new System.ArgumentNullException();//TODO составить текст исключения.
|
||||
if (span.WorldID != WorldID) throw new System.ArgumentException();//TODO составить текст исключения.
|
||||
#endif
|
||||
unchecked { _version++; }
|
||||
EcsSpan result = _aspect.GetIteratorFor(span).CopyToSpan(ref _filteredEntities);
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
_executeMarker.End();
|
||||
#endif
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
382
src/Pools/EcsHybridPool.cs
Normal file
382
src/Pools/EcsHybridPool.cs
Normal file
@ -0,0 +1,382 @@
|
||||
using DCFApixels.DragonECS.Internal;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
public interface IEcsHybridPoolInternal : IEcsPool
|
||||
{
|
||||
void AddRefInternal(int entityID, object component, bool isAppend);
|
||||
void DelInternal(int entityID, bool isAppend);
|
||||
}
|
||||
}
|
||||
/// <summary>Pool for IEcsHybridComponent components</summary>
|
||||
public sealed class EcsHybridPool<T> : IEcsPoolImplementation<T>, IEcsHybridPool<T>, IEcsHybridPoolInternal, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
|
||||
where T : IEcsHybridComponent
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private int _componentTypeID;
|
||||
private EcsMaskBit _maskBit;
|
||||
|
||||
private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
|
||||
private T[] _items; //dense
|
||||
private int[] _entities;
|
||||
private int _itemsCount;
|
||||
|
||||
private int[] _recycledItems;
|
||||
private int _recycledItemsCount;
|
||||
|
||||
private List<IEcsPoolEventListener> _listeners = new List<IEcsPoolEventListener>();
|
||||
|
||||
private EcsWorld.PoolsMediator _mediator;
|
||||
|
||||
#region Properites
|
||||
public int Count => _itemsCount;
|
||||
public int Capacity => _items.Length;
|
||||
public int ComponentID => _componentTypeID;
|
||||
public Type ComponentType => typeof(T);
|
||||
public EcsWorld World => _source;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
void IEcsHybridPoolInternal.AddRefInternal(int entityID, object component, bool isMain)
|
||||
{
|
||||
AddInternal(entityID, (T)component, isMain);
|
||||
}
|
||||
private void AddInternal(int entityID, T component, bool isMain)
|
||||
{
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (itemIndex > 0) EcsPoolThrowHalper.ThrowAlreadyHasComponent<T>(entityID);
|
||||
#endif
|
||||
if (_recycledItemsCount > 0)
|
||||
{
|
||||
itemIndex = _recycledItems[--_recycledItemsCount];
|
||||
_itemsCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemIndex = ++_itemsCount;
|
||||
if (itemIndex >= _items.Length)
|
||||
{
|
||||
Array.Resize(ref _items, _items.Length << 1);
|
||||
Array.Resize(ref _entities, _items.Length);
|
||||
}
|
||||
}
|
||||
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
_listeners.InvokeOnAdd(entityID);
|
||||
if (isMain)
|
||||
component.OnAddToPool(_source.GetEntityLong(entityID));
|
||||
_items[itemIndex] = component;
|
||||
_entities[itemIndex] = entityID;
|
||||
}
|
||||
public void Add(int entityID, T component)
|
||||
{
|
||||
HybridMapping mapping = _source.GetHybridMapping(component.GetType());
|
||||
mapping.GetTargetTypePool().AddRefInternal(entityID, component, false);
|
||||
foreach (var pool in mapping.GetPools())
|
||||
pool.AddRefInternal(entityID, component, true);
|
||||
}
|
||||
public void Set(int entityID, T component)
|
||||
{
|
||||
if (Has(entityID))
|
||||
Del(entityID);
|
||||
Add(entityID, component);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public T Get(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
_listeners.InvokeOnGet(entityID);
|
||||
return _items[_mapping[entityID]];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public ref readonly T Read(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
return ref _items[_mapping[entityID]];
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Has(int entityID)
|
||||
{
|
||||
return _mapping[entityID] > 0;
|
||||
}
|
||||
void IEcsHybridPoolInternal.DelInternal(int entityID, bool isMain)
|
||||
{
|
||||
DelInternal(entityID, isMain);
|
||||
}
|
||||
private void DelInternal(int entityID, bool isMain)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
T component = _items[itemIndex];
|
||||
if (isMain)
|
||||
component.OnDelFromPool(_source.GetEntityLong(entityID));
|
||||
if (_recycledItemsCount >= _recycledItems.Length)
|
||||
Array.Resize(ref _recycledItems, _recycledItems.Length << 1);
|
||||
_recycledItems[_recycledItemsCount++] = itemIndex;
|
||||
_mapping[entityID] = 0;
|
||||
_entities[itemIndex] = 0;
|
||||
_itemsCount--;
|
||||
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
_listeners.InvokeOnDel(entityID);
|
||||
}
|
||||
public void Del(int entityID)
|
||||
{
|
||||
var component = Get(entityID);
|
||||
HybridMapping mapping = _source.GetHybridMapping(component.GetType());
|
||||
mapping.GetTargetTypePool().DelInternal(entityID, false);
|
||||
foreach (var pool in mapping.GetPools())
|
||||
pool.DelInternal(entityID, true);
|
||||
}
|
||||
public void TryDel(int entityID)
|
||||
{
|
||||
if (Has(entityID)) Del(entityID);
|
||||
}
|
||||
public void Copy(int fromEntityID, int toEntityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(fromEntityID);
|
||||
#endif
|
||||
Set(toEntityID, Get(fromEntityID));
|
||||
}
|
||||
public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(fromEntityID);
|
||||
#endif
|
||||
toWorld.GetPool<T>().Set(toEntityID, Get(fromEntityID));
|
||||
}
|
||||
|
||||
public void ClearNotAliveComponents()
|
||||
{
|
||||
for (int i = _itemsCount - 1; i >= 0; i--)
|
||||
{
|
||||
if (!_items[i].IsAlive)
|
||||
Del(_entities[i]);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Callbacks
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID)
|
||||
{
|
||||
_source = world;
|
||||
_mediator = mediator;
|
||||
_componentTypeID = componentTypeID;
|
||||
_maskBit = EcsMaskBit.FromID(componentTypeID);
|
||||
|
||||
const int capacity = 512;
|
||||
|
||||
_mapping = new int[world.Capacity];
|
||||
_recycledItems = new int[128];
|
||||
_recycledItemsCount = 0;
|
||||
_items = new T[capacity];
|
||||
_entities = new int[capacity];
|
||||
_itemsCount = 0;
|
||||
}
|
||||
void IEcsPoolImplementation.OnWorldResize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _mapping, newSize);
|
||||
}
|
||||
void IEcsPoolImplementation.OnWorldDestroy() { }
|
||||
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer)
|
||||
{
|
||||
foreach (var entityID in buffer)
|
||||
TryDel(entityID);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID, (T)dataRaw);
|
||||
object IEcsPool.GetRaw(int entityID) => Read(entityID);
|
||||
void IEcsPool.SetRaw(int entityID, object dataRaw) => Set(entityID, (T)dataRaw);
|
||||
#endregion
|
||||
|
||||
#region Listeners
|
||||
public void AddListener(IEcsPoolEventListener listener)
|
||||
{
|
||||
if (listener == null) { throw new ArgumentNullException("listener is null"); }
|
||||
_listeners.Add(listener);
|
||||
}
|
||||
public void RemoveListener(IEcsPoolEventListener listener)
|
||||
{
|
||||
if (listener == null) { throw new ArgumentNullException("listener is null"); }
|
||||
_listeners.Remove(listener);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region IEnumerator - IntelliSense hack
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => throw new NotImplementedException();
|
||||
IEnumerator IEnumerable.GetEnumerator() => throw new NotImplementedException();
|
||||
#endregion
|
||||
}
|
||||
/// <summary>Hybrid component</summary>
|
||||
public interface IEcsHybridComponent
|
||||
{
|
||||
bool IsAlive { get; }
|
||||
void OnAddToPool(entlong entity);
|
||||
void OnDelFromPool(entlong entity);
|
||||
}
|
||||
public static class EcsHybridPoolExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsNullOrNotAlive(this IEcsHybridComponent self) => self == null || self.IsAlive;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsHybridPool<T> GetPool<T>(this EcsWorld self) where T : IEcsHybridComponent
|
||||
{
|
||||
return self.GetPool<EcsHybridPool<T>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsHybridPool<T> GetPoolUnchecked<T>(this EcsWorld self) where T : IEcsHybridComponent
|
||||
{
|
||||
return self.GetPoolUnchecked<EcsHybridPool<T>>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsHybridPool<T> Include<T>(this EcsAspectBuilderBase self) where T : IEcsHybridComponent
|
||||
{
|
||||
return self.Include<EcsHybridPool<T>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsHybridPool<T> Exclude<T>(this EcsAspectBuilderBase self) where T : IEcsHybridComponent
|
||||
{
|
||||
return self.Exclude<EcsHybridPool<T>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsHybridPool<T> Optional<T>(this EcsAspectBuilderBase self) where T : IEcsHybridComponent
|
||||
{
|
||||
return self.Optional<EcsHybridPool<T>>();
|
||||
}
|
||||
|
||||
//-------------------------------------------------
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsHybridPool<T> GetHybridPool<T>(this EcsWorld self) where T : IEcsHybridComponent
|
||||
{
|
||||
return self.GetPool<EcsHybridPool<T>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsHybridPool<T> GetHybridPoolUnchecked<T>(this EcsWorld self) where T : IEcsHybridComponent
|
||||
{
|
||||
return self.GetPoolUnchecked<EcsHybridPool<T>>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsHybridPool<T> IncludeHybrid<T>(this EcsAspectBuilderBase self) where T : IEcsHybridComponent
|
||||
{
|
||||
return self.Include<EcsHybridPool<T>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsHybridPool<T> ExcludeHybrid<T>(this EcsAspectBuilderBase self) where T : IEcsHybridComponent
|
||||
{
|
||||
return self.Exclude<EcsHybridPool<T>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsHybridPool<T> OptionalHybrid<T>(this EcsAspectBuilderBase self) where T : IEcsHybridComponent
|
||||
{
|
||||
return self.Optional<EcsHybridPool<T>>();
|
||||
}
|
||||
}
|
||||
|
||||
public abstract partial class EcsWorld
|
||||
{
|
||||
private Dictionary<Type, HybridMapping> _hybridMapping = new Dictionary<Type, HybridMapping>();
|
||||
internal HybridMapping GetHybridMapping(Type type)
|
||||
{
|
||||
if (!_hybridMapping.TryGetValue(type, out HybridMapping mapping))
|
||||
{
|
||||
mapping = new HybridMapping(this, type);
|
||||
_hybridMapping.Add(type, mapping);
|
||||
}
|
||||
return mapping;
|
||||
}
|
||||
}
|
||||
|
||||
internal class HybridMapping
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private object[] _sourceForReflection;
|
||||
private Type _type;
|
||||
|
||||
private IEcsHybridPoolInternal _targetTypePool;
|
||||
private List<IEcsHybridPoolInternal> _relatedPools;
|
||||
|
||||
private static Type hybridPoolType = typeof(EcsHybridPool<>);
|
||||
private static MethodInfo getHybridPoolMethod = typeof(EcsHybridPoolExtensions).GetMethod($"{nameof(EcsHybridPoolExtensions.GetPool)}", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
private static HashSet<Type> _hybridComponents = new HashSet<Type>();
|
||||
static HybridMapping()
|
||||
{
|
||||
Type hybridComponentType = typeof(IEcsHybridComponent);
|
||||
foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
|
||||
{
|
||||
var types = assembly.GetTypes();
|
||||
foreach (var type in types)
|
||||
{
|
||||
if (type.GetInterface(nameof(IEcsHybridComponent)) != null && type != hybridComponentType)
|
||||
{
|
||||
_hybridComponents.Add(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public static bool IsEcsHybridComponentType(Type type)
|
||||
{
|
||||
return _hybridComponents.Contains(type);
|
||||
}
|
||||
|
||||
public HybridMapping(EcsWorld source, Type type)
|
||||
{
|
||||
if (!type.IsClass)
|
||||
throw new ArgumentException();
|
||||
|
||||
_source = source;
|
||||
_type = type;
|
||||
_relatedPools = new List<IEcsHybridPoolInternal>();
|
||||
_sourceForReflection = new object[] { source };
|
||||
_targetTypePool = CreateHybridPool(type);
|
||||
foreach (var item in type.GetInterfaces())
|
||||
{
|
||||
if (IsEcsHybridComponentType(item))
|
||||
{
|
||||
_relatedPools.Add(CreateHybridPool(item));
|
||||
}
|
||||
}
|
||||
Type baseType = type.BaseType;
|
||||
while (baseType != typeof(object) && IsEcsHybridComponentType(baseType))
|
||||
{
|
||||
_relatedPools.Add(CreateHybridPool(baseType));
|
||||
baseType = baseType.BaseType;
|
||||
}
|
||||
}
|
||||
private IEcsHybridPoolInternal CreateHybridPool(Type componentType)
|
||||
{
|
||||
//var x = (IEcsHybridPoolInternal)getHybridPoolMethod.MakeGenericMethod(componentType).Invoke(null, _sourceForReflection);
|
||||
//Debug.Log("_" + x.ComponentID + "_" +x.ComponentType.Name);
|
||||
//return x;
|
||||
return (IEcsHybridPoolInternal)getHybridPoolMethod.MakeGenericMethod(componentType).Invoke(null, _sourceForReflection);
|
||||
}
|
||||
|
||||
public IEcsHybridPoolInternal GetTargetTypePool()
|
||||
{
|
||||
return _targetTypePool;
|
||||
}
|
||||
public List<IEcsHybridPoolInternal> GetPools()
|
||||
{
|
||||
return _relatedPools;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/Pools/EcsHybridPool.cs.meta
Normal file
11
src/Pools/EcsHybridPool.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 511487e83f936f94780e572063b68a87
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -2,16 +2,16 @@ using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static DCFApixels.DragonECS.EcsPoolThrowHalper;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
/// <summary>Pool for IEcsComponent components</summary>
|
||||
public sealed class EcsPool<T> : IEcsPoolImplementation<T>, IEcsStructsPool<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
|
||||
public sealed class EcsPool<T> : IEcsPoolImplementation<T>, IEcsStructPool<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
|
||||
where T : struct, IEcsComponent
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private int _id;
|
||||
private int _componentTypeID;
|
||||
private EcsMaskBit _maskBit;
|
||||
|
||||
private int[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
|
||||
private T[] _items; //dense
|
||||
@ -19,46 +19,27 @@ namespace DCFApixels.DragonECS
|
||||
private int[] _recycledItems;
|
||||
private int _recycledItemsCount;
|
||||
|
||||
private IEcsComponentReset<T> _componentResetHandler;
|
||||
private IEcsComponentCopy<T> _componentCopyHandler;
|
||||
private IEcsComponentReset<T> _componentResetHandler = EcsComponentResetHandler<T>.instance;
|
||||
private IEcsComponentCopy<T> _componentCopyHandler = EcsComponentCopyHandler<T>.instance;
|
||||
|
||||
private List<IEcsPoolEventListener> _listeners;
|
||||
private List<IEcsPoolEventListener> _listeners = new List<IEcsPoolEventListener>();
|
||||
|
||||
private EcsWorld.PoolsMediator _mediator;
|
||||
|
||||
#region Properites
|
||||
public int Count => _itemsCount;
|
||||
public int Capacity => _items.Length;
|
||||
public int ComponentID => _id;
|
||||
public int ComponentID => _componentTypeID;
|
||||
public Type ComponentType => typeof(T);
|
||||
public EcsWorld World => _source;
|
||||
#endregion
|
||||
|
||||
#region Init
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
|
||||
{
|
||||
_source = world;
|
||||
_id = componentID;
|
||||
|
||||
const int capacity = 512;
|
||||
|
||||
_mapping = new int[world.Capacity];
|
||||
_recycledItems = new int[128];
|
||||
_recycledItemsCount = 0;
|
||||
_items = new T[capacity];
|
||||
_itemsCount = 0;
|
||||
|
||||
_listeners = new List<IEcsPoolEventListener>();
|
||||
|
||||
_componentResetHandler = EcsComponentResetHandler<T>.instance;
|
||||
_componentCopyHandler = EcsComponentCopyHandler<T>.instance;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public ref T Add(int entityID)
|
||||
{
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (itemIndex > 0) ThrowAlreadyHasComponent<T>(entityID);
|
||||
if (itemIndex > 0) EcsPoolThrowHalper.ThrowAlreadyHasComponent<T>(entityID);
|
||||
#endif
|
||||
if (_recycledItemsCount > 0)
|
||||
{
|
||||
@ -71,7 +52,7 @@ namespace DCFApixels.DragonECS
|
||||
if (itemIndex >= _items.Length)
|
||||
Array.Resize(ref _items, _items.Length << 1);
|
||||
}
|
||||
this.IncrementEntityComponentCount(entityID);
|
||||
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
_listeners.InvokeOnAddAndGet(entityID);
|
||||
return ref _items[itemIndex];
|
||||
}
|
||||
@ -79,7 +60,7 @@ namespace DCFApixels.DragonECS
|
||||
public ref T Get(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
_listeners.InvokeOnGet(entityID);
|
||||
return ref _items[_mapping[entityID]];
|
||||
@ -88,7 +69,7 @@ namespace DCFApixels.DragonECS
|
||||
public ref readonly T Read(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
return ref _items[_mapping[entityID]];
|
||||
}
|
||||
@ -108,7 +89,7 @@ namespace DCFApixels.DragonECS
|
||||
if (itemIndex >= _items.Length)
|
||||
Array.Resize(ref _items, _items.Length << 1);
|
||||
}
|
||||
this.IncrementEntityComponentCount(entityID);
|
||||
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
_listeners.InvokeOnAdd(entityID);
|
||||
}
|
||||
_listeners.InvokeOnGet(entityID);
|
||||
@ -122,7 +103,7 @@ namespace DCFApixels.DragonECS
|
||||
public void Del(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
ref int itemIndex = ref _mapping[entityID];
|
||||
_componentResetHandler.Reset(ref _items[itemIndex]);
|
||||
@ -131,7 +112,7 @@ namespace DCFApixels.DragonECS
|
||||
_recycledItems[_recycledItemsCount++] = itemIndex;
|
||||
_mapping[entityID] = 0;
|
||||
_itemsCount--;
|
||||
this.DecrementEntityComponentCount(entityID);
|
||||
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
_listeners.InvokeOnDel(entityID);
|
||||
}
|
||||
public void TryDel(int entityID)
|
||||
@ -141,20 +122,35 @@ namespace DCFApixels.DragonECS
|
||||
public void Copy(int fromEntityID, int toEntityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
|
||||
if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(fromEntityID);
|
||||
#endif
|
||||
_componentCopyHandler.Copy(ref Get(fromEntityID), ref TryAddOrGet(toEntityID));
|
||||
}
|
||||
public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
|
||||
if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(fromEntityID);
|
||||
#endif
|
||||
_componentCopyHandler.Copy(ref Get(fromEntityID), ref toWorld.GetPool<T>().TryAddOrGet(toEntityID));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Callbacks
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID)
|
||||
{
|
||||
_source = world;
|
||||
_mediator = mediator;
|
||||
_componentTypeID = componentTypeID;
|
||||
_maskBit = EcsMaskBit.FromID(componentTypeID);
|
||||
|
||||
const int capacity = 512;
|
||||
|
||||
_mapping = new int[world.Capacity];
|
||||
_recycledItems = new int[128];
|
||||
_recycledItemsCount = 0;
|
||||
_items = new T[capacity];
|
||||
_itemsCount = 0;
|
||||
}
|
||||
void IEcsPoolImplementation.OnWorldResize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _mapping, newSize);
|
||||
@ -171,8 +167,8 @@ namespace DCFApixels.DragonECS
|
||||
void IEcsPool.AddRaw(int entityID, object dataRaw) => Add(entityID) = (T)dataRaw;
|
||||
object IEcsPool.GetRaw(int entityID) => Read(entityID);
|
||||
void IEcsPool.SetRaw(int entityID, object dataRaw) => Get(entityID) = (T)dataRaw;
|
||||
ref readonly T IEcsStructsPool<T>.Read(int entityID) => ref Read(entityID);
|
||||
ref T IEcsStructsPool<T>.Get(int entityID) => ref Get(entityID);
|
||||
ref readonly T IEcsStructPool<T>.Read(int entityID) => ref Read(entityID);
|
||||
ref T IEcsStructPool<T>.Get(int entityID) => ref Get(entityID);
|
||||
#endregion
|
||||
|
||||
#region Listeners
|
||||
@ -197,19 +193,28 @@ namespace DCFApixels.DragonECS
|
||||
public interface IEcsComponent { }
|
||||
public static class EcsPoolExt
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsPool<TComponent> GetPool<TComponent>(this EcsWorld self) where TComponent : struct, IEcsComponent
|
||||
{
|
||||
return self.GetPool<EcsPool<TComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsPool<TComponent> GetPoolUnchecked<TComponent>(this EcsWorld self) where TComponent : struct, IEcsComponent
|
||||
{
|
||||
return self.GetPoolUnchecked<EcsPool<TComponent>>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsPool<TComponent> Include<TComponent>(this EcsAspectBuilderBase self) where TComponent : struct, IEcsComponent
|
||||
{
|
||||
return self.Include<EcsPool<TComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsPool<TComponent> Exclude<TComponent>(this EcsAspectBuilderBase self) where TComponent : struct, IEcsComponent
|
||||
{
|
||||
return self.Exclude<EcsPool<TComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsPool<TComponent> Optional<TComponent>(this EcsAspectBuilderBase self) where TComponent : struct, IEcsComponent
|
||||
{
|
||||
return self.Optional<EcsPool<TComponent>>();
|
||||
|
||||
@ -30,16 +30,26 @@ namespace DCFApixels.DragonECS
|
||||
void RemoveListener(IEcsPoolEventListener listener);
|
||||
#endregion
|
||||
}
|
||||
public interface IEcsStructsPool<T>
|
||||
public interface IEcsStructPool<T> : IEcsPool
|
||||
{
|
||||
ref T Add(int entityID);
|
||||
ref readonly T Read(int entityID);
|
||||
ref T Get(int entityID);
|
||||
}
|
||||
public interface IEcsClassPool<T> : IEcsPool
|
||||
{
|
||||
T Add(int entityID);
|
||||
T Get(int entityID);
|
||||
}
|
||||
public interface IEcsHybridPool<T> : IEcsPool
|
||||
{
|
||||
void Add(int entityID, T component);
|
||||
T Get(int entityID);
|
||||
}
|
||||
/// <summary>Only used to implement a custom pool. In other contexts use IEcsPool or IEcsPool<T>.</summary>
|
||||
public interface IEcsPoolImplementation : IEcsPool
|
||||
{
|
||||
void OnInit(EcsWorld world, int componentID);
|
||||
void OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID);
|
||||
void OnWorldResize(int newSize);
|
||||
void OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer);
|
||||
void OnWorldDestroy();
|
||||
@ -52,26 +62,15 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
public static void ThrowAlreadyHasComponent<T>(int entityID)
|
||||
{
|
||||
throw new EcsFrameworkException($"Entity({entityID}) already has component {typeof(T).Name}.");
|
||||
throw new EcsFrameworkException($"Entity({entityID}) already has component {EcsDebugUtility.GetGenericTypeName<T>()}.");
|
||||
}
|
||||
public static void ThrowNotHaveComponent<T>(int entityID)
|
||||
{
|
||||
throw new EcsFrameworkException($"Entity({entityID}) has no component {typeof(T).Name}.");
|
||||
throw new EcsFrameworkException($"Entity({entityID}) has no component {EcsDebugUtility.GetGenericTypeName<T>()}.");
|
||||
}
|
||||
}
|
||||
public static class IEcsPoolImplementationExtensions
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void IncrementEntityComponentCount<T>(this IEcsPoolImplementation<T> self, int entityID)
|
||||
{
|
||||
self.World.IncrementEntityComponentCount(entityID);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void DecrementEntityComponentCount<T>(this IEcsPoolImplementation<T> self, int entityID)
|
||||
{
|
||||
self.World.DecrementEntityComponentCount(entityID);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool IsNullOrDummy(this IEcsPool self)
|
||||
{
|
||||
@ -106,7 +105,7 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Callbacks
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID) { }
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID) { }
|
||||
void IEcsPoolImplementation.OnWorldDestroy() { }
|
||||
void IEcsPoolImplementation.OnWorldResize(int newSize) { }
|
||||
void IEcsPoolImplementation.OnReleaseDelEntityBuffer(ReadOnlySpan<int> buffer) { }
|
||||
|
||||
@ -1,54 +1,56 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using static DCFApixels.DragonECS.EcsPoolThrowHalper;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
public sealed class EcsTagPool<T> : IEcsPoolImplementation<T>, IEcsStructsPool<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
|
||||
public sealed class EcsTagPool<T> : IEcsPoolImplementation<T>, IEcsStructPool<T>, IEnumerable<T> //IEnumerable<T> - IntelliSense hack
|
||||
where T : struct, IEcsTagComponent
|
||||
{
|
||||
private EcsWorld _source;
|
||||
private int _id;
|
||||
private int _componentTypeID;
|
||||
private EcsMaskBit _maskBit;
|
||||
|
||||
private bool[] _mapping;// index = entityID / value = itemIndex;/ value = 0 = no entityID
|
||||
private int _count;
|
||||
|
||||
private List<IEcsPoolEventListener> _listeners;
|
||||
private List<IEcsPoolEventListener> _listeners = new List<IEcsPoolEventListener>();
|
||||
|
||||
private T _fakeComponent;
|
||||
private EcsWorld.PoolsMediator _mediator;
|
||||
|
||||
#region Constructors
|
||||
private static bool _isInvalidType;
|
||||
static EcsTagPool()
|
||||
{
|
||||
_isInvalidType = typeof(T).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).Length > 0;
|
||||
}
|
||||
public EcsTagPool()
|
||||
{
|
||||
if (_isInvalidType)
|
||||
throw new EcsFrameworkException($"{typeof(T).Name} type must not contain any data.");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Properites
|
||||
public int Count => _count;
|
||||
int IEcsPool.Capacity => -1;
|
||||
public int ComponentID => _id;
|
||||
public int ComponentID => _componentTypeID;
|
||||
public Type ComponentType => typeof(T);
|
||||
public EcsWorld World => _source;
|
||||
#endregion
|
||||
|
||||
#region Init
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, int componentID)
|
||||
{
|
||||
_source = world;
|
||||
_id = componentID;
|
||||
|
||||
_mapping = new bool[world.Capacity];
|
||||
_count = 0;
|
||||
|
||||
_listeners = new List<IEcsPoolEventListener>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Method
|
||||
public void Add(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (Has(entityID)) ThrowAlreadyHasComponent<T>(entityID);
|
||||
if (Has(entityID)) EcsPoolThrowHalper.ThrowAlreadyHasComponent<T>(entityID);
|
||||
#endif
|
||||
_count++;
|
||||
_mapping[entityID] = true;
|
||||
this.IncrementEntityComponentCount(entityID);
|
||||
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
_listeners.InvokeOnAdd(entityID);
|
||||
}
|
||||
public void TryAdd(int entityID)
|
||||
@ -57,7 +59,7 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
_count++;
|
||||
_mapping[entityID] = true;
|
||||
this.IncrementEntityComponentCount(entityID);
|
||||
_mediator.RegisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
_listeners.InvokeOnAdd(entityID);
|
||||
}
|
||||
}
|
||||
@ -69,11 +71,11 @@ namespace DCFApixels.DragonECS
|
||||
public void Del(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
_mapping[entityID] = false;
|
||||
_count--;
|
||||
this.DecrementEntityComponentCount(entityID);
|
||||
_mediator.UnregisterComponent(entityID, _componentTypeID, _maskBit);
|
||||
_listeners.InvokeOnDel(entityID);
|
||||
}
|
||||
public void TryDel(int entityID)
|
||||
@ -83,14 +85,14 @@ namespace DCFApixels.DragonECS
|
||||
public void Copy(int fromEntityID, int toEntityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
|
||||
if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(fromEntityID);
|
||||
#endif
|
||||
TryAdd(toEntityID);
|
||||
}
|
||||
public void Copy(int fromEntityID, EcsWorld toWorld, int toEntityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(fromEntityID)) ThrowNotHaveComponent<T>(fromEntityID);
|
||||
if (!Has(fromEntityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(fromEntityID);
|
||||
#endif
|
||||
toWorld.GetPool<T>().TryAdd(toEntityID);
|
||||
}
|
||||
@ -117,6 +119,16 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Callbacks
|
||||
void IEcsPoolImplementation.OnInit(EcsWorld world, EcsWorld.PoolsMediator mediator, int componentTypeID)
|
||||
{
|
||||
_source = world;
|
||||
_mediator = mediator;
|
||||
_componentTypeID = componentTypeID;
|
||||
_maskBit = EcsMaskBit.FromID(componentTypeID);
|
||||
|
||||
_mapping = new bool[world.Capacity];
|
||||
_count = 0;
|
||||
}
|
||||
void IEcsPoolImplementation.OnWorldResize(int newSize)
|
||||
{
|
||||
Array.Resize(ref _mapping, newSize);
|
||||
@ -131,22 +143,22 @@ namespace DCFApixels.DragonECS
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
ref T IEcsStructsPool<T>.Add(int entityID)
|
||||
ref T IEcsStructPool<T>.Add(int entityID)
|
||||
{
|
||||
Add(entityID);
|
||||
return ref _fakeComponent;
|
||||
}
|
||||
ref readonly T IEcsStructsPool<T>.Read(int entityID)
|
||||
ref readonly T IEcsStructPool<T>.Read(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
return ref _fakeComponent;
|
||||
}
|
||||
ref T IEcsStructsPool<T>.Get(int entityID)
|
||||
ref T IEcsStructPool<T>.Get(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
return ref _fakeComponent;
|
||||
}
|
||||
@ -154,14 +166,14 @@ namespace DCFApixels.DragonECS
|
||||
object IEcsPool.GetRaw(int entityID)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
return _fakeComponent;
|
||||
}
|
||||
void IEcsPool.SetRaw(int entityID, object dataRaw)
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!Has(entityID)) ThrowNotHaveComponent<T>(entityID);
|
||||
if (!Has(entityID)) EcsPoolThrowHalper.ThrowNotHaveComponent<T>(entityID);
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
@ -189,22 +201,60 @@ namespace DCFApixels.DragonECS
|
||||
public interface IEcsTagComponent { }
|
||||
public static class EcsTagPoolExt
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> GetPool<TTagComponent>(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.GetPool<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> GetPoolUnchecked<TTagComponent>(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.GetPoolUnchecked<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> Include<TTagComponent>(this EcsAspectBuilderBase self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.Include<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> Exclude<TTagComponent>(this EcsAspectBuilderBase self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.Exclude<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> Optional<TTagComponent>(this EcsAspectBuilderBase self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.Optional<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
|
||||
//---------------------------------------------------
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> GetTagPool<TTagComponent>(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.GetPool<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> GetTagPoolUnchecked<TTagComponent>(this EcsWorld self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.GetPoolUnchecked<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> IncludeTag<TTagComponent>(this EcsAspectBuilderBase self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.Include<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> ExcludeTag<TTagComponent>(this EcsAspectBuilderBase self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.Exclude<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static EcsTagPool<TTagComponent> OptionalTag<TTagComponent>(this EcsAspectBuilderBase self) where TTagComponent : struct, IEcsTagComponent
|
||||
{
|
||||
return self.Optional<EcsTagPool<TTagComponent>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,8 @@
|
||||
namespace DCFApixels.DragonECS.Utils
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using System;
|
||||
|
||||
namespace DCFApixels.DragonECS.Utils
|
||||
{
|
||||
internal static class ArrayUtility
|
||||
{
|
||||
@ -12,4 +16,39 @@
|
||||
array[i] = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal static unsafe class UnmanagedArrayUtility
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* New<T>(int capacity) where T : unmanaged
|
||||
{
|
||||
return (T*)Marshal.AllocHGlobal(Marshal.SizeOf<T>() * capacity).ToPointer();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* NewAndInit<T>(int capacity) where T : unmanaged
|
||||
{
|
||||
int newSize = Marshal.SizeOf(typeof(T)) * capacity;
|
||||
byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer();
|
||||
|
||||
for (int i = 0; i < newSize; i++)
|
||||
*(newPointer + i) = 0;
|
||||
|
||||
return (T*)newPointer;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Free(void* pointer)
|
||||
{
|
||||
Marshal.FreeHGlobal(new IntPtr(pointer));
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static T* Resize<T>(void* oldPointer, int newCount) where T : unmanaged
|
||||
{
|
||||
return (T*)(Marshal.ReAllocHGlobal(
|
||||
new IntPtr(oldPointer),
|
||||
new IntPtr(Marshal.SizeOf(typeof(T)) * newCount))).ToPointer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
47
src/Utils/EcsTypeCode.cs
Normal file
47
src/Utils/EcsTypeCode.cs
Normal file
@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS.Internal
|
||||
{
|
||||
public static class EcsTypeCode
|
||||
{
|
||||
private static readonly Dictionary<Type, int> _codes = new Dictionary<Type, int>();
|
||||
private static int _incremetn = 1;
|
||||
public static int Count => _codes.Count;
|
||||
public static int Get(Type type)
|
||||
{
|
||||
if (!_codes.TryGetValue(type, out int code))
|
||||
{
|
||||
code = _incremetn++;
|
||||
_codes.Add(type, code);
|
||||
}
|
||||
return code;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Get<T>() => EcsTypeCodeCache<T>.code;
|
||||
public static bool Has(Type type) => _codes.ContainsKey(type);
|
||||
public static bool Has<T>() => _codes.ContainsKey(typeof(T));
|
||||
public static IEnumerable<TypeCodeInfo> GetDeclared() => _codes.Select(o => new TypeCodeInfo(o.Key, o.Value));
|
||||
}
|
||||
public static class EcsTypeCodeCache<T>
|
||||
{
|
||||
public static readonly int code = EcsTypeCode.Get(typeof(T));
|
||||
}
|
||||
public struct TypeCodeInfo
|
||||
{
|
||||
public Type type;
|
||||
public int code;
|
||||
public TypeCodeInfo(Type type, int code)
|
||||
{
|
||||
this.type = type;
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return this.AutoToString(false) + "\n\r";
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/Utils/EcsTypeCode.cs.meta
Normal file
11
src/Utils/EcsTypeCode.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: be4c15a212b2a6941bdbe78f18b038b3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,8 +1,90 @@
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
namespace Internal
|
||||
{
|
||||
internal static class Throw
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void ArgumentNull()
|
||||
{
|
||||
throw new ArgumentNullException();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void ConstraintIsAlreadyContainedInMask(Type type)
|
||||
{
|
||||
throw new EcsFrameworkException($"The {EcsDebugUtility.GetGenericTypeName(type)} constraint is already contained in the mask.");
|
||||
}
|
||||
|
||||
//[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
//public static void ArgumentDifferentWorldsException()
|
||||
//{
|
||||
// throw new ArgumentException("The groups belong to different worlds.");
|
||||
//}
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void ArgumentOutOfRange()
|
||||
{
|
||||
throw new ArgumentOutOfRangeException($"index is less than 0 or is equal to or greater than Count.");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void Group_AlreadyContains(int entityID)
|
||||
{
|
||||
throw new EcsFrameworkException($"This group already contains entity {entityID}.");
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void Group_DoesNotContain(int entityID)
|
||||
{
|
||||
throw new EcsFrameworkException($"This group does not contain entity {entityID}.");
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void Group_ArgumentDifferentWorldsException()
|
||||
{
|
||||
throw new ArgumentException("The groups belong to different worlds.");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void Pipeline_MethodCalledAfterInitialisation(string methodName)
|
||||
{
|
||||
throw new MethodAccessException($"It is forbidden to call {methodName}, after initialization {nameof(EcsPipeline)}.");
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void Pipeline_MethodCalledBeforeInitialisation(string methodName)
|
||||
{
|
||||
throw new MethodAccessException($"It is forbidden to call {methodName}, before initialization {nameof(EcsPipeline)}.");
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void Pipeline_MethodCalledAfterDestruction(string methodName)
|
||||
{
|
||||
throw new MethodAccessException($"It is forbidden to call {methodName}, after destroying {nameof(EcsPipeline)}.");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void World_InvalidIncrementComponentsBalance()
|
||||
{
|
||||
throw new MethodAccessException("Invalid increment components balance.");
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void World_GroupDoesNotBelongWorld()
|
||||
{
|
||||
throw new MethodAccessException("The Group does not belong in this world.");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
internal static void Ent_ThrowIsNotAlive(entlong entity)
|
||||
{
|
||||
if (entity.IsNull)
|
||||
throw new EcsFrameworkException($"The {entity} is null.");
|
||||
else
|
||||
throw new EcsFrameworkException($"The {entity} is not alive.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class EcsFrameworkException : Exception
|
||||
{
|
||||
|
||||
21
src/Utils/GenericEnumerable.cs
Normal file
21
src/Utils/GenericEnumerable.cs
Normal file
@ -0,0 +1,21 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS.Internal
|
||||
{
|
||||
public readonly struct GenericEnumerable<T, TEnumerator> : IEnumerable<T> where TEnumerator : IEnumerator<T>
|
||||
{
|
||||
public readonly TEnumerator _enumerator;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public GenericEnumerable(TEnumerator enumerator) => _enumerator = enumerator;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public TEnumerator GetEnumerator() => _enumerator;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
IEnumerator<T> IEnumerable<T>.GetEnumerator() => _enumerator;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
IEnumerator IEnumerable.GetEnumerator() => _enumerator;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static implicit operator GenericEnumerable<T, TEnumerator>(TEnumerator enumerator) => new GenericEnumerable<T, TEnumerator>(enumerator);
|
||||
}
|
||||
}
|
||||
11
src/Utils/GenericEnumerable.cs.meta
Normal file
11
src/Utils/GenericEnumerable.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 38e5b41bce0941b488ae64c200dcf965
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
415
src/Utils/IdDispenser.cs
Normal file
415
src/Utils/IdDispenser.cs
Normal file
@ -0,0 +1,415 @@
|
||||
// Sparse Set based ID dispenser, with the ability to reserve IDs.
|
||||
// Warning! Release version omits error exceptions, incorrect use may lead to unstable state.
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS.Utils
|
||||
{
|
||||
[Serializable]
|
||||
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||
public class IdDispenser : IEnumerable<int>, IReadOnlyCollection<int>
|
||||
{
|
||||
private const int MIN_SIZE = 4;
|
||||
|
||||
private int[] _dense = Array.Empty<int>();
|
||||
private int[] _sparse = Array.Empty<int>();
|
||||
private IDState[] _sparseState = Array.Empty<IDState>();
|
||||
|
||||
private int _usedCount; //[ |uuuu| ]
|
||||
private int _reservedCount; //[rrr| | ]
|
||||
private int _size; //[rrr|uuuu|ffffff]
|
||||
|
||||
private int _nullID;
|
||||
|
||||
#region Properties
|
||||
/// <summary> Used Count </summary>
|
||||
public int Count => _usedCount;
|
||||
public int ReservedCount => _reservedCount;
|
||||
public int Size => _size;
|
||||
public int NullID => _nullID;
|
||||
#endregion
|
||||
|
||||
public IdDispenser(int capacity, int nullID = 0)
|
||||
{
|
||||
if (capacity % MIN_SIZE > 0)
|
||||
capacity += MIN_SIZE;
|
||||
Resize(capacity);
|
||||
SetNullID(nullID);
|
||||
|
||||
Reserved = new ReservedSpan(this);
|
||||
Used = new UsedSpan(this);
|
||||
}
|
||||
|
||||
#region Use/Reserve/Release
|
||||
/// <summary>Marks as used and returns next free id.</summary>
|
||||
public int UseFree()
|
||||
{
|
||||
int count = _usedCount + _reservedCount;
|
||||
CheckOrResize(count + 1);
|
||||
int id = _dense[count];
|
||||
Add(id);
|
||||
_sparseState[id] = IDState.Used;
|
||||
return id;
|
||||
}
|
||||
public void UseFreeRange(ref int[] array, int range)
|
||||
{
|
||||
if (array.Length < range)
|
||||
Array.Resize(ref array, range);
|
||||
for (int i = 0; i < range; i++)
|
||||
array[i] = UseFree();
|
||||
}
|
||||
public void UseFreeRange(List<int> list, int range)
|
||||
{
|
||||
for (int i = 0; i < range; i++)
|
||||
list.Add(UseFree());
|
||||
}
|
||||
/// <summary>Marks as used a free or reserved id, after this id cannot be retrieved via UseFree.</summary>
|
||||
public void Use(int id)
|
||||
{
|
||||
CheckOrResize(id);
|
||||
#if DEBUG
|
||||
if (IsUsed(id) || IsReserved(id))
|
||||
{
|
||||
if (IsUsed(id)) ThrowHalper.ThrowIsAlreadyInUse(id);
|
||||
else ThrowHalper.ThrowIsHasBeenReserved(id);
|
||||
}
|
||||
#endif
|
||||
if (IsFree(id))
|
||||
Add(id);
|
||||
_sparseState[id] = IDState.Used;
|
||||
}
|
||||
public void UseRange(IEnumerable<int> ids)
|
||||
{
|
||||
foreach (var item in ids)
|
||||
Use(item);
|
||||
}
|
||||
/// <summary>Marks as reserved and returns next free id, after this id cannot be retrieved via UseFree.</summary>
|
||||
public int ReserveFree()
|
||||
{
|
||||
int count = _usedCount + _reservedCount;
|
||||
CheckOrResize(count + 1);
|
||||
int id = _dense[count];
|
||||
_sparseState[id] = IDState.Reserved;
|
||||
AddReserved(id);
|
||||
return id;
|
||||
}
|
||||
/// <summary>Marks as reserved a free id, after this id cannot be retrieved via UseFree.</summary>
|
||||
public void Reserve(int id)
|
||||
{
|
||||
CheckOrResize(id);
|
||||
#if DEBUG
|
||||
if (!IsFree(id)) ThrowHalper.ThrowIsNotAvailable(id);
|
||||
#endif
|
||||
_sparseState[id] = IDState.Reserved;
|
||||
AddReserved(id);
|
||||
}
|
||||
public void ReserveRange(IEnumerable<int> ids)
|
||||
{
|
||||
foreach (var item in ids)
|
||||
Reserve(item);
|
||||
}
|
||||
public void Release(int id)
|
||||
{
|
||||
CheckOrResize(id);
|
||||
#if DEBUG
|
||||
if (IsFree(id) || IsNullID(id))
|
||||
{
|
||||
if (IsFree(id)) ThrowHalper.ThrowIsNotUsed(id);
|
||||
else ThrowHalper.ThrowIsNullID(id);
|
||||
}
|
||||
#endif
|
||||
if (_sparseState[id] == IDState.Used)
|
||||
Remove(id);
|
||||
else
|
||||
RemoveReserved(id);
|
||||
_sparseState[id] = IDState.Free;
|
||||
}
|
||||
public void ReleaseRange(IEnumerable<int> ids)
|
||||
{
|
||||
foreach (var item in ids)
|
||||
Release(item);
|
||||
}
|
||||
public void ReleaseAll()
|
||||
{
|
||||
_usedCount = 0;
|
||||
_reservedCount = 0;
|
||||
for (int i = 0; i < _size;)
|
||||
{
|
||||
_sparse[i] = i;
|
||||
_sparseState[i] = IDState.Free;
|
||||
_dense[i] = i++;
|
||||
_sparse[i] = i;
|
||||
_sparseState[i] = IDState.Free;
|
||||
_dense[i] = i++;
|
||||
_sparse[i] = i;
|
||||
_sparseState[i] = IDState.Free;
|
||||
_dense[i] = i++;
|
||||
_sparse[i] = i;
|
||||
_sparseState[i] = IDState.Free;
|
||||
_dense[i] = i++;
|
||||
}
|
||||
SetNullID(_nullID);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Checks
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsFree(int id) => _sparseState[id] == IDState.Free;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsReserved(int id) => _sparseState[id] == IDState.Reserved;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsUsed(int id) => _sparseState[id] == IDState.Used;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool IsNullID(int id) => id == _nullID;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Sort
|
||||
/// <summary>O(n) Sort. n = Size. Allows the UseFree method to return denser ids.</summary>
|
||||
public void Sort()
|
||||
{
|
||||
int usedInc = _reservedCount;
|
||||
int reservedInc = 0;
|
||||
int freeInc = _reservedCount + _usedCount;
|
||||
for (int i = 0; i < _size; i++)
|
||||
{
|
||||
switch (_sparseState[i])
|
||||
{
|
||||
case IDState.Free:
|
||||
_sparse[i] = freeInc;
|
||||
_dense[freeInc++] = i;
|
||||
break;
|
||||
case IDState.Reserved:
|
||||
_sparse[i] = reservedInc;
|
||||
_dense[reservedInc++] = i;
|
||||
break;
|
||||
case IDState.Used:
|
||||
_sparse[i] = usedInc;
|
||||
_dense[usedInc++] = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Other
|
||||
private void SetNullID(int nullID)
|
||||
{
|
||||
_nullID = nullID;
|
||||
if (nullID >= 0)
|
||||
{
|
||||
AddReserved(nullID);
|
||||
_sparseState[nullID] = IDState.Reserved;
|
||||
}
|
||||
}
|
||||
private bool IsValid()
|
||||
{
|
||||
for (int i = 0; i < _usedCount; i++)
|
||||
{
|
||||
if (_sparse[_dense[i]] != i || _dense[_sparse[i]] != i)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
private void CheckOrResize(int id)
|
||||
{
|
||||
if (id > _size)
|
||||
{
|
||||
int leftBit = 0;
|
||||
while (id != 0)
|
||||
{
|
||||
id >>= 1;
|
||||
id &= int.MaxValue;
|
||||
leftBit++;
|
||||
}
|
||||
if (leftBit >= 32)
|
||||
Resize(int.MaxValue);
|
||||
else
|
||||
Resize(1 << leftBit);
|
||||
}
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Add(int value)
|
||||
{
|
||||
Swap(value, _reservedCount + _usedCount++);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Remove(int value)
|
||||
{
|
||||
Swap(value, _reservedCount + --_usedCount);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void AddReserved(int value)
|
||||
{
|
||||
Swap(value, _reservedCount + _usedCount);
|
||||
Swap(value, _reservedCount++);
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void RemoveReserved(int value)
|
||||
{
|
||||
Swap(value, --_reservedCount);
|
||||
Swap(value, _reservedCount + _usedCount);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Swap(int sparseIndex, int denseIndex)
|
||||
{
|
||||
int _dense_denseIndex_ = _dense[denseIndex];
|
||||
int _sparse_sparseIndex_ = _sparse[sparseIndex];
|
||||
_dense[denseIndex] = _dense[_sparse_sparseIndex_];
|
||||
_dense[_sparse_sparseIndex_] = _dense_denseIndex_;
|
||||
_sparse[_dense_denseIndex_] = _sparse_sparseIndex_;
|
||||
_sparse[sparseIndex] = denseIndex;
|
||||
}
|
||||
private void Resize(int newSize)
|
||||
{
|
||||
if (newSize < MIN_SIZE)
|
||||
newSize = MIN_SIZE;
|
||||
Array.Resize(ref _dense, newSize);
|
||||
Array.Resize(ref _sparse, newSize);
|
||||
Array.Resize(ref _sparseState, newSize);
|
||||
for (int i = _size; i < newSize;)
|
||||
{
|
||||
_sparse[i] = i;
|
||||
_dense[i] = i++;
|
||||
_sparse[i] = i;
|
||||
_dense[i] = i++;
|
||||
_sparse[i] = i;
|
||||
_dense[i] = i++;
|
||||
_sparse[i] = i;
|
||||
_dense[i] = i++;
|
||||
}
|
||||
_size = newSize;
|
||||
Resized(newSize);
|
||||
}
|
||||
#endregion
|
||||
|
||||
public delegate void ResizedHandler(int newSize);
|
||||
public event ResizedHandler Resized = delegate { };
|
||||
|
||||
internal enum IDState : byte
|
||||
{
|
||||
Free,
|
||||
Reserved,
|
||||
Used,
|
||||
}
|
||||
|
||||
#region Enumerable
|
||||
public UsedSpan Used;
|
||||
public ReservedSpan Reserved;
|
||||
public Enumerator GetEnumerator() => new Enumerator(_dense, _reservedCount, _usedCount);
|
||||
IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
public struct Enumerator : IEnumerator<int>
|
||||
{
|
||||
private readonly int[] _dense;
|
||||
private readonly int _count;
|
||||
private int _index;
|
||||
public int Current => _dense[_index];
|
||||
object IEnumerator.Current => Current;
|
||||
public Enumerator(int[] dense, int startIndex, int count)
|
||||
{
|
||||
_dense = dense;
|
||||
_count = startIndex + count;
|
||||
_index = startIndex - 1;
|
||||
}
|
||||
public bool MoveNext() => ++_index < _count;
|
||||
public void Dispose() { }
|
||||
public void Reset() => _index = -1;
|
||||
}
|
||||
public readonly struct UsedSpan : IEnumerable<int>
|
||||
{
|
||||
private readonly IdDispenser _instance;
|
||||
public int Count => _instance._usedCount;
|
||||
internal UsedSpan(IdDispenser instance) => _instance = instance;
|
||||
public Enumerator GetEnumerator() => new Enumerator(_instance._dense, _instance._reservedCount, _instance._usedCount);
|
||||
IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
public readonly struct ReservedSpan : IEnumerable<int>
|
||||
{
|
||||
private readonly IdDispenser _instance;
|
||||
public int Count => _instance._reservedCount;
|
||||
internal ReservedSpan(IdDispenser instance) => _instance = instance;
|
||||
public Enumerator GetEnumerator() => new Enumerator(_instance._dense, 0, _instance._reservedCount);
|
||||
IEnumerator<int> IEnumerable<int>.GetEnumerator() => GetEnumerator();
|
||||
IEnumerator IEnumerable.GetEnumerator() => GetEnumerator();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Utils
|
||||
private static class ThrowHalper
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowIsAlreadyInUse(int id) => throw new ArgumentException($"Id {id} is already in use.");
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowIsHasBeenReserved(int id) => throw new ArgumentException($"Id {id} has been reserved.");
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowIsNotUsed(int id) => throw new ArgumentException($"Id {id} is not used.");
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowIsNotAvailable(int id) => throw new ArgumentException($"Id {id} is not available.");
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowIsNullID(int id) => throw new ArgumentException($"Id {id} cannot be released because it is used as a null id.");
|
||||
}
|
||||
|
||||
internal class DebuggerProxy
|
||||
{
|
||||
private IdDispenser _dispenser;
|
||||
public DebuggerProxy(IdDispenser dispenser) => _dispenser = dispenser;
|
||||
#if DEBUG
|
||||
public IEnumerable<int> Used => _dispenser.Used;
|
||||
public IEnumerable<int> Reserved => _dispenser.Reserved;
|
||||
public Pair[] Pairs
|
||||
{
|
||||
get
|
||||
{
|
||||
Pair[] result = new Pair[_dispenser.Size];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = new Pair(_dispenser._dense[i], _dispenser._sparse[i]);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
public ID[] All
|
||||
{
|
||||
get
|
||||
{
|
||||
ID[] result = new ID[_dispenser.Size];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
{
|
||||
int id = _dispenser._dense[i];
|
||||
result[i] = new ID(id, _dispenser._sparseState[id].ToString());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
public bool IsValid => _dispenser.IsValid();
|
||||
public int Count => _dispenser.ReservedCount;
|
||||
public int Size => _dispenser.Size;
|
||||
public int NullID => _dispenser._nullID;
|
||||
internal readonly struct ID
|
||||
{
|
||||
public readonly int id;
|
||||
public readonly string state;
|
||||
public ID(int id, string state) { this.id = id; this.state = state; }
|
||||
public override string ToString() => $"{id} - {state}";
|
||||
}
|
||||
internal readonly struct Pair
|
||||
{
|
||||
public readonly int dense;
|
||||
public readonly int sparse;
|
||||
public Pair(int dense, int sparse) { this.dense = dense; this.sparse = sparse; }
|
||||
public override string ToString() => $"{dense} - {sparse}";
|
||||
}
|
||||
#endif
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
11
src/Utils/IdDispenser.cs.meta
Normal file
11
src/Utils/IdDispenser.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54c009cfa7ae0fd49938525468703c8b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -38,13 +38,13 @@ namespace DCFApixels.DragonECS
|
||||
#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)
|
||||
{
|
||||
|
||||
228
src/Utils/SparseArray.cs
Normal file
228
src/Utils/SparseArray.cs
Normal file
@ -0,0 +1,228 @@
|
||||
//SparseArray. Analogous to Dictionary<int, T>, but faster.
|
||||
//Benchmark result of indexer.get speed test with 300 elements:
|
||||
//[Dictinary: 5.786us] [SparseArray: 2.047us].
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS.Utils
|
||||
{
|
||||
public class SparseArray<TValue>
|
||||
{
|
||||
public const int MIN_CAPACITY_BITS_OFFSET = 4;
|
||||
public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET;
|
||||
private const int EMPTY = -1;
|
||||
|
||||
private int[] _buckets = Array.Empty<int>();
|
||||
private Entry[] _entries = Array.Empty<Entry>();
|
||||
|
||||
private int _count;
|
||||
|
||||
private int _freeList;
|
||||
private int _freeCount;
|
||||
|
||||
private int _modBitMask;
|
||||
|
||||
#region Properties
|
||||
public TValue this[int keyX, int keyY]
|
||||
{
|
||||
get => _entries[FindEntry((keyX << 16) | keyY)].value;
|
||||
set => Insert(keyX + (keyY << 16), value);
|
||||
}
|
||||
public TValue this[int key]
|
||||
{
|
||||
get => _entries[FindEntry(key)].value;
|
||||
set => Insert(key, value);
|
||||
}
|
||||
public int Count => _count;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public SparseArray(int minCapacity = MIN_CAPACITY)
|
||||
{
|
||||
minCapacity = NormalizeCapacity(minCapacity);
|
||||
_buckets = new int[minCapacity];
|
||||
for (int i = 0; i < minCapacity; i++)
|
||||
_buckets[i] = EMPTY;
|
||||
_entries = new Entry[minCapacity];
|
||||
_modBitMask = (minCapacity - 1) & 0x7FFFFFFF;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Add/Contains/Remove
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(int keyX, int keyY, TValue value) => Add((keyX << 16) | keyY, value);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(int key, TValue value)
|
||||
{
|
||||
#if DEBUG
|
||||
if (Contains(key))
|
||||
throw new ArgumentException("Contains(hashKey) is true");
|
||||
#endif
|
||||
Insert(key, value);
|
||||
}
|
||||
|
||||
public bool Contains(int keyX, int keyY) => FindEntry((keyX << 16) | keyY) >= 0;
|
||||
public bool Contains(int key) => FindEntry(key) >= 0;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Remove(int keyX, int keyY) => Remove((keyX << 16) | keyY);
|
||||
public bool Remove(int key)
|
||||
{
|
||||
int bucket = key & _modBitMask;
|
||||
int last = -1;
|
||||
for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next)
|
||||
{
|
||||
if (_entries[i].hashKey == key)
|
||||
{
|
||||
if (last < 0)
|
||||
{
|
||||
_buckets[bucket] = _entries[i].next;
|
||||
}
|
||||
else
|
||||
{
|
||||
_entries[last].next = _entries[i].next;
|
||||
}
|
||||
_entries[i].next = _freeList;
|
||||
_entries[i].hashKey = -1;
|
||||
_entries[i].value = default;
|
||||
_freeList = i;
|
||||
_freeCount++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Find/Insert
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private int FindEntry(int key)
|
||||
{
|
||||
for (int i = _buckets[key & _modBitMask]; i >= 0; i = _entries[i].next)
|
||||
if (_entries[i].hashKey == key) return i;
|
||||
return -1;
|
||||
}
|
||||
private void Insert(int key, TValue value)
|
||||
{
|
||||
int targetBucket = key & _modBitMask;
|
||||
|
||||
for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next)
|
||||
{
|
||||
if (_entries[i].hashKey == key)
|
||||
{
|
||||
_entries[i].value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int index;
|
||||
if (_freeCount > 0)
|
||||
{
|
||||
index = _freeList;
|
||||
_freeList = _entries[index].next;
|
||||
_freeCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_count == _entries.Length)
|
||||
{
|
||||
Resize();
|
||||
targetBucket = key & _modBitMask;
|
||||
}
|
||||
index = _count++;
|
||||
}
|
||||
|
||||
_entries[index].next = _buckets[targetBucket];
|
||||
_entries[index].hashKey = key;
|
||||
_entries[index].value = value;
|
||||
_buckets[targetBucket] = index;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TryGetValue
|
||||
public bool TryGetValue(int keyX, int keyY, out TValue value)
|
||||
{
|
||||
int index = FindEntry((keyX << 16) | keyY);
|
||||
if (index < 0)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
value = _entries[index].value;
|
||||
return true;
|
||||
}
|
||||
public bool TryGetValue(int key, out TValue value)
|
||||
{
|
||||
int index = FindEntry(key);
|
||||
if (index < 0)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
value = _entries[index].value;
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Clear
|
||||
public void Clear()
|
||||
{
|
||||
if (_count > 0)
|
||||
{
|
||||
for (int i = 0; i < _buckets.Length; i++)
|
||||
{
|
||||
_buckets[i] = -1;
|
||||
}
|
||||
Array.Clear(_entries, 0, _count);
|
||||
_count = 0;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Resize
|
||||
private void Resize()
|
||||
{
|
||||
int newSize = _buckets.Length << 1;
|
||||
_modBitMask = (newSize - 1) & 0x7FFFFFFF;
|
||||
|
||||
Contract.Assert(newSize >= _entries.Length);
|
||||
int[] newBuckets = new int[newSize];
|
||||
for (int i = 0; i < newBuckets.Length; i++)
|
||||
newBuckets[i] = EMPTY;
|
||||
|
||||
Entry[] newEntries = new Entry[newSize];
|
||||
Array.Copy(_entries, 0, newEntries, 0, _count);
|
||||
for (int i = 0; i < _count; i++)
|
||||
{
|
||||
if (newEntries[i].hashKey >= 0)
|
||||
{
|
||||
int bucket = newEntries[i].hashKey % newSize;
|
||||
newEntries[i].next = newBuckets[bucket];
|
||||
newBuckets[bucket] = i;
|
||||
}
|
||||
}
|
||||
_buckets = newBuckets;
|
||||
_entries = newEntries;
|
||||
}
|
||||
|
||||
private int NormalizeCapacity(int capacity)
|
||||
{
|
||||
int result = MIN_CAPACITY;
|
||||
while (result < capacity) result <<= 1;
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Utils
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
private struct Entry
|
||||
{
|
||||
public int next; // Index of next entry, -1 if last
|
||||
public int hashKey;
|
||||
public TValue value;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
11
src/Utils/SparseArray.cs.meta
Normal file
11
src/Utils/SparseArray.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 25afb1113718f904c85dcbedd993d85e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
236
src/Utils/SparseArray64.cs
Normal file
236
src/Utils/SparseArray64.cs
Normal file
@ -0,0 +1,236 @@
|
||||
//SparseArray64. Analogous to Dictionary<long, T>, but faster.
|
||||
//Benchmark result of indexer.get speed test with 300 elements:
|
||||
//[Dictinary: 6.705us] [SparseArray64: 2.512us].
|
||||
using System;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DCFApixels.DragonECS.Utils
|
||||
{
|
||||
internal class SparseArray64<TValue>
|
||||
{
|
||||
public const int MIN_CAPACITY_BITS_OFFSET = 4;
|
||||
public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET;
|
||||
private const int EMPTY = -1;
|
||||
|
||||
private int[] _buckets = Array.Empty<int>();
|
||||
private Entry[] _entries = Array.Empty<Entry>();
|
||||
|
||||
private int _count;
|
||||
|
||||
private int _freeList;
|
||||
private int _freeCount;
|
||||
|
||||
private int _modBitMask;
|
||||
|
||||
#region Properties
|
||||
public ref TValue this[long keyX, long keyY]
|
||||
{
|
||||
get => ref _entries[FindEntry(keyX + (keyY << 32))].value;
|
||||
//set => Insert(keyX + (keyY << 32), value);
|
||||
}
|
||||
public ref TValue this[long key]
|
||||
{
|
||||
get => ref _entries[FindEntry(key)].value;
|
||||
//set => Insert(key, value);
|
||||
}
|
||||
|
||||
public int Count => _count;
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public SparseArray64(int minCapacity = MIN_CAPACITY)
|
||||
{
|
||||
minCapacity = NormalizeCapacity(minCapacity);
|
||||
_buckets = new int[minCapacity];
|
||||
for (int i = 0; i < minCapacity; i++)
|
||||
_buckets[i] = EMPTY;
|
||||
_entries = new Entry[minCapacity];
|
||||
_modBitMask = (minCapacity - 1) & 0x7FFFFFFF;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Add
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(long keyX, long keyY, TValue value) => Add(keyX + (keyY << 32), value);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Add(long key, TValue value)
|
||||
{
|
||||
#if DEBUG
|
||||
if (Contains(key))
|
||||
throw new ArgumentException("Contains(hashKey) is true");
|
||||
#endif
|
||||
Insert(key, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Find/Insert/Remove
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private int FindEntry(long key)
|
||||
{
|
||||
for (int i = _buckets[unchecked((int)key & _modBitMask)]; i >= 0; i = _entries[i].next)
|
||||
if (_entries[i].hashKey == key) return i;
|
||||
return -1;
|
||||
}
|
||||
private void Insert(long key, TValue value)
|
||||
{
|
||||
int targetBucket = unchecked((int)key & _modBitMask);
|
||||
|
||||
for (int i = _buckets[targetBucket]; i >= 0; i = _entries[i].next)
|
||||
{
|
||||
if (_entries[i].hashKey == key)
|
||||
{
|
||||
_entries[i].value = value;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int index;
|
||||
if (_freeCount > 0)
|
||||
{
|
||||
index = _freeList;
|
||||
_freeList = _entries[index].next;
|
||||
_freeCount--;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_count == _entries.Length)
|
||||
{
|
||||
Resize();
|
||||
targetBucket = unchecked((int)key & _modBitMask);
|
||||
}
|
||||
index = _count++;
|
||||
}
|
||||
|
||||
_entries[index].next = _buckets[targetBucket];
|
||||
_entries[index].hashKey = key;
|
||||
_entries[index].value = value;
|
||||
_buckets[targetBucket] = index;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Remove(long keyX, long keyY) => Remove(keyX + (keyY << 32));
|
||||
public bool Remove(long key)
|
||||
{
|
||||
int bucket = unchecked((int)key & _modBitMask);
|
||||
int last = -1;
|
||||
for (int i = _buckets[bucket]; i >= 0; last = i, i = _entries[i].next)
|
||||
{
|
||||
if (_entries[i].hashKey == key)
|
||||
{
|
||||
if (last < 0)
|
||||
{
|
||||
_buckets[bucket] = _entries[i].next;
|
||||
}
|
||||
else
|
||||
{
|
||||
_entries[last].next = _entries[i].next;
|
||||
}
|
||||
_entries[i].next = _freeList;
|
||||
_entries[i].hashKey = -1;
|
||||
_entries[i].value = default;
|
||||
_freeList = i;
|
||||
_freeCount++;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region TryGetValue
|
||||
public bool TryGetValue(long key, out TValue value)
|
||||
{
|
||||
int index = FindEntry(key);
|
||||
if (index < 0)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
value = _entries[index].value;
|
||||
return true;
|
||||
}
|
||||
public bool TryGetValue(long keyX, long keyY, out TValue value)
|
||||
{
|
||||
int index = FindEntry(keyX + (keyY << 32));
|
||||
if (index < 0)
|
||||
{
|
||||
value = default;
|
||||
return false;
|
||||
}
|
||||
value = _entries[index].value;
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Contains
|
||||
public bool Contains(long keyX, long keyY)
|
||||
{
|
||||
return FindEntry(keyX + (keyY << 32)) >= 0;
|
||||
}
|
||||
public bool Contains(long key)
|
||||
{
|
||||
return FindEntry(key) >= 0;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Clear
|
||||
public void Clear()
|
||||
{
|
||||
if (_count > 0)
|
||||
{
|
||||
for (int i = 0; i < _buckets.Length; i++)
|
||||
{
|
||||
_buckets[i] = -1;
|
||||
}
|
||||
Array.Clear(_entries, 0, _count);
|
||||
_count = 0;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Resize
|
||||
private void Resize()
|
||||
{
|
||||
int newSize = _buckets.Length << 1;
|
||||
_modBitMask = (newSize - 1) & 0x7FFFFFFF;
|
||||
|
||||
Contract.Assert(newSize >= _entries.Length);
|
||||
int[] newBuckets = new int[newSize];
|
||||
for (int i = 0; i < newBuckets.Length; i++)
|
||||
newBuckets[i] = EMPTY;
|
||||
|
||||
Entry[] newEntries = new Entry[newSize];
|
||||
Array.Copy(_entries, 0, newEntries, 0, _count);
|
||||
for (int i = 0; i < _count; i++)
|
||||
{
|
||||
if (newEntries[i].hashKey >= 0)
|
||||
{
|
||||
int bucket = unchecked((int)newEntries[i].hashKey & _modBitMask);
|
||||
newEntries[i].next = newBuckets[bucket];
|
||||
newBuckets[bucket] = i;
|
||||
}
|
||||
}
|
||||
_buckets = newBuckets;
|
||||
_entries = newEntries;
|
||||
}
|
||||
|
||||
private int NormalizeCapacity(int capacity)
|
||||
{
|
||||
int result = MIN_CAPACITY;
|
||||
while (result < capacity) result <<= 1;
|
||||
return result;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Utils
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 4)]
|
||||
private struct Entry
|
||||
{
|
||||
public int next; // Index of next entry, -1 if last
|
||||
public long hashKey;
|
||||
public TValue value;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
11
src/Utils/SparseArray64.cs.meta
Normal file
11
src/Utils/SparseArray64.cs.meta
Normal file
@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cab791e97df38274294035e89dc9de8a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@ -1,299 +0,0 @@
|
||||
using DCFApixels.DragonECS.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
//TODO этот класс требует переработки, изначально такая конструкция имела хорошую производительность, но сейчас он слишком раздулся
|
||||
internal static class WorldMetaStorage
|
||||
{
|
||||
private static int _tokenCount = 0;
|
||||
private static List<ResizerBase> _resizers = new List<ResizerBase>();
|
||||
private static WorldTypeMeta[] _metas = new WorldTypeMeta[0];
|
||||
private static Dictionary<Type, int> _worldIds = new Dictionary<Type, int>();
|
||||
private static class WorldIndex<TWorldArchetype>
|
||||
{
|
||||
public static int id = GetWorldID(typeof(TWorldArchetype));
|
||||
}
|
||||
private static int GetToken(Type worldType)
|
||||
{
|
||||
WorldTypeMeta meta = new WorldTypeMeta(worldType);
|
||||
meta.id = _tokenCount;
|
||||
Array.Resize(ref _metas, ++_tokenCount);
|
||||
_metas[_tokenCount - 1] = meta;
|
||||
|
||||
foreach (var item in _resizers)
|
||||
item.Resize(_tokenCount);
|
||||
return _tokenCount - 1;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetWorldID(Type worldType)
|
||||
{
|
||||
if (!_worldIds.TryGetValue(worldType, out int id))
|
||||
{
|
||||
id = GetToken(worldType);
|
||||
_worldIds.Add(worldType, id);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static Type GetWorldType(int worldTypeID) => _metas[worldTypeID].worldType;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetWorldID<TWorldArchetype>() => WorldIndex<TWorldArchetype>.id;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetComponentID<T>(int worldID) => Component<T>.Get(worldID);
|
||||
public static int GetComponentID(Type type, int worldID) => _metas[worldID].GetComponentID(type);
|
||||
public static bool IsComponentTypeDeclared(int worldID, Type type) => _metas[worldID].IsDeclaredComponentType(type);
|
||||
public static Type GetComponentType(int worldID, int componentID) => _metas[worldID].GetComponentType(componentID);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetPoolID<T>(int worldID) => Pool<T>.Get(worldID);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetAspectID<T>(int worldID) => Aspect<T>.Get(worldID);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetExecutorID<T>(int worldID) => Executor<T>.Get(worldID);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int GetWorldComponentID<T>(int worldID) => WorldComponent<T>.Get(worldID);
|
||||
public static int GetWorldComponentID(Type type, int worldID) => _metas[worldID].GetWorldComponentID(type);
|
||||
|
||||
|
||||
private abstract class ResizerBase
|
||||
{
|
||||
public abstract Type Type { get; }
|
||||
public abstract int[] IDS { get; }
|
||||
public abstract void Resize(int size);
|
||||
}
|
||||
|
||||
#region Containers
|
||||
public static class PoolComponentIdArrays
|
||||
{
|
||||
private static Dictionary<Type, int[]> _componentTypeArrayPairs = new Dictionary<Type, int[]>();
|
||||
|
||||
public static int[] GetIdsArray(Type type)
|
||||
{
|
||||
int targetSize = _tokenCount;
|
||||
if (!_componentTypeArrayPairs.TryGetValue(type, out int[] result))
|
||||
{
|
||||
result = new int[targetSize];
|
||||
for (int i = 0; i < result.Length; i++)
|
||||
result[i] = -1;
|
||||
_componentTypeArrayPairs.Add(type, result);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.Length < targetSize)
|
||||
{
|
||||
int oldSize = result.Length;
|
||||
Array.Resize(ref result, targetSize);
|
||||
ArrayUtility.Fill(result, -1, oldSize, targetSize);
|
||||
_componentTypeArrayPairs[type] = result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static int GetComponentID(Type type, int token)
|
||||
{
|
||||
GetIdsArray(type);
|
||||
ref int id = ref _componentTypeArrayPairs[type][token];
|
||||
if (id < 0)
|
||||
id = _metas[token].DeclareComponentType(type);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
private static class Pool<T>
|
||||
{
|
||||
public static int[] ids;
|
||||
private static Type componentType = typeof(T).GetGenericArguments()[0];
|
||||
static Pool()
|
||||
{
|
||||
ids = PoolComponentIdArrays.GetIdsArray(componentType);
|
||||
_resizers.Add(new Resizer());
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Get(int token)
|
||||
{
|
||||
ref int id = ref ids[token];
|
||||
if (id < 0)
|
||||
{
|
||||
id = PoolComponentIdArrays.GetComponentID(componentType, token);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
private sealed class Resizer : ResizerBase
|
||||
{
|
||||
public override Type Type => typeof(T);
|
||||
public override int[] IDS => ids;
|
||||
public override void Resize(int size)
|
||||
{
|
||||
ids = PoolComponentIdArrays.GetIdsArray(componentType);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static class Component<T>
|
||||
{
|
||||
public static int[] ids;
|
||||
static Component()
|
||||
{
|
||||
ids = PoolComponentIdArrays.GetIdsArray(typeof(T));
|
||||
_resizers.Add(new Resizer());
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Get(int token)
|
||||
{
|
||||
ref int id = ref ids[token];
|
||||
if (id < 0)
|
||||
{
|
||||
id = PoolComponentIdArrays.GetComponentID(typeof(T), token);
|
||||
}
|
||||
return id;
|
||||
}
|
||||
private sealed class Resizer : ResizerBase
|
||||
{
|
||||
public override Type Type => typeof(T);
|
||||
public override int[] IDS => ids;
|
||||
public override void Resize(int size)
|
||||
{
|
||||
ids = PoolComponentIdArrays.GetIdsArray(typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
private static class Aspect<T>
|
||||
{
|
||||
public static int[] ids;
|
||||
static Aspect()
|
||||
{
|
||||
ids = new int[_tokenCount];
|
||||
for (int i = 0; i < ids.Length; i++)
|
||||
ids[i] = -1;
|
||||
_resizers.Add(new Resizer());
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Get(int token)
|
||||
{
|
||||
ref int id = ref ids[token];
|
||||
if (id < 0)
|
||||
id = _metas[token].aspectsCount++;
|
||||
return id;
|
||||
}
|
||||
private sealed class Resizer : ResizerBase
|
||||
{
|
||||
public override Type Type => typeof(T);
|
||||
public override int[] IDS => ids;
|
||||
public override void Resize(int size)
|
||||
{
|
||||
int oldSize = ids.Length;
|
||||
Array.Resize(ref ids, size);
|
||||
ArrayUtility.Fill(ids, -1, oldSize, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static class Executor<T>
|
||||
{
|
||||
public static int[] ids;
|
||||
static Executor()
|
||||
{
|
||||
ids = new int[_tokenCount];
|
||||
for (int i = 0; i < ids.Length; i++)
|
||||
ids[i] = -1;
|
||||
_resizers.Add(new Resizer());
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Get(int token)
|
||||
{
|
||||
ref int id = ref ids[token];
|
||||
if (id < 0)
|
||||
id = _metas[token].executorsCount++;
|
||||
return id;
|
||||
}
|
||||
private sealed class Resizer : ResizerBase
|
||||
{
|
||||
public override Type Type => typeof(T);
|
||||
public override int[] IDS => ids;
|
||||
public override void Resize(int size)
|
||||
{
|
||||
int oldSize = ids.Length;
|
||||
Array.Resize(ref ids, size);
|
||||
ArrayUtility.Fill(ids, -1, oldSize, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
private static class WorldComponent<T>
|
||||
{
|
||||
public static int[] ids;
|
||||
static WorldComponent()
|
||||
{
|
||||
ids = new int[_tokenCount];
|
||||
for (int i = 0; i < ids.Length; i++)
|
||||
ids[i] = -1;
|
||||
_resizers.Add(new Resizer());
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static int Get(int token)
|
||||
{
|
||||
ref int id = ref ids[token];
|
||||
if (id < 0)
|
||||
id = _metas[token].GetWorldComponentID(typeof(T));
|
||||
return id;
|
||||
}
|
||||
private sealed class Resizer : ResizerBase
|
||||
{
|
||||
public override Type Type => typeof(T);
|
||||
public override int[] IDS => ids;
|
||||
public override void Resize(int size)
|
||||
{
|
||||
int oldSize = ids.Length;
|
||||
Array.Resize(ref ids, size);
|
||||
ArrayUtility.Fill(ids, -1, oldSize, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
private class WorldTypeMeta
|
||||
{
|
||||
public readonly Type worldType;
|
||||
public int id;
|
||||
public int componentCount;
|
||||
public int aspectsCount;
|
||||
public int executorsCount;
|
||||
public int worldComponentCount;
|
||||
private Type[] _types = new Type[10];
|
||||
private Dictionary<Type, int> _declaredComponentTypes = new Dictionary<Type, int>();
|
||||
private Dictionary<Type, int> _declaredWorldComponentTypes = new Dictionary<Type, int>();
|
||||
|
||||
public WorldTypeMeta(Type worldType)
|
||||
{
|
||||
this.worldType = worldType;
|
||||
}
|
||||
|
||||
public int DeclareComponentType(Type type)
|
||||
{
|
||||
int id = componentCount++;
|
||||
if (_types.Length <= id)
|
||||
Array.Resize(ref _types, id + 10);
|
||||
_types[id] = type;
|
||||
_declaredComponentTypes.Add(type, id);
|
||||
return id;
|
||||
}
|
||||
public bool IsDeclaredComponentType(Type type) => _declaredComponentTypes.ContainsKey(type);
|
||||
public Type GetComponentType(int componentID) => _types[componentID];
|
||||
public int GetComponentID(Type type) => PoolComponentIdArrays.GetComponentID(type, id);
|
||||
|
||||
|
||||
public int DeclareWorldComponentType(Type type)
|
||||
{
|
||||
int id = worldComponentCount++;
|
||||
_declaredWorldComponentTypes.Add(type, id);
|
||||
return id;
|
||||
}
|
||||
public bool IsDeclaredWorldComponentType(Type type) => _declaredWorldComponentTypes.ContainsKey(type);
|
||||
public int GetWorldComponentID(Type type)
|
||||
{
|
||||
if (!_declaredWorldComponentTypes.TryGetValue(type, out int id))
|
||||
id = DeclareWorldComponentType(type);
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,10 @@
|
||||
#pragma warning disable IDE1006
|
||||
using DCFApixels.DragonECS.Internal;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Runtime.InteropServices;
|
||||
using static DCFApixels.DragonECS.entlong.ThrowHalper;
|
||||
|
||||
namespace DCFApixels.DragonECS
|
||||
{
|
||||
@ -12,16 +12,17 @@ namespace DCFApixels.DragonECS
|
||||
/// <summary>Strong identifier/Permanent entity identifier</summary>
|
||||
[StructLayout(LayoutKind.Explicit, Pack = 2, Size = 8)]
|
||||
[DebuggerTypeProxy(typeof(DebuggerProxy))]
|
||||
[Serializable]
|
||||
public readonly struct entlong : IEquatable<long>, IEquatable<entlong>
|
||||
{
|
||||
public static readonly entlong NULL = default;
|
||||
[FieldOffset(0)]
|
||||
internal readonly long full; //Union
|
||||
[FieldOffset(0)]
|
||||
[FieldOffset(0), NonSerialized]
|
||||
internal readonly int id;
|
||||
[FieldOffset(4)]
|
||||
[FieldOffset(4), NonSerialized]
|
||||
internal readonly short gen;
|
||||
[FieldOffset(6)]
|
||||
[FieldOffset(6), NonSerialized]
|
||||
internal readonly short world;
|
||||
|
||||
#region Properties
|
||||
@ -33,7 +34,7 @@ namespace DCFApixels.DragonECS
|
||||
public bool IsNull
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
get => this == NULL;
|
||||
get => full == 0L;
|
||||
}
|
||||
public int ID
|
||||
{
|
||||
@ -41,7 +42,7 @@ namespace DCFApixels.DragonECS
|
||||
get
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!IsAlive) ThrowIsNotAlive(this);
|
||||
if (!IsAlive) Throw.Ent_ThrowIsNotAlive(this);
|
||||
#endif
|
||||
return id;
|
||||
}
|
||||
@ -52,7 +53,7 @@ namespace DCFApixels.DragonECS
|
||||
get
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!IsAlive) ThrowIsNotAlive(this);
|
||||
if (!IsAlive) Throw.Ent_ThrowIsNotAlive(this);
|
||||
#endif
|
||||
return gen;
|
||||
}
|
||||
@ -63,7 +64,7 @@ namespace DCFApixels.DragonECS
|
||||
get
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!IsAlive) ThrowIsNotAlive(this);
|
||||
if (!IsAlive) Throw.Ent_ThrowIsNotAlive(this);
|
||||
#endif
|
||||
return EcsWorld.GetWorld(world);
|
||||
}
|
||||
@ -74,7 +75,7 @@ namespace DCFApixels.DragonECS
|
||||
get
|
||||
{
|
||||
#if (DEBUG && !DISABLE_DEBUG) || ENABLE_DRAGONECS_ASSERT_CHEKS
|
||||
if (!IsAlive) ThrowIsNotAlive(this);
|
||||
if (!IsAlive) Throw.Ent_ThrowIsNotAlive(this);
|
||||
#endif
|
||||
return world;
|
||||
}
|
||||
@ -94,6 +95,12 @@ namespace DCFApixels.DragonECS
|
||||
{
|
||||
this.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 TryGetters
|
||||
@ -107,57 +114,63 @@ namespace DCFApixels.DragonECS
|
||||
world = EcsWorld.GetWorld(this.world);
|
||||
return IsAlive;
|
||||
}
|
||||
public bool TryGetWorldID(out int worldID)
|
||||
{
|
||||
worldID = world;
|
||||
return IsAlive;
|
||||
}
|
||||
public void Unpack(out EcsWorld world, out int id)
|
||||
{
|
||||
world = EcsWorld.GetWorld(this.world);
|
||||
id = this.id;
|
||||
}
|
||||
public void Unpack(out int worldID, out int id)
|
||||
{
|
||||
worldID = world;
|
||||
id = this.id;
|
||||
}
|
||||
public bool TryUnpack(out EcsWorld world, out int id)
|
||||
{
|
||||
world = EcsWorld.GetWorld(this.world);
|
||||
id = this.id;
|
||||
return IsAlive;
|
||||
}
|
||||
public bool TryUnpack(out int worldID, out int id)
|
||||
{
|
||||
worldID = world;
|
||||
id = this.id;
|
||||
return IsAlive;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Equals
|
||||
#region operators
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(entlong other) => full == other.full;
|
||||
public static bool operator ==(entlong a, entlong b) => a.full == b.full;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public bool Equals(long other) => full == other;
|
||||
public static bool operator !=(entlong a, entlong b) => a.full != b.full;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator long(entlong a) => a.full;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator entlong(long a) => new entlong(a);
|
||||
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator int(entlong a) => a.ID;
|
||||
#endregion
|
||||
|
||||
#region Object
|
||||
#region Other
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override int GetHashCode() => unchecked((int)full) ^ (int)(full >> 32);
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override string ToString() => $"entity(id:{id} g:{gen} w:{world} {(IsNull ? "null" : IsAlive ? "alive" : "not alive")})";
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public override bool Equals(object obj) => obj is entlong other && full == other.full;
|
||||
#endregion
|
||||
|
||||
#region operators
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator ==(in entlong a, in entlong b) => a.full == b.full;
|
||||
public bool Equals(entlong other) => full == other.full;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static bool operator !=(in entlong a, in entlong b) => a.full != b.full;
|
||||
public bool Equals(long other) => full == other;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator long(in entlong a) => a.full;
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static explicit operator entlong(in long a) => new entlong(a);
|
||||
#endregion
|
||||
|
||||
#region ThrowHalper
|
||||
internal static class ThrowHalper
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowIsNotAlive(entlong entity)
|
||||
{
|
||||
if (entity.IsNull)
|
||||
throw new EcsFrameworkException($"The {entity} is null.");
|
||||
else
|
||||
throw new EcsFrameworkException($"The {entity} is not alive.");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region DebuggerProxy
|
||||
internal class DebuggerProxy
|
||||
{
|
||||
private List<object> _componentsList;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user