Історія створення простій мобільної гри

Привіт! Ми — невелика команда з двох осіб, яка намагається робити ігри у вільний час. Зовсім недавно ми нарешті зарелизили своє перше скромне творіння і вирішили поділитися досвідом його створення з хабрасообществом.


“У всіх є свої раннеры, а чим ми гірші? Ми теж зможемо запив за місяць вкрай просту гру для телефонів", — подумали ми трохи більше року тому, і все закрутилося.

Ми намалювали кілька скетчів, написали диздок на 2 листа і взяли Unity, з яким у нас вже був невеликий досвід роботи. Вирішивши, що впораємося з простим раннери приблизно за місяць-два, приступили до роботи.

Обережно, під катом досить багато картинок!

Прототип

Отже, ми вирішили створити щось дуже просте, однокнопочное. Основна ідея — персонаж, який біжить і на ходу розбиває каміння. Гравцеві потрібно в певний момент тапнути по екрану, щоб знищити перешкоду.

Приблизно за тиждень створили прототип, який складався з головного герой — Обме, що переслідує його Динозавра і генератора каменів, які розліталися на шматки навіть із застосуванням фізики.

Графічно це все обернули в простий і яскравий flat-стиль. Для перших кроків — те, що треба.

Один з перших скетчів

Розвиток прототипу
Ми, звичайно, любимо хардкорні гри, але не такі, коли після одного помилкового натискання ви програєте. Тому було вирішено, що за один раунд у гравця буде право на 3 помилки, після чого Динозавр з'їсть головного героя.

Погравши трохи прототип, зрозуміли, що треба хоч якусь різноманітність геймплея. Так у нас з'явилися курочки. Їх кінцева реалізація досить сильно різниться з тим, що було спочатку.

У першій реалізації Огр жбурляв у курочок бумерангом. Гравцеві при цьому необхідно було пальцем «розрізати» курочку, як ріжуть фрукти в Fruit Ninja. Причому потрапити за курочку потрібно було двічі. Перший раз з неї злітала частина оперення, на другій вона перетворювалася в кілька курячих ніжок, які летіли до Динозавра в рот. Діно їв і добрів. Пропускали хоч одну курку — Динозавр злився і підходив ближче. Але це не тільки звучить громіздко, але так само і грається. Курочок повністю переробили: замість кількох птахів і бумеранга — зграя пернатих і, раптово, двостволка. Не, ну справді, це ж гра, тут може відбуватися все, що завгодно. Синій огр, тікає від динозавра? ТАК! Дробовик? Чому ні? Курка в небі? Та це не одна курка, а ціла зграя, і ми їх зараз изрешетим!

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

Чорнова версія всіх цих ідей була готова приблизно через 2 тижні після початку розробки. Найцікавіше, що геймплейно той прототип мало чим відрізнявся від поточного релізу.

Разом ми отримали геймлей, що складається з двох чергуються частин: розбивання каменів і розстріл зграй курочок з дробовика.

Після того, як з геймплеєм було більш-менш зрозуміло, ми вирізали непотрібний контент і стали додавати необхідні елементи. Так за наступні кілька місяців з'явилися: початкова заставка, кілька варіантів задніх фонів, які змінювали один одного, інші декорації, такі, як кущі і квіточки, простенький туторіал, а у Обме почали з'являтися синці після удару об камінь. Після цього настав час все привести в порядок, на що і пішла більша частина часу.


Курячі ніжки і бумеранги майже відразу ж були відправлені в сміттєву корзину

Графіка і анімація
Всі персонажі зазнавали змін в порівнянні з початковими образами, додавалося все більше деталей. Лише курочки відразу вийшли відмінними і графічно не змінилися зовсім.


Скріншот з релизной версії

У даній грі хотілося випробувати скелетну анімацію. У підсумку вирішили спробувати плагін CCD, який був продемонстрований на одній з конференцій Unity. Злегка модифікований, він дожив до релизной версії, і для цього проекту нам його в загальному-то вистачило. Однак надалі, якщо знадобиться скелетна анімація, безумовно будемо використовувати більш просунуті інструменти.


Так як геймплей зобов'язує гравця постійно бути зосередженим на розбивання каменів, анімацію хотілося зробити плавний і без зайвих деталей. Але пару приємний дрібниць ми додали, такі, як грайливі брови, стрибаючий пузико і рухливий пучок волосся.


Музика та звук
Тут вийшло досить гладко. Для релизной озвучення ми купили кілька бібліотек зі звуками в магазині ассетов, парочку звуків записали самостійно. Музику замовили у незалежного композитора.

Єдиним цікавим фактів було те, що для чорнової озвучення були використані звуки ні багато ні мало з першого Half-Life. Зокрема камінь розбивався з характерним звуком смерті від потрапляння з Гауссгана. Замінивши його в підсумку на щось більш схоже на розбивання каменів, ми якийсь час не могли звикнути до нового звуку після такого рідного Гаусса.

