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

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

  1. Vimalakirti

    Vimalakirti

    Member

    Joined:
    Oct 12, 2009
    Messages:
    752
    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!
     
  2. Dreamora

    Dreamora

    Member

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

    Vimalakirti

    Member

    Joined:
    Oct 12, 2009
    Messages:
    752
    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.
     
  4. Vimalakirti

    Vimalakirti

    Member

    Joined:
    Oct 12, 2009
    Messages:
    752
    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

    New Member

    Joined:
    Jun 20, 2010
    Messages:
    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]
     
    gulfam131 likes this.
  6. Dreamora

    Dreamora

    Member

    Joined:
    Apr 5, 2008
    Messages:
    26,589
    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

    Member

    Joined:
    Oct 12, 2009
    Messages:
    752
    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

    Member

    Joined:
    Apr 5, 2008
    Messages:
    26,589
    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

    New Member

    Joined:
    Aug 20, 2010
    Messages:
    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

    Member

    Joined:
    Oct 12, 2009
    Messages:
    752
    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

    Member

    Joined:
    Apr 5, 2008
    Messages:
    26,589
    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

    Member

    Joined:
    Oct 12, 2009
    Messages:
    752
    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

    Member

    Joined:
    Oct 12, 2009
    Messages:
    752
    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

    New Member

    Joined:
    Aug 20, 2010
    Messages:
    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.  
     
  15. Vimalakirti

    Vimalakirti

    Member

    Joined:
    Oct 12, 2009
    Messages:
    752
    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

    New Member

    Joined:
    Aug 20, 2010
    Messages:
    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

    Member

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

    What are you doing?
     
  18. PocJoc

    PocJoc

    Member

    Joined:
    Sep 21, 2014
    Messages:
    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.
     
  19. TwixEmma

    TwixEmma

    Member

    Joined:
    Jan 27, 2014
    Messages:
    219
    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

    Member

    Joined:
    Aug 1, 2013
    Messages:
    4
    Thanks its working.. .
     
  21. nysh

    nysh

    Member

    Joined:
    Oct 15, 2014
    Messages:
    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

    Member

    Joined:
    Feb 21, 2014
    Messages:
    222
    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. }