how to make a function loop continuously

Hi,
I have a set of musical sounds I would like to have play randomly and at random intervals as background music. Here’s the code I have so far (attached to the camera):

public void startIEnumPlayMusic(){
        StartCoroutine(PlayMusic());
    }                          

    private IEnumerator PlayMusic(){
        yield return new WaitForSeconds(Random.Range(1, 10));
        audio.PlayOneShot(flowerNotes[Random.Range(0, 9)]);
        Debug.Log ("play music");
    }

This is called from a GUI script attached to a game object using “musicOn” and “musicOff” buttons

if (GUI.Button(new Rect(4, 344, 16, 16), new GUIContent(music_on), B_chooseButtonGUIStyle)){
    musicOn = true;
    CallPlayMusic();
}

I would like to have the PlayMusic function repeat in a continuous loop while the “musicOn” button is pressed and stop when the “musicOff” button is pressed. I tried setting up a flag in the GUI script: musicOn = false and then I set up a while loop: while (musicOn == true) {CallPlayMusic()}, but unity froze. I would greatly appreciate suggestions. Thanks.
Zaffer

private IEnumerator PlayMusic () {
    yield return new WaitForSeconds (Random.Range (1, 10));

    while (musicOn) {
        audio.PlayOneShot (flowerNotes[Random.Range (0, 9)]);
        Debug.Log ("play music");
        yield return new WaitForSeconds (Random.Range (1, 10));
    }
}
private IEnumerator PlayMusic () {
    while (musicOn) {
        yield return new WaitForSeconds (Random.Range (1, 10));
        audio.PlayOneShot (flowerNotes[Random.Range (0, 9)]);
        Debug.Log ("play music");
    }
}

? :stuck_out_tongue:

The sole difference between the code I posted and Sbizz’s is that Sbizz’s has the possibility of the sound playing 1-9 seconds after musicOn is set to false.

Thank you so much, DanielQuick and Sbizz. Your code works! I just have one little (I hope) problem. I can start the music, but not stop it. I can get the PlayMusic (bool) set to false, but the while loop doesn’t seem to get the false message. It stays true. Can you help? Again, thanks so much. Here’s my code:

public void startIEnumPlayMusic(bool musicSwitch){
        if (musicSwitch == true){
            StartCoroutine(PlayMusic(musicSwitch));
        }
        if (musicSwitch == false){
            StartCoroutine(PlayMusic(musicSwitch));

        }
    }                         

    private IEnumerator PlayMusic(bool musicSwitch){
        Debug.Log(musicSwitch);
        yield return new WaitForSeconds(Random.Range(1, 6));
  
        while (musicSwitch == true){
            audio.PlayOneShot(flowerNotes[Random.Range(0, 9)]);
            yield return new WaitForSeconds(Random.Range(1, 10));
        }
    }

You are passing a copy of the bool to your PlayMusic coroutine, not the actual value. Because your copy starts out as true (I assume), it will always be true.

Thanks DanielQuick, Could you please explain “copy of the bool” or point me to a link with an explanation? Thanks.

Thanks DanielQuick, Sorry I still don’t get it. What do you mean by “passing a copy?” How would I pass in the bool “false,” copy or not. Thanks.

When passing variables to functions it makes a copy of them. Unless you specify it as a ref (reference).

bool playMusic = false;

void MyFunction(ref bool playMyMusic){

}

void Start(){
MyFunction(ref playMusic);
}

First question: What is the result of your debug log in the play music function?
Next: Are you trying to kill the music immediately? ( the way your code is now, it will finish playing what ever it is playing but it shouldn’t grab a new song when false)

for your current code I’d probably do something like:

bool musicSwitch = false;

void ToggleMusic(){
     musicSwitch=!musicSwitch;
     CheckMusic();
}

