- Home /
 
Audio Play Once
So i have a quest system and i need the audio to only play once, how would i do that ?
using UnityEngine; using System.Collections;
 public class QuestSystem : MonoBehaviour {
  
  // Quest things
  public bool isitCompleate;
  public bool Objectives_triger;
  public bool inturuption;
  
  
  
  // what quest?
  public bool Quest1;
  //Sounds
  public AudioSource audio_source;
     public AudioClip sound;
  public bool Mute;
  
  //Quest Audio
  public AudioClip audioname;
  public AudioClip QuestOneStart;
  public AudioClip QuestOneinturuption;
  public AudioClip QuestOneEnd;
 
  // Use this for initialization
  void Start () {
  Mute = false;
  
  GetNewAudio();
  sound = (AudioClip)Resources.Load("audioname");
  Quest1 = true;
  
  }
  
  // Update is called once per frame
  void Update () {
  audio_source.clip = sound;
  if (Quest1 == true)
  QuestOne();
  
  }
  
  void QuestOne(){
  
  // Confiremed start Audio / Objective
  if (isitCompleate == false){
  sound = QuestOneStart;
  if (Mute == false){
  if (!audio.isPlaying)
  audio_source.Play();
 
  
  }
  }
  
  
  
  if (inturuption == true){
  sound = QuestOneinturuption;
  if (Mute == false){
  if (!audio.isPlaying)
  audio_source.Play();
  }
  
  
  }
  
  if (isitCompleate == true){
  sound = QuestOneEnd;
  if (Mute == false){
  if (!audio.isPlaying)
  audio_source.Play();
  }
  
  }
  
  }
  void GetNewAudio(){
  
  audio_source = (AudioSource)gameObject.AddComponent("AudioSource");
    //     sound = (AudioClip)Resources.Load("audioname");
          
  }
 }
 
               
Answer by aldonaletto · Jul 14, 2012 at 04:13 PM
This seems to be a problem of logic: you're playing the sounds at Update, which occurs every frame - the first sound, for instance, will be repeated continuously while isitComplete is false. I suppose you're trying to make a state machine: play the first sound, then wait the question be answered or interrupted, then play the appropriate sound and pass to the next question, and so on - am I correct? If so, you could use coroutines: use a switch to select the question, then enter the question coroutine: a coroutine can play the start sound and wait for interruption or isitComplete to become true, then play the appropriate question and increment questNum, a variable that shows which question is selected. You should also add an AudioSource component to your object only once in the Editor, and use it for all sounds - adding an AudioSource for each sound is unnecessary, and wastes memory and time.
EDITED: Yielding to another coroutine in C# apparently doesn't chain it like in JS, thus I changed the code to just start the question coroutine and wait till it finishes.
using UnityEngine; using System.Collections;
public class QuestSystem : MonoBehaviour {
// Quest things public int questNum; // which question public bool isitComplete; public bool Objectives_triger; public bool interruption;
//Sounds public bool Mute;
//Quest Audio - clips for start, interrupt and end public AudioClip sndStart; public AudioClip sndInt; public AudioClip sndEnd;
void GetSounds(string sStart; string sInt; string sEnd){ // load the specified audio clips: sndStart = (AudioClip) Resources.Load(sStart); sndInt = (AudioClip) Resources.Load(sInt); sndEnd = (AudioClip) Resources.Load(s End); }
IEnumerator Start () { Mute = false; questNum = 1; // start with question 1 while (true){ switch (questNum){ case 1: StartCoroutine(Question1()); // start Question1 coroutine while (waiting) yield return 0; // wait until it finishes break, case 2: StartCoroutine(Question2()); // start Question2 coroutine while (waiting) yield return 0; // wait until it finishes break;
     // add here other questions as
     // new cases like above
     default: // invalid question number finishes script
       return;
     } 
   }
 }
 
               }
bool waiting = false; // this flag shows that a question coroutine is running
// repeat this code for each question: IEnumerator Question1(){ waiting = true; // question coroutine started // get the sounds for this question: GetSounds("QuestOneStart", "QuestOneInterrupt", "QuestOneEnd"); // play the start sound: audio.PlayOneShot(sndStart); // do nothing while interruption and isitComplete are false: while (!interruption and !isitComplete){ yield return null; // let Unity free till next frame } if (interruption){ // if question interrupted play sndInt audio.PlayOneShot(sndInt); // if you want to abort the whole thing... questNum = -1; // set questNum to a non-existent question number } if (isitComplete){ // if question completed... // play sndEnd... audio.PlayOneShot(sndEnd); // and pass to the next question, if any: questNum++; } waiting = false; // question finished }
}
Thank you :) but how do i make the audio play i fixed the little errors in your script like the inability to do yield return new. and now the script is error free but no sound plays
I've just realized that *yield*ing to another coroutine in C# apparently doesn't work as expected: in JS, yielding to another coroutine works like a call to a regular routine - the caller code is resumed only when the routine finishes. I tried this in C#, but the coroutine yielded simply does nothing!
 I edited my answer to include a brute force approach while this coroutine chaining mystery is unsolved.
$$anonymous$$ystery solved: in C#, you can't just yield to another coroutine this way:
 yield return OtherCoroutine();
 
                  this does simply nothing! You must ins$$anonymous$$d use StartCoroutine explicitly:
 yield return StartCoroutine(OtherCoroutine());
 
                  This isn't correctly informed in the docs (http://docs.unity3d.com/Documentation/ScriptReference/Coroutine.html)
the lines
default: // invalid question number finishes script return;
dont work :(
That's one more C# trap - it should be:
   default:
     return false;
 } 
 
                  Actually, this only finishes the Start coroutine (which was running in the background), and nothing else happens. You can use the default case to do other more useful things: I would set a boolean variable, and check this variable in Update, doing the appropriate stuff when it becomes set - like this:
bool endQuestions = false;
IEnumerator Start(){ ... default: endQuestions = true; return false; } }
void Update(){ if (endQuestions){ Application.LoadLevel(2); } }
Your answer
 
             Follow this Question
Related Questions
How To Turn Off Sound For Certain Objects? 1 Answer
Audio Scripting 1 Answer
Audio listener to mono then to left or right speaker? 2 Answers