Easier Way To Add Sound Via GUI?

Basically, I want to add a 'click' sound every time someone clicks on a GUI button. Now I have a lot of buttons, so I don't want to be doing stuff like:

if(GUI.Button(Rect ......

playAudioClip ...

... for every button

Is there a way to define every 'OnButtonDown' to play the click sound? I was thinking about the Input.GetButtonDown(0) // or Fire1 but that just means that no matter where I click - it will make the sound.

Any ideas?

Super simple function decoration

Here is a super simple way to play sound when pressing a button:

var buttonClip : AudioClip;

function OnGUI ( )
{
    if ( Play( GUILayout.Button( "Press me!" ) ) )
        Debug.Log( "Pressed button" );
}

function Play (value : boolean) : boolean
{
    if ( value )   
        audio.PlayOneShot( buttonClip );

    return value;
}

It works by padding the call to GUILayout.Button with a call to Play. If the button return true, the sound is played, and the value is then transparently sent to the if test.


C# Extension Method Version Fun

In C# you can make use of extension methods that can give a slightly different syntax than the JS one.

public class Sound : MonoBehaviour
{
    public AudioClip buttonClip;

    void OnGUI ( )
    {
        if ( GUILayout.Button( "Press me!" ).Play( buttonClip ) )
            Debug.Log( "Pressed button" );
    }
}

It works by adding an extension method to the bool type. This way you can call Play on any boolean value. Beware that this can easily pollute the namespace, so I tend to put these kind of extensions in a separate namespace which I manually import when I want to use them.

To use them, I just include the namespace:

using GUISounds;

And the code could look something like this.

namespace GUISounds
{
    public static class SoundExtension
    {
        static AudioSource audio;

        public static bool Play ( this bool value, AudioClip clip )
        {
            if ( !clip || !value ) return value;

            EnsureAudioExists( );
            audio.PlayOneShot( clip );
            return value;
        }

        public static void DestroyAudio ( )
        {
            if ( audio )
            {
                GameObject.Destroy( audio.gameObject );
            }
        }

        public static void CreateAudio ( )
        {
            GameObject audioObject = new GameObject( "GUISounds Audio Extension" );
            audio = audioObject.AddComponent<AudioSource>( );
        }

        private static void EnsureAudioExists ( )
        {
            if ( !audio )
            {
                CreateAudio( );
            }
        }
    }
}

You can see the C# backend became quite verbose since it has to handle temporary objects to play the sound. The advantage (and disadvantage?) is that you can use Play( clip ) on any boolean value! However, be careful with using extension methods too much on common types such as booleans - it will just pollute your namespace. Again, put them in a namespace so you can choose which scripts can make use of the extensions.


You can easily adopt the JS version to C#, but not the other way around. In the end both techniques work similarly; You check the value of a boolean and pass it on, but decorating the call with a sound play. This is more reusable than the answer by skovacs1 where code duplication can lead to a maintainability issue. You can use any version of GUI.Button, GUILayout.Button or any other call that return a boolean. You can easily see how you can extend other unity functions following the same principle.

You could wrap GUI.Button within functions which add and play your sound:

static function SoundButton (sound : AudioClip, position : Rect, text : String) : boolean {
    if(GUI.Button(position, text)) {
        audio.PlayOneShot(sound);
        return true;
    }
    return false;
}

static function SoundButton (sound : AudioClip, position : Rect, image : Texture) : boolean {
    if(GUI.Button(position, image)) {
        audio.PlayOneShot(sound);
        return true;
    }
    return false;
}

static function SoundButton (sound : AudioClip, position : Rect, content : GUIContent) : boolean {
    if(GUI.Button(position, content)) {
        audio.PlayOneShot(sound);
        return true;
    }
    return false;
}

static function SoundButton (sound : AudioClip, position : Rect, text : String, style : GUIStyle) : boolean  {
    if(GUI.Button(position, text, style)) {
        audio.PlayOneShot(sound);
        return true;
    }
    return false;
}

static function SoundButton (sound : AudioClip, position : Rect, image : Texture, style : GUIStyle) : boolean  {
    if(GUI.Button(position, image, style)) {
        audio.PlayOneShot(sound);
        return true;
    }
    return false;
}

static function SoundButton (sound : AudioClip, position : Rect, content : GUIContent, style : GUIStyle) : boolean  {
    if(GUI.Button(position, content, style)) {
        audio.PlayOneShot(sound);
        return true;
    }
    return false;
}

You could then call if(SoundButton(....))

You could even go so far as to extend the GUI class to include them.