void CheckMusic(){

     if(musicSwitch){
          int i = Random.Range(0, flowerNotes.Length); // EDIT: took out the -1 Daniel is correct with Random.Range(int,int)
          audio.Play(flowerNotes[i]);
          Invoke("CheckMusic", Random.Range(1, 10)); // you do this
          // I'd use something like
          Invoke("CheckMusic", (flowerNotes[i].length + 0.5f)); //just added a constant .5 sec between... can do anything here
     }
     else {
          CancelInvoke(CheckMusic()); // make sure the cycle stops
          audio.Stop();  // stop the current audio
     }
}

You wouldn’t want to subtract 1, Random.Range (int, int) is [inclusive, exclusive)

1 Like

Hi Novashot,
To answer your first question, the Debog.Log shows “true” or “false” correctly according to the button pushed, but the while loop just doesn’t get the message and keeps playing as though “true” is still pressed. Thanks for the new code, but before I start over, I’m going to see if I can get some help with the “ref” word.

Hi Aaro4130,
Here’s the path followed by my code:

  1. button press in ColorCubeGUI calls CallPlayMusic() function-- also in ColorCubeGUI.
if (GUI.Button(new Rect(4, 344, 16, 16), new GUIContent(music_on), B_chooseButtonGUIStyle)){
                musicOn = true;
                CallPlayMusic(ref musicOn);
            }
            if (GUI.Button(new Rect(28, 344, 16, 16), new GUIContent(music_on), B_chooseButtonGUIStyle)){
                musicOn = false;
                CallPlayMusic(ref musicOn);
            }
  1. CallPlayMusic() function calls StartIEnumPlayMusic() in PlaySounds script.
void CallPlayMusic(ref bool musicOn){
        GameObject cameraMain = GameObject.Find("Camera");
        PlaySounds otherScript = (PlaySounds) cameraMain.GetComponent(typeof(PlaySounds));
        if (musicOn == true){
            otherScript.StartIEnumPlayMusic(ref musicOn);
        }
        if (musicOn == false){
            otherScript.StartIEnumPlayMusic(ref musicOn);
        }
    }

3)StartIEnumPlayMusic() calls IEnumerator PlayMusic() also in PlaySounds script

public void StartIEnumPlayMusic(ref bool musicOn){
        if (musicOn == true){
            StartCoroutine(PlayMusic(true));
        }
        if (musicOn == false){
            StartCoroutine(PlayMusic(false));
        }
    }
  1. Finally, IEnumerator PlayMusic() has the while loop and actually plays the sounds.
private IEnumerator PlayMusic(bool musicOn){
        yield return new WaitForSeconds(Random.Range(1, 6));
        Debug.Log(musicOn);
        while (musicOn == true){
      
            audio.PlayOneShot(flowerNotes[Random.Range(0, 9)]);
            yield return new WaitForSeconds(Random.Range(1, 10));
        }
    }

I managed to use the “ref” word all through this chain, but the IEnumerator PlayMusic() function won’t take the “ref” word. Any suggestions will be greatly appreated, up to and including just killing the IEnumerator function when I want the music to stop. Thanks.
Zaffer

Hi NovaShot,

I finally wrapped my head around your code and it worked! Thanks so much. Here’s what I did:

void CheckMusic(){      
        if(musicSwitch){
            //int i = Random.Range(0, flowerNotes.Length); // EDIT: took out the -1 Daniel is correct with Random.Range(int,int)
            audio.PlayOneShot(flowerNotes[Random.Range(0, 11)]);
            Invoke("CheckMusic", Random.Range(0.2f, 2.0f)); // you do this
            // I'd use something like
            //Invoke("CheckMusic", (flowerNotes[i].length + 0.5f)); //just added a constant .5 sec between... can do anything here
        }
        else {
            CancelInvoke("CheckMusic"); // make sure the cycle stops
            audio.Stop();  // stop the current audio
        }
    }

I was afraid of commands I hadn’t heard before. I’m new to C# and hanging on my my fingernails. Thanks again.
Zaffer

No problem.