Sound Manager для невеликих ігор та прототипів на Unity

Відтворити звук у Unity просто. Потрібно створити компонент AudioSource прикріпити до нього звуковий файл як AudioClip і викликати audioSource.Play() зі скрипта. Або навіть поставити автовідтворення при створенні об'єкта (Play on Awake).

Складнощі починаються коли звуків у грі стає багато. Їх усе потрібно розставити, прописати пріоритети. Звуки окремо, музику окремо. При регулюванні гучності звуків і музики окремо складності. Можна, звичайно, регулювати гучність різних каналів у AudioMixer, але він не працює у WebGL. А Webplayer зараз вважається застарілим.

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

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

sound

Отже що має бути SoundManager? Ну, по-перше, їм має бути зручно користуватися. Тобто ніяких "знайти об'єкт на сцені", "приєднати компонент" та іншого для користувача, все всередині. Так що відразу робимо його синглтон (Код скорочений, щоб виділити суть).

Тепер менеджер сам створить себе на сцені, тому додавати його самостійно не потрібно (і не рекомендується). Створюється він префабом, шлях до якого прописаний в коді, так що переміщати префаб не варто. Можна створювати і за допомогою new GameObject() та AddComponent() якщо хочеться.UPD. Тепер створюється не за префабом.Крім того, об'єкт відразу позначається за допомогою DontDestroyOnLoad. Потрібно це для того, щоб музика та звуки продовжували грати без перебоїв при перезавантаженнях сцен. Тепер до будь-яких методів можна звертатися просто написавши SoundManager.Instance.Method(). Щоб ще трохи скоротити цей запис для всіх методів, я дописав статичний враппер:

Так що писати можна ще коротше SoundManager.Method().

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

Спершу кілька перевірок звуку. Що він не порожній і що таких звуків не стало занадто багато (якщо десь у циклі помилково викликається). Після чого завантажуємо звук із ресурсів, чекаємо на завантаження, створюємо новий об'єкт на сцену, додаємо AudioSource, налаштовуємо його та запускаємо. Функція LoadClipAsync запускає асинхронне завантаження звукового файлу з ресурсів на ім'я. Отже, файл потрібно буде покласти в папку «Resources/Sounds/Sounds». Створення об'єкта відбувається за префабом, який завантажений із ресурсів. Так що частина параметрів (на зразок пріоритету звуку), можна встановити префабу з інспектора. Гучність встановлюється також у кожного об'єкта окремо. На відміну від установки гучності AudioListener це дозволяє регулювати гучність звуків і музики окремо. Збережемо об'єкт у списку звуків _sounds, щоб мати можливість регулювати його гучність та знищувати після закінчення.

Параметр pausable потрібен, щоб розділити UI звуки та ігрові звуки. Перші повинні грати завжди і ніколи не ставитися на паузу. Другі зупиняються під час паузи та продовжуються при відновленні гри. Робиться це автоматично за допомогою прапора soundSource.ignoreListenerPause, який чомусь недоступний з Inspector.

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

У більшості невеликих проектів достатньо одного треку, що програється в даний момент, так що запуск нової музики зупиняє попередні треки автоматично, так що на кожній сцені достатньо викликати лише SoundManager.PlayMusic("MusicForCurrentScene"); Крім того, при створенні та зупинці музики додається плавне наростання гучності та плавне згасання. Це дозволяє зробити перехід плавним та не б'є по слуху. Саму плавну зміну гучності можна робити Tween-ом, але можна і ручками, щоб було менше залежностей.

Далі нам потрібна можливість поставити паузу. Так як у всіх звуків вже проставлена ​​настройка, чи ставляться вони на паузу при паузі AudioListener-а, то методи виходять дуже простими.

Або можна настроїти автоматичне увімкнення паузи.

Далі нам знадобляться методи встановлення та отримання гучності.

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

Ну от і все. SoundManager, яким легко користуватись готовий. Так як ми винесли шаблони для звуків та музики в префаби, то до них легко можна підключити output з AudioMixer. Крім того є ще невеликий клас, що спрощує виклики потрібних методів з анімацій, обробників кнопок та ін., щоб не потрібно було писати скрипт через один рядок.

невеликих

Плюси отриманого менеджера: + Простота у використанні + Чистий код та об'єкти сцени. Не потрібно вішати компоненти звуку ніде, шукати та викликати їх з коду + Музика, яка не переривається під час завантаження сцени тазмінюється плавно + Геймплейні та UI звуки + Підтримка паузи + Підтримка AudioMixer + Робота на всіх платформах, включаючи не підтримують AudioMixer (наприклад WebGL) + Підтримка голосу оповідача(у статті не згадано, але у повному коді реалізовано)

Обмеження поточної реалізації(Поки немає): — Поки що немає позиційного 3d звуку — Зміни pitch-а звуку, щоб багатократне повторення однакових звуків не приїдалося — Завантаження звуку при використанні може призводити до лагам(Непомітно на дрібні проекти і невеликі звуки) — Немає регулювання гучності окремо взятого звуку — Немає зациклених звуків, на зразок амбіента

Повний код менеджера можна подивитися на моєму GitHub-і: https://github.com/Gasparfx/SoundManager

Хардкорна конфа за С++. Ми запрошуємо лише профі.