AmbienceSource
AmbienceSource
is a MonoBehaviour
component to play an Ambience
.
How it works
When it plays an Ambience
connected to the slot, each AmbienceLayer
listed inside that asset gets a corresponding GameObject
which runs the layer. The parent is the object with AmbienceSource
.
You can clearly see them in Play Mode. (If "Layers Debugging" is still checked.)
On each child, you can inspect to see how each layer works :
- Looping Clip (LC) : An
AudioSource
with Loop checked, playing a singleAudioClip
. It potentially hasAnimation
attached if you use automations. - One Shot Program (OSP) : An
AudioSource
with noAudioClip
assigned, stationed to be a target ofPlayOneShot
. - Timeline (TL) : A
PlayableDirector
withAmbienceLayer
assigned, looping just that layer.
Where to attach AmbienceSource
As explained, the parent of corresponding child GameObject
of each layer is the object with AmbienceSource
.
Ambiences are usually positional on your scene, so it is a good idea to attach this to an object that would make the sound. Though, what determines positional or global nature of an ambience is the connected AudioSource
, not AmbienceSource
Or you can create an empty object floating around the area that make sounds. e.g. an ambience representing groups of tree, you don't have any specific tree to attach.
Sometimes ambience can be global, such as constantly running wind ambience. In that case you can create an empty object at 0,0,0 and set the source to 2D.
Ambience
Attach an Ambience
you want to play here. The component has Play
method that use an asset connected to this slot, parallels to AudioSource
and AudioClip
.
Additionally, the component also has a Play
overload that accepts any Ambience
via scripting, ignoring this slot.
Play On Awake always use Ambience
connected here. It is also the most popular use case, because usually you don't manually want to tell the trees and rivers to start playing audio. Ambience is a part of scene design and you want to do as many things as possible without scripting.
Audio Source
The AudioSource
field will become an output for all AmbienceLayer
.
Where to attach AudioSource
The AudioSource
component is usually attached right next to this AmbienceSource
component, though this component does not force you to do so. Just don't forget to connect the AudioSource
or it won't work.
How it works
Exact implementation differs between layer modes :
Looping Clip : The
AudioSource
acts as a template. Each layer gets a personal copy ofAudioSource
with inherited values from the template on play.The personal copy of
AudioSource
can have Volume, Pitch, and Stereo Pan animated live via additionalAnimation
component, due to automations.One Shot Program : Again, the
AudioSource
acts as a template. Each layer gets a personal copy ofAudioSource
with inherited values from the template on play.The personal copy is the target of
PlayOneShot
. The reason why we cannotPlayOneShot
directly on the template is that pitch adjustment is shared for all shots played on that source. Each ourAmbienceLayer
can have different Speed (Pitch) Adjustment.Timeline : The
AudioSource
is a binding target for everyAudioTrack
thatPlayableDirector
is directing. Timeline layers do not get a personal copy ofAudioSource
but directly output audio stream to it.
Useful settings
You need not to worry about ticking Loop, it will be ticked for you when the recipient layer is on Looping Clip. AudioClip can always be left empty. Play On Awake does not matter, as we have our own Play On Awake on the AmbienceSource
.
While you can set any other settings you want, there are some settings that ambience author likely wants to do :
- Output : It is very useful to have an
AudioMixerGroup
exclusively for ambiences in your project. After directing all ambiences in the game to one mixer, you can then fade everything out smoothly, for example. - Priority : Unity has a maximum limit of concurrent audio for mixing, which at that point it will begin cutting out sounds. Priority of ambiences likely needs to be 0 (the same as BGM). Otherwise, it is possible for the player to perform attacks so rapidly that it permanently removes forest ambience...
- Volume, Pitch, Stereo Pan : These are inherited on top of what the layer wants to do. Normally you will adjust these on
Ambience
adjustments, or adjust on theAmbienceLayer
directly and leave these at1
,1
, and0
respectively. - Spatial Blend : This starts at 2D. But ambiences are often positional (3D) so set it accordingly, unless it is an ambience that affects an entire scene.
- Reverb Zone Mix : Unity Audio Effects is quite useful for ambiences! It can blends together your field recordings to be more cohesive. Something that maybe annoying to get reverbs like wind ambience can get different mix by this settings.
- 3D Sound Settings : When ambience is positional, you will want to tune how close the player (
AudioListener
) has to be to hear the audio. Very important settings if Spatial Blend is 3D. This deserves its own page here : Advanced/3D Sound Settings.
Time Source
Choose from :
- Unscaled : Nothing happens to the ambience when you change
Time.timeScale
. This is howAudioSource
works by default. - Scaled : Ambience speed up and slow down together with
Time.timeScale
. (Which is weird, but I learned from both Introloop and Native Audio customers that someone will eventually ask for it...)
Implementation details is described in Advanced/Time Source.
Adjusting "master volume"
You may occasionally want to fade out the ambience instead of just stopping them. (If you fade them out a bit slower than BGM, that feels good too!)
The solution is on the Output field on your AudioSource
that is connected with AmbienceSource
. It let you specify AudioMixerGroup
. All 3 kinds of layers can output to this mixer. After routing everything, you can then fade out on the mixer's fader, completely unrelated to Tiny Ambience now.
Scripting API
Methods you can call on AmbienceSource
are as you typically expected from an audio player :
// Use the `Ambience` connected on the slot.
public void Play() { /** impl **/ }
public void Play(PlayOptions playOptions) { /** impl **/ }
// Ignores `Ambience` connected on the slot.
public void Play(Ambience a) { /** impl **/ }
public void Play(IAmbienceLayerProvider a) { /** impl **/ }
public void Play(Ambience a, PlayOptions playOptions) { /** impl **/ }
public void Play(IAmbienceLayerProvider a, PlayOptions playOptions) { /** impl **/ }
// Playback controls.
public void Stop() { /** impl **/ }
public void Pause() { /** impl **/ }
public void Resume() { /** impl **/ }
PlayOptions
currently can't do anything, but they are there so I could add some special features without breaking your code in the future, like fast-forwarding to the virtual future time on start playing.
Also, IAmbienceLayerProvider
is the same as Ambience
right now. There is currently no other thing that could provide layers in the package.
UnityEvent
targeting Play
This Play
overload in particular is extra cool because if you connect UnityEvent
to it, editor will draw a nice asset picker to pick your Ambience
!
public void Play(Ambience a) { /** impl **/ }
It maybe useful in niche use case where one source may need to play many kinds of ambience, commanded by some other things on the scene.
[SerializeField] private UnityEvent eventTargetingPlay;
Bonus dynamic UnityEvent
variations
UnityEvent
is a cool class which the invoke target is serializable. UnityEvent<T>
is even cooler as the invoker can specify arguments, making it dynamic instead of static. But a caveat is that Unity can't serialize generic classes, you must declare a new subclass that is solid.
The package provide some subclassed types which pairs with those Play
overloads :
public class UnityEventAmbience
: UnityEvent<Ambience> { /** impl **/ }
public class UnityEventAmbienceWithOption
: UnityEvent<Ambience, PlayOptions> { /** impl **/ }
public class UnityEventAmbienceOnlyOption
: UnityEvent<PlayOptions> { /** impl **/ }