April 12, 2018

История спасенного времени

Каждый уровень игры Spreadstorm состоит из небольших элементов, которые мы называем "узлами". Узлы связаны между собой и у каждого узла может быть не больше 4 соединений, что представляет собой довольно простой граф. Игроки могут перемещаться по узлам и взаимодействовать с некоторыми из них. Самый маленький, первый уровень в игре состоит из 22 узлов.

Структура уровня внутри игрового движка

Когда работа над проектом только началась, стало очевидно, что расставлять каждый узел вручную в редакторе Unity и указывать каждую связь с соседями будет мучительно долго и непродуктивно.

К счастью, буквально первая глава книги "Game Programming Gems" дает два совета:

  • отделяйте данные от поведения в своей игре;
  • выносите отделенные данные в текстовые файлы, чтобы иметь возможность быстро их менять и играться с параметрами.

Воспользовавшись советом, был изобретен JSON-формат данных, который описывал уровень игры, каждый узел, соединения между ними и возможные взаимодействия, доступные игроку.

При запуске, игра загружает файл с описанием уровня, парсит файл с помощью Newtonsoft.JSON, конвертируя его во внутреннюю структуру игры, и далее генерирует по внутренней структуре уровень. Это была своего рода победа, потому что такой способ довольно-таки неплохо работал, позволял производить быстрые изменения и немедленно получать результат, но к примеру, файл с описанием первого уровня состоял из 112 строк.

С использованием текстовых файлов уровни создавались быстрее, потому что ты мог копировать повторяющиеся части описания и быстро вносить в них изменения. Но возникла другая проблема: ошибки. При любом, даже самом маленьком изменении возникала вероятность возникновения ошибки. Ты забыл изменить параметр, скопировал лишнее, недокопировал нужное - и уровень генерируется некорректно или игра вообще не запускается. И чтобы найти и исправить такую ошибку, требовалось невероятное количество времени. К тому же создание файла уровня с 50+ узлами копипастой было тоже достаточно медленным процессом.

Пример структуры файла первого уровня

Мы создали пять уровней, используя этот подход, и решили двигаться в сторону создания хотя бы простейшего варианта редактора уровней, который выглядел примерно так:

Отвратительный дизайн и интерфейс, однако это лучше, чем бездушные строки текста

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

Тем не менее с этой версией редактора было тоже большое количество проблем. Он мог генерировать только структуру уровня, но другие вещи, как взаимодействия, враги, модули должны были вноситься в файл вручную (привет ошибки!). Да и структура создавалась не самым удобным образом. Ты должен был ввести штук пять параметров, чтобы получить простую линию из узлов. И если в процессе допускалась ошибка (например, случайно удалил не тот узел), то отменить действие не было возможности.

После того, как файл был завершен мы должны были его импортировать в движок, сгенерировать уровень, разместить освещение, декорации, все это запечь, протестировать и повторить по новой, если нужно внести какие-либо изменения. Несмотря на это, было создано 25 уровней, используя этот "Кусок инструмента".

Игра успешно вышла в Раннем доступе в Стиме и настала пора задуматься о создании нормальной версии редактора уровней, которая бы:

  • давала бы возможность делать Undo и Redo любой операции;
  • делала процесс максимально простым;
  • избавляла от необходимости лезть в текстовые файлы по любому поводу и без;
  • позволяла бы немедленно тестировать созданный уровень.

С Undo и Redo оказалось меньше всего проблем. Ты просто берешь замечательный паттерн проектирования Command и оборачиваешь в него все возможные операции редактора. И все магическим образом начинает работать.

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

А вот перенос всех возможностей игры в редактор вместе с упрощением процесса создания уровней отняли почти два месяца разработки. По большей части это был довольно рутинный процесс, прерываемый переосмыслением структуры некоторых элементов игры.

Режим соединения узлов: достаточно выбрать любых два зеленых шарика и редактор сам рассчитает и создаст соединения между ними

Однако результат полностью оправдал все ожидания. Если раньше создание одного простейшего уровня занимало добрых четыре часа времени, и изменения в него вносились потом с трудом, то с новым редактором уровень создавался буквально за минуты. И все остальное время можно было плейтестить и полировать уровень, доводя его до совершенства. Изменение параметров отдельных узлов получало немедленный визуальный фидбэк, что превратило создание уровней в приятный и интересный процесс. И даже добавление новых механик и типов узлов, несмотря на возросшее количество программирования (нужно запрограммировать поведение для игры и интерфейсы для редактора), стало происходить быстрее, так как уже в редакторе ты мог сразу посмотреть, как они работают.

Стоило ли сделать редактор раньше? И да, и нет. Да — потому что было потрачено гигантское количество времени на создание уровней вручную. Нет — так как редактор проще делать для состоявшейся игры и механик, которые точно в ней останутся и будут несильно меняться. Да и понять, какие процессы нужно хорошо оптимизировать, лучше всего после того, как ты немного пострадал.

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

И под конец небольшое видео о том, как сейчас выглядит процесс создания уровня для игры Spreadstorm: