It is possible to have a random audio pitch with one AudioSource and PlayOneShot?

This sample only work when play one audio, when I call it again, both clips play with new pitch.

function Play(audioClip: AudioClip) {
  audio.pitch = Random.Range(0.5, 1.5);
  audio.PlayOneShot(clip, Random.Range(0.8, 1.2));
}

My solution is to use a pool of audiosource and controlling by self what source is being used, because PlayOneShot don’t update AudioSource.isPlaying).

I think a little wierd, but it is the way?

Anyone who is looking for a solution to this just like I was, ignore the snark and venom that Calum and komodor couldn’t seem to help themselves with, AlpayKasal’s answer worked perfectly for me with playoneshot, and can be very easily applied to JS as well.

These older threads often come up first in google searches, so I’m grateful people respond with solutions, even if they are on old threads. If the question persists and comes up first when looking for a solution, by all means, LET’S RESOLVE IT.

This pool of resources is what makes Unity unique, so Calum and komodor’s responses are as inappropriate as they are wrong and detrimental to the very idea of Unity and the reason we’re all using it.

@sisso this is an old thread but I thought I’d answer anyway, for future googlers:

put an f character after your floats in the random ranges, that’s the first problem I see… here’s some code that s working for me (I needed pitch variations).

public class plinkotrigger : MonoBehaviour {
    private AudioSource audioSource;
    public AudioClip Snare;

    void OnCollisionEnter(Collision col)
    {
        if (col.gameObject.name == "bullets(Clone)")
        {
            audioSource = GetComponent<AudioSource>();
            audioSource.clip = Snare;
            audioSource.pitch = (Random.Range(0.6f, .9f));
            audioSource.Play();
        }
    }
}

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

Because I have no answer I will consider as “No, your best solution is to create a pool of sources.”