Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Question Wait N frames until resuming the rest of the script

Discussion in 'Scripting' started by weight_theta, Aug 2, 2021.

  1. weight_theta

    weight_theta

    Joined:
    Aug 23, 2020
    Posts:
    65
    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.

    Code (CSharp):
    1.  private int time_steps = 0;
    2.     IEnumerator WatchForEnoughSteps(int time_steps_interval)
    3.     {
    4.         while (time_steps < time_steps_interval)
    5.         {
    6.             yield return null;
    7.         }
    8.         time_steps = 0;
    9.         ItiActive = false;
    10.     }
    Code (CSharp):
    1. public void triggered(GameObject touchedSphere)
    2.     {
    3.         // if more than than 1 target object
    4.         if (targetObjects.Length > 1)
    5.         {
    6.             if (touchedSphere == targetObjects[checkActive(gameSequence)])
    7.             {
    8.                 collision = true;
    9.                 agent.GetComponent<ReacherAgent>().AddReward(rewardToGive);
    10.                 // Suspend script for N time frames
    11.                 gameSequence++;
    12.                 rewardToGive = 1.0f;
    13.                 failCounter = 0;
    14.                 // sets the tag of GoalOn objct of the touchedSphere to Untagged
    15.                 touchedSphere.transform.GetChild(0).gameObject.tag = "Untagged";
    16.                 // set the color of the target back to its original
    17.                 touchedSphere.GetComponent<Renderer>().material.color = temp;
    18.                 if (gameSequence > 3)
    19.                 {
    20.                     gameSequence = 0;
    21.                     failCounter = 0;
    22.                     SequenceEnd = true;
    23.                     if (RandomSequence == false)
    24.                     {
    25.                         initializeFixedRound();
    26.                         SequenceEnd = false;
    27.                         gameSequence = 0;
    28.                     }
    29.                     else if (RandomSequence == true)
    30.                     {
    31.                         initializeNewRound();
    32.                         SequenceEnd = false;
    33.                         gameSequence = 0;
    34.                     }
    35.                 }
    36.                 else
    37.                 {
    38.                     int active = checkActive(gameSequence);
    39.                     // sets the tag of the next balls GoalOn objct Active
    40.                     targetObjects[active].transform.GetChild(0).gameObject.tag = "Active";
    41.                     // save its color to temp
    42.                     temp = targetObjects[checkActive(gameSequence)].GetComponent<Renderer>().material.color;
    43.                     // let it light up
    44.                     targetObjects[active].GetComponent<Renderer>().material.color = new Color(224, 224, 224);
    45.                 }
    46.             }
    47.             else
    48.             {
    49.                 // if wrong sphere fail counter increments
    50.                 failCounter++;
    51.                 //TODO: fail increment per frame. needs to be reduced to 1 touch per collision
    52.             }
    53.         }
    54.     }
     
  2. mikeohc

    mikeohc

    Joined:
    Jul 1, 2020
    Posts:
    215
    There are:
    Code (CSharp):
    1.         yield return new WaitForEndOfFrame();
    2.         yield return new WaitForFixedUpdate();
    3.         yield return new WaitForSeconds(2f);
    4.         yield return new WaitForSecondsRealtime(2f);
    Or is there a specific reason why you want to count the frames?
     
    Bunny83 likes this.
  3. weight_theta

    weight_theta

    Joined:
    Aug 23, 2020
    Posts:
    65
    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.

    Code (CSharp):
    1. while(frames_after touch < frames_to_reach)
    2. {
    3. // Do nothing
    4. }
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,380
    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:
    Code (csharp):
    1.     for(int i = 0; i < framesToWait; i++)
    2.     {
    3.         yield return null;
    4.     }
    Just yield null the number of frames you want to wait.
     
    Bunny83 and mikeohc like this.
  5. weight_theta

    weight_theta

    Joined:
    Aug 23, 2020
    Posts:
    65
    This sadly does not work, public void triggered() cannot apparently not be an utterable block. Any idea how to resolve it?

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

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,144
    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. :D
     
    weight_theta likes this.
  7. MartinMa_

    MartinMa_

    Joined:
    Jan 3, 2021
    Posts:
    455
    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.
     
  8. weight_theta

    weight_theta

    Joined:
    Aug 23, 2020
    Posts:
    65
    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.
     
    Last edited: Aug 2, 2021
  9. weight_theta

    weight_theta

    Joined:
    Aug 23, 2020
    Posts:
    65
    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.

    Code (CSharp):
    1. private int time_steps = 0;
    2.     IEnumerator WaitForNSteps(int NStepsToWait)
    3.     {
    4.         for (int i = 0; i < NStepsToWait; i++)
    5.         {
    6.             yield return null;
    7.         }
    8.     }
     
  10. kdgalla

    kdgalla

    Joined:
    Mar 15, 2013
    Posts:
    4,355
    did you forget to add
    ItiActive = false;
    after the loop, or whatever it was you wanted this to do?
     
  11. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,144
    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.
     
    weight_theta likes this.
  12. weight_theta

    weight_theta

    Joined:
    Aug 23, 2020
    Posts:
    65
    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 !