Брудні трюки мобільного розробки від J2ME до Android

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

J2ME
Самий брудний трюк в розробці під J2ME – поставити всередину основного циклу гри один загальний try-catch.
public void run()
{
while(isGame)
{
try()
{
gameField.Update();
}
catch (Exception ex)
{
}
}
} 

В результаті будь exception перехоплюється всередині циклу. Кадр обробляється не до кінця, але гра не падає — як ні в чому не бувало починає обраховуватися наступний кадр. Ніяких вильотів.
Побічний ефект – зависання гри, якщо проблема зовсім серйозна і виникає на кожному кадрі.
Каюсь, один раз довелося скористатися цим методом. Як у всіх буває, терміни підганяли, а баг відмовлявся перебувати.

Трюк, до якого доводилося вдаватися практично в кожному проекті – це виклик двох апдейтів логіки на одну обробку.
Оновлення об'єктів залежно від часу кадру ми намагалися не використовувати, так як операції з флотами забирали багато процесорного часу. Тому в той момент, коли складність логіки і кількість об'єктів в кадрі виростали так, що телефон не справлявся, ми запускали два апдйта поспіль. Найчастіше це відбувалося ще до фінальної балансування гри, так що ні на що особливо не впливало.
Чому в таких випадках просто не змінювати баланс, збільшивши відстані, подоланні об'єктами за кадр? В першу чергу, напевно, через колізії. Для переміщень об'єктів на великі дистанції довелося б писати більш складну логіку колізій, що позначилося б на продуктивності. Також логіка при русі на великі відстані іноді давала збої, і її довелося б ускладнювати. Ну і в цілому, відображення займала набагато більше часу, ніж обробка логіки навіть в подвійному обсязі, так що це було найпростіше.

Ще одна проблема, з якою доводилося стикатися на J2ME, – розмір пам'яті. Причому як оперативної пам'яті, так і поставки самого білду. Експериментальним шляхом було встановлено, що досить багато можна заощадити, зменшивши кількість класів. Як підсумок, вся ігрова логіка лежала в одному класі. Всі дані були розкладені по масивам. В одному з них лежав тип об'єкта, в іншому – тип його логіки, в третьому – стейт логіки, в четвертому та п'ятому – X і Y координати і т. д. В комплекті йшов цикл з великим світчем за типом об'єкта, який все це крутив. Ніяких динамічних виділень і вилучень, менше промахів по кешу. Працювало досить спритно. Ще пару класів для меню, спрайтів, тайлового бекграунду, основного циклу гри. В результаті вся гра займає менше десяти java-класів. На щастя, жоден мій знайомий «правильний» Java-розробник, ніколи цей код не бачив.
BREW

У ранніх версіях BREW геть відсутнє API для роботи з екраном безпосередньо. А ті функції для малювання, які надавала система, були дуже повільними. Довелося знайти буфер екрана самим. Для цього ми залили весь екран червоним кольором, створили порожній спрайт (чи як він там у них називався) і від його адреси сканували пам'ять в обидві сторони. Переміщалися на кількість байт трохи менше, ніж площа екрану. Якщо знаходили схоже поєднання, заливали екран іншими квітами і за допомогою пари обчислень знаходили формат пікселів, початок буфера екрану і є відступ після кожного рядка. Все займало менше секунди, гравець не встигав нічого помітити. Ясна річ, що оновлення всього цього процесу на екран ми не включали.
iOS (WoT Blitz)

Саме жорстке, з чим довелося зіткнутися в iOS – це необхідність розчищення пам'яті під час запуску гри. В силу деяких обставин ми не стали використовувати для всіх аллокаций свій пул пам'яті з кастомних аллокаторами. Але наш витрата пам'яті часом наближався до позначки в 300 мб, що не дуже добре позначалося на стабільності додатки на слабких девайсах. Справжні проблеми почалися з виходом iOS 7 або 8. Вісь стала іноді «помилятися» і закривати Blitz в моменти пікового навантаження на пам'ять, хоча було видно, що залишаються висіти інші, менш пріоритетні програми (скайп або поштовий клієнт). Після додаткового дослідження ми з'ясували, що iOS дуже не любить виділення великої кількості пам'яті за один твк. Але якщо виділяти пам'ять поступово, то можна перевалити далеко за наші ліміти без побоювання бути закритим.
Перерозподіляти виділення пам'яті на проекті, який вже грають сотні тисяч гравців, довго і дуже небезпечно. Тому ми прийняли інше рішення. На старті гри ми поступово, шматками по 10 мб, виділяємо пам'ять до обсягу, необхідного при грі піку навантаження. А потім відразу всю її і очищаємо. Займає це все менше секунди. А в балці видно, як вісь закриває інші додатки. Таким хитрим способом ми виправили падіння гри при піковому навантаженні на пам'ять.

