Hello! I’ve realized that I’ve been through this thread before and figured I’d try and provide some solutions for other folks that may drift through seeking answers.
Below I’ll provide 2 options.
The first, provides an additional extension function to AudioSource.PlayOneShot that generates a temp Gameobject based on the original AudioSource and plays the clip with randomized pitch there to then destroy the gameobject once the audio is finished playing.
The second, for those who prefer to utilize a pool, will add a PlayOneShot function to a AudioSource property that provides, pretty much, similar options.
Let’s jump into the first option.
To start, we need a component that waits for an AudioSource to finish playing then destroys the relevant gameObject after.
using System.Collections;
using UnityEngine;
public class DestroyOnAudioFinish : MonoBehaviour
{
private bool _audioPlaying;
private AudioSource _audioSource;
private void Start()
{
_audioSource = GetComponent<AudioSource>();
StartCoroutine(WaitForAudioComplete());
}
private IEnumerator WaitForAudioComplete()
{
while (_audioPlaying == false)
{
if (_audioSource.isPlaying)
{
_audioPlaying = true;
}
yield return null;
}
while (_audioSource.isPlaying && Time.timeScale > 0f)
{
yield return null;
}
Destroy(gameObject);
yield return null;
}
}
Now that that’s available, let’s create an extension function for the AudioSource class that’ll give us an additional option with PlayOneShot. If you’re unfamiliar with Extension Methods, learn more about it here: [1]
using UnityEngine;
public static class AudioSourceHelper
{
public static void PlayOneShot(this AudioSource audioSource, AudioClip clip, Vector2 pitchRange, float volumeScale = 1.0f)
{
// Instantiate a duplicate of the original audiosource gameobject
GameObject newGo = GameObject.Instantiate(audioSource.gameObject);
// Ensure positions are matched
newGo.transform.position = audioSource.transform.position;
AudioSource oneShot = newGo.GetComponent<AudioSource>();
if (oneShot != null)
{
newGo.AddComponent<DestroyOnAudioFinish>();
oneShot.clip = clip;
oneShot.volume *= volumeScale;
oneShot.pitch = Random.Range(pitchRange.x, pitchRange.y);
oneShot.Play();
}
else
{
GameObject.Destroy(newGo);
}
}
}
The above is a static class that allows you to continue calling what you’re already familiar with, AudioSource.PlayOneShot, and gives an additional parameter for specifying a Vector2 pitch range.
When invoked, this function creates a duplicate Gameobject based on the gameobject that had the AudioSource the function was invoked from, adds the previously created class above, assigns our clip, specifies a random pitch, then hits play. Our added script component we created above should handle cleaning the temp AudioSource up once the audio has finished playing.
Now for our second option that utilizes a pool of AudioSources. This method requires no additional classes to be created and adds a new PlayOneShot function to AudioSource properties.
using UnityEngine;
public static class AudioSourceHelper
{
public static void PlayOneShot(this AudioSource[] audioSource, AudioClip clip, Vector3 position, Vector2 pitchRange, float volumeScale = 1.0f)
{
for (int i = 0; i < audioSource.Length; i++)
{
if(audioSource*.isPlaying == false)*
{
audioSource*.transform.position = position;*
audioSource*.clip = clip;*
audioSource*.volume = volumeScale;*
audioSource*.pitch = Random.Range(pitchRange.x, pitchRange.y);*
audioSource*.Play();*
break;
}
}
}
}
To call this function, simply take your AudioSource[] property and invoke .PlayOneShot. With the added Vector2 pitch range parameter, we now have a parameter for position. This is to specify where we would want our AudioSource from the array to position itself in worldspace prior to playing an AudioClip. So, when you invoke this function, it’ll search through your array and find the first AudioSource that is not currently playing, set it up based on your parameters, then hits Play with a randomized Pitch.
----------
That’s it! Hope these options provide some assistance to other dev’s that stumble across this thread or at least inspires other solutions.
Cheers,
azevedco
_*[1]: Learn game development w/ Unity | Courses & tutorials in game design, VR, AR, & Real-time 3D | Unity Learn