Меню закінчення раунду
Ну яке може бути меню в такій маленькій грі? Але не тут-то було, виявилося, що доведеться примусити чимала кількість кнопок, щоб був мінімальний нині набір: допомога, рекорди, оцінка додатки, вимкнути звук. І ще треба не забути додати можливість відключити рекламу. А також потрібні очки, кращий результат і винагороду гравця за встановлення нового рекорду. Ну і, зрештою, більш приємне отримання зароблених монет, ніж просто стрибок лічильника.

Такими винагородами стали плавно збільшується кількість грошей з характерним дзвоном монет і анімація кубка з оплесками у разі встановлення рекорду. Дрібниці, а приємно.


Один з скетчів меню. Практично так воно виглядає в релизной версії.

До речі, після Unity 4.6 створення UI стало набагато більш простим процесом, так що тут нам ще пощастило.

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

За основу ми вирішили взяти ідею колекціонування. Відкриваються персонажі — круто, але довго. Потрібно багато арту, багато анімацій, більше тестування того, як вони ведуть себе в грі. Також думали зробити відкриттям фонові пейзажі, камені, але в підсумку ми вирішили колекціонувати яйця.

Так ми додали магазин яєць, в якому, однак, виявилися різні трофеї.


Магазин «яєць». До покупки можна лише гадати, що ховається за силуетом

До речі, дався цей магазин не так легко, як здавалося спочатку. У результаті ми зробили кастомный шейдер для ще не куплених яєць і допрацювали стандартний ScrollRect.

Монетизація
Ми вирішили спробувати додати яку-ніяку монетизацію. Так сказати, спробувати це справа технічно і, можливо, зібрати трохи чайових.

У підсумку наш вибір впав на один з найпростіших варіантів — рекламу. Так як вставляти банери прямо в гру, тим самим спотворюючи ігрову сцену, ми були не мають наміру, вирішили показувати межстраничную рекламу на весь екран в кінці кожного третього раунду. Начебто більш-менш демократично. Ми також додали можливість відключення реклами за мінімальний платіж (0.99 USD). Тут нам допоміг In-App Purchasing сервіс від Unity. Все досить тривіально, докладніше можна прочитати в офіційному туториале. Працює як для iOS, так і Android.

Також можна згадати, що UI для цього платежу трохи відрізняється для iOS і Android. Вся різниця в тому, що в iOS потрібно обов'язково дати можливо користувачеві руками відновити його вже здійснений платіж при зміні пристрою або перевстановлення ігри (Android, наскільки нам відомо, робить це автоматично).

Крім того, ми додали перегляд відео реклами за монетки, але тут все строго за бажанням гравця.

З реклами почали спершу підключати AdMob, але досить швидко перейшли на Appodeal. З плюсів — досить проста інтеграція і непогана тех. підтримка для безкоштовного сервісу.

З рекламою, до речі, виникло кілька проблем, деякі з яких до цих пір залишилися: гра подлагивает на початку на слабких і не дуже пристроях, відео за винагороду відображається не у всіх користувачів (зазвичай лікується скиданням рекламного ID, але хто буде цим займатися).

Підключення лидербордов
Для роботи з ачивками і лидербордами у Unity існує інтерфейс Social, який за замовчуванням має реалізацію для iOS. Для Android ж реалізація є, наприклад, в плагіні від Google — play-games-plugin-for-unity. При складанні під Android потрібно лише засунути ініціалізацію PlayGamesPlatform #if UNITY_ANDROID блок, а сам код, пов'язаний з Social, і для Android, для iOS можна використовувати один і той же.

За замовчуванням play-games-plugin-for-unity намагається билдиться і для iOS складання. Щоб виключити його і використовувати реалізацію від Unity, необхідно додати в Scripting Define Symbols ключ NO_GPGS (знаходиться в Player Setting → Other Setting):


До речі, після додавання play-games-plugin-for-unity до проекту зіткнулися з проблемою 65к при складанні під Android. Врятувалися тим, що створили порожній проект і закинули в нього тільки Appodeal і play-games-plugin-for-unity.

Оптимізація
Коли майже все було готове, незважаючи на тести на реальних девайсах, ми зіткнулися з проблемою продуктивності. Забігаючи вперед, скажу, що оптимізація зайняла відчутне кількість часу.

Ось кілька посилань на одні з найбільш корисних для нас статей: про, про алокації в C# частина 1, про алокації в C# частина 2.


