[recovery mode] Первый опыт разработки игры на Sprite Kit

После создания и выкладки в App Store игры на Sprite Kit хотелось бы поделиться опытом. Немного расскажу о создании геймплея, а также о попытках связаться с издателями и о впечатлениях от фреймворка.8fdb629e77144eeaa522406de22dd0fd.png

ИдеяИтак, перед нами стояла цель — придумать и создать игру в one-tap endless жанре. Мы это команда из трех человек: программист (он же автор статьи), дизайнер (он же руководитель проекта) и иллюстратор-аниматор. Как и во всех играх этого жанра, процесс игры должен быть довольно простым, управляться одним касанием и в то же время должен затягивать. После некоторых размышлений родилась идея, а через некоторое время и некий прототип игры.Итак, некий персонаж прыгает с одной раскачивающейся платформы на другую. По мере прохождения игра усложняется за счет изменения параметров платформ. После приземления на следующую платформу герой бежит к ее краю. После прыжка видимая область игры смещается.

2d9a54692be1401e8306d5644406c81b.png

Реализация Начал с создания «качель». Каждая из них состоит из держателя (в самом верху), балки и платформы. Все это соединяется с помощью двух шарнирных соединений (SKPhysicsJointPin). Затем нужно было добиться того, чтобы герой стоял на платформе и его не болтало из стороны в сторону. Я испробовал несколько подходов. Попытался вручную задавать velocity объекта в методе update в зависимости от скорости платформы, на которой он стоит (посоветовали на stackoverflow), однако это привело к неприятному эффекту «запаздывания». Герой как бы не успевал за своей платформой и двигался с небольшим опозданием. Эксперименты с изменением трения объекта также привели к некоторым неприятным эффектам. В итоге, единственный способ железно закрепить героя на платформе, который я нашел, это добавление SKPhysicsJointFixed. Перед прыжком удалять его и затем заново добавлять, когда необходимо будет остановиться.Для того чтобы герой прыгал, нужно назначить ему некий импульс. Силу прыжка регулирует игрок. Она зависит от длительности нажатия. Здесь пришлось немного усиливать импульс, если платформа в момент прыжка движется назад, иначе герой взлетал почти вертикально вверх и приземлялся на ту же платформу.

Следующая задача, правильно зафиксировать момент приземления и после приземления вновь добавить joint, который будет удерживать героя на платформе. Оказалось, что недостаточно просто добавлять joint в методе didBeginContact, так как, во-первых, он вызывается в момент, когда между объектами еще есть некоторое пространство и при добавлении джойнта герой как бы зависал в нескольких сантиметрах над платформой, и во вторых, он может удариться о платформу сбоку и в этом случае ничего делать не нужно. Первую проблему я решил добавлением булевой переменной, которая указывает, что был зафиксирован контакт и проверкой ее в следующем вызове метода didSimulatePhysics. В этом случае джойнт добавляется уже в didSimulatePhysics и объекты в этот момент стоят вплотную друг к другу. А чтобы исключить случаи, когда герой ударяется сбоку, проверяем взаимное расположение героя и платформы, чтобы убедиться, что герой находится на платформе.

После приземления, герой должен добежать до края платформы. Здесь как раз сработал метод задания вручную velocity герою. В методе update берем velocity платформы, по которой он должен бежать, добавляем некоторое положительное значение по x, и назначаем полученное velocity герою. Как итог, герой перемещается по платформе. В этом случае эффект запаздывания если и есть, то его не видно и выглядит все хорошо. После достижения края платформы вновь добавляем фиксирующий джойнт.

Вся сцена состоит из отдельных ноудов, имеющих разное значение zPosition, образующих в итоге слои: слой для героя и платформ, слой для заднего фона, слой для элементов интерфейса. После приземления героя слои сдвигаются, при этом слой заднего фона движется медленнее, чем слой с героем, для обеспечения эффекта удаленности заднего фона. Получается, что координата x каждой следующей «качели» увеличивается внутри своего ноуда, а сам ноуд сдвигается влево внутри сцены. Тут я столкнулся с различным поведением в разных версиях iOS. В версиях, начиная с iOS 7.1 координаты джойнтов нужно всегда конвертировать в координаты сцены, иначе объекты, соединенные этими джойнтами, начинают вести себя очень странно. А в версиях до 7.1 этого делать не нужно, иначе, опять таки, объекты начинают беспорядочно болтаться в воздухе.

Вобщем, в то что получилось на данном этапе уже можно было играть и было довольно интересно.

Графика Тем временем, иллюстратор придумал и нарисовал главного героя.98ab18a794cd4ef982430fe89c33dc7a.png

Всем очень понравилось и вскоре были созданы основные анимации. Итак, я получил все необходимые секвенции для добавления анимаций. Каждый тип анимации засовываем в отдельный текстурный атлас и добавляем в проект. После запуска игры, перед появлением игровой сцены, загружаем все текстуры и сохраняем в отдельные массивы. Создаем методы для всех анимаций, каждый из которых «заставляет» героя удалить все предыдущие анимации и добавляет соответствующую новую.

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

