Run kolobok run! Клеточная система

В данной статье хотели бы поделиться реализацией клеточной системы координат, которую мы придумали для нашего проекта. Эта система является слоем абстракции над стандартной системой координат и привносит свои ограничения.

Пример создания игрового объекта на примере препятствия​

Для чего это нужно?

В нашем проекте на основе клеточной системы координат строится навигация персонажей и других игровых объектов на сцене; поиск пути колобком, процедурное расположение интерактивных объектов на сцене.

В дальнейшем собираемся реализовать обнаружение столкновений игрока с другими объектами (сейчас это реализовано при помощи коллайдеров).

Примечание:

В рамках статьи показана реализация конечного уровня. Реализация бесконечного уровня уже готова, но достаточно сырая.

Реализация

Упрощенная диаграмма классов

На диаграмме показаны классы, поля, свойства, методы непосредственно связанные с реализацией клеточной системы. Свойства и поля находятся в одной куче.
На диаграмме показаны классы, поля, свойства, методы непосредственно связанные с реализацией клеточной системы. Свойства и поля находятся в одной куче.

CellPosition

Хранит значения координат по осям x, y и z. Для наших целей можно было использовать и Vector3, но решили добавить свою структуру, чтобы меньше путаться. Когда работаем с переменной с типом CellPosition, сразу понимаем, что речь идет о клеточной системе координат.

CellType

Возможно, название перечисления не самое правильное. Хранит список статусов клеток (названия статусов подобраны относительно персонажей):

  • CanRun — персонаж может бежать по клетке, то есть клетка свободна.
  • CanRunButNotRecommend — персонаж может бежать по клетке, но лучше ее обойти. Таким образом, мы обозначили клетки, на которых находятся проходимые препятствия, на данный момент кусты.
  • CanSlide — персонаж не сможет пробежать по клетке, но есть возможность проскользить.
  • CantRunAndSlide — персонаж не сможет пробежать и проскользить по клетке. Другими словами, на клетке находится непроходимое препятствие, например, камень или дерево.
  • NotDefined — неопределенный статус. На данный момент не используется, но планируем обозначать таким образом клетки на пути движения движущихся препятствий, например Лось.
  • Environment — клетка находится за пределами игровой дорожки. Персонаж на них не может попасть, но на них будут появляться колобки и движущиеся препятствия, прежде чем доберутся до игровой дорожки.

CellDescription

Хранит в себе тип и координаты.

GameObjectBase

Базовый класс в иерархии классов. Основная его обязанность — позиционирование объекта на сцене по клеткам. Обязательный компонент (или производные от него компоненты) для всех интерактивных объектов на сцене. Является наследником MonoBehaviour.

  • CellPosition — координаты объекта в клеточной системе координат, пивот-поинт объекта.
Run kolobok run! Клеточная система

Инкапсулирует логику перемещения на сцене. Реализация метода GetWorldPositionByCellPosition будет показана ниже.

  • CellsDescriptions — описания клеток, из которых состоит объект. координаты указываются относительно пивот-поинта объекта.
  • DimensionByZ, DimensionByY, DimensionByZ — габариты по соответствующим осям.
Пример препятствия (Иерархия классов: GameObjectBase <- … <- ObstacleBase <- StrongObstacle)
Пример препятствия (Иерархия классов: GameObjectBase <- … <- ObstacleBase <- StrongObstacle)

Стоит обратить внимание, где находится пивот-поинт, а также на CellsDescriptions. Размер этой телеги 4х2х2 клеток. Всего 16 клеток, их и указываем в CellsDescriptions, не забываем, что под телегой можно прокатиться, поэтому для некоторых клеток выбираем тип CanSlide.

Cell

Компонента объекта клетки на сцене. Производный от MonoBehaviour.Хранит в себе описание и ссылку на занимающий ее объект.

Префаб клетки​
Префаб клетки​

Дополнительно добавлена визуальная часть. Описание клетки запрещено менять из редактора, так как оно инициализируется процедурно при создании клетки в фабрике или при перемещении объекта на нее.

