Search code examples
c#unity-game-enginevuforia

Different sounds, different imageTargets and one button. Vuforia (Unity3d)


I am going to recognize few imageTargets and I would like to play sound with onClick(button). This button is on canvas and it is always on top during the app lifetime. So You can see it always. Behind this button You can see camera view and recognize markers.

F.e I have two markers: Dog and Cow. Dog has assigned audio - bark. Cow has assigned audio - muu.

When I recognize Cow -> click the button and it should give me muu sound, but when I recognize Dog, the same button, when clicked should give me bark sound. Here is a problem. I cannot resolve it. I think I should write a script for this button to play sound onClick for appropriate marker, but I do not know how to tell button that now I can see Cow and another time I can see Dog.

I made a script which plays sound when image is recognized, but I would like to do it with button.

If something is not clear enough - let me know and I will write it better or again.

So far in my "Default trackable event handler" it’s worth it:

using UnityEngine;
using Vuforia;

public class DefaultTrackableEventHandlerEng : MonoBehaviour, ITrackableEventHandler
{
    //------------Begin Sound----------
    public AudioSource soundTarget;
    public AudioClip clipTarget;
    private AudioSource[] allAudioSources;

    //function to stop all sounds
    void StopAllAudio()
    {
        allAudioSources = FindObjectsOfType(typeof(AudioSource)) as AudioSource[];
        foreach (AudioSource audioS in allAudioSources)
        {
            audioS.Stop();
        }
    }

    //function to play sound
    void playSound(string ss)
    {
        clipTarget = (AudioClip)Resources.Load(ss);
        soundTarget.clip = clipTarget;
        soundTarget.loop = false;
        soundTarget.playOnAwake = false;
        soundTarget.Play();
    }

    //-----------End Sound------------


    #region PROTECTED_MEMBER_VARIABLES

    protected TrackableBehaviour mTrackableBehaviour;
    protected TrackableBehaviour.Status m_PreviousStatus;
    protected TrackableBehaviour.Status m_NewStatus;

    #endregion // PROTECTED_MEMBER_VARIABLES

    #region UNITY_MONOBEHAVIOUR_METHODS

    protected virtual void Start()
    {
        mTrackableBehaviour = GetComponent<TrackableBehaviour>();
        if (mTrackableBehaviour)
            mTrackableBehaviour.RegisterTrackableEventHandler(this);
        //Register / add the AudioSource as object
        soundTarget = (AudioSource)gameObject.AddComponent<AudioSource>();

    }

    protected virtual void OnDestroy()
    {
        if (mTrackableBehaviour)
            mTrackableBehaviour.UnregisterTrackableEventHandler(this);
    }

    #endregion // UNITY_MONOBEHAVIOUR_METHODS

    #region PUBLIC_METHODS

    /// <summary>
    ///     Implementation of the ITrackableEventHandler function called when the
    ///     tracking state changes.
    /// </summary>
    /// 
    public void OnTrackableStateChanged(
        TrackableBehaviour.Status previousStatus,
        TrackableBehaviour.Status newStatus)
    {
        m_PreviousStatus = previousStatus;
        m_NewStatus = newStatus;


        if (newStatus == TrackableBehaviour.Status.DETECTED ||
            newStatus == TrackableBehaviour.Status.TRACKED ||
            newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
        {
            Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found");

            if (mTrackableBehaviour.TrackableName == "1")
            {
                playSound("audio/1_eng");
            }

            if (mTrackableBehaviour.TrackableName == "2")
            {
                playSound("audio/2_eng");
            }

            if (mTrackableBehaviour.TrackableName == "3")
            {
                playSound("audio/3_eng");
            }

            OnTrackingFound();
        }
        else if (previousStatus == TrackableBehaviour.Status.TRACKED &&
                 newStatus == TrackableBehaviour.Status.NO_POSE)
        {
            Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " lost");

            StopAllAudio();

            OnTrackingLost();
        }
        else
        {
            // For combo of previousStatus=UNKNOWN + newStatus=UNKNOWN|NOT_FOUND
            // Vuforia is starting, but tracking has not been lost or found yet
            // Call OnTrackingLost() to hide the augmentations
            OnTrackingLost();
        }
    }

    #endregion // PUBLIC_METHODS

    #region PROTECTED_METHODS

    protected virtual void OnTrackingFound()
    {
        var rendererComponents = GetComponentsInChildren<Renderer>(true);
        var colliderComponents = GetComponentsInChildren<Collider>(true);
        var canvasComponents = GetComponentsInChildren<Canvas>(true);

        // Enable rendering:
        foreach (var component in rendererComponents)
            component.enabled = true;

        // Enable colliders:
        foreach (var component in colliderComponents)
            component.enabled = true;

        // Enable canvas':
        foreach (var component in canvasComponents)
            component.enabled = true;
    }


