Pull to refresh

Воспроизведение и управление звуками в Unity 3D (Sound complete event, Play in edit mode)

Reading time 5 min
Views 61K
К этой статье будет приложен небольшой, но полезный csharp скрипт, и показано как им пользоваться.
Поводом для написания скрипта стало то, что появилась необходимость в настройке и тестировании звуковых эффектов не запуская сцены проекта. А так же в отслеживании основных событий воспроизведения.

Скрипт работает одинаково как в 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 не находит звук в ресурсах, надо его просто выделить (посмотреть настройки аудиофайла).
Tags:
Hubs:
+2
Comments 3
Comments Comments 3

Articles