Оптимізація гри на Unity і dev story Tap Tap Builder

В скарбничці кожного інді-розробника повинен бути свій сіті-білдер, може бути тому я зважився «сконструювати» свій велосипед. Звичайно ж, з квадратними колесами і кріслом-гойдалкою замість сідушки. Працюю я один, тому жодних дизайнерів, художників, і тим більше моделювання, в проекті немає. Крім того, у загальному-те, це моя перша гра з тривимірною графікою. Щоб не обтяжувати себе вивченням просунутих інструментів для створення тривимірних моделей, я вирішив все зробити своїми руками і засобами ігрової середовища Unity. Там є тільки примітиви, начебто кубів та циліндрів, а також можливість їх розфарбувати. Що ж, слід запастися терпінням і почати «творити», занурившись в роль архітектора. Корисною інформацією для початківців інді-розробників може виявитися мій досвід роботи з видавцем, а також способи оптимізації гри.

image

Про гру
Основною особливістю Tap Tap Builder є симбіоз з кликером, а цей жанр міцно влаштувався серед широкої публіки. Отже, для зведення будівель від гравця потрібно, крім деякої кількості ресурсів, неабияк понабивали пальці, жмакая по екрану. Правда, можна побудувати будівельний кран, який буде будувати за вас, але дуже повільно. Більшість будівель можна покращувати, при цьому через кожні 5 рівнів змінюється їх модель. Кожна будівля в грі грає свою певну роль.

image

У житлових будинках живуть люди. Без них населення міста рости не буде. Коли городяни обзаводяться житлом, вони починають шукати роботу. Наприклад, можу піти працювати в офіс. Офіси — основне джерело надходження кредитів до міського бюджету у вигляді податків. Хтось може піти працювати на завод. Заводи виробляють ресурси — метал і бетон. Для роботи всіх будівель потрібна електроенергія. Для цього доведеться побудувати електростанцію. Крім того, в деяких будинках можна працювати самому, при цьому буде витрачатися енергія гравця, яка відновлюється з часом. Приміром, гравцеві не вистачає металу. Замість того, щоб чекати кілька годин, поки він буде вироблятися на заводі, гравець може перейти в будівлю заводу і попрацювати там сам. І звичайно ж, суть роботи полягає в тому, щоб 100 раз клікнути по кнопці з написом «Працювати»)

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

Будівель у грі досить багато. Є готелі і ресторани, поліцейські ділянки і пожежні станції, банки, фітнес центри, біржі і т. д. За будівництво та поліпшення будівель гравець отримує досвід і може підвищити рівень міста. При цьому він отримує 1 золотий ключ, який знадобиться при будівництві спеціальних споруд. Щось на зразок очок вміння в RPG. Наприклад, можна побудувати банк, тоді при виході з гри всі накопичені кошти будуть перераховуватися на депозит, а гравець буде отримувати відсотки за весь час відсутності в грі. Чи можна побудувати податкову інспекцію, яка збільшує надходження податків до міського бюджету на 10%. Крім основних і спеціальних споруд, є ще службові, унікальні і декоративні.

Транспорт
Ще в місті є транспортні засоби. Зроблені вони тільки для краси. Якщо побудувати міську адміністрацію, по місту почнуть їздити лімузини. Можна також побудувати шоу-рум, тоді по місту почнуть ганяти спорткари.

image

Взаємодія гравців
Серед службових будівель є пивний паб і пошта. Побудувавши їх, гравці зможуть взаємодіяти один з одним, наприклад, обмінюватися инвайтами і ресурсами. При цьому не потрібен доступ в мережу. Гравці будуть обмінюватися біт-кодами, це щось на зразок промо-кодів або купонів. Кожне місто має унікальний індекс, який відображається в будівлі пошти. Його потрібно вказувати в якості одержувача при відправленні посилки. Крім того, звичайно ж, потрібно буде вибрати, що відправити. Наприклад, можна відправити 1000 одиниць металу, а отриманий код треба буде повідомити одному. При цьому цей код не вийде відправити кільком гравцям, оскільки в ньому зашифрований індекс одержувача.

image

Схожим чином генеруються промо-коди, які вставляються в повідомлення на стіну в соціальних мережах.

image

Наскільки вдало це рішення, покаже час. Буду сподіватися, що гравці оцінять оригінальну ідею.

Участь у Jam Games
Проект встиг взяти участь у Jam Games Kanobu 2016 і опинитися в добірці цікавих проектів. На жаль, до фіналу не дійшов. Зате вдалося поспілкуватися з іншими розробниками та отримати корисні відгуки. На жаль, реальних гравців на таких заходах практично не буває. Але найголовніше, гра наша видавця. Ним стала російська компанія Herocraft. І це круто! Крім того, було ще 2 пропозиції, обговорення яких закінчилася без результату.
image
Отже, коротко поділюся поділюся своїми порадами з приводу хакатонов (hackathon) і джемів (jam):
— шукайте команду. Ймовірність знайти команду на хакатонах невелика. Зазвичай беруть участь вже сформовані команди, а вільні художники на хакатони навідуються вкрай рідко. Пробуйте, але не розраховуйте;
— дивіться на інші проекти і їх результати. Визначайте для себе найбільш популярні напрямки в розробці і «запозичує» хороші ідеї);
— шукайте відгуки про гру;
— шукайте видавця;

