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. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more..
    Dismiss Notice
  3. Dismiss Notice

Static StartCoroutine

Discussion in 'Wish List' started by Mortoc, Dec 3, 2008.

  1. Mortoc

    Mortoc

    Joined:
    Nov 3, 2006
    Posts:
    50
    I wanna be able to do something like this:

    Code (csharp):
    1.  
    2. public class NotAMonoBehaviour {
    3.     public void MaybeCalledBeforeAwake() {
    4.         Coroutine.Start( MyCoroutine() );
    5.     }
    6. }

    There isn't a front-end reason for StartCoroutine to be linked to a particular MonoBehaviour (although I suspect there is some sort of book keeping reason on the back-end that would have to be worked around for this functionality).
     
  2. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,306
    I don't know exactly how CoRoutines are implemented, but they seem to be executed somewhere within the game loop, blocking the game loop until you hit a "yield". So, there is a reason why CoRoutines are only available inside MonoBehaviours - because MonoBehaviours are designed to be part of the game loop.

    One thing that shows this is that CoRoutines will stop when the MonoBehaviour is disabled (not perfectly sure) or the game object is deactivated (definitely ;-) ). Also, you can start CoRoutines if the game object is deactivated (you get a warning or even an error when you try that). So, I doubt they would execute before Awake(), probably not even before Start().

    Another thing related: If you have a loop without a yield in a CoRoutine, this can bring the whole game to a halt. CoRoutines are not threads, but ... CoRoutines ;-)
     
  3. Mortoc

    Mortoc

    Joined:
    Nov 3, 2006
    Posts:
    50
    All of our script code runs in the game loop. All of what you said about Coroutines is true, but it misses the point I was trying to make. What I propose doesn't change the behaviour of the engine. Take this example which is valid today:


    Code (csharp):
    1.  
    2. public class Coroutiner : MonoBehaviour {
    3.     // This will be null until after Awake()
    4.     public static Coroutiner instance = null;
    5.  
    6.     private void Awake() {
    7.         instance = this;
    8.     }
    9.     public Coroutine StartCoroutine(IEnumerator firstIterationResult) {
    10.         return this.StartCoroutine( firstIterationResult );
    11.     }
    12. }
    13.  
    14. // If we use this functionality before Awake(), it will be a null reference
    15. Coroutiner.instance.StartCoroutine( MyCoroutine() );
    16.  
    17.  
    This works now, but the problem is that you cannot rely on that instance variable being valid until after *all* the Awake calls have been made (there's no guarantee on order of Awake calls). It's also dangerous in the editor, if you ran the scene twice without recompiling, that static variable would still have the reference from the last play session.

    A static call to StartCoroutine doesn't change the security of it. It makes MonoBehaviour less monolithic and it allows Unity to ensure that Coroutines started before Awake() will function properly.
     
  4. AngryAnt

    AngryAnt

    Keyboard Operator Moderator

    Joined:
    Oct 25, 2005
    Posts:
    3,045
    That's why Start is there.

    This won't be a problem since you're obviously cleaning all variables when needed - right? :wink:

    Code (csharp):
    1. public void OnApplicationQuit()
    2. {
    3.     instance = null;
    4. }
     
  5. Mortoc

    Mortoc

    Joined:
    Nov 3, 2006
    Posts:
    50
    I agree. This isn't really a problem, just remember to clean up after yourself.

    I don't agree. The fact that Awake is called before Start helps a lot, but not enough. It's only one degree of ordering. What if you have 3 or more order dependent operations? That's the case I'm dealing with right now. I'm trying to set up a system that needs to be in place for all my running game objects, and I have to do weird things to support it. Additionally, the system I am creating isn't a MonoBehaviour, so I have to start the coroutines on some other arbitrary object (like in my example above). It seems that linking a Coroutine's execution to the state of a GameObject can be useful in some cases, but definitely not in all cases.
     
  6. jashan

    jashan

    Joined:
    Mar 9, 2007
    Posts:
    3,306
    That sounds interesting. How would you transfer control of execution to those arbitrary objects before any call to Awake()? In my understanding, the first possible entry point for "custom code" within a Unity application is Awake(). So the question is who could call "MaybeCalledBeforeAwake" if not another MonoBehaviour attached to a game object in its Awake().

    As long as Awake() would be "early enough" you should be able to start Coroutines that are implemented in non-MonoBehaviour instances from Awake(), respectively Start(). So the implementation could be anywhere - just control is bound to game objects.

    Btw, one thing I and others are doing frequently is having game objects just serve the purpose of managing code. Maybe that would be an option to solve what you need (i.e. having "empty" game objects that only have one or more MonoBehaviours attached that do "management stuff")?

    I agree that it's sometimes quite tricky to have only Awake() and Start() and no guaranteed ordering between the individual calls to Awake(), and later between the individual calls to Start(). But all the troubles I encountered with this were comparatively easy to solve by simply making my design cleaner in respect to Unity's framework (e.g. not accessing Singletons in Awake(); earliest time in which I access a Singleton is in Start() ... and I'm using a "Unityfied Singleton Pattern", which sets the instance-variable in the Awake() method instead of in the accessor).

    If you absolutely need an order for Awake()/Start(), one solution might be having scenes with only a few "manager game objects" which then instantiate prefabs of your "actual game objects". With that approach, you can be sure that the Awake()s of those instantiated prefabs are called after your management system has been properly initialized.

    IMHO that breaks parts of the "usual workflow" of the Unity editor, so personally, I avoid this approach. But I'm sure there's quite a few use cases where it's the best available approach.
     
  7. Mortoc

    Mortoc

    Joined:
    Nov 3, 2006
    Posts:
    50
    Yeah, we have come to the same conclusion for the current state of things. But we see it differently.

    Because MonoBehaviour is monolithic, it requires us to couple the the things from it we need (Coroutines in this case), with GameObjects where you have no control over construction. The answer we have today is to do these work-arounds to make sure our data structures get initialized correctly. That's why I'd like to see a different entry point for Coroutines (and other MonoBehaviour functionality).
     
  8. Fehr

    Fehr

    Joined:
    Mar 3, 2011
    Posts:
    23
    Hey guys, I came across this thread and thought I'd share my solution with you all: View attachment $Coroutiner.cs

    This little script allows you to start a coroutine from basically anywhere by calling Coroutiner.StartCoroutine(...). Hopefully you'll all find it as useful as I have.

    Enjoy!