Run kolobok run! Клеточная система
В данной статье хотели бы поделиться реализацией клеточной системы координат, которую мы придумали для нашего проекта. Эта система является слоем абстракции над стандартной системой координат и привносит свои ограничения.
Для чего это нужно?
В нашем проекте на основе клеточной системы координат строится навигация персонажей и других игровых объектов на сцене; поиск пути колобком, процедурное расположение интерактивных объектов на сцене.
В дальнейшем собираемся реализовать обнаружение столкновений игрока с другими объектами (сейчас это реализовано при помощи коллайдеров).
Примечание:
В рамках статьи показана реализация конечного уровня. Реализация бесконечного уровня уже готова, но достаточно сырая.
Реализация
Упрощенная диаграмма классов
CellPosition
Хранит значения координат по осям x, y и z. Для наших целей можно было использовать и Vector3, но решили добавить свою структуру, чтобы меньше путаться. Когда работаем с переменной с типом CellPosition, сразу понимаем, что речь идет о клеточной системе координат.
CellType
Возможно, название перечисления не самое правильное. Хранит список статусов клеток (названия статусов подобраны относительно персонажей):
- CanRun — персонаж может бежать по клетке, то есть клетка свободна.
- CanRunButNotRecommend — персонаж может бежать по клетке, но лучше ее обойти. Таким образом, мы обозначили клетки, на которых находятся проходимые препятствия, на данный момент кусты.
- CanSlide — персонаж не сможет пробежать по клетке, но есть возможность проскользить.
- CantRunAndSlide — персонаж не сможет пробежать и проскользить по клетке. Другими словами, на клетке находится непроходимое препятствие, например, камень или дерево.
- NotDefined — неопределенный статус. На данный момент не используется, но планируем обозначать таким образом клетки на пути движения движущихся препятствий, например Лось.
- Environment — клетка находится за пределами игровой дорожки. Персонаж на них не может попасть, но на них будут появляться колобки и движущиеся препятствия, прежде чем доберутся до игровой дорожки.
CellDescription
Хранит в себе тип и координаты.
GameObjectBase
Базовый класс в иерархии классов. Основная его обязанность — позиционирование объекта на сцене по клеткам. Обязательный компонент (или производные от него компоненты) для всех интерактивных объектов на сцене. Является наследником MonoBehaviour.
- CellPosition — координаты объекта в клеточной системе координат, пивот-поинт объекта.
Инкапсулирует логику перемещения на сцене. Реализация метода GetWorldPositionByCellPosition будет показана ниже.
- CellsDescriptions — описания клеток, из которых состоит объект. координаты указываются относительно пивот-поинта объекта.
- DimensionByZ, DimensionByY, DimensionByZ — габариты по соответствующим осям.
Стоит обратить внимание, где находится пивот-поинт, а также на CellsDescriptions. Размер этой телеги 4х2х2 клеток. Всего 16 клеток, их и указываем в CellsDescriptions, не забываем, что под телегой можно прокатиться, поэтому для некоторых клеток выбираем тип CanSlide.
Cell
Компонента объекта клетки на сцене. Производный от MonoBehaviour.Хранит в себе описание и ссылку на занимающий ее объект.
Дополнительно добавлена визуальная часть. Описание клетки запрещено менять из редактора, так как оно инициализируется процедурно при создании клетки в фабрике или при перемещении объекта на нее.
GameObjectFabric
CellFabric
Фабрика клеток. Создает клетки на сцене.
- Prefab — образец клетки. Это объект с компонентом Cell.
- Parent — родительский объект для клеток на сцене.
- Dimensions — свойство, возвращающее transform.localeScale.
Методы:
- FillWorld выполняет заполнение уровня пустыми клетками.
- CreateEmptyCell создает новую пустую клетку.
- GetCell возвращает ссылку на клетку по ее координатам, если клетка не найдена, то выбрасывается соответствующее исключение.
- GetCells возвращает список клеток по списку позиций.
- SetEmptyToCells ищет клетки по списку координат и меняет их тип на пустой.
- SetFillToCells ищет клетки, занимаемые объектом и меняет их тип на тот, который указан в CellDescriptions у объекта.
- GetWorldPositionByCellPosition возвращает фактические координаты на сцене по координатам клетки. Изначально предполагалось, что размеры клеток могут быть различными, но, судя по всему, решили упростить этот момент и жестко привязались к размеру сторон клеток равному 1 по общей системе координат.Но при этом мы оставили возможность смещения (_firstCellWorldPosition) начала координат клеточной системы относительно основной системы координат.
WorldConstructor
Модуль верхнего уровня, который хранит внутри себя фабрики различных различных объектов: CellFabric, GameObjectFabric, ObstacleFabric, LocationFabric и отвечает на формирование сцены. Наследует MonoBehaviour для простоты настройки.
- Create создает игровой объект по образцу, на указанной позиции и присваивает родителя, если он указан.
- ReplaceObject перемещает объект, если все клетки, которые объект займет после перемещения, на данный момент свободны.
Сначала получаем ссылку на клетку по ее координатам. Далее получаем ссылки на клетки, занимаемые объектом на текущий момент, потом ссылки на клетки, которые займет объект после перемещения. Если среди них нет занятых клеток, освобождаем изначальные клетки, перемещаем и занимаем новые клетки.
- В методе Awake заполняем мир клетками.
После заполнения меняем тип клеток, которые находятся за пределами игровой дорожки.
Результат
Заключение
К достоинствам такой системы можно отнести:
- Возможность отказа от избыточной физики (коллайдеры, RigidBody)
- Простота навигации, особенно это заметно в реализации ИИ для колобков.
- Сложнее допустить ошибку при размещении объектов на сцене за счет ограничений системы.
Недостатки:
- Низкая производительность. Во-первых, на сцене создаются большое количество клеток. Игровые камеры не рисуют клетки, а чтобы не отображались и в окне сцены, мы добавили пометку необходимости отрисовки клеток (Если true — рисовать, иначе не надо). И тем не менее на сцене создаются 4х5х150 = 3000 клеток (при текущей настройке). Во-вторых, необходимо производить выборку клеток из списка по условиям, например поиск клеток по координатам происходит очень часто. В-третьих, необходимо пересоздавать клетки в бесконечном уровне. Частично мы обошли эту проблему через использование пула объектов.
- Очень неудобно настраивать клетки, из которых состоит объект (CellDescriptions). Очень надеюсь, что получится выделить время на разработку расширения редактора для визуальной настройки.
- Без сильной переработки системы не получится сделать участки дороги как препятствия (например, река, над которой сломан мост).
- На данный момент не реализована фича вращения препятствий, из-за чего мы не можем сделать движущиеся препятствия. Также требуется доработка.
Крутой пост. Много и подробно, наверняка пригодится другим разработчикам в качестве гайда.
Благодарю
Я человек простой - вижу UML, ставлю лайк