Досвід роботи з видавцем
Першим ділом складається договір, в якому обумовлюються платформи для випуску гри, юридичні та фінансові питання. До підписання договору варто підготувати список питань та отримати на них відповіді. Наприклад:
— які доопрацювання потрібні?
— на скільки може розтягнутися процес доробок?
— які умови розірвання договору?
Після цього починається процес доопрацювання гри. Видавець складає список завдань, які потрібно виконати. Раджу відразу визначитися з зручною системою управління проектом. У моєму випадку це був GitHub. Там можна зберігати исходники ігри, білди та інші файли, а також заводити завдання і баги.
image

Щодо Tap Tap Builder основними завданнями виявилися:
— доробка системи навчання
— поліпшення інтерфейсу
— інтеграція інструментів аналітики
— збільшення продуктивності ігри
— створення контенту для ігрових магазинів (іконка, банер, опис, скріншоти, відео)
— розробка тестів

Зауважу, що багато підзадачі довелося відкласти до релізу зважаючи трудомісткості їх виконання. На етапі розробки тестів проходить ітерація тестування на стороні розробника, адже всі передані тести повинні бути як мінімум коректними.
Після доопрацювання гри починається ітерація тестування на стороні видавця та виправлення виявлених помилок.
І ось доведений до кондиції і відлежаний білд відправляється на софтланч (soft launch). Софтланч це обмежена публікація ігри, наприклад в якійсь одній країні і на одній платформі. Завдання софтланча — отримати відгуки реальних гравців і визначити основні метрики ігри (дохід, утримання та інші). На основі цих даних приймається рішення про доцільність світового релізу. У разі провалу софтланча, контракт розривається, гру повертають розробнику, і він може самостійно опублікувати її.

Оптимізація гри
Перші спроби запустити гру на мобільних пристроях виявили суттєві проблеми з продуктивністю. Гра показувала 10-15 FPS на порожньому острові, при цьому пристрої були далеко не найслабші (Sony Z1 та Asus Transformer). Подальша терапія дозволила збільшити швидкість гри в кілька разів. Отже, по порядку.

Оптимізація скриптів
Не варто гарячково починати шукати проблеми в грі та оптимізувати найбільш ймовірні вузькі місця. Першим ділом треба запустити Profiler. На жаль, дана функція є лише в pro-версії юніті.
У мене відразу ж виявилися проблеми в скриптах. Виправлення не зайняло багато часу, вся оптимізація звелася до кешированию інформації та скорочення кількості викликів. До речі, рекомендую шаблон проектування Memoizer. Суть його полягає в запам'ятовуванні результату роботи функції для наступних викликів з ідентичними параметрами. Параметрами можуть бути не тільки прості типи, але й структури і класи. Нижче наведу приклад використання цього шаблону.
public static readonly Func<int, int, int> SumMemoized = Memoizer.Memoize<int, int, int>(Sum);

private static int Sum(int a, int b)
{
return a + b;
}

Імплементація шаблону Memoizer
public class Memoizer
{
public static Func<TReturn> Memoize<TReturn>(Func<TReturn> func)
{
object cache = null;

return () =>
{
if (cache == null) cache = func();

return (TReturn) cache;
};
}

public static Func<TSource, TReturn> Memoize<TSource, TReturn>(Func<TSource, TReturn> func)
{
var cache = new Dictionary<TSource, TReturn>();

return s =>
{
if (!cache.ContainsKey(s))
{
cache[s] = func(s);
}

return cache[s];
};
}

public static Func<TSource1, TSource2, TReturn> Memoize<TSource1, TSource2, TReturn>(Func<TSource1, TSource2, TReturn> func)
{
var cache = new Dictionary<string, TReturn>();

return (s1, s2) =>
{
var key = s1.GetHashCode() + "." + s2.GetHashCode();

if (!cache.ContainsKey(key))
{
cache[key] = func(s1, s2);
}

return cache[key];
};
}
}

Оптимізація тривимірних моделей
Після того, як проблема зі сценаріями було вирішено, настав час зайнятися візуалізації (rendering). Значний вплив на продуктивність надавав той факт, що моделі будівель були зроблені з примітивів, які мали різні матеріали (і кольору). Статистика показувала близько 600 draw call (після роботи автобатчинга) і 100.000 трикутників. Для порівняння — на поточний момент для сферичної гри в вакуумі рекомендується мати до 100 draw call і до 30.000 трикутників.

image

