Skip to content
Dimitry edited this page Jun 29, 2019 · 12 revisions

Сущности

Сущность (entity) - хранит информацию об объекте. Каждой сущности присваивается уникальный индекс (ID)

Как создавать?

Вариант 1

Вернет новую сущность.

public void Setup()
{
    ent e = Entity.Create();
}

Вариант 2

Вернет новую сущность и назначит ей новый игровой объект (gameobject). Объект будет получен из папки Resources по имени.

public void Setup()
{    
     ent e = Entity.Create("Obj Fluffy Unicorn");
}

Вариант 3

Вернет новую сущность и назначит ей новый игровой объект (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() деактивирует объект для дальнейшего использования.

Уничтожение является отложенным действием. Сущность будет уничтожена на следующий кадр предварительно отписав все свои компоненты.

Как получить доступ к unity компонентам через сущность

Если сущность была создана вместе с объектом, то корневой трансформ объекта автоматически кешируется и к нему можно обратиться.

ent e = Entity.Create("Obj Bunny");
var tr = e.transform;

Если нужно получить конкретный компонент то можно воспользоваться вспомогательными методами get<T> - он возьмет компонент из корня или дочернего объекта.

ent e = Entity.Create("Obj Bunny");
var spr = e.Get<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.Get<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

Структура ent является типовым значением и указателем сущности, может быть автоматически конвертирована в int число. Именно через ent разработчик производит большинство операций с объектом. Структура состоит из индекса (id) и поколения (age).

Теория

Приготовьтесь. Будет спойлер. Из описний выше можно сделать предположение что сущностью является некий контейнер хранящий компоненты, трансформы и прочие переменные. На самом деле это только так выглядит для удобства восприятия. В действительности сущность это инкрементный индекс обращающиеся к множеству разных массивов компонентов. Из этого следует что размер всех массивов всегда равен или больше максимально использованному индексу.

Каждый раз когда сущность уничтожается ее индекс высвобождается и уходит в пул свободных индексов. Что-то вроде рая. Каждый раз когда сущность создается идет проверка на наличие свободных индексов. Если такой есть возвращается самый нижний и его поколение увеличивается. Если свободного индекса нет, то создается новый.

Зачем сущности поколения? Для точного сравнения. Сущность является типовым значением, а не ссылочным. Например кролик хранит информацию о цели. Целью выступает морковка с сущностью под индексом 10. Если морковка была уничтожена раньше чем кролик до нее добрался то кролик всеравно будет продолжать двигаться к cущности с индексом 10 которая к этому времени может стать шлангом для полива газона. Кролик обладает устаревшей информацией. Но благодаря поколениям можно провести сравнение. Так, морковка была индексом 10 с поколением 0, а поливочный шланг с индексом 10 уже имеет поколение 1. Хотя индексы совпадают из-за разности поколений кролик делает правильный вывод что поливочный шланг не его обед.

Практика создания сущностей

В фреймворке есть два подхода к созданию сущностей и объектов. Напрямую и через акторов. В этой статье был изложен принцип создания сущностей напрямую однако как разработчик вы чаще будете работать с акторами (Actor). Прямой подход к созданию не дает обратной связи от игрового объекта к фреймворку.

Случаи когда имеет смысл напрямую создавать сущности:

  • Вам нужна абстрактная сущность
  • Вам не нужна обратная связь от игрового объекта
  • Объект взаимодействует с миром, но не мир с объектом.

Самый простой пример: пули из жанра игр shoot em up/bullet hell. Пуля может получать информацию об окружающем мире через рейкасты в системе. У пули нет unity компонента с указателем на свою сущность и поэтому окружающий мир не сможет обратиться к сущности пули (однако он разумеется будет видеть ее физический объект)

Такой подход выгоден для максимально облегченного создания примитивных объектов без monobehavior компонентов с коротким сроком жизни и очень прямолинейной логикой создал-отработал-уничтожил.