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

Delegates and Co-routines and yield WaitforSeconds

Discussion in 'Scripting' started by aerende, May 17, 2010.

  1. aerende

    aerende

    Joined:
    Apr 27, 2009
    Posts:
    316
    I'm calling a delegate, AnimTranslate, and would like to use "yield WaitforSeconds" within the delegate. It appears that one cannot call yield WaitforSeconds within a delegate but must use a Co-routine, but I can't get it to work. What I have is the following:

    Code (csharp):
    1. function TranslateWait(secWait : float){
    2.     yield WaitForSeconds(secWait);
    3. }  
    4.  
    5. function AnimTranslate(frame : int ){
    6.   for(var t : int = 0; t < 20; t++){
    7.     Debug.Log("frame = " + frame);
    8.     yield TranslateWait(1.0);
    9.   }
    10. }
    where AnimTranslate is the delegate. Does anyone know how to use yield WaitforSeconds within a delegate or to use yield WaitforSeconds with a Co-routine from a delegate?
     
  2. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    I just tested it in C# and it does work there:
    Code (csharp):
    1. using UnityEngine;
    2. using System.Collections;
    3.  
    4. public class TestIng : MonoBehaviour {
    5.     public delegate IEnumerator WaitFunction(int f);
    6.        
    7.     void Start ()
    8.     {
    9.         WaitFunction func = Wait;
    10.         StartCoroutine(func(2));
    11.     }
    12.    
    13.     IEnumerator Wait(int f)
    14.     {
    15.         yield return new WaitForSeconds(1f);
    16.         Debug.Log(f + " " + Time.time);
    17.     }
    18. }
    Unfortunately, I have no idea how to declare delegates in JS, so I can't solve your question directly.

    Things that may (or may not) make it work in JS:
    - Start the function using StartCoroutine.
    - Explicitly define the return value of your delegate as IEnumerator.
     
  3. aerende

    aerende

    Joined:
    Apr 27, 2009
    Posts:
    316
    @tomvds

    Thanks for testing this. The Unity docs say that StartCoroutine isn't needed in Javascript:

    • When using JavaScript it is not necessary to use StartCoroutine, the compiler will do this for you. When writing C# code you must call StartCoroutine.

    so that's not the problem. Although I did try calling the function TranslateWait using StartCoroutine, and the function TranslateWait is called, just that there isn't any delay when TranslateWait is called.
     
  4. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    This is true for most cases, but not all. Specifically, when you wish to run a coroutine on another class than where it is defined (for example, when the class defining the Coroutine is not a MonoBehaviour itself) you need to use StartCoroutine.

    My theory was that the delegates might also be such an exception.

    I'm afraid I do not have any suggestions beyond the two I gave. If you could post the entire code involved (specifically the part where the AnimaTranslate delegate is defined and called, as I have no idea how delegates work in JS), I might try getting it to work by making a strict C# to JS conversion. No guarantees that it can be done at all, though: JS can simply not express everything C# can.
     
  5. aerende

    aerende

    Joined:
    Apr 27, 2009
    Posts:
    316
    @tomvds

    The delegate, AnimTranslate, is setup in Start() below using SetAnimFrameDelegate which is within SpriteManager 2. SM2 is written in C#. I posted initially on that thread but the problem hasn't been solved there yet. Here's the latest version of the code.

    When the TESTAnim is playing in the function CharacterAction, using PlayAnim, the delegate AnimTranslate, is called for each frame. When AnimTranslate receives frame = 9, then Debug statements should be printed out delayed every 1.0 second using TranslateWait. The debug statement in TranslateWait gets printed 20 times, but there is no delay between the printings of the debug statement in TranslateWait.

    Code (csharp):
    1.  
    2. private var TEST_GO : GameObject;
    3. private var TEST_PS : PackedSprite;
    4. private var TEST_SB : SpriteBase;
    5.  
    6. function Start(){
    7.  
    8.     TEST_PS = GetComponent("PackedSprite");
    9.     TEST_GO = TEST_PS.gameObject;
    10.     TEST_SB = TEST_GO.GetComponent("SpriteBase");
    11.  
    12.     TEST_SB.SetAnimFrameDelegate(AnimTranslate);
    13. }
    14.  
    15. function TranslateWait(secWait : float){
    16.     Debug.Log("TranslateWait started");
    17.     yield WaitForSeconds(secWait);
    18. }  
    19.  
    20. function AnimTranslate(frame : int ){
    21.   if((frame == 9)  (TEST_PS.GetCurAnim() == TESTAnim) ){
    22.     for(var t : int = 0; t < 20; t++){
    23.        Debug.Log("frame = " + frame + ", t = " + t);
    24.       StartCoroutine(TranslateWait(1.0));
    25.     }
    26.   }
    27. }
    28.  
    29. function CharacterAction(){
    30.  
    31.      var TESTAnim          : UVAnimation;  
    32.      TESTAnim            = TEST_PS.GetAnim("charAnim");
    33.     TEST_PS.PlayAnim(TESTAnim);
    34.  
    35. }
    36.  

    Calling TranslateWait using StartCoroutine caused the calls to TranslateWait to go through, but the WaitForSeconds inside TranslateWait doesn't seem to get executed.
     
  6. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    Ah, well I don't have SM2, so it's tricky to reproduce this example. However, I do notice that your StartCoroutine(TranslateWait(1.0)); is missing a yield statement in front of it.

    If that does not solve it, it becomes a question of how SM2 executes the delegate. Does it execute it as a Unity coroutine or is it iterated by SM itself. If it is the latter, then yield WaitForSeconds simply may not work at all. That question may be something for Brady to answer, though.
     
  7. aerende

    aerende

    Joined:
    Apr 27, 2009
    Posts:
    316
    Putting yield in front of StartCoroutine causes TranslateWait to not get called at all.

    I'll ask Brady about how the delegate is executed.
     
  8. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    Ah, well your first sentence sort of explains the answer to the question ;).

    The function is executed as a normal function (not containing yield) and can therefore not run as a Coroutine. You'll simply not be able to yield in the delegate.
     
  9. aerende

    aerende

    Joined:
    Apr 27, 2009
    Posts:
    316
    Supposedly one can yield in the delegate if the code is written in C# instead of javascript.

    1) Is the issue that I am using javascript?

    2) Or do you think that the delegate code in SM2 is written in a manner that yields cannot be used in the delegate?
     
  10. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    The delegate is called as a normal method, however it is called as a result of a coroutine (the animation pump which updates animations). But it itself is called normally (no StartCoroutine()).
     
  11. aerende

    aerende

    Joined:
    Apr 27, 2009
    Posts:
    316
    Does this mean that I can't use "yield WaitForSeconds" in a delegate called from SetAnimFrameDelegate in SM2 using javascript?
     
  12. tomvds

    tomvds

    Joined:
    Oct 10, 2008
    Posts:
    1,028
    It means you cannot use yield of any kind in that function, JS, C# and Boo alike.
     
  13. Brady

    Brady

    Joined:
    Sep 25, 2008
    Posts:
    2,474
    If you're wanting to delay execution of something this way, perhaps use Invoke() instead?
     
  14. Fishypants

    Fishypants

    Joined:
    Jan 25, 2009
    Posts:
    444
    @ tomvds -

    This is cool, and I've been looking into using delegates with coroutines. Would it be possible to store an IEnumerator in a delegate, then switch which IEnumerator the delegate is pointing to?

    So for example say I have these IEnumerators:

    IEnumerator TestA(){
    Debug.Log("In Test A ...");
    yield return new WaitForSeconds(1.0f);
    }

    IEnumerator TestB(){
    Debug.Log("In Test B ...");
    yield return new WaitForSeconds(1.0f);
    }

    Would it be possible for the delegate to point to one, then switch to the other and continue execution on the new IEnumerator?
     
  15. slon_ru

    slon_ru

    Joined:
    Mar 19, 2013
    Posts:
    1
    Hi All!
    I try to use

    public delegate IEnumerator bonuceSize();
    public static event bonuceSize sizeBS;
    ...
    {
    StartCoroutine(sizeBS());
    }
    ...
    it work fine, but how can I stop my corutine ?
    StopCorutine (sizeBS()) or StopCorutine ("sizeBS") dont"t work :(.
    Thank you!