Перше рішення — зменшення кількості поверхонь. Довелося імпортувати ассет для редагування мешей. Багато будівлі містили циліндри, а стандартний циліндр має 20 граней і 80 трикутників. Спрощення геометрії циліндрів до 12 граней дало відчутний ефект у збиток невеликий незграбності будівель.
image
Наступний крок — видалення невидимих граней з примітивів. В результаті істотне скорочення загальної кількості трикутників (близько 20%) ніякого приросту FPS не принесло. Ймовірно, движок і так не витрачає ресурси на обробку невидимою геометрії, навіть якщо вона знаходиться в межах видимості камери. Підсумок — даремний крок.

Батчинг (batching)
Наступний крок — спроба використання статичного батчинга (batching). Коротко розповім про батчинг, для тих, хто не в курсі. Батчинг — це групування мешей (mesh) в 1 загальний меш перед викликом перемальовування (draw call). Просто так виходить, що відеокарті простіше намалювати 1 об'єкт відразу, ніж по частинах за кілька викликів. При цьому є кілька нюансів. Об'єднання можливе тільки для мешей, які використовую один матеріал. По-друге, сумарна кількість вершин в загальному не повинно перевищувати 900. І по-третє, зміна transform (позиція, поворот і масштабування) дочірніх об'єктів неможливо. Приміром, у «сбатченой» моделі автомобіля не будуть крутитися колеса. Батчинг буває статичний і динамічний. Динамічний батчинг працює під час виконання і не потребує ніяких дій з боку розробника. Результат роботи динамічного батчинга можна побачити у вікні Statistics.

image

Статичний батчинг можна реалізувати двома способами — поставити галочку Static для об'єкта на сцені або префаба (prefab). Це можуть бути статичні ігрові об'єкти, наприклад, елементи ландшафту, рослинність і будівлі. Другий спосіб — використання StaticBatchingUtility під час виконання.

Цілком логічно було виконати статичний батчинг для будівель, що я і зробив. В результаті гра стала працювати ще повільніше! Як виявилося, примусовий статичний батчинг порушує роботу динамічного батчинга, який працює ефективніше і групує мешы з різних логічних об'єктів, наприклад, належать різним будівлям. Крім того, автоматично згруповані об'єкти можуть рухатися відносно один одного (при цьому групування скасовується). Підсумок — застосування статичного батчинга може бути корисно тільки при глибокому розумінні логіки його роботи.

Оптимізація тіней
Наступне спостереження — відключення динамічних тіней скорочує кількість трикутників рівно в 2 рази, що давало приріст FPS близько 20%. Зміна якості тіней видимого ефекту не дало. В даній ситуації є просте стандартне рішення — заміна тіней спрайтами Такі тіні часто називають статичними. Відразу ж виникає питання — як отримати тіні об'єктів, адже не малювати ж їх вручну?! Найпростіше рішення — використовувати розмиті колу. Це підійшло б для тіней персонажів у простому раннери або стрілялки. Мені таке рішення не підійшло — тіні повинні повторювати геометрію будівель. Що ж, довелося написати скрипт для дампа (dump) тіней. Алгоритм роботи простий — скрипт по черзі створює будівлі (prefab) і робить скріншот будівлі з тінню, зберігаючи його на диск. Потім другий скрипт робить елементарну обробку отриманих зображень, виконуючи заливку тіні чорним кольором і видаляючи фон (за кольором). На жаль, готових рішень я не знайшов, поділіться своїм досвідом в коментарях.

image

Підсумок — збільшення швидкості на 20% при появі незначних артефактів (при накладенні і нашаруванні тіней). До речі, спрайтам з тінями потрібно встановити Packing Tag, наприклад, Shadow. Тоді Unity створить для них атлас (atlas), і побудова ВСІХ тіней буде виконуватися за 1 draw call. І ще одне зауваження — на деяких пристроях можливі проблеми з відображення динамічних тіней, так що заміна їх на статичні тіні дозволить уникнути проблем.

Подальша оптимізація тривимірних моделей
На жаль, результат проведеної оптимізації виявився незадовільним — гра показувала 20-25 FPS на потужному пристрої. Я зважився на написання ще одного скрипта для запікання (bake) моделей. Суть в тому, щоб згенерувати новий mesh і накласти на нього текстуру для розмальовки будівель. Текстура являє собою просту палітру кольорів розміром 8х8, яка вміє автоматично заповнюватися при появі нових матеріалів (квітів). При цьому необхідно сформувати коректну UV-розгортку (UV-mapping). На Asset Store є плагіни для запікання об'єктів, але вони не вміли генерувати текстуру по квітам матеріалів. Не буду детально зупинятися на деталях реалізації, оскільки розумію, що моя ситуація досить специфічна. Хоча з іншого боку, цей метод може знайти застосування в популярній нині воксельної (voxel) графіку. Адже робити прототипи моделей у Unity набагато зручніше, ніж вивчати моделювання. Якщо, звичайно, у вас в команді, немає моделлера, як у мене. У мене взагалі нікого не було).

image

Кінцевий результат перевершив всі очікування — швидкість гри збільшилася в рази. Гра показує 50-60 FPS на величезному місті. Подальша оптимізація не потрібно.

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

Посилання на гру в Google Play
Джерело: Хабрахабр

0 коментарів

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