Батчи, батчи, батчи
Першою нашою проблемою було непристойно велика кількість Draw Calls — близько 50. І тут нам на допомогу прийшов FrameDebugger, який вперше з'явився в Unity 5.0. Подивившись на послідовне побудова кадру, перепакувати текстурні атласи і налаштувавши Order in Layer, ми отримали в середньому 15 Draw Calls. Вже непогано. Трохи пізніше ми знову повернулися до цього питання — об'єднали деякі атласи (наприклад, Огр, Динозавр, Курочка і гніздо у нас тепер зберігаються в одній текстурою), зменшили кількість Sorting Layer, і в результаті в середньому у нас вийшло 7-8 Draw Calls.

Крім того, FrameDebugger дозволяє знайти графіку, яка малюється, але в підсумку не видно на результуючої сцені. Наприклад, ми не прибрали з анімації бігу хмарка над головою Динозавра, яке необхідно тільки в заставці:


FrameDebugger в дії

Крім FrameDebugger, Xcode також відмінно показує, що відбувається з промальовкою. Він теж дозволяє подивитися побудова кадру, причому воно може відрізнятися від того, що вам показував FrameDebugger:


Профілювання з допомогою Xcode

Також за допомогою Xcode ми дізналися про другу проблему: невиправдано велика кількість вершин. Можливо, деякі запитають: «Спрайт — це ж 2 трикутника, звідки там бути купі вершин?». Справа в тому, що на сучасних графічних прискорювачах дешевше намалювати меш з розумною кількістю вершин, чим обробити купу прозорих пікселів. Тому Unity будує меш по контуру спрайту, але робить це не завжди оптимально.

Тут нам на допомогу прийшов не безкоштовний, але вкрай корисний ассет SpriteSharp. Після його застосування кількість вершин у сцені рідко перевищує тисячу.


До речі, цю можливість можна відключити, і тоді спрайт будуть будуватися класично з двох трикутників. Налаштування знаходяться ці параметри текстури:


Кількість Draw Call і кількість вершин можна дізнатися і без застосування профайлеров — досить заглянути в Stats:


І ще трохи порад:

  • Залиште фінальне об'єднання текстурних атласів на потім, коли у вас дійсно нормалізується вміст сцени.
  • Уважно дивіться за тим, як змінюється колір спрайтів. SpriteRenderer.color не розбиває батч, а ось SpriteRenderer.material.color, як і будь-яка інша зміна матеріалу, розбиває.
  • Пам'ятайте, що динамічний батчинг має ряд обмежень, наприклад, меші, які будуть об'єднані, в сумі не можуть перевищувати 900 вершин. Докладніше можна подивитися на офіційному сайті Unity.
Трохи про шрифти
Спочатку лічильник очок у нас був зроблений з використанням динамічного шрифту і опції, що могло викликати подлагивание при кожному успішному розбитті каменю, так як лічильник збільшується на одиницю, а разом з цим перебудовувався меш написи. Вихід — використання Custom set в самих шрифтів і вимикання Best Fit в UI.Text.



Оптимізації логіки і фізики
Про фізику Unity можна почерпнути з хорошою статті на Хабре. Про нас ж скажемо, що в релизной версії ми взагалі її не використовуємо.

Наприклад, спочатку у нас були колайдери на всіх каменях і Огре. Однак немає необхідності вважати перетин Обме з усіма камінням, досить лише знати відстань до найближчого. Причому відстань тільки по x — ніяких квадратних коренів не потрібно.

Також у нас були колайдери у вигляді кола на кожній курочці. Знову ж таки немає сенсу перевіряти перетин координат тапа з усіма коллайдерами. Так як при одному натисканні можна вбити до двох курочок, достатньо знайти перші два перетину і припинити подальші розрахунки.

Попадання ми шукали, використавши відстань від центру курочки до координат тапа, причому тут теж можна обійтися без квадратний корінь — досить використовувати sqrMagnitude і квадрат радіуса. Трохи докладніше тут.

Для каменів ж ми застосували «запікання» фізики в анімацію. На кожен камінь було записано по 3 анімації для успішного розбивання кулаком і по 3, коли Огр врізається в камінь. На ділі виглядає майже так само, як і з застосуванням симуляції фізики. Скрипти, які ми написали для запікання можна знайти на bitbucket — раптом комусь знадобляться.

Ще ми перейшли від підходу «кожен елемент сцени сам займається своїм оновленням» до схеми «менеджер і набір елементів, за які він відповідає».

Наприклад, у випадку з курочками можна було наділити кожну з них своїм методом Update, в якому вона б шукала інших курок і застосовувала логіку поведінки в зграї. Однак скрипт курки у нас містить тільки базові методи, такі як розвернутися або померти. За поведінку всіх пернатих у зв'язці відповідає скрипт зграї.