    protected virtual void OnTrackingLost()
    {
        var rendererComponents = GetComponentsInChildren<Renderer>(true);
        var colliderComponents = GetComponentsInChildren<Collider>(true);
        var canvasComponents = GetComponentsInChildren<Canvas>(true);

        // Disable rendering:
        foreach (var component in rendererComponents)
            component.enabled = false;

        // Disable colliders:
        foreach (var component in colliderComponents)
            component.enabled = false;

        // Disable canvas':
        foreach (var component in canvasComponents)
            component.enabled = false;
    }

    #endregion // PROTECTED_METHODS
}

Solution

  • So if I understand you correctly your code basically works fine but instead of directly playing the according sound for the recognized target you instead want to only play it if a button is clicked, right?

    You could simply add a method PlayCurrentSound and reference it in the onClick:

    // SET THIS NAME INSTEAD OF DIRECTLY PLAYING IT
    private string currentSoundName;
    
    // THIS IS THE METHOD CALLED BY THE BUTTON
    public void PlayCurrentSound()
    {
        if(!string.IsNullOrWhiteSpace(currentSoundName)) playSound(currentSoundName);
    }
    

    and instead in OnTrackableStateChanged only change the value of currentSoundName instead of directly replaying it

    public void OnTrackableStateChanged(TrackableBehaviour.Status previousStatus, TrackableBehaviour.Status newStatus)
    {
        m_PreviousStatus = previousStatus;
        m_NewStatus = newStatus;
    
        if (newStatus == TrackableBehaviour.Status.DETECTED ||
            newStatus == TrackableBehaviour.Status.TRACKED ||
            newStatus == TrackableBehaviour.Status.EXTENDED_TRACKED)
        {
            Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " found");
    
            // HERE BETTER USE A SWITCH INSTEAD
            switch(mTrackableBehaviour.TrackableName)
            {
                case "1":
                    currentSoundName = "audio/1_eng";
                    break;
    
                case "2":
                    currentSoundName = "audio/2_eng";
                    break;
    
                case "3":
                    currentSoundName = "audio/3_eng";
                    break;
    
                default:
                    currentSoundName = "";
                    break;
            }
            // OR ALTERNATIVELY IF YOU ANYWAY WANT TO
            // SET THE NAME FOR ALL POSSIBLE NAMES YOU COULD EVEN GO
            currentSoundName = string.Format("audio/{0}_eng", mTrackableBehaviour.TrackableName);
    
            OnTrackingFound();
        }
        else if (previousStatus == TrackableBehaviour.Status.TRACKED &&
                 newStatus == TrackableBehaviour.Status.NO_POSE)
        {
            Debug.Log("Trackable " + mTrackableBehaviour.TrackableName + " lost");
    
            StopAllAudio();
    
            // RESET currentSoundName 
            currentSoundName = "";
    
            OnTrackingLost();
        }
        else
        {
            // For combo of previousStatus=UNKNOWN + newStatus=UNKNOWN|NOT_FOUND
            // Vuforia is starting, but tracking has not been lost or found yet
            // Call OnTrackingLost() to hide the augmentations
            OnTrackingLost();
    
            // RESET currentSoundName 
            currentSoundName = "";
        }
    }
    

    However there are some other tiny things I would change as well:

    soundTarget.loop = false;
    soundTarget.playOnAwake = false;
    

    this can already be done on game start, only once and should not get repeated everytime. So do it in:

    private void Awake()
    {
        soundTarget.loop = false;
        soundTarget.playOnAwake = false;
    }
    

    than

    clipTarget = (AudioClip)Resources.Load(ss);
    

    loads (maybe) the same sound over and over again from the Resources ... not very efficient. You rather might want to keep the reference once loaded like

    private Dictionary<string, AudioClip> clips = new Dictionary<string, AudioClip>();
    
    void playSound(string ss)
    {
        if(clips.ContainsKey(ss) && clip[ss] != null)
        {
            clipTarget = clips[ss];
        else
        {
            clip = (AudioClip)Resources.Load(ss);
            if(clipTarget == null) 
            {
                Debug.LogError("Couldn't get clip for " + ss, this);
                return;
            }
    
            clips.Add(ss, clipTarget);
        }
    
        soundTarget.clip = clipTarget;
        soundTarget.Play();
    }
    

    You also might want to use soundTarget.PlayOneShot(clipTarget) instead. The difference is that PlayOneShot plays the whole sound and allows concurrent sounds while Play interrupts the current sound and starts a new one (depends on your needs).