Комментарии 9
Спасибо, ваш класс хорош, для новичка, и как база для написания «под себя».
P.S. Я бы если не переписал PlayMusicInternal(string musicName) и PlaySoundInternalSoon(string soundName, bool pausable) так добавил бы методы использующие AudioClip и GameObject чтобы избавится от файндов и лоадов…
P.S. Я бы если не переписал PlayMusicInternal(string musicName) и PlaySoundInternalSoon(string soundName, bool pausable) так добавил бы методы использующие AudioClip и GameObject чтобы избавится от файндов и лоадов…
Спасибо!
На счет передачи AudioClip. Я хотел единообразия кода и хранения всех звуковых файлов в одном месте. А с передачей по Clip-у его можно будет положить куда угодно. Начнешь класть в другую папку, а потом захочется этот же звук по имени вызвать и не заработает. В общем то дело вкуса и класс легко можно дописать по нраву.
На счет передачи AudioClip. Я хотел единообразия кода и хранения всех звуковых файлов в одном месте. А с передачей по Clip-у его можно будет положить куда угодно. Начнешь класть в другую папку, а потом захочется этот же звук по имени вызвать и не заработает. В общем то дело вкуса и класс легко можно дописать по нраву.
Замените
if (_sounds.Count > 16) {
на
if (sameCountGuard > 16) {
P.S. Магические числа смотрят на вас… с желанием превратиться в константы или настраиваемые параметры.
if (_sounds.Count > 16) {
на
if (sameCountGuard > 16) {
P.S. Магические числа смотрят на вас… с желанием превратиться в константы или настраиваемые параметры.
Спасибо за поправку. Заменил эту проверку на две. На количество таких же звуков и на общее количество.
А на счет констант. Я вынес в константы то, что с чем можно играться(AutoPause, MusicFadeTime). Мне не хотелось, чтобы кто то увидел это число в константах и решил что его стоит тюнить под себя. Все же это не настройка менеджера в моем понимании, а защита от зацикливаний.
А на счет констант. Я вынес в константы то, что с чем можно играться(AutoPause, MusicFadeTime). Мне не хотелось, чтобы кто то увидел это число в константах и решил что его стоит тюнить под себя. Все же это не настройка менеджера в моем понимании, а защита от зацикливаний.
Я разделяю FX-ы (спецэффекты, короткие звуки, включая UI) и фоновую музыку. Учитывая, что на мобилках параллельное проигрывание очень сильно сажает производительность, нужно ограничивать количество параллельно проигрываемых звуков (я использую 1 канал для фоновой музыки и 3 канала для FX-ов).
Проигрывание музыки (если совпадает с уже проигрываемой, то прерывания нет — переключение между сценами происходит бесшовно по звуку):
Путь указывается от корня Resources, что позволяет переносить данный класс между проектами без изменений.
Проигрывание короткого звука:
Сам класс SoundManager является кросс-сценовым синглтоном с ленивой инициализацией без необходимости дополнительной настройки в редакторе, все управление для дизайнера ведется через 3 вспомогательных класса:
и
Настройки громкости разделены на громкость музыки и громкость FX-ов и хранятся отдельно в настройках пользователя, тут все стандартно.
Проигрывание музыки (если совпадает с уже проигрываемой, то прерывания нет — переключение между сценами происходит бесшовно по звуку):
public void PlayMusic (string music, bool isLooped = false) { }
public void StopMusic () { }
Путь указывается от корня Resources, что позволяет переносить данный класс между проектами без изменений.
Проигрывание короткого звука:
public enum SoundFXChannel {
First = 0,
Second = 1,
Third = 2
}
...
public void PlayFX (AudioClip clip, SoundFXChannel channel = SoundFXChannel.First, bool forceInterrupt = false) { }
public void StopFX (SoundFXChannel channel) { }
Сам класс SoundManager является кросс-сценовым синглтоном с ленивой инициализацией без необходимости дополнительной настройки в редакторе, все управление для дизайнера ведется через 3 вспомогательных класса:
public sealed class MusicOnStart : MonoBehaviour {
public string Music = null;
public bool IsLooped = true;
IEnumerator Start () {
yield return null;
if (SoundManager.Instance.MusicVolume == 0f) {
SoundManager.Instance.StopMusic ();
}
SoundManager.Instance.PlayMusic (Music, IsLooped);
}
}
и
public sealed class SoundOnStart : MonoBehaviour {
public AudioClip Sound = null;
public SoundFXChannel Channel = SoundFXChannel.First;
public bool IsInterrupt = false;
IEnumerator Start () {
yield return null;
SoundManager.Instance.PlayFX (Sound, Channel, IsInterrupt);
}
}
public sealed class SoundOnEnable : MonoBehaviour {
public AudioClip Sound = null;
public SoundFXChannel Channel = SoundFXChannel.First;
public bool IsInterrupt = false;
void OnEnable () {
SoundManager.Instance.PlayFX (Sound, Channel, IsInterrupt);
}
}
Настройки громкости разделены на громкость музыки и громкость FX-ов и хранятся отдельно в настройках пользователя, тут все стандартно.
На счет музыки не совсем понял. То есть при смене музыки трек меняется резко, без приглушения? У нас композитор явно настаивал на плавном угасании первого трека и нарастании второго. Иначе может по слуху бить.
А вот идея с указанием в какой канал воспроизводить звук мне не очень понятна. Вот у вас эти классы по умолчанию в первый канал пишут, так если их два запустится, то один прибьется сразу, хотя два канала пустые еще? Зачем указывать номер канала, не лучше ли просто ограничить число каналов до 3 и играть если есть свободные. Причем так можно сделать даже лучше. При запуске звука указывать его приоритет и прибивать менее приоритетные звуки если свободных каналов нет.
Но в целом я думаю, что ограничивать звук 3 каналами смысла мало. Более менее больших звуков в один момент времени больше 1-2 быть не должно. А мелких звуков, вроде клика может быть много, но их можно импортировать без сжатия в wav. Тогда их проигрывание не будет просаживать производительность сильно. Какое то ограничение, конечно нужно, но тут уже экспериментально и от таргет устройства зависит.
А вот идея с указанием в какой канал воспроизводить звук мне не очень понятна. Вот у вас эти классы по умолчанию в первый канал пишут, так если их два запустится, то один прибьется сразу, хотя два канала пустые еще? Зачем указывать номер канала, не лучше ли просто ограничить число каналов до 3 и играть если есть свободные. Причем так можно сделать даже лучше. При запуске звука указывать его приоритет и прибивать менее приоритетные звуки если свободных каналов нет.
Но в целом я думаю, что ограничивать звук 3 каналами смысла мало. Более менее больших звуков в один момент времени больше 1-2 быть не должно. А мелких звуков, вроде клика может быть много, но их можно импортировать без сжатия в wav. Тогда их проигрывание не будет просаживать производительность сильно. Какое то ограничение, конечно нужно, но тут уже экспериментально и от таргет устройства зависит.
То есть при смене музыки трек меняется резко, без приглушения? У нас композитор явно настаивал на плавном угасании первого трека и нарастании второго. Иначе может по слуху бить.
За фейдинг между сценами отвечает другой менеджер — FadeManager, который так же может управлять громкостью AudioListener-а в сцене. Те если известно, что в новой сцене будет другая мелодия, то в фейдинг добавляется флаг, уменьшающий громкость «уха» вслед за угасанием экрана. В новой сцене музыка начнет играться как обычно, а фейдер плавно покажет экран и так же плавно нарастит громкость. Переключения мелодии внутри одной сцены не планировалось.
Вот у вас эти классы по умолчанию в первый канал пишут, так если их два запустится, то один прибьется сразу, хотя два канала пустые еще?
Они не пишут все в один канал, его можно выбирать. Каналы — это по сути 3 параллельных источника звуков. Тут уже дизайнер / разработчик должен решить, какие звуки куда идут. Например, UI идет в первый канал, взрывы — во второй, голоса — в третий. И они будут играться без тормозов на бюджетках (у меня просто основное направление — мобильные устройства, а не десктоп, где ресурсов много и можно не контролировать).
Но в целом я думаю, что ограничивать звук 3 каналами смысла мало.
Раньше даже при 4 параллельных звуках звук начинал «пищать» на части китайфонов + сильно тормозить сам процесс (прошло 4 года, ситуация стала немного получше, но не кардинально).
Более менее больших звуков в один момент времени больше 1-2 быть не должно.
Мобилки поддерживают аппаратное декодирование только одного потока, поэтому канал «музыки» один и грузится из ресурсов по имени. Сам аудио-клип настраивается как стриминговый — не ест много памяти, тянется с устройства хранения напрямую, высока вероятность того, что будет декодироваться аппаратно (если у игрока не играет фоновая музыка через itunes, например). Так же если музыка зациклена, то ее нужно хранить в wav с дальнейшим выбором формата сжатия в unity (впрочем, как и все остальные ресурсы) — это уберет глюк с щелчком при переходе из конца в начало (в mp3 юнити обрезает последние несколько мс и получается неприятный эффект).
Какое то ограничение, конечно нужно, но тут уже экспериментально и от таргет устройства зависит.
Ну вот, собственно, такое ограничение и есть. Обычно хватает 2 каналов — музыка + FX, в который идет все короткие звуки. 3 FX канала — это уже жир.
З.Ы. Надо всех десктопных «инди» посадить на бюджетные мобилки и заставить переписать их продукты так, чтобы на самом плохом девайсе (типа iphone4) они шли без долгих фризов и хотя бы на 30фпс — вот тогда начнут думать об оптимизации и неуемной трате ресурсов. А то «х*як — х*як и в продакшн» так и будет процветать…
Так все хорошо излагаете. И не только по звуку. О всяких менеджерах интересно было бы почитать. И о «ресурсной экономике», поделитесь опытом. Может статью напишете?
К сожалению, писать там особо нечего — просто набор триков по жесткой экономии (по сути шишки, полученные на своем опыте) и постоянные тесты на максимально дохлом железе. Если запускается и функционирует сносно, то на более мощном железе будет просто летать (или не летать, но хотя бы работать не медленнее). На самом деле конечное решение сильно зависит от того, насколько программная часть сможет договориться с дизайнерской частью (программист vs дизайнер) и прийти к компромиссу.
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Sound Manager для небольших игр и прототипов на Unity