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

C# Coroutines in static functions

Discussion in 'Scripting' started by meeydoo, May 2, 2012.

Thread Status:
Not open for further replies.
  1. meeydoo

    meeydoo

    Joined:
    Apr 8, 2012
    Posts:
    28
    Code (csharp):
    1. public class A : Monobehaviour
    2. {
    3. IEnumerator Wait()
    4. {
    5. yield return new WaitForSeconds(5);
    6. Debug.Log("blabla");
    7. }
    8.  
    9. public static void DoSomething()
    10. {
    11. StartCoroutine(Wait());  // not possible ?
    12.  
    13. }
    14. public class B : Monobehavior
    15. {
    16.  
    17. void Start()
    18. {
    19. A.DoSomething();
    20. }
    21.  
    22. }
    23.  
    So I'm not allowed to start Coroutines in static functions ? Is there a work around ?
     
  2. flaminghairball

    flaminghairball

    Joined:
    Jun 12, 2008
    Posts:
    868
    It is indeed impossible. The workaround is to turn it into an instance method, and just pipe the static call into your instance. For example

    Code (csharp):
    1.  
    2. public class StaticCoroutine : MonoBehaviour{
    3.  static public StaticCoroutine instance; //the instance of our class that will do the work
    4.  
    5.  void Awake(){ //called when an instance awakes in the game
    6.   instance = this; //set our static reference to our newly initialized instance
    7.  }
    8.  
    9.  IEnumerator PerformCoroutine(){ //the coroutine that runs on our monobehaviour instance
    10.   while(true){
    11.    yield return 0;
    12.   }
    13.  }
    14.  
    15.  static public void DoCoroutine(){
    16.   instance.StartCoroutine("PerformCoroutine"); //this will launch the coroutine on our instance
    17.  }
    18. }
    Now you can call StaticCoroutine.DoCoroutine(). Make sure you drop the StaticCoroutine on an object in your scene or you won't have an instance and you'll get a NullReferenceException.
     
    PepperPaige, Bunny83, ow3n and 15 others like this.
  3. kingcharizard

    kingcharizard

    Joined:
    Jun 30, 2011
    Posts:
    1,137
    I thought to use static functions you needed to make the class static too... or is that wrong?
     
  4. grim2594

    grim2594

    Joined:
    Nov 5, 2010
    Posts:
    104
    You use static when you don't want to create an instance of a class object. The OP is stating that he can't use coroutines in a static method.

    The only other alternative is to create a class instance, and then execute the method from that instance.

    Code (csharp):
    1. class corout { public void doCor() { // do something } }
    2.  
    3. corout newInstance = new corout();
    4. newInstance.doCor();
     
  5. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Only make a class static if you NEVER want an instance of it. If a class is static then all members and methods must also be static, but not the other way around.
     
    Laiken and Mycroft like this.
  6. szi_John

    szi_John

    Joined:
    Feb 10, 2012
    Posts:
    70
    There is a tutorial video you can find on youtube (or through google) for writing a coroutine manager as well as the proper singleton pattern for Unity.
     
    andreyefimov2010 likes this.
  7. brunoleos

    brunoleos

    Joined:
    May 14, 2014
    Posts:
    30
    If you don't want a singleton (because your code can become very dependent on them, which makes it difficult to refactor) nor using dummy GameObjects (because you have to create/manage them), you can pass a MonoBehaviour as argument to your static method, and them call StartCoroutine from it.

    If you call the static method from a script (Monobehaviour-based object attached to GameObject), you can call the static method using the "this" keyword:

    Example static method:
    Code (CSharp):
    1.     public class Utility
    2.         {
    3.             public static void mysql_query(string sServer, string sQuery, MonoBehaviour justToStartCoroutine)
    4.             {
    5.                 sQuery = WWW.EscapeURL(sQuery);
    6.                 www = new WWW(sServer + "?Query=" + sQuery);
    7.                 justToStartCoroutine.StartCoroutine(WaitForRequest(www));
    8.                 if(www.error == null)
    9.                 {
    10.                     ResultSet = www.text;
    11.                 }
    12.                 else
    13.                 {
    14.                     Debug.Log("Error at: " + www.error);
    15.                 }
    16.             }
    17.         }
    Example call at YourScript.cs:
    Code (CSharp):
    1. public class YourScript : MonoBehaviour
    2.         {
    3.     // Class members
    4.  
    5.             void Start()
    6.             {
    7.                 Utility.mysql_query("", "", this);
    8.             }
    9.         }
     
  8. Trinary

    Trinary

    Joined:
    Jul 26, 2013
    Posts:
    395
    If you use MEC then there are no longer any issues with starting coroutines from static functions/classes or outside of MonoBehaivour classes.

    (MEC is a free plugin)
     
    Last edited: Jun 17, 2018
  9. CykesDev

    CykesDev

    Joined:
    Feb 15, 2018
    Posts:
    3
    To make life easier with static methods and coroutines, I created a class called StaticCoroutine that allows the user to fire coroutines in static methods with an internally stored, privately managed instance. The script requires no scene dependencies, so no need to add the monobehaviour script to a GameObject (unless you want to). To use, simply create a C# Script named "StaticCoroutine" and paste the following code in the script:

    Code (CSharp):
    1.  
    2. using System.Collections;
    3. using UnityEngine;
    4.  
    5. public class StaticCoroutine : MonoBehaviour {
    6.     private static StaticCoroutine m_instance;
    7.  
    8.     // OnDestroy is called when the MonoBehaviour will be destroyed.
    9.     // Coroutines are not stopped when a MonoBehaviour is disabled, but only when it is definitely destroyed.
    10.     private void OnDestroy()
    11.     { m_instance.StopAllCoroutines(); }
    12.  
    13.     // OnApplicationQuit is called on all game objects before the application is closed.
    14.     // In the editor it is called when the user stops playmode.
    15.     private void OnApplicationQuit()
    16.     { m_instance.StopAllCoroutines(); }
    17.  
    18.     // Build will attempt to retrieve the class-wide instance, returning it when available.
    19.     // If no instance exists, attempt to find another StaticCoroutine that exists.
    20.     // If no StaticCoroutines are present, create a dedicated StaticCoroutine object.
    21.     private static StaticCoroutine Build() {
    22.         if (m_instance != null)
    23.         { return m_instance; }
    24.  
    25.         m_instance = (StaticCoroutine)FindObjectOfType(typeof(StaticCoroutine));
    26.  
    27.         if (m_instance != null)
    28.         { return m_instance; }
    29.  
    30.         GameObject instanceObject = new GameObject("StaticCoroutine");
    31.         instanceObject.AddComponent<StaticCoroutine>();
    32.         m_instance = instanceObject.GetComponent<StaticCoroutine>();
    33.  
    34.         if (m_instance != null)
    35.         { return m_instance; }
    36.  
    37.         Debug.LogError("Build did not generate a replacement instance. Method Failed!");
    38.  
    39.         return null;
    40.     }
    41.  
    42.     // Overloaded Static Coroutine Methods which use Unity's default Coroutines.
    43.     // Polymorphism applied for best compatibility with the standard engine.
    44.     public static void Start(string methodName)
    45.     { Build().StartCoroutine(methodName); }
    46.     public static void Start(string methodName, object value)
    47.     { Build().StartCoroutine(methodName, value); }
    48.     public static void Start(IEnumerator routine)
    49.     { Build().StartCoroutine(routine); }
    50. }
    51.  
    I preferred this method over using Awake because I only query the instance whenever I need it. Whenever I don't have an actively-loaded instance, I can find another one (should it exist), or even dynamically create a blank GameObject with the needed script. This is why no scene alterations are needed, and that the code is simply plug and play.
     
  10. unbi

    unbi

    Joined:
    Aug 15, 2017
    Posts:
    1
    Thanks a lot! Works like a charm,
     
    CykesDev likes this.
  11. BoysWhoCry

    BoysWhoCry

    Joined:
    Jan 19, 2015
    Posts:
    12

    Plug in and Play? So instead I should use

    StaticCoroutine.Start(BLAH);

    or is there something else? Because I made a script and did that and still get the non-static member error
     
  12. CykesDev

    CykesDev

    Joined:
    Feb 15, 2018
    Posts:
    3
    You have to create a C# Script named StaticCoroutine.cs. Inside of that script, clear everything you see, then copy and paste my code into it. Once that's done, go into any other C# Script and use:

    StaticCoroutine.Start("methodName");


    Could you please provide more information about the error? I'd like to know where it's occurring, and if any other errors are present.
     
    schmosef and Algal like this.
  13. LukeNukem44

    LukeNukem44

    Joined:
    Sep 2, 2015
    Posts:
    17
    That's a bit hard to read.
     
  14. Owen-Reynolds

    Owen-Reynolds

    Joined:
    Feb 15, 2012
    Posts:
    1,921
    It's a computer program.
     
    Kurt-Dekker and schmosef like this.
  15. santiagolopezpereyra

    santiagolopezpereyra

    Joined:
    Feb 21, 2018
    Posts:
    91
    I know this is not the most interesting reply, but that code is so cool. Thanks.
     
    schmosef, homemacai and antsonthetree like this.
  16. CykesDev

    CykesDev

    Joined:
    Feb 15, 2018
    Posts:
    3
    Thankies! Glad my code helped. :)
     
    schmosef likes this.
  17. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    Three posts into your tenure here.....way to make a good impression....
     
    schmosef likes this.
  18. unique11

    unique11

    Joined:
    Feb 9, 2018
    Posts:
    5
    Hey I'm trying your code but its not working for me :(
    Code (CSharp):
    1. Coroutine 'xxxx' couldn't be started!
    2. UnityEngine.MonoBehaviour:StartCoroutine(String)
    3. StaticCoroutine:Start(String) (at Assets/Scripts/StaticCoroutine.cs:46)
    thats error what can i do to fix it?
     
  19. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,731
    Best guess is you've misspelt the name of the coroutine in the StartCoroutine("name") function call.

    Double check your spelling and capitalisation.
     
  20. WhendricSo

    WhendricSo

    Joined:
    Jan 1, 2011
    Posts:
    171
    You can also use the non-string-based method by placing the coroutine header inside of StartCoroutine() like this

    StartCoroutine(MyCoroutine(someValue));
     
  21. unique11

    unique11

    Joined:
    Feb 9, 2018
    Posts:
    5
    thanks for your answer but my problem is not StartCoroutine its another script made by CykesDev which allows me to use static coroutines
     
  22. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,195
    I don't think that part of @CykesDev example will work. I'm not sure (it's not documented), but I believe that StartCoroutine(string) looks for a method with that name on the behaviour that's running the coroutine - in this case the StaticCoroutine instance, not the script you're running the coroutine from.

    That's not a big deal, though, you shouldn't use the string-based overload. So you want to do:

    Code (csharp):
    1. StaticCoroutine.Start(xxxx());
    Instead of

    Code (csharp):
    1. StaticCoroutine.Start("xxxx");
     
    schmosef and unique11 like this.
  23. unique11

    unique11

    Joined:
    Feb 9, 2018
    Posts:
    5
    Edit: Fixed it thanks for your help :) but now there is another problem I use another Startcoroutine in that coroutine but

    when i call xxxx from another script using staticcoroutine.start('xxxx');

    I get an error at 16th row at xxxx's script which is startcoroutine(yyyy); but yyyy is in that script why isn't it working? i tried making it staticcoroutine but nothing changes
     
    Last edited: Oct 1, 2018
  24. nakkalokesh99

    nakkalokesh99

    Joined:
    Jul 19, 2017
    Posts:
    7
    when using this
    yield return StaticCoroutine.DoCoroutine(somefunction());

    i'm getting this error
    Cannot implicitly convert type 'void' to 'object'
     
  25. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,195
    In order to yield the static coroutines, you'll have to change StaticCoroutine to return the coroutine:

    Code (csharp):
    1. public static Coroutine Start(IEnumerator routine)
    2. { return Build().StartCoroutine(routine); }
     
    Valastir and nakkalokesh99 like this.
  26. AndersenCastaneda

    AndersenCastaneda

    Joined:
    Oct 2, 2018
    Posts:
    1
    I hope this solution works for everyone.

    If you want to use a static class, this is the most simple way to implement:

    1. Define a class that actually inheritance from MonoBehaviour inside your static class
    2. create a public static variable of the class
    3. Initialize the variable

    The end result should look like this
    Code (CSharp):
    1. using System.Collections;
    2. using UnityEngine;
    3.  
    4. public static class MyStatic
    5. {
    6.     public class MyStaticMB: MonoBehaviour { }
    7.     private static MyStaticMB myStaticMB;
    8.  
    9.     private static void Init()
    10.     {
    11.         if (myStaticMB == null)
    12.         {
    13.             GameObject gameObject = new GameObject("MyStatic");
    14.             myStaticMB = gameObject.AddComponent<MyStaticMB>();
    15.         }
    16.     }
    17.  
    18.     public static void PerformCoroutine()
    19.     {
    20.         Init();
    21.         myStaticMB.StartCoroutine(MyCoroutine());
    22.     }
    23.  
    24.     private static IEnumerator MyCoroutine()
    25.     {
    26.         Debug.Log("YEAH");
    27.         yield return null;
    28.     }
    29. }
    30.  
    Testing the static class
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class TestingMyStatic : MonoBehaviour
    4. {
    5.     void Start()
    6.     {
    7.         MyStatic.PerformCoroutine();
    8.     }
    9. }
    10.  
    :)
     
    Last edited: Aug 30, 2020
    DCTShinobi, schmosef and isotian like this.
  27. SushiWaUmaiyo

    SushiWaUmaiyo

    Joined:
    Jun 5, 2019
    Posts:
    2
    Is there a way to do this without using a singleton?
     
  28. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,195
    A coroutine needs a MonoBehaviour to run on. If you have an arbitrary one, you can use that instead of the singleton approach, but be aware that the lifetime of the coroutine is tied to the lifetime of the monobehaviour.

    You could also stick something in the player loop to handle a coroutine-like system that you roll on your own. The docs are a bit sparse, but here they are.

    The singleton approach is probably better than both of those, just for ease of use. Any reason you want to avoid it?
     
    Bunny83 likes this.
  29. KelsoMRK

    KelsoMRK

    Joined:
    Jul 18, 2010
    Posts:
    5,539
    You might be able to get away with it by using Observable.FromCoroutine in UniRx https://github.com/neuecc/UniRx

    (Amazing library btw if you're into reactive/functional programming)
     
  30. Exabits

    Exabits

    Joined:
    Apr 22, 2021
    Posts:
    3
    So, I did want to give you thanks as this worked pretty flawlessly.
    But I did have a thing that I'm having a little confusion over. In my use case, I'm using it to start up an IEnumerator to call upon a server and getting some data back. I've got a yield return uwr.downloadHandler.text and I was curious on where I can access this data to send it to another class.
     
    schmosef likes this.
  31. schmosef

    schmosef

    Joined:
    Mar 6, 2012
    Posts:
    851
    This worked for me, thank you.
     
  32. grobonom

    grobonom

    Joined:
    Jun 23, 2018
    Posts:
    335
    god-developpers piss me off :p

    they're the best and can give you the way of doing impossible things !
    But practically it's not a convenient solution.....

    My trial is to make an internet accessibility global watcher.

    * global means static
    * static means you got no coroutines
    * application means no poll of a bool

    So.... how to have a background coroutine that checks a ping and sets or resets a bit saying YES/NO for the whole application ?

    This is damn simple !

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public static class InternetAccess {
    6.  
    7. private static bool accessible=false;
    8.  
    9.    public static void SetStatus(bool s)
    10.    {
    11.       accessible=s;
    12.    }
    13.  
    14.    public static bool HaveAccess()
    15.    {
    16.       return(accessible);
    17.    }
    18.  
    19. }
    20.  
    21.  
    22.  
    23. public class InternetAccessHandler : MonoBehaviour
    24. {
    25.  
    26.    public void LaunchInternetConnectivityChecker()
    27.    {
    28.       StartCoroutine(DoPing());
    29.    }
    30.  
    31.  
    32.    public IEnumerator DoPing()
    33.    {
    34.       while(true)
    35.       {
    36.       Ping pinging = new Ping("1.1.1.1"); // google ping
    37.          yield return new WaitForSeconds(5f); // sleep a while
    38.      
    39.          if(!pinging.isDone)
    40.             yield return new WaitForSeconds(5f);
    41.          if(!pinging.isDone)
    42.          {
    43.             Debug.Log("Timeout");
    44.             InternetAccess.SetStatus(false);
    45.          }
    46.          else
    47.          {
    48.             Debug.Log("Pt="+pinging.time);
    49.             // we got internet :D
    50.             InternetAccess.SetStatus(true);
    51.          }
    52.       }
    53.    }
    54.  
    55.  
    56.  
    57.     // Start is called before the first frame update
    58.    void Start()
    59.    {
    60.       LaunchInternetConnectivityChecker();
    61.    }
    62.  
    63.  
    64. }
    Just add this script to an empty gameobject and all your scripts of your whole app can check internet access through:

    bool internetAllowed = InternetAccess.HaveAccess();

    :D

    sorry guyz ! Am the best ! Because i'm old :p

    happy unitying !
     
  33. Munchy2007

    Munchy2007

    Joined:
    Jun 16, 2013
    Posts:
    1,731
    For something like this where I'd want it to be running from app startup up, I'd be more inclined to use RuntimeInitializeOnLoadMethod to auto instantiate the Monobehaviour GameObject, something like this (incorporating the previous poster's example).

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public static class InternetAccess
    6. {
    7.     private static bool accessible = false;
    8.  
    9.     public static void SetStatus(bool s)
    10.     {
    11.         accessible = s;
    12.     }
    13.  
    14.     public static bool HaveAccess()
    15.     {
    16.         return (accessible);
    17.     }
    18.  
    19.     public class MyStaticMB : MonoBehaviour { }
    20.     private static MyStaticMB myStaticMB;
    21.  
    22.     [RuntimeInitializeOnLoadMethod]
    23.     private static void Init()
    24.     {
    25.         GameObject gameObject = new GameObject("MyStatic");
    26.         Object.DontDestroyOnLoad(gameObject);
    27.         myStaticMB = gameObject.AddComponent<MyStaticMB>();
    28.         myStaticMB.StartCoroutine(DoPing());
    29.     }
    30.  
    31.     private static IEnumerator DoPing()
    32.     {
    33.         while (true)
    34.         {
    35.             Ping pinging = new Ping("1.1.1.1"); // google ping
    36.             yield return new WaitForSeconds(5f); // sleep a while
    37.  
    38.             if (!pinging.isDone)
    39.                 yield return new WaitForSeconds(5f);
    40.             if (!pinging.isDone)
    41.             {
    42.                 Debug.Log("Timeout");
    43.                 SetStatus(false);
    44.             }
    45.             else
    46.             {
    47.                 Debug.Log("Pt=" + pinging.time);
    48.                 // we got internet :D
    49.                 SetStatus(true);
    50.             }
    51.         }
    52.     }
    53.  
    54. }
    In a production implementation I would add extra checks to handle the situation where the co-routine gameobject is inadvertently deleted.

    Just my personal preference though, I'm also old, but undoubtably not the best! :)
     
    Last edited: Oct 10, 2022
    DCTShinobi and grobonom like this.
  34. TreyK-47

    TreyK-47

    Unity Technologies

    Joined:
    Oct 22, 2019
    Posts:
    1,796
    Locking down this very old thread. If you would like to keep the conversation rolling, please create a new thread. :)
     
    schmosef and Bunny83 like this.
Thread Status:
Not open for further replies.