Wait in a loop

Hello!
I have a problem with waiting in a loop.
The idea: I split my GameObject on button press. After x seconds they should merge again.
I had a solution, but I didn’t like it, because all my GameObjects merged at once. I want a little pause between each merge, but either they still merge at once or my game/unity freezes for ever.

Here is the code I have now. This version will freeze unity and eats RAM like crazy.

I have the fear, that the while () { StartCoroutine();} in Update is the problem. Can it be that it will try to finish everything down there before it moves to the next frame? But it should break after any time, even if it would be this case. Can somebody help me fix this please?

using UnityEngine;
using System.Collections;

public class Merge : MonoBehaviour {
    public float splitTime; //Time.time of last split
    public bool split; //Are we split
    public GameObject center; //The parent GameObject, where everyone is child of in this class.

    void Start() {
        split = false;
        splitTime = 0;
    }

    void Update () {  
        if (Time.time - splitTime > 30 && split) { 
            split = false; //I first thought this would help to put it up here, so we would not jump in every frame...

            GameObject player = GameObject.Find ("Player"); //Finde the original Player
            GameObject clone;

            while(center.transform.childCount > 1) {
                clone = center.transform.GetChild(1).gameObject; //Get a clone, original is always 0 (?)
                StartCoroutine(MergeAndWait(player, clone, 1));
                Destroy(clone);
            }

            //split = false;
            GameObject.FindGameObjectWithTag("GameController").GetComponent<Score>().split = 1;
        }
    }

    IEnumerator MergeAndWait(GameObject player, GameObject clone, float sec) {
        float scale = clone.transform.localScale.x + player.transform.localScale.x;
        player.transform.localScale = new Vector3(scale, scale, scale);

        yield return new WaitForSeconds (sec);
    }
}

You could try something like:

using UnityEngine;
using System.Collections;
public class Merge : MonoBehaviour {
    public float splitTime; //Time.time of last split
    public bool split; //Are we split
    public GameObject center; //The parent GameObject, where everyone is child of in this class.
    public bool exitFlag = false;

    void Start() {
        split = false;
        splitTime = 0;

        StartCoroutine( MyLoopCoroutine() );
    }
    IEnumerator MyLoopCoroutine ()
    {
       while( !exitFlag )
       {
            if (Time.time - splitTime > 30 && split)
            {
                 split = false; //I first thought this would help to put it up here, so we would not jump in every frame...
                 GameObject player = GameObject.Find ("Player"); //Finde the original Player
                 GameObject clone;
                 while(center.transform.childCount > 1)
                 {
                     clone = center.transform.GetChild(1).gameObject; //Get a clone, original is always 0 (?)
                     yield return StartCoroutine(MergeAndWait(player, clone, 1));  // <--- notice "yield return"
                     Destroy(clone);
                 }
                 //split = false;
                 GameObject.FindGameObjectWithTag("GameController").GetComponent<Score>().split = 1;
            }

            yield return null; //<--- wait one frame
       }
    }

    IEnumerator MergeAndWait(GameObject player, GameObject clone, float sec)
    {
        float scale = clone.transform.localScale.x + player.transform.localScale.x;
        player.transform.localScale = new Vector3(scale, scale, scale);
        yield return new WaitForSeconds (sec);
    }
}

Basically your previous code had few mistakes in it. Update() is called each frame, so you cannot stop it by waiting few seconds. Calling StartCoroutine() inside Update() will just create a standalone coroutine that will clean itself up after it is done. You have to create a custom loop by using a coroutine to achieve what you want.

In the example above, a MyLoopCoroutine will perform endlessly, along side Update(), with choice to exit by setting exitFlag boolean to true.

Not only that, but your code will now wait properly because we have initiated:

 yield return StartCoroutine(MergeAndWait(player, clone, 1));

now your MyLoopCoroutine will wait correctly for the newly created to finish executing before it continues with the rest of the code.

Hope this helps!

1 Like

Thank you very much! This gave me the right idea!
I still need to check every frame, if the time is up, so my working code is this:

using UnityEngine;
using System.Collections;

public class Merge : MonoBehaviour {
    public float splitTime;
    public bool split;
    public GameObject center;

    void Start() {
        split = false;
        splitTime = 0;
    }

    void Update() {
        if (Time.time - splitTime > 30 && split) {
            StartCoroutine(FindObjectsToMerge());
        }
    }

    IEnumerator FindObjectsToMerge() {
        split = false;
       
        GameObject player = GameObject.Find ("Player");
        GameObject clone;
       
        while (center.transform.childCount > 1) {
            clone = center.transform.GetChild (1).gameObject;
            yield return StartCoroutine (MergeAndWait (player, clone, 0.3f));
        }
       
        GameObject.FindGameObjectWithTag ("GameController").GetComponent<Score> ().split = 1;

    }

    IEnumerator MergeAndWait(GameObject player, GameObject clone, float sec) {
        float scale = clone.transform.localScale.x + player.transform.localScale.x;
        player.transform.localScale = new Vector3(scale, scale, scale);
        Destroy (clone); //Note, that I moved Destroy down here, so it will not use a GameObject twice.

        yield return new WaitForSeconds (sec);
    }
}