-
Notifications
You must be signed in to change notification settings - Fork 76
(RU) Entities
Сущность (entity) - хранит информацию об объекте. Каждой сущности присваивается уникальный индекс (ID)
Вернет новую сущность.
public void Setup()
{
ent e = Entity.Create();
}
Вернет новую сущность и назначит ей новый игровой объект (gameobject). Объект будет получен из папки Resources по имени.
public void Setup()
{
ent e = Entity.Create("Obj Fluffy Unicorn");
}
Вернет новую сущность и назначит ей новый игровой объект (gameobject). Объект будет получен из предоставленного разработчиком префаба (prefab).
public GameObject prefabFluffyUnicorn;
public void Setup()
{
ent e = Entity.Create(prefabFluffyUnicorn);
}
Игровые объекты созданные вместе с сущностью можно разместить в пуле. Для этого достаточно указать дополнительный аргумент при создании сущности.
public void Setup()
{
// помечаем что объект из пула
ent e = Entity.Create("Obj Fluffy Unicorn", true);
}
Краткое описание что такое объектный пул для незнакомых с этим шаблоном программирования.
Модель (model) - вспомогательный объект фреймворка хранящий карту компонентов их настроек для сущности. Например, нам нужно в коде создать кролика.
Каждый раз когда нам понадобится кролик мы будем описывать его. По моим представлением кролик это нечто что красиво, умеет прыгать, какать шариками и может быть съеденым.
public void Setup()
{
// помечаем что объект из пула
ent e = Entity.Create("Obj Bunny", true);
// добавляем компоненты нашему кролику
e.Add<ComponentCute>();
e.Add<ComponentJumping>();
e.Add<ComponentConsumable>();
e.Add<ComponentCanPoo>();
}
Однако описывать так сущности не очень весело. Возможно завтра у нас появится кролик-хищник мстящий людям за съеденных собратьев. Этот кролик больше не какает и вышел на тропу войны.
public void Setup()
{
// помечаем что объект из пула
ent e = Entity.Create("Obj Bunny", true);
// добавляем компоненты нашему кролику-хищнику
e.Add<ComponentCute>();
e.Add<ComponentJumping>();
e.Add<ComponentConsumable>();
e.Add<ComponentKiller>();
}
Обладая схемами этих видов кроликов мы бы могли создавать их таким образом:
ent e = Entity.Create("Obj Bunny", Models.Bunny);
ent e = Entity.Create("Obj Bunny", Models.BunnyPredator);
О том как создавать и настраивать модели можно почитать здесь.
Для уничтожения сущности используется метод Release()
ent e = Entity.Create("Obj Bunny");
e.Release();
При размещении объекта в пуле метод Release()
деактивирует объект для дальнейшего использования.
Уничтожение является отложенным действием. Сущность будет уничтожена на следующий кадр предварительно отписав все свои компоненты.
ВАЖНО: повторный вызов Release для уже мертвой сущности может приводить к багам, используйте проверку через свойство exist сущности или создайте метод-расширение:
public static bool TryRelease(this ent entity)
{
if(!entity.exist)
return false;
entity.Release();
return true;
}
Если сущность была создана вместе с объектом, то корневой трансформ объекта автоматически кешируется и к нему можно обратиться.
ent e = Entity.Create("Obj Bunny");
var tr = e.transform;
Если нужно получить конкретный компонент то можно воспользоваться вспомогательными методами get<T>
- он возьмет компонент из корня или дочернего объекта.
ent e = Entity.Create("Obj Bunny");
var spr = e.GetMono<SpriteRenderer>();
Поиск по пути производите стандартно штатными средствами Unity через transform.
ent e = Entity.Create("Obj Bunny");
var tail = e.transform.Find("Tail").GetComponent<SpriteRenderer>();
Более быстрой и эффективной альтернативой является поиско по индексу дочернего объекта.
ent e = Entity.Create("Obj Bunny");
var tail = e.GetMono<SpriteRenderer>(2); // где два является индексом дочернего объекта в кролике. Допустим, это хвостик.
ent e = Entity.Create("Obj Bunny");
ent e2 = Entity.Create("Obj Bunny Predator");
var isAlive = e.Exist(); // проверка жива ли конкретная сущность.
var isTheSame = e.Equals(e2); // проверка является ли это одной и той же сущностью.
Структура ent является типовым значением и указателем сущности, может быть автоматически конвертирована в int число. Именно через ent разработчик производит большинство операций с объектом. Структура состоит из индекса (id) и поколения (age).
Приготовьтесь. Будет спойлер. Из описний выше можно сделать предположение что сущностью является некий контейнер хранящий компоненты, трансформы и прочие переменные. На самом деле это только так выглядит для удобства восприятия. В действительности сущность это инкрементный индекс обращающиеся к множеству разных массивов компонентов. Из этого следует что размер всех массивов всегда равен или больше максимально использованному индексу.
Каждый раз когда сущность уничтожается ее индекс высвобождается и уходит в пул свободных индексов. Что-то вроде рая. Каждый раз когда сущность создается идет проверка на наличие свободных индексов. Если такой есть возвращается самый нижний и его поколение увеличивается. Если свободного индекса нет, то создается новый.
Зачем сущности поколения? Для точного сравнения. Сущность является типовым значением, а не ссылочным. Например кролик хранит информацию о цели. Целью выступает морковка с сущностью под индексом 10. Если морковка была уничтожена раньше чем кролик до нее добрался то кролик всеравно будет продолжать двигаться к cущности с индексом 10 которая к этому времени может стать шлангом для полива газона. Кролик обладает устаревшей информацией. Но благодаря поколениям можно провести сравнение. Так, морковка была индексом 10 с поколением 0, а поливочный шланг с индексом 10 уже имеет поколение 1. Хотя индексы совпадают из-за разности поколений кролик делает правильный вывод что поливочный шланг не его обед.
В фреймворке есть два подхода к созданию сущностей и объектов. Напрямую и через акторов. В этой статье был изложен принцип создания сущностей напрямую однако как разработчик вы чаще будете работать с акторами (Actor). Прямой подход к созданию не дает обратной связи от игрового объекта к фреймворку.
Случаи когда имеет смысл напрямую создавать сущности:
- Вам нужна абстрактная сущность
- Вам не нужна обратная связь от игрового объекта
- Объект взаимодействует с миром, но не мир с объектом.
Самый простой пример: пули из жанра игр shoot em up/bullet hell. Пуля может получать информацию об окружающем мире через рейкасты в системе. У пули нет unity компонента с указателем на свою сущность и поэтому окружающий мир не сможет обратиться к сущности пули (однако он разумеется будет видеть ее физический объект)
Такой подход выгоден для максимально облегченного создания примитивных объектов без monobehavior компонентов с коротким сроком жизни и очень прямолинейной логикой создал-отработал-уничтожил.