[Из песочницы] Наша с девушкой первая видео игра. Разработка на Unity. Часть 1
Ну как первая… Если не считать релизы только под Android и с десяток заброшенных проектов у финиша, то да, это первая наша игра с замахом больше чем одну платформу. Как же всё начиналось? А всё просто, работали мы значит над другим проектом, назовем его «проект А», работали уже долгое время и решили, а не сделать ли нам за пару месяцев игру и потренировать на ней наши маркетинговые навыки, а «проект А» выпустим сразу после с большим опытом в продвижении игр. Но звезды не сошлись, петух не свистнул и «проект А» залег на дно ровно на год. Но эта история не о нем, а о логической игре под названием «Cubicity: Slide puzzle».
По первому плану задумывалось следующее: минимум графики, минимум UI и всего что только возможно по минимуму, игра должна была быть в стиле сегодняшних казуалок, которых на рынке так же много, как и Match-3. В итоге наша цель была следующей, круглые фишки соединяются в заданную фигуру, перемещаясь свайпом в 4х направлениях. Кто уже играл в Cubicity, тот знает, что от этой задачи мы далеко не ушли, но по остальной части совершили довольно большой скачек, как для команды состоящей всего с 2х человек.
Если кто-то из читателей ожидает найти здесь секрет успешной и быстрой разработки игр, то знайте, секрета нет. Мы не делимся тут большим опытом или знаниями, здесь описана только история одного проекта небольшой компании. Успешного или нет, нам еще не известно. Но для многих из вас, наши читатели — это послание из прошлого от самих разработчиков.
Возвращаясь к истории создания Cubicity, работаем мы в основном только на Unity и стандартный набор любого уважающего себя Unity разработчика здесь: Newtonsoft.json, Zenject, Cinemachine, Dotween и т.д… Как видели выше, первый прототип игры выглядел именно так, кубы и блинчики. После недели раздумий, как разнообразить игру и завлечь игроков, пришла гениальная мысль… Посмотреть на Asset store кубических или круглых персонажей. Ну и понеслась, несколько паков с персонажами были куплены без раздумий. Такая же ситуация произошла и с блоками по которым сейчас перемещаются персонажи. Также составили список новых элементов геймплея, со списка примерно в 30 новых плюшек, отобрали для начала нейтральные вещи, такие как: перенаправляющие блоки/стрелки, лифт и телепорт. Остальное решили оставить на новые уровни и внедрять их по одному в 30–35 уровней.
Честно, не можем вспомнить, что нас побудило на первых парах сделать так много уровней, но как есть, и в первый релиз пошло 95 уровней. Очень много на самом деле и мы не раз об этом пожалели. Почему пожалели? А потому, что игра была сырой и много чего изменялось по ходу. Приходилось довольно часто получать дозу «дня сурка», заходя в каждый из 95 уровней и внося изменения. На все уровни ушло 2 месяца непрерывной работы. Это не были уже на 100% готовые уровни, но очень близко. В продуктивные дни, 10 уровней не составляло особого труда переместить с головы на бумагу, а после и в сцену. Но были и те дни, когда чувствуешь себя Хенком Муди из Блудливой Калифорнии, переживающим творческий кризис, думаешь всё, иссяк, но наступает новый день и новые идеи.
Если говорить про визуальную составляющую, то тут все несколько сложнее. Отрисовка как и в большинстве игр проводится во вне экранную поверхность с разрешением меньше нативного и блитится в основную поверхность, но UI для четкости и читабельности рисуется без каких либо изменений в разрешении. Таким образом, мы получаем лучшее от двух миров — не размытый UI, но и не слишком прожорливый рендер в игре. Для сглаживания было экспериментальным путем выбрано 2x MSAA + FXAA, как те которые дают лучшую картинку при наименьших затратах ресурсов. Здраво рассудив, что логической игре ни к чему 60 кадров в секунду, мы решили не изобретать велосипед и установить лимит кадров в 30fps (чего уж говорить, даже консоли обычно этим занимаются). Установка лимита кадров позитивно сказывается не только на потреблении энергии, но и на нагреве телефона, что в свою очередь не дает телефону тротлить из-за перегрева.
Нелегкое решение ждало нас впереди, и это Finish points. Поскольку при каждом запуске уровня персонажи выбирались рандомно из доступных игроку, то рисовать какую либо миниатюру фигуры из персонажей было бы проблематично. Можете не верить, но именно эту задачу решали дольше всего и оттягивали на потом. Кубы на финишах не казались тогда столь жуткой идеей, и бумажная живопись помогала пройти уровень и довести каждого на свое место. После было принято решение вместо кубов использовать тех же персонажей, но поменьше, стало лучше, но только для нас. Еще спустя несколько дней, этих персонажей развернули и подсветили, стало гораздо понятнее кто есть кто, но все еще не удовлетворительно. Окончательный вариант был принят еще через месяц, методом проб и ошибок, и еще пара недель уходит на создание иконок для финишей. До свидания лето, скоро с тобой вновь встретимся!
На наш скромный взгляд, тучи у нас получились довольно приятными на вид. Но по факту это простейший и не очень грязный хак. Когда только решили добавить тучи, то первая мысль была, сделать задник 360 видео. Этот подход не оправдал себя, так как для мобильных платформ желательно уместить игру в лимит размера для скачивания по LTE. Чтобы видео выглядело чуть лучше, чем отданное на растерзание шакалам сжатия, ему самому нужно было выделить 10–15 Мбайт, что в сочетании с наличием в игре ночных уровней со своими тучами, слишком много (весь конечный билд игры на Android занимает 61 Мб). Вторым желанием было написать свою систему для облаков, это было заманчиво как для разработчика, но как для человека, который хочет закончить игру поскорее это не подходило. Решение пришло в виде создания текстуры для облака и создания системы частиц с бесконечным временем жизни частицы, и также ограниченным количеством частиц в общем. После добавили случайные размеры между двумя константами на ряду со случайным вращением. Результат был более чем удовлетворительный — наше небо заполнилось облаками, которые были миловидны и не вызывали у нас желание плакать глядя на них.
Тени в игре (в мобильной версии) полностью состоят из квадов, которые просто расставлены вручную, так как не хотелось добавлять реальные тени в мобильную версию. Одной из причин является отсутствие мягких теней на мобильных платформах с OpenGLES 2.0, ну и конечно деградация производительности на слабых устройствах.
Как говорили ранее, для сглаживания мы использовали 2x MSAA + FXAA, но это еще не все! Также к нашему процессу пост обработки добавлен AmplifyColor — отличный ассет за свои деньги, позволяющий применять разные Lut-ы на пост обработке. При правильно подобранном lut, картинка становится лучше. В процессе разработки, мы пробовали разные подходы, включая стандартный unity post processing stack, но в билде его шейдеры и варианты занимали столько, что ни в сказке сказать, ни пером описать. Некоторые решения были очень красивы, но работали крайне плохо на телефонах не первой свежести (поверьте, если вы думаете, что у всех сейчас хотя бы «нормальные» телефоны — вы ошибаетесь. Огромное количество людей, до сих пор ходят с китайцем за 40$ и жалуются вам в комментах, что на их микроволновке ваш DOOM не идет).
Баланс игры — это всегда не просто и даже сейчас всплывают мысли, а не слишком ли сложные уровни, а не часто ли сложные уровни выпадают и т.д. Отбалансив, как могли одной левой ногой, решили внедрить инструменты для облегчения жизни игроку (Ход назад, Бомба, Ледяной блок, Телепорт), и да, стало жить проще, но не нам, а только будущим игрокам. У нас же работы и багов прибавилось.
Добрались к меню игры, силы и нервы на пределе, творческая натура ударила по тормозам, и не будем утаивать, пришлось вдохновляться другими играми, за что им огромное спасибо. И вот «На утро вышла черепаха!». Непрямо на следующее утро, но вышла, UI был готов по предварительно созданным макетам.
Желание быть стильным, модным, молодежным не обошло и нас. Мы решили добавить облачные сохранения и в целом не пожалели об этом. Это не было самой простой задачей, так как на разных платформах, разные провайдеры облачного сохранения. На Steam — это Steamworks, для мобильных — GooglePlay и GameRoom. Так что пришлось унифицировать систему сохранения для возможности подмены для нужной платформы. Для начала мы решили использовать EasyMobile для этих целей, но увы, рано или поздно отказались от этой идеи. Плагин сам по себе хорош, и имеет огромное количество возможностей, но сама специфика работы с нативными облачными хранилищами нам не очень понравилась. Как результат выбор пал на Firebase Realtime Database и аутентификацию через Facebook. Если коротко, то пришлось пройти 7 кругов ада, чтобы это все заработало (и тут дело не в программировании, а скорей в 100500 настройках, которые нужно сделать в 100500 местах приложения и кабинетах в Facebook, Firebase и т.д.). Так же в базе есть лимиты по трафику и чтобы экономить его, мы каждый раз при записи создаем GUID и записываем его как в базу так и на устройстве. Таким образом если мы видим что GUIDы на устройстве и в облаке совпадают, мы можем быть уверенными, что не нужно вычитывать все данные из облака, а можно пользоваться локальной копией данных. В результате синхронизация была добавлена, но… Одним из самых странных для нас багов, было неочевидное поведение Firebase Database в некоторых случаях. Так как мы используем Json, мы сериализируем классы предназначенные для хранения состояния, но Firebase иногда ведет себя несколько странно.
Если мы передаем Firebase для записи объект-словарь, например такого вида:
var dict = new Dictionary { { 0, new SlotState() }, { 1, new SlotState() }, { 2, new SlotState() };
Когда мы будем считывать его из базы, мы получим не объект Json, а массив Json (What?)
Ну вроде, понятно, будем использовать везде списки и не будем испытывать проблем, да? Но не тут-то было.
Если мы запишем в Firebase:
var dict = new Dictionary { { 0, new SlotState() }, { 1, new SlotState() }, { 100500, new SlotState() };
Или даже:
var dict = new Dictionary { { 0, new SlotState() }, { 1, null }, { 2, new SlotState() };
Когда мы будем читать его из базы, мы таки получим Json объект с ключами и значениями.
Понять логику разработчиков в какой то степени можно, но это может привести к багам, которые возможно проявиться лишь через время (Помните про вышесказанные GUID добавленные для сохранения? Как результат редкие чтения из базы при относительно частых записях в нее).
Когда релиз? Этот вопрос слышали чаще всего. Но нужно было основательно подготовиться к этому дню. Составить список маркетов, выбрать дату релиза, избегать крупных распродаж, довольно много нюансов, из-за которых релиз сдвинулся, как минимум на 2 месяца. Послушав совета одной статьи, выбрали вторник и среду для релиза. Решили точно заказать обзор на 4pda, закинуть новость о игре на несколько форумов и бомбить соц.сети в частности Instagram (конечно же платно). Что из всего этого сработало, мы с вами узнаем во второй части этой истории, но уже позднее.
Что имеем в итоге? Создать быстро игру — это не всегда быстро. И не исключено, что ожидаемые сроки создания игры придется умножить на 5. Обзаведитесь людьми, которые смогут помочь вам дельным советом в незнакомых вам отраслях. Расслабляйтесь при любой возможности, так как создание чего либо, не только игр, забирает много сил. Негоже подбираясь к релизу быть вялой сосиской и быть менее полезным, чем на старте проекта. Ну и деньги, ищите деньги, они вам понадобятся. А от нас, спасибо за внимание, удачи и до встречи в следующей статье.