Написал для себя простейший менеджер звуков интерфейса.
Может проигрывать неограниченное количество звуков "на точке" - не поддерживает трех- и двух- мерные направления (щелчки кнопок, музыка, и т.д.).
Плюсы: + Не требует сложной установки. + Не инстанциирует объекты. + Прост в использовании.
Минусы: - Не мешало бы прикрутить обертки источникам звуков для более гибких настроек. - Не поддерживает пространственные звуки. - Требует небольшой оптимизации (думаю добавить очередь, чтобы облегчить ожидание readyToPlay).
Примечание: = Скрипт прикреплять можно только к одному объекту, либо убирать статическое поле I и использовать поиск.
Скрипт вешается на любой объект сцены, который не уничтожается на загрузках. На камеру вешается пустой объект с координатами (0,0,0). Обзывается "SoundSourcerInstance" (название задается в скрипте).
Код C#:
Код
using System.Collections.Generic; using System.Threading; using UnityEngine;
public class SoundController : MonoBehaviour { public static SoundController I { get; private set; } // Метод статического доступа к скрипту. public GameObject SoundSourcer; // Объект, в котором будут создаваться источники звука. Должен быть привязан к камере и быть в 0,0,0 от неё. Создается автоматически в методе Start().
private readonly List<AudioSource> _pool; // Пул источников звука. При необходимости расширяется. Очищается автоматически. // TODO Сделать обертку источникам. В обертке указывать кол-во воспроизведений в лупе и т.д. private AudioSource _ambientMusicSource; // Отдельный источник для воспроизведения музыки. private float _purgeTick; // Временная переменная, считает время до очистки пула. private float _realMusicVolume, _toMusicVolume; // Временные переменные для эффекта затухания музыки.
private SoundController() { // Конструктор. Устанавливает ссылку для статического доступа и создает пул. I = this; _pool = new List<AudioSource>(); }
void Start() { // При старте ищет объект SoundSourserInstance. Объект должен быть пустым и "висеть на камере". SoundSourcer = GameObject.Find("SoundSourcerInstance"); _ambientMusicSource = SoundSourcer.AddComponent<AudioSource>(); _realMusicVolume = _toMusicVolume = Config.GetConfig("musicvolume", 100) / 100f; }
void Update() { // Очистка пула _purgeTick += Time.deltaTime; if (_purgeTick > Config.GetConfig("soundpurgertime", 20)) { // Здесь задается периодичность очистки. По умолчанию 20сек. _purgeTick = 0f; foreach (AudioSource source in SoundSourcer.GetComponents<AudioSource>()) { // Собственно очистка пула. Перебирает все источники и удаляет при необходимости. if (!source.isPlaying && !source.loop) { // По лупу определяются звуки окружения... if (_pool.Contains(source)) { _pool.Remove(source); } Destroy(source); } } }
// Эффект затухания музыки if (_toMusicVolume > _realMusicVolume) { _realMusicVolume += 0.1f * Time.deltaTime; if (_toMusicVolume < _realMusicVolume) { _realMusicVolume = _toMusicVolume; } } else if (_toMusicVolume < _realMusicVolume) { _realMusicVolume -= 0.1f * Time.deltaTime; if (_toMusicVolume > _realMusicVolume) { _realMusicVolume = _toMusicVolume; } } }
// Проигрывает звук из ресурсов. Звук должен находиться в папке "Resources/Sound/". public void PlayResourceClip(string soundName) { PlayAudioClip(Resources.Load("Sound/" + soundName) as AudioClip); }
// Проигрывает загруженный клип звука. Необязательные переменные: проигрыватьНемедленно, зациклить. public AudioSource PlayAudioClip(AudioClip clip, bool playImmediately = true, bool loop = false) { if (clip == null) return null; AudioSource source = GetFreeAudioSource(); source.clip = clip; source.loop = loop; source.volume = Config.GetConfig("mastervolume", 100) / 100f; while (!source.clip.isReadyToPlay) { Thread.Sleep(10); // TODO Придумать способ получше. Может быть очередь. Пожалуй даже не "может быть", а очередь. А в апдейтах Dequeue в проигрывание. } if (playImmediately) source.Play(); return source; }
// Ищет свободный источник или создает его, если не нашел. public AudioSource GetFreeAudioSource() { AudioSource source = null; foreach (AudioSource pooledSource in _pool) { if (!pooledSource.isPlaying) { source = pooledSource; break; } }
if (source == null) { source = SoundSourcer.AddComponent<AudioSource>(); _pool.Add(source); }
return source; }
// Начинает проигрывание фоновой музыки. public AudioSource PlayAmbientMusic(AudioClip clip, bool playImmediately = true) { _ambientMusicSource.Stop(); _ambientMusicSource.clip = clip; _ambientMusicSource.priority = 50; _ambientMusicSource.volume = _realMusicVolume; _ambientMusicSource.loop = true; while (!_ambientMusicSource.clip.isReadyToPlay) { Thread.Sleep(10); // TODO Придумать способ получше. Может быть переменная-переходник. } if (playImmediately) _ambientMusicSource.Play(); return _ambientMusicSource; }
// Включает и выключает приглушение музыки. public void MuteMusic(float toVolume = 0f) { _toMusicVolume = toVolume; } public void UnMuteMusic() { _toMusicVolume = Config.GetConfig("musicvolume", 100) / 100f; } }
Использование (в любом потоке):
Код
SoundController.I.PlayResourceClip("key");
Принцип действия: при воспроизведении звука берет источник из пула (или создает, если нету свободных), и воспроизводит на нем клип. Раз в N (по-умолчанию 20) секунд просматривает пул на наличие неиспользованных источников и уничтожает их.
С удовольствием выслушаю минусы такого способа.
Автор я (Johnson), прошу оставлять ссылку на источник и автора при копировании.
Сообщение отредактировал Johnson - Вс, 09 Июн 2013, 17:14