MultiComponent
Мультикомпоненты - позволяют наделять сущность множеством одинаковых свойств (компонентов)
- Представляют собой оптимизированные компоненты-списки, содержащие N значений
- Все элементы, всех мультикомпонентов с одним типом для всех сущностей в мире хранятся в едином хранилище, это обеспечивает:
- оптимизированное потребление памяти
- позволяет хранить до 32768 значений в одном компоненте
- быстрый и удобный доступ к данным
- быстрое создание, добавление и расширение
- не требуется создавать ссылочные типы массивов или списков внутри компонента и отслеживать их очистку или возврат в пул и тд.
- Является реализацией Component, все базовые правила и способы работы аналогичны
- Представлен в виде:
- стандартной структуры
Multi<T>
, где T - тип элементов - или в виде пользовательской структуры с интерфейсом
IMultiComponent<T>
- стандартной структуры
- На базе мультикомпонентов реализованы Отношения сущностей, например компонент
Children
является мультикомпонентом
Требуется регистрация в мире между созданием и инициализацией
Пример:
Есть два способа опеределить мультикомпонент:
Первый - Использовать дефолтный Multi<T>
// Определим тип значения мультикомпонента
public struct Item {
public string Value;
}
MyEcs.Create(EcsConfig.Default());
//...
// где defaultComponentCapacity - минимальная вместительность мультикомпонента, со степенью двойки, в диапазоне от 4 до 32768 (4, 8, 16, 32 ...)
MyEcs.World.RegisterMultiComponentType<Multi<Item>, Item>(defaultComponentCapacity: 4);
//...
MyEcs.Initialize();
Второй - Использовать кастомную реализация IMultiComponent<T>
// Определим тип значения мультикомпонента
public struct Item {
public string Value;
}
// Определим тип компонента
public struct Inventory : IMultiComponent<Item> {
// Определим значения мультикомпонента
public Multi<Item> Items;
// Добавляем любые пользовательские данные если нужно, как и в обычный компонент
public int SomeUserData;
// Реализуем метод интерфейса IMultiComponent<Item>, для доступа и автоматического менеджемента значений
// необходимо указать доступ к значениям через access.For(ref Values)
public void Access<A>(A access) where A : struct, AccessMulti<Item> => access.For(ref Items);
}
MyEcs.Create(EcsConfig.Default());
//...
// вместо Multi<Item> указываем кастомный компонент Inventory
// где defaultComponentCapacity - минимальная вместительность мультикомпонента, со степенью двойки, в диапазоне от 4 до 32768 (4, 8, 16, 32 ...)
MyEcs.World.RegisterMultiComponentType<Inventory, Item>(defaultComponentCapacity: 4);
//...
MyEcs.Initialize();
Основные операции:
MyEcs.World.RegisterMultiComponentType<Multi<Item>, Item>(defaultComponentCapacity: 4);
// При добавлениие мультикомпонента по умолчанию вместимость будет defaultComponentCapacity, по мере добавления элементов будет расширяться
// Добавление мультикомпонента как и обычного компонента
// в случае дефолтной реализации
ref var items = ref entity.Add<Multi<Item>>();
// в случае кастомной реализации
ref var inventory = ref entity.Add<Inventory>();
ref var items = ref inventory.Items;
// Доступны все остальные бызовые методы работы с компонентами
entity.TryAdd<Multi<Item>>();
entity.HasAllOf<Multi<Item>>();
// ...
// При удалении мультикомпонента - список элементов будет автоматически очищен
entity.Delete<Multi<Item>>();
entity.TryDelete<Multi<Item>>();
// При копировании и перемещении мультикомпонента - будут автоматически скопированы все элементы
entity.CopyComponentsTo<Multi<Item>>(entity2);
entity.Clone();
entity.MoveComponentsTo<Multi<Item>>(entity2);
entity.MoveTo(entity2);
// Мультикомпонент ведет себя как динамический массив (список)
// Доступны следующие операции:
// Информационные:
ushort capacity = items.Capacity; // Текущая вместимость
ushort count = items.Count; // Количество элементов
bool empty = items.IsEmpty(); // True если нет элементов
bool notEmpty = items.IsNotEmpty(); // True если есть элементы
bool full = items.IsFull(); // True если текущая емкость заполнена
// Доступ:
ref Item element = ref items[1]; // Индексатор
ref Item first = ref items.First(); // Сслыка на первый элемент
ref Item last = ref items.Last(); // Ссылка на последний элемент
foreach (ref var item in items) { // Цикл foreach по ссылкам элементов
//..
}
for (ushort i = 0; i < items.Count; i++) { // Цикл for по элементам
ref var item = ref items[i];
}
// Добавление и расширение:
items.Add(new Item()); // Добавить элемент
items.Add(new Item("a"), new Item("b"), new Item("c"), new Item("d")); // Добавить элементы (1 - 4)
items.Add(new[] { new Item("f"), new Item("g") }); // Добавить элементы из массива
items.Add(new[] { new Item("f"), new Item("g") }, 1, 1); // Добавить элементы из массива c указанием старта и количества
items.Add(ref entity2.RefMut<Multi<Item>>()); // Добавить элементы из другого компонента
items.Add(ref entity2.RefMut<Multi<Item>>(), 1, 1); // Добавить элементы из другого компонента c указанием старта и количества
items.InsertAt(idx: 1, new Item("e")); // Вставить элемент в указанный индекс, остальные элементы будут сдвинуты
items.EnsureSize(10); // Обеспечить вместимость еще на N элементов если требуется
items.Resize(16); // Расширить вместимость до N если требуется
// Удаление и очистка
items.DeleteFirst(); // Удалить первый элемент и сдвинуть последующие (если важен порядок эелментов)
items.DeleteFirstSwap(); // Удалить первый элемент и заменить последним (если НЕ важен порядок эелментов) (Быстрее)
items.DeleteLast(); // Удалить последний элемент и сбросить значение в default
items.DeleteLastFast(); // Удалить последний элемент и БЕЗ сброса значения в default (если НЕ важен сброс значения) (Быстрее)
items.DeleteAt(idx: 1); // Удалить элемент по индексу и сдвинуть последующие (если важен порядок эелментов)
items.DeleteAtSwap(idx: 1); // Удалить элемент по индексу и заменить последним (если НЕ важен порядок эелментов) (Быстрее)
items.Clear(); // Очистить элементы и сбросить в default
items.ResetCount(); // Сброс количества без очистки
// Поиск
short idx = items.IndexOf(new Item("a")); // Получить индекс элемента или -1
bool contains = items.Contains(new Item("a")); // Проверить наличие элемента с дефолтным IEqualityComparer
bool contains = items.Contains(new Item("a"), comparer); // Проверить наличие элемента с кастомным IEqualityComparer
// Дополнительно
var array = new Item[items.Capacity];
items.CopyTo(array); // Копировать элементы в указанный массив
items.Sort(); // Отсортировать элементы c дефолтным Comparer
items.Sort(comparer); // Отсортировать элементы с переданым Comparer