Когда то давно, я уже писал статью о GUI дизайнере и вот нашёлся очередной повод написать статью о мапмейкере.
Я не буду расписывать о чувстве прекрасного и т.д. Это понимает каждый, кто хоть раз, хоть где ни будь создавал локацию на любом игровом, и не только, движке. В этой статье буду освещать исключительно технические особенности движка Roblox.
Создание мира — только две возможности
Но изначально хотелось бы отметить, что база плейса может быть из двух составляющих:
— это Terrain (инструмент создания ландшафта)
— это BasePart (базовые элементы и меши на их основе)
И как итог, они могут быть совмещёнными.
У любого варианта есть как плюсы так и минусы. Террейн позволяет создать бесконечный бесшовный мир на котором прекрасно работает поиск путей. Блочный вариант же даёт больше вариативности в наполнении данного мира. Как следствие, зачастую используются оба варианта вместе.
И как обычно бывает проблемы, зачастую, возникают лишь после того, как мир игры создан и его надо оживить. Почему? Потому что в 75% случаев сперва делают карту и лишь потом начинают задумываться о том, как всё на ней будет работать. А стоило бы подумать об этом на этапе создания самой карты и множество проблем бы даже не возникло.
Итак. У нас есть пара проблем связанных с тем, как работает клиентская часть игры. А именно об этом обычно не в курсе mapmaker когда создаёт карту.
Проблема один — параллельность загрузки
Есть серверная часть — где после загрузки игры карта полностью доступна и клиентская часть — где у игрока имеется лишь некоторая часть карты (обычно в радиусе 1024 шпилек). Принцип работы клиента такой: загружаются элементы перед игроком (в приоритете видимые) на обозначенном радиусе, после чего начинает подгружаться мир вокруг игрока в том же радиусе. Ну и параллельно подгружаются элементы GUI, HUD, скрипты, аудио сопровождение. Основная же проблема в том, что эта псевдо параллельность каждый раз будет производиться в неизвестной последовательности. Программист никогда не узнает, что загружено раньше, а что будет грузиться позже. И для этого у него доступны только два варианта определения — найти первый элемент, ожидать указанный элемент. Казалось бы стоит всегда использовать второй вариант?! Но нет. Второй вариант работает так, что по истечении Х тиков (обычно это 8 секунд) с момента запуска выполнения команды, если элемент не был обнаружен происходит остановка дальнейшего выполнения скрипта. Что же, тогда стоит использовать первый вариант в цикле пока не появится нужный элемент мира? То же нет. Согласен что сама команды исполняется быстрее, но в случае если нужный элемент так и не появится в мире игрока (банально он за пределами стриминга отрисовки), то исполнение скрипта так же будет прервано (по факту мы зациклимся в режиме ожидания). Но это всё тонкости. Главное же стоит о чём знать, что скорость загрузки окружения различная у разных пользователей — кто-то сидит на 100 мега битном проводе, а кто-то с мобилки на 3G.
Проблема два — количество полигонов и элементов
Итак, от их числа зависит скорость прогрузки уровня у клиента. С этим то понято. И как следствие из этого — первая проблема. Но есть ещё и вторая сторона медали — скорость отрисовки и вычисление физики. Очерёдность примерно такая:
- на сервере добавлен элемент (в момент запуска игры или кодом)
- вычисляется у кого из клиентов элемент будет загружен и им отправляется информация о нём
- сервер задаёт геометрию и внешний вид элемента
- эта информация передаётся клиентам
- сервер определяет физические свойства элемента
- эта информация передаётся клиентам
И тут возникает диссонанс. На сервере есть полностью рабочий элемент. А вот с клиентами не всё так — до определённого момента клиенты не знают о его существовании. А даже если и знают, то не имеют полного представления о взаимодействии с ним.
Чтобы свести к минимуму данные проблемы, опытным путём было определено несколько ограничений (кстати у самого Роблокса они в несколько раз выше по документации, а то и совсем без ограничений)
- Количество базовых элементов на плейсе не должно превышать 10 000 (текущий проект имеет в лобби 40К элементов)
- Количество полигонов элемента не должно превышать 1-2 тысяч (в текущем проекте модели имели по 100К+ полигонов)
Как это не странно, но с первым ограничением большинство игр справляется. А вот со вторым бывают проблемы. В любом случае есть несколько путей решения этих ограничений:
- просто забить и будь что будет
- превратить Union и нагромождение BasePart в единый Mesh (создать аналог в том же Blender)
- превратить группы однотипных Mesh-ей в единый Mesh
- убрать невидимые и/или не доступные игроку элементы с карты
- сократить у Mesh-ей число полигонов до минимума, пока не нарушается геометрия (в том же Blender есть для этого инструменты работающие в пару кликов)
- загружать в игру, при необходимости, оба вида моделей — цельный Mesh и составной из элементов, в приоритете используя цельный (само собой оптимизация по полигонам у них разная)
Организация элементов
Зачастую, элементы карты (да и скриптеры этим страдают) располагаются game.Workspace. Вот только это специальная зона которая обслуживается движком. Например, в ней располагаются персонажи игроков и встроенные эффекты. В следствии чего это место должно быть максимально очищенно от постороннего. Самый простой вариант это создание в ней папки в которой и стоит размещать элементы мира. А лучше две, для разделения декора мира и элементов управления этим миром.
И вот тут мы пришли к ещё одной проблеме — расположение и именование элементов. Зачастую мапмейкеру некогда или просто лень переименовать модель в понятное название и размещать совокупность окружения в модель (свои прелести от группирования) или в подкаталог. Как следствие оживляющий мир кодер будет создавать свои группы элементов управления (что логично) или пытаться закодировать то, что имеется. Попробуйте, на примере ниже, найти нужную часть:
Поэтому всегда отделяйте элементы декора от нагрузочных.
Последствием всего этого может стать.. скандал. Когда мапмейкер удаляет то, что им не создано у скриптера (он просто выделил элементы на карте и туда попали элементы программиста) или переделывает свои элементы, к чему уже привязаны скрипты и игра ломается. Хорошо если кодер быстро разберётся что случилось и выяснит почему перс начал проваливаться под землю (заменили толстый парт на тонкий меш или просто перенесли его в другое место), а если нет? Мапмейкер уже и не помнит что он менял, а то и просто ничего не менял в ближайшее время. Просто потому что ошибка вылезла не сразу, а через время. Хуже всего если это будет плавающая ошибка которая будет проявлять себя лишь при определённом стечении обстоятельств.
Ещё один момент состоит в том, что добавление элементов окружения создаёт дополнительную нагрузку на систему встроенного поиска пути. Зачастую можно просто сделать элементы прозрачными для прохождения. А вот когда этот элемент должен ограничивать передвижение (особенно для различных NPC) то тут выход банальный — их надо покрывать невидимым барьером. Этот барьер должен выполнять две вещи: ограничивать расчёт матрицы пути и/или давать возможность взойти на препятствие. С учётом того, что расчёт пути происходит по контрольным точкам (сетке мира), то наличие тонкого объекта, через который нельзя пройти, станет краеугольным камнем в алгоритме поиска пути. Есть ещё такое ограничение, что если обрыв более 6,5 пунктов, то запрыгнуть считается не возможным. Не смотря на то, какой бы силу прыжка не поставили. Ещё пара моментов это расстояние между элементами — сможет ли между ними NPC протиснуться и высота прохода — сможет ли он в нём пройти. Это всё накладывает ограничения на использование различных дверей, арок, нор, нависающих конструкций и прочего, что может ограничить прямое передвижение персонажей из точки А в точку В. Даже два рядом стоящих ящика, между которыми может пройти игрок, могут стать непроходимым препятствием для NPC.
Оживление мира
Вопрос конечно тривиальный, ведь ни кому не интересно быть в мёртвом мире в котором ничего не происходит, ничего не движется и не звучит. Пусть даже он прекрасен как картина Да Винчи. Наши органы чувств для того и созданы чтобы что-то чувствовать. А игра делается что-бы напрячь наши чувства.
Конечно первое что приходит в голову — небо. Правильно выбранный SkyBox усилит окружение добавив Солнце, Луну, облака…
Самое простое спецэффекты — туман, дым, огонь, звёздочки и прочее. Включенный глобальный ветер заставит отклонятся огонь и дым, шевелится траву, погонит облака…
Правильно подобранный материал у элементов под ногами озвучит нужным образом шаги.
Добавить точечные источники света с нужной гаммой придадут контраста картинке.
Установленный позиционный звук в элементах придаст шарма, когда будет слышно как трещит огонь в костре, шумит водопад или просто играет радиоприёмник при приближении к ним.
Смена дня и ночи, включение и отключение дополнительных источников света в зависимости от времени суток.
Партиклы снежинок, листьев, пыли… Простое средство оживления окружения.
Тригеры срабатывающие на наличие игрока в определённом месте и запускающие какое-то действие.
NPC выполняющие простейшие анимации.
GUI и окружение мира
Тут всё банально. Зачастую гуи игры делается в последнюю очередь и переделывается по несколько раз. Собственно как и сама карта. В этом нет ничего особенного. Но есть один нюанс — это соответствие элементов HUD и GUI окружению. По простому если карта в мультяшном стиле, то и GUI должно быть в том же стиле. Если за основу взяли техногенный GUI, то и карта должна ему соответствовать. Да-да. Нет варианта кто главней — они дополняют друг-друга. Или противоречат… Но всё же карту переделать сложней чем GUI. Но не всегда. Особенно если она генерируется.
Заключение
Работа мапмейкера это не просто раскидать по плейсу нужные элементы в нужных местах для придания красоты.
Как минимум мапмейкер должен заранее озаботится:
- как оживить карту
- как оптимизировать её загрузку
- как оптимизировать работу карты возле игрока и за пределами его загруженной области
- как организовать работу с программистами
- как должна сочетаться его работа с художниками GUI
Стоит морально и физически подготовиться. Как и любая творческая работа — эта работа будет не только радовать, но и угнетать. И угнетать сильней всего. Даже больше, чем работа GUI дизайнера. Переделывать готовое придётся много раз. И даже полностью, если поменяется концепт.
Собственно в gamedev практика переделки готового и начала с нуля — обычное дело. Мало кому посчастливится без этого обойтись.