Kingspud
Kingspud

Reputation: 65

Playing audio and playing it some more

I'm making a small game in unity 5 with C#.

So far ive managed to design the level and done some basic scripting.

Currently i have a trigger that spawns an object and want it to play and audio source once the user enters. However, because i want it to be a jump scare, the trigger is very small and currently the sound only plays IF i stay in the trigger.

The code I have is below:

using UnityEngine;
using System.Collections;

public class Spawner : MonoBehaviour {

    public GameObject monster;
    public AudioSource aud; 
    public AudioClip piano; 

    void Start () 
    {
        monster.SetActive (false);
        aud = monster.GetComponent<AudioSource>();
    }

    void OnTriggerEnter(Collider other){
        if (other.CompareTag ("Player")) {
            monster.SetActive (true);
            AudioSource.PlayClipAtPoint(piano, monster.transform.position);
        }

    }

    void OnTriggerExit(Collider other){
        if (other.CompareTag ("Player")) {
            monster.SetActive (false);
        }
        Destroy (this.gameObject);
    }

}

I was wondering if theres a way to keep the sound playing even after the persons left the trigger.

Thanks!

Upvotes: 3

Views: 591

Answers (2)

Programmer
Programmer

Reputation: 125455

The GameObject audio is attached to is being destroyed before the audio finish playing. Using AudioSource.PlayClipAtPoint will create and destroy Audiosource which makes caching of aud to be useless. Also, If you have too many trigger GameObjects, that will be slow because of GC. Use Coroutine and wait for the audio to finish playing then destroy gameObject.

public GameObject monster;
public AudioSource aud;
public AudioClip piano;

void Start()
{
    monster.SetActive(false);
    aud = GetComponent<AudioSource>();
}

void OnTriggerEnter(Collider other)
{
    if (aud.isPlaying)
    {
        return; //Exit if Audio is already playing
    }

    if (other.CompareTag("Player"))
    {
        monster.SetActive(true);
        aud.PlayOneShot(piano);
    }

}

void OnTriggerExit(Collider other)
{
    if (other.CompareTag("Player"))
    {
        monster.SetActive(false);
    }
    StartCoroutine(waitForAudioThenDestroy());
}

IEnumerator waitForAudioThenDestroy()
{
    //Wait until we are done playing
    while (aud.isPlaying)
    {
        yield return null;//Don't freeze
    }

    //We are no longer playing audio so we can destroy this gameObject now
    Destroy(this.gameObject);
}

Upvotes: 5

Danny Herbert
Danny Herbert

Reputation: 2031

Your problem is that you are deleting the gameObject that the AudioSource is attached to in the OnTriggerExit function before the sound can finish playing. An easy way to fix this would be to attach the AudioSource to your monster instead and access it from there. This will work because you are not Destroying the monster. I think all you would need to change would be this (after you have moved the AudioSource to the monster in the inspector):

void Start () 
{
    monster.SetActive (false);
    aud = monster.GetComponent<AudioSource>();
}

Another, even quicker, way to sort this would be to call PlayClipAtPoint in your OnTriggerEnter method:

IEnumerator OnTriggerEnter(Collider other){
    if (other.CompareTag ("Player")) {
        monster.SetActive (true);
        AudioSource.PlayClipAtPoint(piano, monster.transform.position);
    }

}

As this is a static method, you can then remove the aud audio source entirely. The AudioSource.PlayClipAtPoint works by instantiating a new AudioSource component in the world at a point you specify (monster.transform.position), playing the clip specified (piano), and then deleting the AudioSource once the clip is finished playing.

Edit: also, I don't think your OnTriggerEnter method needs the return type of IEnumerator as you aren't using the yield keyword anywhere in the method. you could simply change it to have a void return type. This won't effect the functionality of your code in anyway though.

Edit2: I foolishly overlooked the fact that you are setting the monster to be inactive. I think your best option in this case is to use PlayClipAtPoint.

Upvotes: 2

Related Questions