Часть 1. От финансовых аналитиков… Или глава пролог
С чего начинается день финансового аналитика, спросите вы? С рутины, отвечу я. Утро, офис, на автомате готовим чашечку кофе, сигарета и в довесок дюжина мыслей, которые устремляются куда то вдаль… И так они себе летят, летят и бац… Дерево…
На протяжение многих лет одно и то же. Надоело. Понятно было, что нужен новый вызов, а то завязли в своём маленьком и уютном болотце. Проходили дни и недели: утро, кофе, сигарета, дневная рутина и под вечер ты просто никакой. И так каждый день. “Стена плача” всё росла и росла (о ней чуть позже, фото покажем).
И вот оно - долгожданное явление, послужившее толчком в развитии дальнейших событий. Как-то с утра, то ли 19, то ли 20 сентября 2017, один из коллег кидает статью, где рассказывается от первого лица о двух ребятишках, которые, едва окончив школу, без малейшего опыта в разработке, написали игру. И тут меня осенило: вот он! Вот тот вызов, которого я ждал для своей команды (которая никогда не писала игры и опыта на этом поле не имела, от слова совсем). Да так осенило что я оказался залит утренним кофе.
А дальше было просто. Собрал ребят, обсудили данный вопрос и решили, а чем чёрт не шутит. Как минимум в будущем, когда будем скучать и делать свою рутинную работу, параллельно можно будет поиграть в собственные игры. В сумасшедшие собственные игры. Задача руководства команде была довольно простой: “Сделай то - не знаю что, сделай так - не знаю как, но на выходе должна получиться игра”.
Теперь утро финансового аналитика начиналось так:
1. Переступаю порог офиса
2. Спрашиваю как продвигается разработка игры
3. Прошу скинуть рабочую версию чтобы поиграть
Вот только после всего этого - кофе и перекур.
Мы разбили статью на несколько глав, в каждой из которых повествование ведется от лица человека, который выполнял изложенную часть работы. Надеемся, что статья не только покажет обратную сторону разработки, но и послужит катализатором конструктивной критики в наш адрес.
Часть 2. Нарисуй пингвина и да, он должен летать. Или глава о текстурах
После того, как мы определились с жанром, персонажами и динамикой игры, настало время воплотить идеи в текстурах. После общения с главным программистом, идея отрисовки всех текстур только в векторе потерпела фиаско. Нужен растр. Мы пораскинули мозгами и логично пришли к выводу что нам нужны наборы текстур под разные разрешения экранов. Так же логично мы пришли к выводу что если хотим быстро что-то сделать, то это очень сильное кунг-фу для нас. Поэтому пошли таким путем: оригинал в векторе, его экспортируем в растр низкого разрешения и на выходе получаем текстуры стилизованные под пиксель арт. Ну, в общем, текстуры очень похожие на пиксель арт, но не пиксель арт.
Отрисовка велась в основном в open source программах (GIMP и Inkscape), но ими одними нам не удалось обойтись, поэтому пришлось задействовать Aseprite и After Effects для анимации. Например, вот разобранный морж, подготовленный к анимации
В первые дни разработки, все тайлы экспортировались в один спрайт, а по указанным во вручную созданной таблице координатам программист вырезал нужный кадр. Настал момент когда размеры спрайта перевалили за 12000px по вертикали… Что-то начало нам подсказывать что мы движемся в неправильном направлении. Совсем неправильном. И если вначале мы думали что уж игру-то мы кое-как накодим, то тут мы поняли что в словаре под статьей как не надо делать игры, должны быть наши наработки. Спасло нас то, что мы (внезапно!) не первые кто пишет игру под Андроид и почти все проблемы, которые могут возникнуть у чайников уже давно описаны в какой-нибудь статье. Окей, гугл и спустя сутки мы уже получаем удовольствие от работы с Atlas в libgdx.
Тут самое время подметить один момент: если уж и пришла в голову мысль окунуться в разработку игр, даже для самого себя составьте четкое техническое задание, опираясь на которое вы сможете грамотно организовать процесс. Без него слаженной работы не получится.
По итогу мы пришли к тому, что исходники всех текстур храним в одном большом svg файле (даже изначально растровые изображения) и, при необходимости экспортируем тайлы в png (либо поодиночке, либо пакетно). Выглядит это, конечно монструозно, но на деле - не так страшен черт, как его малюют.
Далее несколько примеров работы с анимированными персонажами.
Часть 3. Delphi жив и как он вписался в разработку. Или рассказ от лица прикладного программиста
30 сентября на совещании было озвучено, что кто-то из нас должен написать программу для генерации уровней, на базе созданной карты объектов в tiled.exe.
Итого ТЗ:
В генераторе tiled.exe создаем карту объектов, нажимаем экспорт в PNG, загружаем в какую-то программу эту картинку, после на эту картинку вешаем активные элементы (монетки, черепки, замедлители, моржи, медведи и т.д.), в специальных полях задаем настройки уровня (общее время уровня, очки, и т.д.), жмем на кнопку «Готово» и на выходе получаем JSON файл.
Все уставились на меня, мол, ты же вроде прикладной программист, примусы починяешь, давай быстренько накидай чего-нибудь. Я такой «Да не вопрос, я же High Level Developer: в универе калькулятор написал».
Ставим Delphi 2010. Начинаем с нуля. Окей Google, как двигать картинки по форме?Накидал форму, на форме панель с инструментами (активные элементы игры), при двойном клике на канве основной игровой области должен создаваться объект, который должен двигается простым перетаскиванием. Добавил кнопки загрузки карты и фона уровня. Все на английском, надо же показать свой мега скилл.
Далее, надо это все дело сохранять, поэтому вешаем дополнительно кнопочку.
Добавляю поля для основных настроек уровня, и для красоты вешаю кнопку сохранения скриншота уровня и кнопки очистки всех расставленных элементов, а также кнопку загрузки JSON.
Все, интерфейс накидал, на том и порешили что 90% работы сделано за 6 часов и остались сущие мелочи.
5 дней и ночей спустя…
Все начало создаваться, очищаться, память уже не течет, объекты двигаются, JSON сохраняется и загружается, даже скриншоты делаются. Даже сделал учет масштаба 1:2 игровой области в игре и у меня на форме (1920 * 1080 в игре, а у меня область 960 * 540).
Прихожу в офис, отчитываюсь что долг родине отдал, дело за вами, господа. Но ни тут-то было, Вова (JAVA developer) в курилке говорит «Бро, а какая система координат у тебя на выходе в JSON?». На мой вопросительный взгляд последовало объяснение что в libgdx начало координат это нижний левый угол экрана, и соответственно у каждого объекта также… Я начинаю нервно курить и вспоминаю про себя, что в Delphi самая лучшая система координат и XY=0,0 это верхний левый угол. Ну ок, докурил и начал: отнять, прибавить, поделить, бац профит!
Жму на кнопку «На! Смотри! Все нижний левый, так нижний левый»
7 дней спустя релиз. И, нет, у медведей не фиговые листочки в промежности, а так мы отметили на карте уровня точки и направление, откуда появляются новые монеты.
Часть 4. While (!release) { Кофе, перекур, код; } . Или глава о собственно разработке
Написание игры - занятие довольно увлекательное, которое возвращает тебя в детство. Только за происходящим на экране ты видишь не внешнюю магию, а внутренний мир, скрытый от обычного игрока, ты устанавливаешь в нем свои законы и правила, ты заставляешь его обрести видимую форму, ожить и зазвучать.
Так как опыта в разработке игр было аж 0.0, то сразу же появилось много вопросов. Первый из них - “для какой платформы писать?” разрешился довольно просто, так как есть опыт разработки приложений под Андроид, то и писать решили под Андроид. Просмотрев доступные движки и фреймворки остановился на связке libGdx и Java так как предполагалось что, игра будет в 2d формате.
По началу решил не использовать движок box2d, а задавать скорость объектов игры и самому отслеживать коллизии. Быстренько накидал тестовый уровень - всё ОК, пингвин бегает, монетки собирает. Начальство сразу вопрос - “Когда релиз?” Отвечаю - “Две недели за глаза.” Но не тут-то было. После того как назрел вопрос о том, что монетки должны откуда то вылетать и красиво отскакивать от поверхностей, а пингвин должен прыгать по этим самым поверхностям, подключил физический движок box2d, пришлось в нем ковыряться. Задал поведение объектов внутри уровня так, чтобы оно соответствовало любой конфигурации уровня. Витя сваял прогу в которой можно было редактировать уровни и сохранять их конфиги в файлы. При загрузке уровня мой класс лоадер читает файл и строит по настройкам уровень и заполняет его объектами добавляет игрока (пингвина) и вуаля можно играть и время от времени добавлять различные уровни не переписывая кода игры.
По наступлению определенных событий в мире прикрутил проигрывание звуков и музыки, с одним “но”: при запуске десктопной версии музыка и звуки часто приводит к вылетам из игры, побороть которые так и не получилось, в андроиде такого не случается.
Где хранить результаты? Конечно в БД. И тут нужны небольшие танцы с бубном, так как ядро игры не умеет работать с sqlite так как это делает андроид. Поэтому используется драйвер sqlit - jdbc для подключения к уже созданной базе и работе с ней в десктопной версии. А при работе в андроид версии проверяется есть ли в песочнице приложения файл базы и если его нет то он копируется из ассетов, если есть то подключаемся через sqldroid.
Для различных экранов создал самописные кнопки, на которые отлеживаю нажатия, вся фишка в том, что на экране когда ты рисуешь элементы нулевые координаты находятся в левом нижнем углу, а при событии ввода нулевые координаты находятся в левом верхнем углу, приходится каждый раз проводить над ними операцию конвертации.
Монетизация - довольно таки просто с использованием интерфейса обратных вызовов можно в любой момент показать или скрыть баннер, полноэкранное объявление либо видео за вознаграждение.
Ну вот, впринципе, и всё. В планах поработать над повышением быстродействия, подключить к игровым сервисам Google Play и добавить функционала.
Часть 5. Немое кино в прошлом. Или глава о звуковом наполнении игры
Процесс разработки затронул не всех и в свободные от кода руки мы отдали звуковое сопровождение игры. Требования звучали достаточно лаконично: “Музыка должна быть просто зашибись, чтобы игроки прям слюни пускали”.
Сижу никого не трогаю, тут мне заявляют коллеги по цеху “Ну все, Валера настало твое время, на тебя делегирована звуковая часть работы, надо её сделать со скоростью звука”.
Мне то, что сиди слушай музыку, выбирай то что понравилось и в папку скидывай, вроде работёнка не пыльная, но… Пингвин прыгает, ходит, летает, собирает монетки, выигрывает и проигрывает… Мозг болит, уши кровоточат, о, супер, этот звук подходит, так-с, а вот указание авторских прав нам точно не подходит.
Плюнул на все, залез в медиатеку YouTube слил себе много-много мб треков и семплов, переслушал все, только звукового кофе, чтоб перебить уже прослушанное я не нашел, пришлось слушать для отдыха звуки отбойного молотка, перфоратора и болгарки...
Всё, необходимый набор звуков собран, а мерзкие программисты заявляют: “Слишком много мегабайтов, надо резать и сжимать, циклить все что можно, а что нельзя то уменьшать...”
Пришлось знакомиться с аудиоредакторами, привет Adobe Audition!
Готово! Есть архив, музыка нарезана и зациклена, звуки почищены и собраны! Красота одним словом!
Итого: четыре дня и четыре ночи скакал Илья Муромец, пока скакалку не отобрали!
Часть 6. … до разработчиков мобильных игр. Или глава эпилог
У каждого из нас есть много чего рассказать, у каждого из нас был свой путь до того как мы стали настоящей командой, для всех из нас он был непростым, да чего уж там, и сейчас не всё просто.
Уже много лет перед глазами графики цифры, валютные пары и индексы, от них голова кругом идёт. Смена занятия (хоть и на месяц) реально привела в чувства и позволила отдохнуть от финансовой рутины.
Кстати, вот тут и настало время для нашей “Стены Плача” (Курение вредит вашему здоровью!)
Не судите строго, но критикуйте конструктивно, просто это реальный путь от финансового аналитика до разработчика мобильных игр. Надеемся, что это не последняя наша история и мы продолжим развиваться и радовать вас нашими новыми достижениями и играми!
С уважением Команда GCN, разработчики мобильной игры "Tapstazy".
Так как я являюсь частью команды разработчиков, то ставлю тэг "моё", указывающий на оригинальное авторство статьи.