К этой статье будет приложен небольшой, но полезный 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 не находит звук в ресурсах, надо его просто выделить (посмотреть настройки аудиофайла).