Затем были созданы фон и текстуры для плафторм. Фон был разделен на три слоя, каждый из которых движется с разной скоростью. Это еще больше добавляет эффекта объемности. Текстуры платформы и балки состоят из повторяющихся паттернов. Также добавили несколько анимаций (вспыхивающие звезды, падающие кометы), которые рандомно возникают на фоне, дабы немного разнообразить внешний вид.

Я обнаружил странное поведение при добавлении полупрозрчаных представлений UIView в представление класса SKView. Полупрозрачное представление не отображается, если в нем нет каких-либо непрозрачных дочерних представлений. Точнее при его добавлении оно появляется на очень короткое время, а затем исчезает. То есть, если добавить представление с alpha = 0.5 в SKView без каких-либо дочерних представлений, то оно не отобразится. Нужно добавить в него хотя бы одно дочернее представление с alpha = 1, чтобы оно отобразилось нормально.

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

Впечатления от Sprite Kit Прежде чем начать работу над игрой я прочитал всевозможные статьи, сравнивающие различные игровые фреймворки. В качестве плюсов Sprite Kit везде указывалось то, что он является частью Cocoa и его легче всего начать использовать для первого проекта. Для меня это стало решающим фактором при выборе фреймворка. Недостатки Sprite Kit, отмеченные в статьях, казались мне малозначимыми для нашего проекта. В итоге, самым главным недостатком для меня оказалось то, что частота обновления физики в Sprite Kit зависит от fps. То есть в моменты, когда фпс падает (например во время показа банеров гейм центра), стабильность физики оставляет желать лучшего. Довольно много времени ушло на то, чтобы отловить и исправить вызванные этим баги. Также не удалось избавиться от небольших задержек при смене анимаций героя на старых устройствах (до iPhone 5). Опыта работы с другими фреймворками нет, за исключением нескольких уроков по Cocos2D, поэтому сравнивать не с чем. Но есть в планах переписать игру на Cocos2D c Box2D и сравнить результаты. В остальном все очень удобно и просто. Думаю, для игр с очень простой физикой или вообще без нее Sprite Kit подойдет идеально, если разрабатывать только под iOS.Маркетинг и монетизация В процессе разработки отправляли запросы различным издателям. Большинство даже не ответило на письмо, но некоторые все же откликнулись. Среди них Ketchapp, Chillingo и Ayopa games. Все они установили игру через TestFlight. Признаться, мы больше всего рассчитывали на Ketchapp, так как наша игра больше всего подходит под их формат. Однако, после установки игры, они больше так и не написали. Chillingo ответили, что им мол нравится графика и персонаж, но на играх такого жанра сейчас сложно заработать, попросили держать их в курсе наших разработок. Больше всего мы были близки к соглашению с Ayopa games. Они впервые ответили только через 3 недели после того, как скачали игру. Написали, что нужно внести некоторые исправления, но в целом они заинтересованы. Мы было воодушевились, сделали все, что они просили, выложили новую версию. К сожалению, следующий ответ поступил опять только через 2 недели, когда мы уже устали ждать, и приложение было на пол пути к App Store. Мы попросили у них некоторых гарантий того, что они возьмутся за наше приложение, если мы отложим выкладку в App Store и будем менять игру по их желанию. В ответе они выразили желание узнавать о наших будущих проектах, однако от этого, в сложившейся ситуации, отказались. В общем, не сложилось, как-то устали мы ждать ответа по 2–3 недели, хотелось уже быстрее выложить приложение.В итоге решили рекламировать своими силами: социльные сети, запросы на обзор в различные интернет ресурсы, google AdMob. В AdMob закинули 20 тысяч рублей. Они израсходовались за один день и принесли в сумме 55000 показов, 1140 кликов и около 450 установок. Также рекламировали в группе в контакте с количеством пользователей около 700 тысяч. Это принесло всего около 200 установок. Вывод такой, что без больших сумм на рекламу вряд ли стоит рассчитывать на успех, если, конечно, вам не повезет и о вас не напишут бесплатный отзыв на каком-нибудь крупном сайте. Список сайтов, которым отправили запрос на бесплатный обзор, посмотрел в публикации пользователя RedRoseSinging, а также в этой публикации пользователя EugeneDM, спасибо им.

В качестве монетизации выбрали стандартный для таких игр подход — показ рекламы + встроенная покупка, предлагающая эту рекламу отключить. В качестве рекламной сети выбрали Chartboost. Там очень гибко можно настраивать показываемую рекламу и сам банер выглядит довольно приятно. Немного поработал над показом банера, чтобы он не нажимался случайно. Во многих играх именно так и происходит и это сильно раздражает.

Вот видео с геймплеем:

[embedded content]

Спасибо всем, кто дочитал статью до конца, жду комментариев!

© Habrahabr.ru