К этой статье будет приложен небольшой, но полезный csharp скрипт, и показано как им пользоваться.
Поводом для написания скрипта стало то, что появилась необходимость в настройке и тестировании звуковых эффектов не запуская сцены проекта. А так же в отслеживании основных событий воспроизведения.
Скрипт работает одинаково как в PlayMode так и в EditMode, и позволяет:
1. Воспроизвести звук с необходимой задержкой и отследить начало воспроизведения.
2. Отследить окончание звука, в том числе каждый момент завершения зацикленного воспроизведения.
3. Отследить незапланированное окончание воспроизведения звука.
4. Использовать событие для отслеживания и изменения параметров в процессе воспроизведения.
Для воспроизведения звука используются статические методы:
Эти методы возвращают экземпляр класса SoundTrack, к которому в последствии можно прикрепить необходимые события. Первый метод создаёт на сцене GameObject, второй добавляет указанному GameObject компоненты SoundTrack и AudioSource.
Параметры volume и pitch не нуждаются, наверное, в представлении.
loopTime – можно использовать для задания времени в секундах, которое будет длиться цикл воспроизведения. При значении 0 звук проиграется только один раз, при значении float.PositiveInfinity звук будет проигрываться бесконечно.
delayTime – это задержка перед воспроизведением звука в секундах.
Итак, самый простой пример кода для воспроизведения звука в игре.
И для воспроизведения звука в редакторе.
Теперь можно попробовать разобраться с событиями. Думаю для наглядности лучше сразу взять пример для редактора, так как в режиме игры всё работает аналогично.
Данный пример запускается сочетанием клавиш SHIFT+F1, и показывает как использовать события.
Для того чтобы пример работал, этот скрипт надо положить в папку Editor, а так же иметь в ресурсах проекта хотя бы один звуковой файл. Если нужен тест в запущенном проекте, то можно просто из функции TestSound перенести код в функцию Start, и если нужно, то сделать все свойства и метода нестатическими.
Это событие срабатывает непосредственно при начале воспроизведения звука.
Т.е. в случае, если delayTime > 0, оно сработает не при создании звука, а при старте воспроизведения.
Это событие может быть полезно в ряде случаев.
К примеру можно музыке и эффектам задать разные события, и одной переменной регулировать громкость всей музыки в игре, а другой всех эффектов. А потом добавить и эффектам и музыке ещё одно событие, в котором уже регулировать скорость их воспроизведения, если в игре есть эффект Slow Motion.
В данном примере звук должен в течении каждой секунды менять громкость.
Это событие может пригодиться в ситуации, когда loopTime превышает длину звукового файла, это значит, что звук будет проигрываться циклично. В этом случае событие будет срабатывать в тот момент, когда звук начинается заново. В остальных случаях вызов этого события не гарантируется.
Это событие сработает в 2-х случаях. Первый случай, это когда воспроизведение звука завершено, по истечении loopTime или при одноразовом воспроизведении. Второй случай, это никак не связанное с логикой скрипта удаление звука со сцены (например вручную удалив объект из иерархии сцены). Отличить один случай от другого можно с помощью параметра atEndOfSound, если он равен false, то это как раз второй случай.
Во втором случае мы уже не будем иметь доступ к GameObject'у и компонентам звука.
Если к примеру необходимо последовательно проиграть несколько мелодий и создать ощущение непрерывного звучания, можно попробовать сделать это как показано в примере, в функции soundDestroyEvent.
Помимо этого в скрипте есть параметры которые могут понадобиться:
Всё время расчитывается относительно Time.realtimeSinceStartup.
Здесь можно скачать сам скрипт и приведённый выше пример.
Если в примере не работает сочетание клавиш, можно найти пункт TestSound в меню.
Если Unity не находит звук в ресурсах, надо его просто выделить (посмотреть настройки аудиофайла).
Поводом для написания скрипта стало то, что появилась необходимость в настройке и тестировании звуковых эффектов не запуская сцены проекта. А так же в отслеживании основных событий воспроизведения.
Скрипт работает одинаково как в PlayMode так и в EditMode, и позволяет:
1. Воспроизвести звук с необходимой задержкой и отследить начало воспроизведения.
2. Отследить окончание звука, в том числе каждый момент завершения зацикленного воспроизведения.
3. Отследить незапланированное окончание воспроизведения звука.
4. Использовать событие для отслеживания и изменения параметров в процессе воспроизведения.
Для воспроизведения звука используются статические методы:
public static SoundTrack PlaySound(AudioClip clip, float volume = 1, float pitch = 1, float loopTime = 0, float delayTime = 0); public static SoundTrack PlaySound(GameObject target, AudioClip clip, float volume = 1, float pitch = 1, float loopTime = 0, float delayTime = 0);
Эти методы возвращают экземпляр класса SoundTrack, к которому в последствии можно прикрепить необходимые события. Первый метод создаёт на сцене GameObject, второй добавляет указанному GameObject компоненты SoundTrack и AudioSource.
Параметры volume и pitch не нуждаются, наверное, в представлении.
loopTime – можно использовать для задания времени в секундах, которое будет длиться цикл воспроизведения. При значении 0 звук проиграется только один раз, при значении float.PositiveInfinity звук будет проигрываться бесконечно.
delayTime – это задержка перед воспроизведением звука в секундах.
Примечание:
По прошествии loopTime, первый метод удалит созданный для звука GameObject, второй метод удалит только созданные для звука компоненты.
Итак, самый простой пример кода для воспроизведения звука в игре.
public AudioClip testSound; void Start () { SoundTrack.PlaySound (testSound); }
И для воспроизведения звука в редакторе.
[MenuItem("MyMenu/TestSound #F1")] static void TestSound(){ AudioClip[] clips=Resources.FindObjectsOfTypeAll<AudioClip>(); if(clips==null||clips.Length==0){ Debug.LogError("No clips in the resources!"); return; } SoundTrack.PlaySound(clips[0]); }
Теперь можно попробовать разобраться с событиями. Думаю для наглядности лучше сразу взять пример для редактора, так как в режиме игры всё работает аналогично.
Данный пример запускается сочетанием клавиш SHIFT+F1, и показывает как использовать события.
using UnityEngine; using UnityEditor; using System.Collections; public class SoundTrackTest : Editor { [MenuItem("MyMenu/TestSound #F1")] static void TestSound(){ clips=Resources.FindObjectsOfTypeAll<AudioClip>(); Debug.Log("clips " + clips.Length); if(clips==null||clips.Length==0){ Debug.LogError("No clips in the resources!"); return; } rePlayCount = 0; StartNextSound (0); } static AudioClip[] clips; static int rePlayCount=0; static void StartNextSound(float timePosition){ SoundTrack track=SoundTrack.PlaySound(clips[Random.Range(0,clips.Length-1)]); rePlayCount++; // событие начала звука track.start_action += soundStartEvent; // событие срабатывает каждый кадр track.processing += soundProcessEvent; // это событие подходит только для зацикленного воспроизведения // оно срабатывает каждый раз, когда звук завершается track.complete_action += soundCompleteEvent; // событие срабатывает при далении звука track.destroy_action += soundDestroyEvent; if(timePosition>0){ // перематывает точку воспроизведения на определённый момент, в секундах track.setTimePosition (timePosition); } } static void soundStartEvent(SoundTrack track){ Debug.Log("Sound Start event!"); } static void soundProcessEvent(SoundTrack track){ track.volume = track.playing_time % 1f; } static void soundCompleteEvent(SoundTrack track, float offset){ Debug.Log("Sound Complete event! "+offset); } static void soundDestroyEvent(SoundTrack track, bool atEndOfSound, float offset){ Debug.Log("Sound Destroy event! "+offset); // проверяет было ли уничтожение инициировано из за окончания воспроизведения // atEndOfSound будет равен false в случае если воспроизведения было прервано по другим причинам // к примеру при удалении звука со сцены вручную и т.д. if(atEndOfSound){ // данный подход демонстрирует как можно плавно соединить несколько последовательных звуков // offset - это неизбежная задержка вызова события // можно её компенсировать используя track.setTimePosition для следующего звука // это сделано чтобы звуки в редакторе не воспроизводились бесконечно // так как если звук очень короткий, это будет весьма неприятный тест if(rePlayCount<4){ StartNextSound (offset); } } } }
Для того чтобы пример работал, этот скрипт надо положить в папку Editor, а так же иметь в ресурсах проекта хотя бы один звуковой файл. Если нужен тест в запущенном проекте, то можно просто из функции TestSound перенести код в функцию Start, и если нужно, то сделать все свойства и метода нестатическими.
void Start(){ clips=Resources.FindObjectsOfTypeAll<AudioClip>(); Debug.Log("clips " + clips.Length); if(clips==null||clips.Length==0){ Debug.LogError("No clips in the resources!"); return; } rePlayCount = 0; StartNextSound (0); }
Это событие срабатывает непосредственно при начале воспроизведения звука.
track.start_action += soundStartEvent; // событие начала звука
Т.е. в случае, если delayTime > 0, оно сработает не при создании звука, а при старте воспроизведения.
Это событие может быть полезно в ряде случаев.
track.processing += soundProcessEvent; // событие срабатывает каждый кадр
К примеру можно музыке и эффектам задать разные события, и одной переменной регулировать громкость всей музыки в игре, а другой всех эффектов. А потом добавить и эффектам и музыке ещё одно событие, в котором уже регулировать скорость их воспроизведения, если в игре есть эффект Slow Motion.
В данном примере звук должен в течении каждой секунды менять громкость.
track.complete_action += soundCompleteEvent; // событие срабатывает каждый раз, когда звук завершается
Это событие может пригодиться в ситуации, когда loopTime превышает длину звукового файла, это значит, что звук будет проигрываться циклично. В этом случае событие будет срабатывать в тот момент, когда звук начинается заново. В остальных случаях вызов этого события не гарантируется.
track.destroy_action += soundDestroyEvent; // событие срабатывает при удалении звука
Это событие сработает в 2-х случаях. Первый случай, это когда воспроизведение звука завершено, по истечении loopTime или при одноразовом воспроизведении. Второй случай, это никак не связанное с логикой скрипта удаление звука со сцены (например вручную удалив объект из иерархии сцены). Отличить один случай от другого можно с помощью параметра atEndOfSound, если он равен false, то это как раз второй случай.
Во втором случае мы уже не будем иметь доступ к GameObject'у и компонентам звука.
Если к примеру необходимо последовательно проиграть несколько мелодий и создать ощущение непрерывного звучания, можно попробовать сделать это как показано в примере, в функции soundDestroyEvent.
Помимо этого в скрипте есть параметры которые могут понадобиться:
- time_position — позиция воспроизведения аудиофайла, от 0 до длины файла;
- life_time — время с начала воспроизведения звука;
- playing_time — время затраченное на воспроизведения звука, не учитывает паузы;
- loop_time — время, которое звук будет проигрываться;
- delay_time — время задержки перед воспроизведением;
- startTime — момент начала воспроизведения звука;
- created_time — момент создания звука (startTime-delay_time).
Всё время расчитывается относительно Time.realtimeSinceStartup.
Здесь можно скачать сам скрипт и приведённый выше пример.
Если в примере не работает сочетание клавиш, можно найти пункт TestSound в меню.
Если Unity не находит звук в ресурсах, надо его просто выделить (посмотреть настройки аудиофайла).