Я разработчик игры Raildale, и в этой статье расскажу вам историю разработки. Но для начала скажу несколько слов о самой игре. На поле появляются разноцветные города, которые нужно соединять железной дорогой. Из городов выходят поезда, и их нужно отправлять в город соответствующего цвета, используя стрелки и светофоры и избегая столкновений. Еще есть разные дополнительные фичи: ремонтные поезда, экспрессы и другие. На мой взгляд игра получилась очень интересная, да и отзывы я пока что получал только положительные. Игра непростая и пройти ее действительно сложно. Raildale доступна под iOS и Mac OS X в App Store.
Подробнее об игре можно посмотреть на www.raildale.com.
Предыстория
2 года назад (осень 2012) я работал программистом и занимался на тот момент системами управления предприятием уже 10 лет. У таких систем есть одно общее свойство: эти проекты никогда не кончаются. Работа мне нравилась, проект и задачи были интересные, но за 10 лет я устал от этой области. К тому же хотелось больше свободы в принятии технологических и интерфейсных решений.
Поэтому я задумался о реализации своего проекта. Я думал о разработке приложения или игры под iOS. В конце концов я решил делать игру. Это максимально далеко от того, чем я занимался до этого, и поэтому очень интересно. Разумеется, проект не должен был быть очень большим, так как у меня не было опыта ни в разработке игр, ни в маркетинге.
У меня было много идей, но остановил я свой выбор на паровозиках. Эта была та игра, в которую я сам бы с удовольствием поиграл. Прямо скажу, идея не нова, в детстве я играл в похожую игру. Ожидал найти что–то подобное под iPad, но все, что было мне совсем не понравилось. Поэтому я решил, что это отличный вариант для первого проекта.
В общем я решил уволиться и заняться своим проектом. Зарплата у меня была хорошая, так что были некоторые сбережения. Поэтому финансы не довлели и можно было спокойно поработать некоторое время без прибыли, тем более жена продолжала работать. Но когда зашел разговор об увольнении, руководство предложило несколько интересных для меня задач, и я решил остаться еще на какое–то время. В результате уволился я только в июне 2013 года. Все это время я немножко вел разработку игры параллельно с работой в свободное время, но много времени на это я уделить никак не мог. Потом в июле я еще на 3 недели съездил в Исландию в отпуск. Так что по полной в работу я окунулся только в августе 2013.
2D
Изначально я разрабатывал игру в 2D в изометрической проекции. Я использовал Cocos2D – самый распространенный движок для 2D игр под iOS сегодня. Не могу сказать, что он мне понравился. Основная проблема, что он использует модель спрайтов. А это практически исключает разделение игры на слои (модель, контроллер, представление) и делает невозможным изоляцию состояния. У меня не получалось с ним добиться кода, который бы мне понравился. Все время получался самозапутывающийся код. Я пытался найти аналоги, но все, что я нашел было построено по тому же принципу. А Cocos2D все таки обладает неплохой документацией и активным сообществом, что безусловно плюс.
Для разработки я использовал JetBrains AppCode. XCode тоже неплохое IDE, но мне сложно сравнивать, я до этого пользовался IDEA и привык ко многому. Точно могу сказать, что в AppCode отладчик гораздо удобнее. Правда, в XCode есть прекрасная утилита для отладки рисования в OpenGL. Она может вывести все операции одного кадра, что иногда очень удобно, даже пытается что–то подсказывать. Так что, можно сказать, XCode я тоже использовал.
Через некоторое время я понял, что Objective–C меня раздражает. Причин много, я писал об этом статью (англ.). Конечно, далеко не все согласны со мной, но к моим методикам разработки Objective–C точно не очень хорошо подходит, хотя в Objective–C есть и интересные идеи. Поэтому я разрабатывал свой язык параллельно с разработкой игры и назвал его Objective–D. Я писал об этом отдельную статью на Хабре.
Для рисования спрайтов я использовал Sketch – векторный редактор под Mac. Хорошая в целом программа, немного глючная, правда. К тому же почему–то нет возможности выгрузить результат в произвольное разрешение. Только в том, в котором нарисовано или в 2 раза больше для ретины. Что очень странно и неудобно, особенно для иконки: приходится копировать и уменьшать вручную. И еще недостаток – необоснованно большой размер файлов, что неудобно при работе с VCS.
Я столкнулся с проблемой поворотов поездов. Для этого приходится рисовать вагончики под каждый угол, что занимало очень много времени. Да и результат был не очень хорошим – поезд дергался. Чтобы этого эффекта не было, нужно было нарисовать много углов. Тогда мне пришла в голову идея использовать 3D редактор для этого. Я выбрал Cheetah3D, купил модельку вагончика за пару долларов, выставил углы камеры, как в изометрической проекции, настроил свет. Я вращал вагончик вокруг своей оси и делал снимки. Получилось гораздо быстрее, чем рисование, но удовлетворительный результата достигался только при очень большом количестве углов поворота. Поэтому файл с этими углами получался громадным, особенно для ретины. С таким файлом мой iPad справлялся плохо – все откровенно тормозило.
3D
Я решил попробовать отказаться от 2D и перейти в 3D. Тем более что я уже немного познакомился с 3D редактором. Я стал искать движок для 3D и наверное самым очевидным выбором было бы Unity3D. По нему много документации и его в основном хвалят. Но мне не понравилось, что там множество операций делаются в конструкторе, а мне хотелось писать код.
Я попробовал Cocos3D – расширение Cocos2D до 3D, но мне эта библиотека совершенно не понравилась. Те же самые проблемы, что и в Cocos2D, плюс почти полное отсутствие документации. В результате я не нашел ни одного движка, который бы мне подошел, и решил попробовать просто OpenGL.
Немного помучился, чтобы добиться вывода треугольника и экранного цикла. Начал разрабатывать игру, параллельно выделяя общую библиотеку для будущих игр. Основная идея – разделение кода на слои и изоляция изменяемых данных. Таким образом, почти все изменяемые данные изолированы в слое контроллеров, а слой отображения содержит только чистые функции рисования. В слое отображения допускаются изменяемые данные, необходимые только для кэширования или анимации. Слой контроллеров я покрыл автоматическими тестами. В результате качеством кода игры я остался удовлетворен, мне приятно что–то там дорабатывать и хочется еще игру улучшать. Когда код плохой, такого желания обычно не возникает.
В начале я нашел не очень удачные руководства по OpenGL. Там использовались такие вещи, как стэк матриц, например. Когда я добрался до написания шейдеров, оказалось, что матрицы в шейдер не передаются, да и вообще все это уже давно устарело, как и много другое. Пришлось почти все, что я сделал до этого переделать на современный манер. Так что важно сразу найти правильное руководство. Я бы посоветовал вот это.
Изначально я разрабатывал просто под Mac, так проще тестировать: не нужно подключать устройство к компьютеру. На эмуляторе игры проверять вообще бесполезно – жутко тормозит. Когда что–то стало вырисовываться, решил попробовать перенести на iOS. Основная проблема была в том, что язык шейдеров в OpenGL ES немного отличается от обычного. Но я справился довольно быстро.
В игре достаточно простая модель освещения – один глобальный направленный источник света (солнце). Теней у меня изначально не было и я решил, что было бы здорово их добавить. Для этого я использовал карты теней. Это не было очень сложно для моего освещения, но карта теней съедает много ресурсов, так как приходится рисовать многие вещи по два раза. Игра стала тормозить, и я занялся оптимизацией.
В этом деле довольно неплохо помогают инструменты Apple: есть специальный инструмент, который показывает вам возможные проблемы и советует, как их решить. Очень полезный инструмент для начинающего разработчика OpenGL. Например, для меня было открытием, что не стоит вызывать функции OpenGL, которые меняют состояния с одними и теми же параметрами. Они напрямую передаются в графический процессор, без проверки того, совпадает ли это состояние с текущим. Так что эту проверку нужно делать самому. Как ни странно, это небольшое изменение сильно улучшило производительность. Кроме того, сильно улучшило ситуацию использование Vertex Array Object (VAO).
Для оживления картинки добавил в игру ветер, дождь и снег. Ветер клонит деревья, сносит дым поездов и осадки. Основное направление и сила ветра не меняется в течение уровня, но периодически случаются порывы ветра. Громкость звука ветра меняется в зависимости от силы. Кроме того, также для оживления, добавил звуки птиц. Они отличаются для разных локаций и погоды, воспроизводятся в случайный момент с указанной для конкретного звука средней периодичностью.
Я прикрутил физический движок Bullet для того, чтобы моделировать разлетание вагончиков при столкновении. Первые результаты оказались довольно скучными – вагончики только немного сходили с рельсов. Тогда я решил применить эффект, который часто применяют в фильмах для повышения зрелищности: подкинул вагончики в воздух в момент столкновения и придал им случайное вращение. По–моему, получилось отлично.
Завершение
Тестированием игры занималась моя жена. Для нее тестирование не было в новинку – она занимается разработкой программного обеспечения и тестировать ей до этого приходилось немало. Ей удалось выявить множество проблем и недочетов. Она прошла игру на трех разных устройствах, чего пока не удалось мне. Я застрял на 15-ом из 16-ти уровней, но рано или поздно я все-таки рассчитываю ее пройти. Как ни странно, мне самому интересно играть в Raildale. Често говоря, думал, что к концу разработки желания поиграть у меня не останется.
Ближе к готовности я выложил версию на TestFlight для организации бета–тестирования. Отличный сервис и удивительно, что бесплатный. Я встроил их SDK, которое позволяет выставлять контрольные точки, чтобы понимать, куда заходили тестеры. Также оно собирает крэш–репорты в том числе и уже на рабочей версии. Это очень полезно, так как эти репорты должны вроде бы появляться в iTunesConnect, но это очень странно работает и далеко не всегда они там оказываются. Не могу сказать, правда, что бета–тестинг очень удался. Я опубликовал предложение о тестировании на форумах. Люди откликнулись, но их целью в основном было не тестирование, а просто поиграть. Поэтому баг–репортов я не получил, но хотя бы поиграли на разных устройствах, и игра не падала.
Когда игра была уже близка к готовности, я закал перевод на One Hour Translation. Сервис мне понравился: быстро, удобно и дешевле я не нашел. Правда, про качество перевода мне сложно сказать. Можно заказывать и очень маленькие тексты. Я дозаказал потом фразу из 5–ти слов. Цена определяется только количеством слов. Я заказывал перевод с английского на бразильский португальский, французский, немецкий, итальянский, испанский, японский, корейский и китайский упрощенный. На английский я перевел сам и закал пруфридинг, то есть мой текст проверил англоговорящий человек. Получается примерно в 7 раз дешевле перевода, да и качественнее, я думаю. Так что хороший путь, если неплохо владеете английским.
С локализацией возникла небольшая проблема. Для вывода текста я использовал битмэп шрифты. То есть имеется картинка с буквами и файл, в котором написано, где какая буква на этой картинке расположена. Но такой метод совсем не подходит для азиатских языков. Так что пришлось быстро менять систему вывода текста и рисовать сначала текстуру с буквами методами iOS.
Вообще локализация оказалась процессом небыстрым. iTunesConnect очень неудобен и работает неторопливо. Вроде бы простая задача перевести название досок в GameCenter превращается в пытку. Но мне удалось найти решение – iTunesConnect Transporter. Он позволяет написать все, что есть в iTunesConnect, в xml–файле и загрузить его вместе со скриншотами. Это меня спасло.
Во время разработки я записывал, сколько времени и на что я потратил. На момент написания статьи:
- Программирование – 623 часа;
- Дизайн (3D модели, текстуры, иконка) – 60 часов;
- Objective–D – 234 часа;
- Маркетинг – 249 часов;
Это моя первая игра, так что времени на все можно было бы затратить меньше. Про маркетинг, например, я был вынужден прочитать множество информации, попробовать то, что не имеет смысла. Все равно, не могу понять, как получилось так много времени. Когда занимаешься маркетингом время утекает, а кажется, что ничего и не сделал.
Апрув занял 2 дня для Мак–версии и 5 дней для iOS. На данный момент цена $1.99. 15 промо–кодов для iOS версии можно получить здесь. Было бы очень интересно узнать ваше мнение. Я собираюсь улучшать игру и поэтому хотелось бы услышать, что бы вы улучшили в игре, что понравилось, что нет.
Знаю, что на Хабре любят читать также о маркетинге приложения. Разумеется это интересно, чтобы получить представление о прибылях, поучиться на чужих ошибках и успехах. Но сейчас об этом немного рано говорить, так как игра вышла совсем недавно. Думаю, выводы можно будет сделать примерно через месяц.
Автор: Anthony