Такий підхід дозволяє більш гнучко і централізовано керувати оновленням сцени. Наприклад, елементи, що не впливають на геймплей, можна оновлювати лише кілька разів в секунду, замість апдейта кожного кадру. Або, якщо колекція дуже велика, оновлювати не всі елементи Update, а рознести їх оновлення кадрів: кадр N — оновлення першої половини колекції, кадр N+1 — оновлення другої половини.

І завжди слід пам'ятати, що кожен виклик методу MonoBehaviour, начебто Update або Start, має певний overhead. Детальніше про це можна почитати в блозі Unity.

Пул об'єктів це «must have» при використанні Unity (та й будь-якого іншого движка в принципі). Благо у нас він був спочатку.

Ну і ніколи не слід забувати про «переповнення» float:


Метаморфози в далеких світах. Вгорі праворуч значення координати по осі X

В нашому випадку при досягненні значення 100000 по осі X сцена зсувається на -100000. Інша техніка, яку можна було застосувати, — це імітувати рух. Огр і Динозавр стоять на місці, а все оточення рухається справа наліво.

Для пошуку вузьких місць завжди використовуйте профайлери, як самої Unity, так і Xcode. Причому робіть це на конкретному пристрої, а не у редакторі.

На закінчення глави про оптимізацію хочеться додати, що, незважаючи на всі зусилля, лаги все одно трапляються: активності, пов'язані з рекламою, платежами, Google Play/Game Center і самою операційною системою.

Ну і, звичайно, де-то ми самі недогледіли.

Розміщення в App Store

Напевно всі знають, що для того, щоб зарелизить на iOS свій додаток, необхідний який-небудь mac-девайс? Так, звичайно, можна спробувати всілякі прийоми типу «хакінтоша» або діставати друзів, щасливих власників макбук.

Невідомо, як поведе себе Apple, якщо раптом дізнається, що додаток було залито з хакінтоша. А отримати банхаммером і втратити всі свої додатки в App Store як-то не хочеться. Від цього варіанту відмовилися відразу.

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

У підсумку ми обзавелися Mac mini середньої конфігурації. Тут, до речі, непогано працює схема: розробляти на могутньому ПК, а слабенький мак використовувати тільки для активностей, пов'язаних з App Store.

Ще один цікавий факт про App Store — опис і скріншоти ви можете залити з будь-якої платформи, а ось відео тільки з Safari не нижче 8, що працює під управлінням OS X не нижче 10.10. Причому на обробку відео після заливки відводиться аж до 24 годин.

Ну і в цілому правила публікації більш суворі, нежеле в Google Play, так що, якщо отримали аппрув від Apple, Android точно розберетеся.

З часу отримання аппрува — зараз йде близько двох днів. Стаття про підводні камені маркетів не дуже давно була на Хабре.

Ну і в офіційному мануалі теж досить непогано описані требования до розміщуваних матеріалів.

Розміщення в Google Play
Розміщення в Google Play пройшло набагато легше. Відео та скріншоти для телефонів у нас вже були підготовлені, нам залишилося тільки зробити версії для планшетів розмірами 7 і 10 дюймів і залити відео на YouTube.

Після цього підготували релізний апк і перевірили все на бета-тесті. Далі потрібно було відправити його на розгляд. І ось тут слід бути уважним, якщо не хочете відразу зарелизить свій додаток. Ми знали, що це зробити простіше простого, і все одно наступили на ці граблі, зарелизив версію для Android трохи раніше, ніж планували.

З цього приводу можна сказати: завжди стежте за тим, що вибрано у списку на сторінці Production праворуч зверху:



І пам'ятайте, що після кожної відкладеної публікації цей параметр скидається до стандартної публікації.

З часу отримання аппрува — зараз йде зазвичай кілька годин.

Знову ж таки більше деталей можна знайти все в тій же статті з Хабра офіційному мануалі про підготовку матеріалів.

Висновок. Про терміни
Ну, і на десерт — головне питання: «Чому так довго?!» (а з моменту початку роботи до релізу пройшло майже півтора року).

Насправді основний геймплей, який майже без змін потрапив в реліз, був створений за перший місяць розробки.

А далі додавання магазину, меню, доробки, переробки, перемальовування, лагодження після оновлення Unity і очікування баг фіксів від них, оптимізація і ще купа технічних складнощів, які по недосвідченості робилися дуже повільно. Наприклад, кілька вечорів ми витратили лише на те, щоб організувати процес складання та тестування бети під iOS.

Були проблеми з рекламою, були проблеми зі складанням під Android, коли ми підключили всі плагіни.

Ну і звичайно ж «людський фактор», він же лінь. Бо як грою ми займалися тільки у вільний час і скоріше як хобі, не завжди після важкого трудового дня вдавалося зробити
щось корисне.

Однак головною метою для нас було пройти повний цикл розробки: від прототипу до релізу, і ми це зробили.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.