Одним із серйозних недоліків мобільних GPU є повільна відтворення напівпрозорої геометрії. А в грі конче потрібні кущі та дерева. І як можна більше. Справа ускладнювалась і тим, що у нас є снайперський режим, в якому гравець спостерігає все, що відбувається прямо з прицілу танка, в хорошому наближенні. Часом филлрейт в кущах і деревах перевищував 10 екранів напівпрозорої геометрії.
Для кущів рішення було досить простим, і придумали його навіть не програмісти, а художники. Ми ввели близький лод, який включався практично впритул до куща і складався з одного білборда, площині завжди повернутим до гравця. Це дало можливість відносно безболісно засісти в кущах і навіть вицілювати ворожі танки.
З деревами справа виявилося трохи складніше, так як вони можуть бути повалені на землю в будь-якому напрямку. Для них ми створили спеціальний шейдер, який включався, коли гравець перебував дуже близько до дерева. Цей шейдер-садівник обрізав усі зайві гілки дерева, залишаючи тільки три найближчі до камери. З-за того, що ближні гілки заступають практично весь огляд, гравець не помічає відсутність інших. А ось GPU дуже навіть помічає.
Я б не сказав, що дані рішення повністю позбавили нас від проблем, але вони однозначно дозволили додавати на карту хоч якусь рослинність в прийнятному кількості.
З тією ж проблемою у снайперському режимі ми зіткнулися при створенні ефектів. Снайперський режим плюс якісь партикловые ефекти поблизу камери – і лічильник FPS гарантовано завмре на позначці 10 (менше просто не дозволить движок). Рішення було схоже з кущами, хоч технічно далася набагато складніше. Ми ввели рівні деталізації для частинок. І на самому близькому лоде позбавилися від всього зайвого (наприклад, постріл виглядає як просто спалах). В результаті при максимальному наближенні залишаються тільки самі необхідні ефекти. Як не парадоксально, але наш далекий лод ефектів більш деталізований, ніж близький.

Цікава історія вийшла з сервісом реєстрації користувачів. Він був написаний під старшого брата і без докорів сумління відправляв шматки JavaScript в надії, що клієнт це все запустить, а вже цей JavaScript сформує наступний http-запит. Виявилося, мобільний web view для цих цілей не дуже підходить. В нашому випадку його потрібно робити прихованим, а на внутрішніх тестових сервісах він питав у користувача підтвердження на підключення. До того ж це все треба було крутити в окремому потоці. Часу було мало, а замовити переробку сервісу реєстрації в необхідні строки – практично нереально. І нам довелося на своїй стороні парсити приходять від сервера скрипти, формувати на їх підставі наступні запити, відправляти в сервіс. Пізніше було пару фіксів, які дозволили трохи стандартизувати цей процес. Звучить дивно, але досі працює.
Android (WoT Blitz)

Працюючи над Android-версією нам довелося шукати багато нетривіальних рішень, але навряд чи вони гідні статті. Є одна дійсно кумедна штука, яку нам довелося застосувати для проведення тестів продуктивності. Справа в тому, що в процесі бою, або декількох боїв, багато Android-пристрої встигають нагрітися, частота процесора автоматично знижується. Що веде до зниження FPS. А результати щоденних тестів продуктивності нам дуже хотілося бачити більш-менш стабільними. Після дослідження ситуації ми прийшли до висновку, що після кожного тесту необхідно перезавантажити пристрій. Телефон встигає охолонути і одуматися, а ми отримуємо досить передбачувані результати. Це жодним чином не скасовує плейтесты, на яких QA перевіряє играбельность і якість збірок, але дає можливість помітити якісь зміни в продуктивності пристрою і вжити заходів.
А щеА ще у нас є свій скайп-бот, який збирає білди, оновлює сервера, стежить за станом транка та юніт-тестів, призначає програмістів на рев'ю коду. Але це вже зовсім інша історія.
Якщо у вас є якісь питання, або ви готові поділитися своїми мобільними лайфхаками – пишіть коментарі, обговоримо.
Джерело: Хабрахабр

0 коментарів

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