GameObjectFabric

Все, что осталось от фабрики. Избавимся от нее при первом же рефакторинге.
Все, что осталось от фабрики. Избавимся от нее при первом же рефакторинге.

CellFabric

Фабрика клеток. Создает клетки на сцене.

  • Prefab — образец клетки. Это объект с компонентом Cell.
  • Parent — родительский объект для клеток на сцене.
  • Dimensions — свойство, возвращающее transform.localeScale.

Методы:

  • FillWorld выполняет заполнение уровня пустыми клетками.
Run kolobok run! Клеточная система
  • CreateEmptyCell создает новую пустую клетку.
Run kolobok run! Клеточная система
  • GetCell возвращает ссылку на клетку по ее координатам, если клетка не найдена, то выбрасывается соответствующее исключение.
Run kolobok run! Клеточная система
  • GetCells возвращает список клеток по списку позиций.
Run kolobok run! Клеточная система
  • SetEmptyToCells ищет клетки по списку координат и меняет их тип на пустой.
Run kolobok run! Клеточная система
  • SetFillToCells ищет клетки, занимаемые объектом и меняет их тип на тот, который указан в CellDescriptions у объекта.
Run kolobok run! Клеточная система
  • GetWorldPositionByCellPosition возвращает фактические координаты на сцене по координатам клетки. Изначально предполагалось, что размеры клеток могут быть различными, но, судя по всему, решили упростить этот момент и жестко привязались к размеру сторон клеток равному 1 по общей системе координат.Но при этом мы оставили возможность смещения (_firstCellWorldPosition) начала координат клеточной системы относительно основной системы координат.
Run kolobok run! Клеточная система

WorldConstructor

Модуль верхнего уровня, который хранит внутри себя фабрики различных различных объектов: CellFabric, GameObjectFabric, ObstacleFabric, LocationFabric и отвечает на формирование сцены. Наследует MonoBehaviour для простоты настройки.

  • Create создает игровой объект по образцу, на указанной позиции и присваивает родителя, если он указан.
​Две перегрузки метода <b>Create</b>.
​Две перегрузки метода Create.
  • ReplaceObject перемещает объект, если все клетки, которые объект займет после перемещения, на данный момент свободны.
Run kolobok run! Клеточная система

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

  • В методе Awake заполняем мир клетками.
Run kolobok run! Клеточная система

После заполнения меняем тип клеток, которые находятся за пределами игровой дорожки.

Результат

Заключение

К достоинствам такой системы можно отнести:

  • Возможность отказа от избыточной физики (коллайдеры, RigidBody)
  • Простота навигации, особенно это заметно в реализации ИИ для колобков.
  • Сложнее допустить ошибку при размещении объектов на сцене за счет ограничений системы.

Недостатки:

  • Низкая производительность. Во-первых, на сцене создаются большое количество клеток. Игровые камеры не рисуют клетки, а чтобы не отображались и в окне сцены, мы добавили пометку необходимости отрисовки клеток (Если true — рисовать, иначе не надо). И тем не менее на сцене создаются 4х5х150 = 3000 клеток (при текущей настройке). Во-вторых, необходимо производить выборку клеток из списка по условиям, например поиск клеток по координатам происходит очень часто. В-третьих, необходимо пересоздавать клетки в бесконечном уровне. Частично мы обошли эту проблему через использование пула объектов.
  • Очень неудобно настраивать клетки, из которых состоит объект (CellDescriptions). Очень надеюсь, что получится выделить время на разработку расширения редактора для визуальной настройки.
  • Без сильной переработки системы не получится сделать участки дороги как препятствия (например, река, над которой сломан мост).
  • На данный момент не реализована фича вращения препятствий, из-за чего мы не можем сделать движущиеся препятствия. Также требуется доработка.
1010
3 комментария

Крутой пост. Много и подробно, наверняка пригодится другим разработчикам в качестве гайда.

4
Ответить

Благодарю

Ответить

Я человек простой - вижу UML, ставлю лайк

2
Ответить