Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

How can I make a C# Method wait a Number of Seconds?

Discussion in 'Scripting' started by Vimalakirti, Sep 22, 2010.

  1. Vimalakirti

    Vimalakirti

    Joined:
    Oct 12, 2009
    Posts:
    755
    How do I make a specific Method (function) in my C# script simply wait a number of seconds?

    I'm not using an Update() function and I'd rather not. In JS there's a method called Yield I think that does it. How does one do it in C#??!?!

    Thanks!
     
    blox5000 likes this.
  2. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    You call the function you want to pause through StartCoroutine and then use yield return new WaitForSeconds(...) or whichever yield you need in it
     
    blox5000 and SteveDLosk like this.
  3. Vimalakirti

    Vimalakirti

    Joined:
    Oct 12, 2009
    Posts:
    755
    Thanks, Dreamora, I think you've got me going in the right direction, but I need the c# syntax that will work.

    Could you or someone throw me some actual code for that?

    thanks.
     
    Metrical likes this.
  4. Vimalakirti

    Vimalakirti

    Joined:
    Oct 12, 2009
    Posts:
    755
    It's throwing this error:

    Assets/Scripts/Processor.cs(53,14): error CS1624: The body of `Processor.Process(string)' cannot be an iterator block because `void' is not an iterator interface type

    so do I have to make a separate function??!?

    :D
     
  5. Hoff

    Hoff

    Joined:
    Jun 20, 2010
    Posts:
    223
    The function needs to return IEnumerator, not void and yes you need another function!

    You should go check out the wiki, it explains this stuff pretty good :)

    Code (csharp):
    1.  
    2. public class MyScript : MonoBehaviour
    3. {
    4.     void Start()
    5.     {
    6.         StartCoroutine(MyCoroutine());
    7.     }
    8.  
    9.     IEnumerator MyCoroutine()
    10.     {
    11.         //This is a coroutine
    12.         DoSomething();
    13.        
    14.          yield return 0;    //Wait one frame
    15.    
    16.          DoSomethingElse();
    17.     }
    18. }
    [/code]
     
  6. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    yes you must make it an extra function.
    you cant yield update (neither can unityscript, it just pretends to but in reality it likely splits the function apart at the yield creating a new coroutine which it calls and immediately yields)
     
  7. Vimalakirti

    Vimalakirti

    Joined:
    Oct 12, 2009
    Posts:
    755
    Okay, it's a little squirrely...

    I wrote it as a separate function called Wait():

    Code (csharp):
    1.         IEnumerator Wait(float duration)
    2.     {
    3.         //This is a coroutine
    4.        Debug.Log("Start Wait() function. The time is: "+Time.time);
    5.         Debug.Log( "Float duration = "+duration);
    6.          yield return new WaitForSeconds(duration);   //Wait
    7.         Debug.Log("End Wait() function and the time is: "+Time.time);
    8.     }
    9.  

    which is called from within the Process() function like this:

    Code (csharp):
    1.             Debug.Log("Process() function calling Wait function at "+Time.time);
    2.             Debug.Log("procDuration is "+procDuration);
    3.         StartCoroutine(Wait(procDuration));
    4.             Debug.Log("Process() function after returning from the Wait Function, the time is:"+Time.time);
    The debug log reads this way:

    Process() function calling Wait function at 3.290453
    procDuration is 1.5
    Start Wait() function. The time is: 3.290453
    Float duration = 1.5
    Process() function after returning from the Wait Function, the time is:3.290453


    then the debug log goes on to show a lot of other stuff going on in the Process function and elsewhere, then after 1.5 seconds, it spits this out:

    End Wait() function and the time is: 4.802543

    So what is happening here is that the Wait function immediately returns to the Process function, but stalls at the line "yield return new WaitForSeconds(duration);" before going on.

    Perhaps I can work my code around that, but I was hoping to stall the Process function itself by 'duration' number of seconds.

    Is there any way to do that?

    Thanks for the detailed explanations!
     
  8. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    you can not stall the calling function unless its an IEnumerator itself which will not work for update (would be useless anyway as update is called once per frame independent if the previous one finished or not)

    to wait on a coroutine, you would use yield return StartCoroutine(...) if you call it from within a coroutine.


    and reworking the logic is trivial as mentioend. just transfer all after the yield into a new function with return IEnumerator, then move the yield to the beginning of that function and in update where you had the yield, start the coroutine and the nreturn.
     
  9. KennyW

    KennyW

    Joined:
    Aug 20, 2010
    Posts:
    128
    I think you may need to figure out how the Timer class works in C#.

    Or simply,

    Code (csharp):
    1.  
    2. public static void MyDelay( int seconds )
    3. {
    4.   TimeSpan ts = DateTime.Now + TimeSpan.FromSeconds( seconds );
    5.  
    6.   do {} while ( DateTime.Now < ts );
    7. }
    8.  
     
  10. Vimalakirti

    Vimalakirti

    Joined:
    Oct 12, 2009
    Posts:
    755
    Thanks, Kenny, I'll give that a whirl!

    Dreamora: I don't have an Update function, I'm not sure why you keep talking about Update functions and what I can't do from Update.

    There is no Update.
     
  11. Dreamora

    Dreamora

    Joined:
    Apr 5, 2008
    Posts:
    26,601
    doh, overread the "not" in your initial posting, thats why.

    though the rest still applies: Function that wants to pause must itself be a coroutine, and transforming a non-coroutine to one you can yield in the middle is basically a matter of adding in place of the yield line ;)
    Code (csharp):
    1.   StartCoroutine(OtherHalf());
    2. }
    3.  
    4. IEnumerator OtherHalf()
    5. {

    I say basically cause there is one thing that will require a few words more and that is if you have local variables in the original functions that are needed in the other half. but you can just specify parameters in OtherHalf and transfer them too. instance and class variables don't need to be sent as they are accessable
     
  12. Vimalakirti

    Vimalakirti

    Joined:
    Oct 12, 2009
    Posts:
    755
    Kenny,

    using that loop crashes my computer instantly. This is the function:

    Code (csharp):
    1.             endTime = Time.time + procDuration;    
    2.            
    3.             Debug.Log("Process() function calling Wait function at "+Time.time);
    4.             Debug.Log("procDuration is "+procDuration);
    5.             Debug.Log("Time.time = "+Time.time+", endTime = "+endTime);
    6.  
    7. do {} while ( Time.time < endTime );
    8.            
    9.             //StartCoroutine(Wait(procDuration));
    10.             Debug.Log("Process() function after returning from the Wait Function, the time is:"+Time.time);
    It doesn't even print ANYTHING in the debug log at all! It just freezes up! WTF?!

    It was a good idea, but it just ain't happening.

    Maybe the compiler doesn't like the empty do while loop at runtime.
     
  13. Vimalakirti

    Vimalakirti

    Joined:
    Oct 12, 2009
    Posts:
    755
    Kk, Dreamora, thanks for the help. I think I can make that work by either making the variables global or passing them from function to function. I can do that!

    I'm going to sleep on it... There may be some other things to consider.

    Thanks you two!
     
  14. KennyW

    KennyW

    Joined:
    Aug 20, 2010
    Posts:
    128
    Stand corrected,

    Code (csharp):
    1.  
    2. public static void MyDelay(int seconds)
    3. {
    4.     DateTime dt = DateTime.Now + TimeSpan.FromSeconds(seconds);
    5.  
    6.     do { } while (DateTime.Now < dt);
    7. }
    8.  
     
    persianapp1378 likes this.
  15. Vimalakirti

    Vimalakirti

    Joined:
    Oct 12, 2009
    Posts:
    755
    Kenny, idk why but it just doesn't work properly. That freezes everything up for the duration in seconds. It puts the whole program on pause while it waits. No updates execute, it stops ALL scripts!

    Idk what's up.
     
  16. KennyW

    KennyW

    Joined:
    Aug 20, 2010
    Posts:
    128
    perhaps it's because the function is static and is in a single threaded program. You may try to add the codes instead of calling a static method,

    Code (csharp):
    1.  
    2. DateTime dt = DateTime.Now + TimeSpan.FromSeconds(seconds);
    3.  
    4. do { } while (DateTime.Now < dt);
    5.  
    If it doesn't, then you may need to work on the Timer class, as the Timer class works on its own CPU thread.
     
  17. npsf3000

    npsf3000

    Joined:
    Sep 19, 2010
    Posts:
    3,830
    Why can't you simply use a coroutine?

    What are you doing?
     
  18. PocJoc

    PocJoc

    Joined:
    Sep 21, 2014
    Posts:
    1
    I know that this thread is old, but I just start with Unity and I want to give an answer to all who see this thread.

    First of all, it is not the same thing to do a passive wait than an active wait. Doing what KennyW suggest (do ... while) is an active wait that consumes CPU.
    Using WaitForSeconds function is a passive wait that does not consumes CPU.

    The reason why Vimalakirti can not get his code works is because the method that really waits is not that who initiates the call but the call itself. So, notice the result of the following code:
    Code (csharp):
    1.  
    2. void Start ()
    3. {
    4.   Debug.Log (string.Format("Start time = {0}", Time.time));
    5.   StartCoroutine("CreaIEspera");
    6.   Debug.Log (string.Format("Time before call CreaIEspera = {0}", Time.time));
    7. }
    8.  
    9. IEnumerator CreaIEspera()
    10. {
    11.   for (int iCnt = 0; iCnt < numDaus; iCnt++)
    12.   {
    13.     yield return new WaitForSeconds(3f);
    14.     Instantiate (theCube, position.transform.position, position.transform.rotation);
    15.     Debug.Log (string.Format("Timer before a cube creation = {0}", Time.time));
    16.   }
    17. }
    18.  
    Start time = 0
    UnityEngine.Debug:Log(Object)

    Time before call CreaIEspera = 0
    UnityEngine.Debug:Log(Object)

    Timer before a cube creation = 3.016229
    UnityEngine.Debug:Log(Object)

    Timer before a cube creation = 6.021845
    UnityEngine.Debug:Log(Object)

    Timer before a cube creation = 9.027622
    UnityEngine.Debug:Log(Object)

    The thing is that when we call a method using StarCoroutine, the unity calls and immediately returns, thats why the Start time and the time before the call CreaIEspera is 0.
    Once the program is executing the CreaIEspera function, it waits 3 seconds for each loop in the for.

    P.S.: Another important point, do not concatenate strings with +, use auxiliary calls as string.Format... they are more efficient.
     
    Thanaor likes this.
  19. BmxGrilled

    BmxGrilled

    Joined:
    Jan 27, 2014
    Posts:
    238
    I was just nosying by and thought I'd clarify an issue within this thread, the reason that method freezes unity is because it wont return until the while method completes, so effectively you lock up the main thread until that function returns. Hope this helps! :)
     
  20. gulfam131

    gulfam131

    Joined:
    Aug 1, 2013
    Posts:
    10
    Thanks its working.. .
     
  21. nysh

    nysh

    Joined:
    Oct 15, 2014
    Posts:
    11
    lol@timer thing, anyways bottom line is you cannot block update() thats called every frame so to 'yield' you have to write another function that returns IEnumerator which is nothing but a way of telling the compiler that "dude this bitch aint no regular method call, hold onto execution step where it yields so yea know where to start at in next frame"
     
  22. A.Killingbeck

    A.Killingbeck

    Joined:
    Feb 21, 2014
    Posts:
    483
    Code (CSharp):
    1. IEnumerator Process()
    2. {
    3.  
    4. //Wait 1 second
    5.   yield return StartCoroutine(Wait(1.0f));
    6.   //Do process stuff
    7. }
    8.  
    9. IEnumerator Wait(float seconds)
    10. {
    11.   yield return new WaitForSeconds(seconds);
    12. }
     
  23. Orami

    Orami

    Joined:
    Mar 12, 2015
    Posts:
    20
    All in all you can actually stop parts of update or the entire function. What people are failing to realize is that with a timer you could set a bool to freeze parts of update or sections put a check at the beginning of update to see if the bool needs to be changed back and bam you can stop parts of Update() just as easily as anything.... since I really don't like java script at all here is the C# version.

    C#
    <code>
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. [RequireComponent(typeof(AudioSource))]
    6.  
    7. public class Player_Movement_Sounds : MonoBehaviour
    8. {
    9.  
    10.     public AudioClip Walking;
    11.     public AudioClip Running;
    12.     public float Movement_Volume = 0.05f;
    13.     public float delay_set = 0.75f;
    14.     private float delay = 0.0f;
    15.     // Use this for initialization
    16.     void Start ()
    17.     {
    18.        
    19.     }
    20.    
    21.     // Update is called once per frame
    22.     void Update ()
    23.     {
    24.         Movement_Sounds();
    25.     }
    26.  
    27.     void Movement_Sounds()
    28.     {
    29.  
    30.         if(Input.GetKey(KeyCode.Space) || delay > 0.0f)
    31.         {
    32.             if(delay > 0.0f)delay -= Time.deltaTime;
    33.             else delay = delay_set;
    34.             audio.Stop();
    35.         }else
    36.         if(Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.D))
    37.         {
    38.             if(Input.GetKey (KeyCode.LeftShift))
    39.             {
    40.                 //running
    41.                 if(!audio.isPlaying)
    42.                 {
    43.                     audio.clip = Running;
    44.                     audio.volume = (Movement_Volume);
    45.                     audio.Play();
    46.                 }
    47.             }else
    48.             {
    49.                 if(!audio.isPlaying)
    50.                 {
    51.                     audio.clip = Walking;
    52.                     audio.volume = (Movement_Volume);
    53.                     audio.Play();
    54.                 }
    55.             }
    56.         }else
    57.         {
    58.             if(Input.GetKey(KeyCode.S))    //can't run backwards.
    59.             {
    60.                 if(!audio.isPlaying)
    61.                 {
    62.                     audio.clip = Walking;
    63.                     audio.volume = (Movement_Volume);
    64.                     audio.Play();
    65.                 }
    66.             }else
    67.             {
    68.                 //not moving
    69.                 audio.Stop();
    70.             }
    71.         }
    72.     }
    73. }
    74.  
    75. [code]
     
  24. jhawkes

    jhawkes

    Joined:
    Jul 25, 2016
    Posts:
    1
    None of these methods are working for me PLEASE HELP!!!
     
  25. LeftyRighty

    LeftyRighty

    Joined:
    Nov 2, 2012
    Posts:
    5,148
    do you really expect anyone to be able to assist you given that you've provided absolutely nothing?

    ask a specific detailed question providing information about what you have/want so the members of the forum have something to work with and you'll find these forums invaluable. Necro old threads with nonsense nothing posts and you'll get no where.

    Your choice.
     
    rabinpun and TaleOf4Gamers like this.
  26. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    825
    Honestly, I like to use InvokeRepeating.
    It does not generate garbage unlike coroutines (built-in no addons).
    As I do not know what your use case is this may not be ideal but the options is here if you want it.
    https://docs.unity3d.com/ScriptReference/MonoBehaviour.InvokeRepeating.html

    1) You would start by creating a function which does what you would like to do.
    Code (CSharp):
    1. void DoSomething()
    2. {
    3.     // Do something here
    4.     Debug.Log("DoSomething has been called!");
    5. }
    2) Either use Invoke or InvokeRepeating to call the function.
    Code (CSharp):
    1. void Start()
    2. {
    3.     // Use InvokeRepeating to start the function.
    4.     // InvokeRepeating calls the function every few seconds. (specified when calling InvokeRepeating)
    5.     // InvokeRepeating ("FunctionName", DelayTimeBeforeStart, RepeatEveryXSeconds)
    6.     InvokeRepeating("DoSomething", 1f, 1f);
    7.  
    8.     // Alternatively if you do not want it to repeat you could use Invoke
    9.     // Invoke ("FunctionName", DelayTimeBeforeStart);
    10.     Invoke("DoSomething", 10f);
    11. }
    3) You can then cancel the InvokeRepeating with CancelInvoke();
    Code (CSharp):
    1. CancelInvoke();
    Full Code:
    Code (CSharp):
    1. void Start()
    2. {
    3.     // Use InvokeRepeating to start the function.
    4.     // InvokeRepeating calls the function every few seconds. (specified when calling InvokeRepeating)
    5.     // InvokeRepeating ("FunctionName", DelayTimeBeforeStart, RepeatEveryXSeconds)
    6.     InvokeRepeating("DoSomething", 1f, 1f);
    7.  
    8.     // Alternatively if you do not want it to repeat you could use Invoke
    9.     // Invoke ("FunctionName", DelayTimeBeforeStart);
    10.     Invoke("DoSomething", 10f);
    11. }
    12.  
    13. void DoSomething()
    14. {
    15.     // Do something here
    16.     Debug.Log("DoSomething has been called!");
    17. }
    18.  
    19. void StopInvoke()
    20. {
    21.     CancelInvoke();
    22. }
    DISCLAIMER:
    If it is supposed to be running for a long time. (Full playtime span) Then that is what Coroutines are designed for.
     
    Jack-Draak and Jeremy-Borton like this.
  27. TaleOf4Gamers

    TaleOf4Gamers

    Joined:
    Nov 15, 2013
    Posts:
    825
    I agree, the useless posts and begging for free code does get very annoying. However, I was there at a point, young and wanting to create games so I can sympathize a little. (Although not too much as it is extremely annoying)
     
  28. Vedrit

    Vedrit

    Joined:
    Feb 8, 2013
    Posts:
    514
    For anyone that is concerned about performance, you can declare your WaitForSeconds so you're not generating a new one every time
    Code (csharp):
    1. public WaitForSeconds 1SecPause = 1;
    2. ...
    3. ...
    4. yield return 1SecPause;
     
    TaleOf4Gamers likes this.
  29. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    I wouldn't recommend the "infinite loop" method of waiting, because that's going to block everything. Coroutines sound exactly like what you need here, but yes, all the functions that you want to be doing the waiting need to be coroutines.

    So if you want to pause inside Update ... don't. Update is called every single frame, it doesn't logically make sense to pause inside it. If you're doing it in response to user input, then have it start a separate coroutine:
    Code (csharp):
    1. void Update () {
    2.   if(Input.GetKey(KeyCode.Space) {
    3.     StartCoroutine(Foo());
    4.   }
    5. }
    6.  
    7. IEnumerator Foo () {
    8.   // Do something
    9.   yield return new WaitForSeconds(3f);  // Wait three seconds
    10.   // Do something else
    11. }
    If you're trying to implement logic like "the object moves forward for three seconds, then goes into reverse", then either:
    A) Use a coroutine / invoke instead of Update.
    B) Keep some state variables which you refer to in Update.
     
  30. Glemau

    Glemau

    Joined:
    Jul 24, 2015
    Posts:
    37
  31. marcola_gt

    marcola_gt

    Joined:
    Apr 19, 2017
    Posts:
    6
    Ok.

    The 2 ways above have worked for me.

    This:
    Code (CSharp):
    1. void Update () {
    2.   if(Input.GetKey(KeyCode.Space) {
    3.     StartCoroutine(Foo());
    4.   }
    5. }
    6. IEnumerator Foo () {
    7.   // Do something
    8.   yield return new WaitForSeconds(3f);  // Wait three seconds
    9.   // Do something else
    10. }
    And this:
    Code (CSharp):
    1. void Start()
    2. {
    3.     // Use InvokeRepeating to start the function.
    4.     // InvokeRepeating calls the function every few seconds. (specified when calling InvokeRepeating)
    5.     // InvokeRepeating ("FunctionName", DelayTimeBeforeStart, RepeatEveryXSeconds)
    6.     InvokeRepeating("DoSomething", 1f, 1f);
    7.     // Alternatively if you do not want it to repeat you could use Invoke
    8.     // Invoke ("FunctionName", DelayTimeBeforeStart);
    9.     Invoke("DoSomething", 10f);
    10. }
    11. void DoSomething()
    12. {
    13.     // Do something here
    14.     Debug.Log("DoSomething has been called!");
    15. }
    Finally!!
    I've always found reference about the IEnumerator/yield stuff, but it has never worked for me! This time it has! And the Invoke function where we can set a delay time to start i found awesome! Very easy!

    I guess i was trying to implement the StartCoroutine function inside my OncollisionEnter(). When i placed it in the Update() it worked! :p

    Thank you all!
     
  32. Brathnann

    Brathnann

    Joined:
    Aug 12, 2014
    Posts:
    7,186
    While this is a very old thread...I just wanted to point something out to you. GetKey triggers while the user holds down the key. Just be careful you are getting the result you want. While holding down space for example, you will call Foo repeatedly, thus creating several calls to the coroutine that will all yield for 3 seconds before doing something. Just keep that in mind if that isn't the result you wanted.
     
    gjones1911 likes this.
  33. Doku_Demon

    Doku_Demon

    Joined:
    Sep 2, 2017
    Posts:
    1
    all this over one question XD Must Take A Lot Of Effort
     
  34. watercolorheartdev

    watercolorheartdev

    Joined:
    Sep 25, 2017
    Posts:
    17
    Is there a way to stop it from doing that?

    I tried using:

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using System.Collections.Generic;
    4. using UnityEngine;
    5. using System;
    6.  
    7. public class Remains : MonoBehaviour
    8. {
    9.  
    10.     public GameObject remains;
    11.  
    12.     /*
    13.    IEnumerator Foo()
    14.     {
    15.        yield return new WaitForSeconds(3f);  // Wait three seconds
    16.        Destroy(remains);
    17.     }
    18.     */
    19.  
    20.     // Update is called once per frame
    21.     void Update()
    22.     {
    23.         if (Input.GetKeyDown("space"))
    24.         {
    25.             Instantiate(remains, transform.position, Quaternion.identity);
    26.             //StartCoroutine(Foo());
    27.             Invoke("Foo", 4f);
    28.             Destroy(gameObject);
    29.         }
    30.     }
    31.  
    32.     void Foo()
    33.     {
    34.         // Do something here
    35.         Debug.Log("DoSomething has been called!");
    36.     }
    37. }

    But it never calls the destroy part at all on the Instantiated cubes.
     
  35. Errorsatz

    Errorsatz

    Joined:
    Aug 8, 2012
    Posts:
    555
    Two problems:
    1) You're destroying the GameObject that has the Remains component on it, not the new one you instantiated.
    2) You're doing it directly inside Update, not in Foo, so it would happen instantly after instantiating them.

    Since you need to keep track of the instantiated remains, you either need to use a coroutine instead of Invoke, or else store a reference in a member field.

    Neither coroutines or Invoke applies any delay to code that's on the next line:
    Code (csharp):
    1. void Foo () {
    2.    Debug.Log("This happens immediately.");
    3.    Invoke("Bar", 3f);
    4.    Debug.Log("This also happens immediately.");
    5. }
    6.  
    7. void Bar () {
    8.   Debug.Log("THIS happens after three seconds.");
    9. }
    But yield statements inside a coroutine do:
    Code (csharp):
    1. IEnumerator Foo () {
    2.    Debug.Log("This happens immediately.");
    3.    yield return new WaitForSeconds(3f);
    4.    Debug.Log("This happens after three seconds.");
    5. }
     
  36. watercolorheartdev

    watercolorheartdev

    Joined:
    Sep 25, 2017
    Posts:
    17
  37. KazaHiya

    KazaHiya

    Joined:
    Dec 3, 2017
    Posts:
    1
    Are there any function(wait a number of seconds) that works in EditorWindow ? I mean not in MonoBehaviour.
     
  38. dhruvmohandatta1234

    dhruvmohandatta1234

    Joined:
    Jan 13, 2018
    Posts:
    2
    is there a function similar to yield wait for seconds in update window?
     
  39. unity_ri0RVj253oPBfw

    unity_ri0RVj253oPBfw

    Joined:
    Mar 17, 2021
    Posts:
    7
    no, you just have to call the coroutine in start and it should continue to be called...?
    (also I do know this is old, kind of just checking my knowledge and incorrect terminology)
     
  40. tanmaymaloo

    tanmaymaloo

    Joined:
    Apr 14, 2021
    Posts:
    1