Nested Coroutine not finishing

Hello, I’m trying to make a platform blink a few times before being destroyed.

bool IsPieceStoped()
    {
        foreach (Transform cube in transform)
        {
            pos = new IntVector3(cube.position);
            if (pos.Y == 0 || boardController.spaces[pos.X, pos.Y - 1, pos.Z])
            {
                Destroy(shadow);
                StartCoroutine(WaitAndDestroy());
                return true;
            }

        }
        return false;
    }

IEnumerator WaitAndDestroy()
    {
        yield return new WaitForSeconds(0.5f);
        var numberPlatformsDestroyed = 0;
        foreach (Transform cube in transform)
        {
            pos = new IntVector3(cube.position);
            cube.tag = pos.Y.ToString();
            boardController.spaces[pos.X, pos.Y, pos.Z] = true;

            if (boardController.IsPlatformComplete(pos.Y))
            {
                numberPlatformsDestroyed++;
                yield return StartCoroutine(boardController.DestroyPlatform(pos.Y));
            }
        }

        if (numberPlatformsDestroyed > 0)
            boardController.Score(1000 * numberPlatformsDestroyed * numberPlatformsDestroyed);

        gameObject.tag = "stoped";
        boardController.Score(5);
        boardController.InstantiatePiece();
        Destroy(gameObject.GetComponent<PieceController>());
    }

public IEnumerator DestroyPlatform(int y)
    {
        var cubesToBeDestroyed = GameObject.FindGameObjectsWithTag(y.ToString());
        GameObject temp = new GameObject();

        foreach (var cube in cubesToBeDestroyed)
            cube.transform.parent = temp.transform;

        temp.SetActive(false);
        yield return new WaitForSeconds(1);
        temp.SetActive(true);
        yield return new WaitForSeconds(1);
        temp.SetActive(false);
        yield return new WaitForSeconds(1);
        temp.SetActive(true);
        yield return new WaitForSeconds(1);
        Destroy(temp);
        MovePlatformsDown(y + 1);
        speed *= 0.95f;
    }

But the code inside DestroyPlatform(), after the first WaitForSeconds doesn’t run anymore, instead, the WaitAndDestroy() coroutine doesn’t wait, starts running and it spawns a new piece.

Any help is apreciated, thanks.

Where is this script attached to? You most likely “shoot yourself in the foot”. I guess that this script is attached to one of the gameobjects that you deactivate in your coroutine.

Note that this line:

yield return StartCoroutine(boardController.DestroyPlatform(pos.Y));

starts the coroutine DestroyPlatform on this monobehaviour and not on the boardController. You can try doing this:

yield return boardController.StartCoroutine(boardController.DestroyPlatform(pos.Y));

This will start the coroutine on the boardController as we use StartCoroutine of the boardController.

I find various issues with the script. The most important in my opinion is using gameObject.tag to mark the objects at a certain height to be destroyed. Try to use List cubesInLine instead (for each line) and first gather all the objects in the line, then foreach(GameObject cube in cubesInLine) { Destroy (cube); }
In fact, I would recommend that boardController.spaces store the cube itself instead of just marking it as filled (but you will have to remember to clear boardController.spaces when an object is Destroyed).

Also, what @Bunny83 said: your coroutine starts on this instance of the script, not on BoardController. Depending on where it is located, it is possible that the coroutine stops because you SetActive(false) on the GameObject you started your coroutine (coroutines don’t run on inactive game objects and disabled scripts).

There are some other things, but I think this will point you in the right direction.

EDIT: so I re-read the code once more, and what really bothers me is that both IsStoped() and WaitAndDestroy() will run on all Transforms within transform, which very likely messes up the logic. Then the coroutine boardController.DestroyPlatform() is started for each cube that fulfils the condition of boardController.IsPlatformComplete(), which again may mess up the logic. What I would do is to move the logic of determining if a cube/Piece needs to be destroyed into boardController, and add the ones to be destroyed into a list, and when all have been gathered, notify each that they need to be blink and destroy themselves. Basically, separate the responsibilities: only boardController should know if a Piece needs to be destroyed, and only the Piece should know what happens when it is being destroyed.

@Bunny83

Thank you for the answer. I might be wrong but i don’t think I am shooting myself in the foot because both IsPieceStoped() and WaitAndDestroy() are on the Piece gameobject. The DestroyPlatform() is on the Board gameobject. But the objects I deactivate in DestroyPlatform() are cubes that were child of Piece, not the Piece itself.

I tryed what you said, doing boardController.StartCoroutine, and it did the blink animation but my other Coroutine, the one in Piece, didn’t wait for the animation to to end, so there was code running before it was suposed to. And I think that that’s the root of the problem.

When I do

yield return StartCoroutine(boardController.DestroyPlatform(pos.Y));

or

yield return boardController.StartCoroutine(boardController.DestroyPlatform(pos.Y));

shouldn’t it wait for DestroyPlatform() to end before running the rest of the code?

@Harinezumi

I haven’t thought of having the gameobject itself in my array, I think it is a good idea, but it might be really heavy because my array has a length of 960. Maybe I could just use the Transform. I really don’t know whats better for performance. Thank you