Як додати glow і 2D-світло в стару 2D-гру на Unity URP
Як дати старій 2D-грі на Unity URP вибірковий glow через Bloom і HDR, динамічне 2D-світло та чіткий UI для сучасних екранів. Покроково на прикладі Dreamica.

Стара 2D-гра на Unity з вбудованим render pipeline на сучасному телефоні виглядає пласко: освітлення немає, glow або відсутній, або підсвічує весь кадр разом з UI, а sprite інтерфейсу розмиті. В Unity URP це вирішують три речі: Bloom з порогом 1 та HDR-колір лише на об'єктах, які мають світитися, 2D-світло з 2D renderer і UI з більшими sprite, що враховує безпечну зону екрана.
Покажемо це на нашій грі Dreamica (App Store, Google Play). Це пригода з падінням, у якій ви малюєте пальцем лінії й ведете ними бульбашку зі сплячою дитиною крізь чотири світи. Спочатку гру створили в Unity 5.3, UI працював на плагіні NGUI, glow давав плагін MK Glow, а графіку готували для iPhone 7 з екраном 1334 × 750 пікселів. Геймплей лишився тим самим, змінилася тільки графіка.
Стару гру часто відкривають через магазин, а не через графіку. Google Play з серпня 2019 року вимагає 64-бітні версії, і гра лише з 32-бітним build з нього зникає. Саме так сталося з Dreamica. Якщо ви все одно переносите проєкт на Unity 6, графіка стає відносно дешевим бонусом.
Чому старого повноекранного glow недостатньо
Старші glow-плагіни працюють як повноекранний ефект. Вони підсвічують усе світле, зокрема іконки кнопок у меню. У Dreamica під MK Glow в Unity 6 іконки майже зникли. Офіційний пакет Post Processing Stack v2 замінює його лише для вбудованого pipeline, в URP він уже не застосовується. Якщо вам потрібен glow, яким можна керувати для кожного матеріалу, і динамічне 2D-світло, переходьте на URP і використовуйте вбудований у нього Bloom.
Крок 1: Переведіть проєкт на URP
Після переходу на URP стара 2D-гра майже напевно натрапить на дві пастки. Обидві виглядають так, ніби картинка зламана або точно така сама, як раніше, хоча shader у порядку.
Сірий екран: налаштуйте camera stack
Якщо вікно Game в Editor показує лише сіру площину, а вікно Scene і одноразовий рендер камери виглядають правильно, проблема не в shader, а в тому, як поєднуються камери. Старі ігри часто мають дві камери, одну для світу й одну для UI, причому камера UI очищає лише глибину. Так камери накладаються у вбудованому pipeline, і URP цього не підтримує. Йому потрібен явний camera stack: одна камера базова (Base), інші є накладками (Overlay) у її стеку. Налаштуйте його в кожній сцені, де камери накладаються.
Sprite ігнорують світло: перейдіть на 2D renderer
Sprite з освітлюваним shader не реагують на жодне світло, якщо активний звичайний URP renderer. Він узагалі не обчислює 2D-світло. Перейдіть на 2D renderer. Але й він не малює освітлення, поки в сцені немає хоча б одного 2D-світла. Коли після зміни щось виглядає точно як раніше, спершу перевірте, який renderer працює.
Частинки та старі плагіни
Частинки на старих shader під 2D renderer малюються чорними або не малюються зовсім. Дайте їм новий простий shader.
Обережно з Asset Store. Під час оновлення assets Editor може сам застосувати «оновлення» старого плагіна, яке додає метадані й перейменовує shader. Матеріали шукають shader за назвою, тож тихо ламаються. Після кожного більшого оновлення assets у старому проєкті перегляньте в системі контролю версій усі змінені файли, навіть ті, з якими зараз не працюєте.
Крок 2: Вибірковий glow через HDR-колір і Bloom з порогом 1
Мета в тому, щоб світилися вибрані об'єкти, у Dreamica це намальована лінія, лазери, іскри, світлячки й руни, але не фон і UI. Bloom в URP розмиває й додає все, що яскравіше за його поріг. Встановіть поріг 1 і підніміть колір матеріалів, які мають світитися, вище 1.
shader лінії:
колір = текстура × колір вершини × відтінок × інтенсивність (інтенсивність = 6)
Bloom:
поріг = 1, сила = 0,3
світиться лише те, що після обчислення яскравіше за 1
Фон і кнопки мають кольори від 0 до 1, не проходять поріг і лишаються чіткими. Так зникає й проблема з вибіленими іконками. Умова: камера з HDR. Без нього колір обрізається до 1 і нічого не світиться. 2D renderer не показує перемикач HDR в інспекторі камери, тому увімкніть його скриптом.


