Wait N frames until resuming the rest of the script

Hi Everyone,

I have a sequential light up game for which a player has to reach the correct target, the water the more points. There are 4 targets. One of the four targets lights up and the player has to touch that target. The target then loses its light after which the next target activates by lighting up and so on.
The sequence of activation and the light up is governed by a game_manager script.
Now after a touch I would however like the game_manager to wait N number of frames until it continues with activation. The idea is that the player then has N frames to think about which target may activate next.

Since the next activation occurs after touch I tried adding an empty while loop to line 10, to let the game wait for 250 time frames until resuming the the rest of the script. I then tried a coroutine, by also adding StartCoroutine(WatchForEnoughSteps(IntertrialIntervalDuration)); to line 10 which did not work either.
Any suggestions on how I could resolve this ? Id be happy to learn more.

 private int time_steps = 0;
    IEnumerator WatchForEnoughSteps(int time_steps_interval)
    {
        while (time_steps < time_steps_interval)
        {
            yield return null;
        }
        time_steps = 0;
        ItiActive = false;
    }
public void triggered(GameObject touchedSphere)
    {
        // if more than than 1 target object
        if (targetObjects.Length > 1)
        {
            if (touchedSphere == targetObjects[checkActive(gameSequence)])
            {
                collision = true;
                agent.GetComponent<ReacherAgent>().AddReward(rewardToGive);
                // Suspend script for N time frames
                gameSequence++;
                rewardToGive = 1.0f;
                failCounter = 0;
                // sets the tag of GoalOn objct of the touchedSphere to Untagged
                touchedSphere.transform.GetChild(0).gameObject.tag = "Untagged";
                // set the color of the target back to its original
                touchedSphere.GetComponent<Renderer>().material.color = temp;
                if (gameSequence > 3)
                {
                    gameSequence = 0;
                    failCounter = 0;
                    SequenceEnd = true;
                    if (RandomSequence == false)
                    {
                        initializeFixedRound();
                        SequenceEnd = false;
                        gameSequence = 0;
                    }
                    else if (RandomSequence == true)
                    {
                        initializeNewRound();
                        SequenceEnd = false;
                        gameSequence = 0;
                    }
                }
                else
                {
                    int active = checkActive(gameSequence);
                    // sets the tag of the next balls GoalOn objct Active
                    targetObjects[active].transform.GetChild(0).gameObject.tag = "Active";
                    // save its color to temp
                    temp = targetObjects[checkActive(gameSequence)].GetComponent<Renderer>().material.color;
                    // let it light up
                    targetObjects[active].GetComponent<Renderer>().material.color = new Color(224, 224, 224);
                }
            }
            else
            {
                // if wrong sphere fail counter increments
                failCounter++;
                //TODO: fail increment per frame. needs to be reduced to 1 touch per collision
            }
        }
    }

There are:

        yield return new WaitForEndOfFrame();
        yield return new WaitForFixedUpdate();
        yield return new WaitForSeconds(2f);
        yield return new WaitForSecondsRealtime(2f);

Or is there a specific reason why you want to count the frames?

1 Like

So there are four different methods to choose from ? I am assuming the 2f indicates that we are waiting for frames ?

Yes I was thinking that counting the frames once the target had been touched can allow me to suspend the action for with more ease.
i.e.

while(frames_after touch < frames_to_reach)
{
// Do nothing
}

So none of those 4 wait for N frames. They wait for the things they’re named for.

So the one that takes 2f is “WaitForSeconds”, so it’s “waiting for 2 seconds”.

See the thing here is that you won’t actually know the framerate on the target device because framerates can vary wildly depending on the hardware, what’s going on on screen, if they have other applications running in the background, and more.

So waiting for specific number of frames is usually not common (note there are exceptions, but it’s not common). What even is 10 frames in a real world setting? If my machine plays at 30fps that’s 1/3 of a second. But if my machine runs at 100fps it’s 1/10 of a second. Meaning better performing machines get less time to react.

Instead you generally wait in seconds… hence the “WaitForSeconds” and “WaitForSecondsRealtime” yield instructions.

…

With that said.

If you need to wait for a specific number of frames:

    for(int i = 0; i < framesToWait; i++)
    {
        yield return null;
    }

Just yield null the number of frames you want to wait.

2 Likes

This sadly does not work, public void triggered() cannot apparently not be an utterable block. Any idea how to resolve it?

error CS1624: The body of 'Game_Manager.triggered(GameObject)' cannot be an iterator block because 'void' is not an iterator interface type

Triggered is not a coroutine. You have a coroutine setup already in the code you showed, so it appears you know how to setup a coroutine. You can’t just toss a yield into a regular old method. You can solve this easily. :smile:

1 Like

That is really bad solution do some action after specific ammount of frames imo because it will take different time for each player.You should find better solution.ALso that is a reason why we have Time class.

Well I actually want to use time steps and I think the closest thing to it in unity are frames or execution that happen per void updated.

I define the coroutine as below and call it at line 10 with StartCoroutine(WaitForNSteps(250)). Is that correct? I am asking because it did not work too well. The error message disappeared but the desired behaviour has not been reached either.

private int time_steps = 0;
    IEnumerator WaitForNSteps(int NStepsToWait)
    {
        for (int i = 0; i < NStepsToWait; i++)
        {
            yield return null;
        }
    }

did you forget to add ItiActive = false; after the loop, or whatever it was you wanted this to do?

Coroutines don’t pause other methods. They only yield within the coroutine. So, calling StartCoroutine in a method will still allow that method to continue once the yield is hit in the coroutine.

It does not start the method, switch to the coroutine, loop and yield until the coroutine is done, and then switch back to the method.

You would have to turn triggered into a coroutine and then integrate the yield directly in it.

1 Like

I believe this to have worked. Not sure if I did the same thing but it provided great inspiration to resolve my issue. Thank you !