Коли glow так контрольований, він не мусить бути опціональним. У Dreamica ми прибрали перемикач glow з налаштувань, glow є частиною вигляду гри.
Карта емісії: світиться лише частина sprite
Якщо має світитися лише частина sprite, додайте йому карту емісії (emission map): другу, чорно-білу текстуру, яка позначає, яка частина sprite світиться. Лише ця частина отримує колір понад 1, тож у руни сяє серцевина, а не весь контур.

Пульсація в ступенях експозиції
Якщо світіння має пульсувати, не множте колір лінійно. HDR-матеріал може мати яскравість близько 2 у восьмому степені, і лінійний множник від 0 до 10 на ньому взагалі не помітний. Рахуйте пульс у ступенях експозиції (EV), так само як повзунок інтенсивності у виборі HDR-кольору в Unity:
колір = відтінок × 2 ^ ev, ev коливається між мінімумом і максимумом
Крок 3: Динамічне 2D-світло навколо гравця
Додайте гравцеві точкове 2D-світло з м'яким ореолом навколо. У Dreamica одна синусоїда водночас керує яскравістю ореолу, радіусом світла та його інтенсивністю, тож вони пульсують разом. Колір світла може задавати середовище: у лісі білий, на болоті помаранчевий, узимку фіолетовий, у пеклі жовтий. Переведіть фон і перешкоди на освітлювані shader, інакше вони не реагують на світло.
Короткі спалахи світла оживлюють події. Коли стріляє білка з гарматою, спалахує бурштинове світло з інтенсивністю 3, яке гасне за 0,3 секунди, а іскри з дула світяться. Світитися має не все: пил, коли зникає жолудь, навмисно коричневий і не світиться.
Однаковий shader на суміжних поверхнях
Якщо два фони стикуються, вихідні зображення мають однакову яскравість, а шов між ними все одно видно, перевірте shader. У Dreamica початковий фон і нескінченний фон лісу мали різні shader. Один ігнорував відтінок матеріалу 0,733, другий застосовував його й затемнював ліс до 73 %. Рішення: однаковий shader на обох поверхнях.


Спалах: перехід до піку замість додавання
Під час переходу до наступного світу світло й ореол спалахують: наростання 0,6 секунди, коротке утримання, згасання 1,4 секунди, і ореол при цьому один раз обертається. Якщо просто додати спалах до пульсу, пік залежить від того, у якій фазі саме зараз синусоїда, і кожен спалах має іншу силу. Натомість плавно переходьте від пульсу до фіксованого піку:
вага = крива спалаху (від 0 до 1)
значення = пульс + (пік - пульс) × вага


Крок 4: Чіткий UI для сучасних екранів
UI, намальований для iPhone 7, має малі sprite. Кнопка меню в Dreamica мала sprite 128 × 64 пікселі й на телефоні з утричі більшою щільністю пікселів розтягувалася в кілька разів. Перемалюйте sprite у вищій роздільності: нова кнопка має 800 × 434, індикатори прогресу виросли з 200 × 26 до 800 × 104.


Якщо UI побудований на NGUI, подумайте про переписування. Якорі NGUI обчислюються частинами протягом кількох кадрів, довгі ланцюжки якорів іноді встановлюються неправильно, а масштаб різниться між Editor, телефоном та iPad. У Dreamica виправлення не зробили це надійним, тому ми переписали UI на uGUI і TextMeshPro. Рухайтеся екран за екраном, а стару версію тримайте запущеною поруч як зразок. Масштабуйте UI за висотою відносно референсної роздільності (у нас 434 × 941 точок) і враховуйте безпечну зону екрана, тобто виріз і заокруглені кути. Після переписування повністю видаліть NGUI з проєкту, щоб у ньому не лишилося двох систем UI.
Обмеження: нестиснуті текстури
Цей підхід змінює лише графіку. У Dreamica ми свідомо не стискали текстури, бо хотіли повної чіткості. Ціна в розмірі: уже оригінальна гра мала в Editor 313 МБ текстур у пам'яті, а в App Store вона займає 299 МБ. За нашою оцінкою, стиснення зменшило б текстури приблизно до 40 МБ. Якщо розмір гри для вас важливий, економте спершу тут.
Інший приклад гри на Unity від першого рядка коду до релізу описано в статті про Space Impact Watch, а продуктивності в Unity присвячена стаття про навігацію орди в Unity DOTS. Якщо гра ще на Unity 4, не відкривайте її одразу в Unity 6; безпечний шлях описано в інструкції як перенести проєкт з Unity 4 на Unity 6. Якщо у вас є стара гра на Unity, яка зникла з магазинів, і ви хочете її повернути, напишіть нам.


