- Home /
StartCoroutine can't be called from a GameObject. Am I late to this party?
I recently realized that I can't call StartCoroutine from a gameObject. Instead, I need a reference to a monobehaviour that is attached to the desired game object.
When was this API changed made? I particularly dislike it for two main reasons. 1) Coroutines are bound to gameObjects, not monobehaviours. Disabling the monobehaviour that started the coroutine won't affect the coroutine at all. Disabling the gameObject does.
2) If I want to start a coroutine from a reference to a gameObject other than the current, I have to either a) hope that gameObject already has a monobehaviour, or B) add a new dummy monobehaviour myself. This can be annoying if the referenced gameObject isn't always the same. Creating mono behaviours may trigger garbage collection.
So, why doesnt the API just let us do GameObject go; go.StartCoroutine( coroutine()) ;
Having to find (or create) a mono behaviour on the reference gameObject is almost as nonsensical as having to find (or create) a monobehaviour to activate or deactivate a gameobject.
Wait...so, you started out by saying an API change was made. Now you're taking that back - you never meant to say that StartCoroutine used to be a gameObject member function?
You're new Q is just asking why coroutines always have to be started by and owned by monobehaviours? I'm only asking because it seems odd to specifically claim the API was changed without a reason. Do you have old code that used to run, and now runs wrong?
A partial reason is that a coroutine can handle several gameObjects, or specific components. Having it "owned by" one gameObject wouldn't help with those cases. It would also mess-up how StopCoroutine works (when Unity was written, using StopAllCoroutines, which worked on your coroutines only, was often the only way to solve some problems.)
It's not a new question really, it's the "what they were even trying to achieve with all this" part of things. And I think it's clear now (if you read the whole page) that with the API change bit they were just misremembering a complex system they'd put together in the past.
I think it's all good now, both sides of things have been dealt with by Bunny83 and my answers. No?
Answer by Bonfire-Boy · Oct 21, 2016 at 01:11 PM
I'm not sure what you mean by "can't call StartCoroutine from a gameObject". Of course you can. Yes, the GameObject has to have a MonoBehaviour in which to do it. "Having to create a MonoBehaviour on the gameObject," doesn't make sense to me. Without a MonoBehaviour, a GameObject has no behaviour and can only be manipulated by other behaviours. MonoBehaviours are how we get GameObjects to do stuff, it's hardly a restriction.
Or is it just that you've just got the code wrong? From one MB you can start a coroutine in another, but you do it using
ComponentType c = go.GetComponent<ComponentType>();
StartCoroutine(c.coroutine());
Being able to do this gives us more control options (the coroutine dies with the calling object).
I appreciate I may be missing something. If so, maybe a bit more code and a fuller description of your intended set-up might help.
[Edit] See comments below for further discussion of the situation including answers to my questions and resolution.
Also, while this accepted answer and thread deals with the "what are you even trying to do here and why?" side of things, and resolves it, the other answer by Bunny83 correctly addresses the "change of API" aspect of the question.
I need a monobehaviour attached to a gameObject in order to start a coroutine. This means having a reference to the gameObject is not enough. I cannot do the following:
GameObject go = GameObject.Find("$$anonymous$$yObj" ) ;
//Error in the next line. StartCoroutine isnt a member of GameObject.
go.StartCoroutine( someIEnumeratorFunc()) ;
The current way to accomplish this is:
GameObject go = GameObject.Find("$$anonymous$$yObj" ) ;
$$anonymous$$onoBehaviour dummy = go.AddComponent();
dummy.StartCoroutine(SomeIEnumeratorFunc());
See how I need a dummy? Now, I can destroy the dummy right after and the coroutine is left untouched because Coroutines are bound to gameObjects, not to the monobehaviours from which they are created. In my example, the monobehaviour serves no purpose other than starting the coroutine, which once more isn't even bound to the monobehaviour.
This seems silly to me, if Coroutines are bound to a gameObject, the StartCoroutine should be a member of GameObject.
Ok, I think I'm a bit closer to getting it. But there's still a lot of code missing as far as I'm concerned. For example, where do you want to put that code you're showing us? And which $$anonymous$$onoBehaviour is the SomeIEnumeratorFunc coroutine defined in?
A use case would probably help with understanding too.
Sorry to have to keep asking questions, I'm genuinely curious as to what the issue is. I don't think I've ever felt that the coroutine system is anything but quite straightforward. There have been times when I've needed to do the coroutine iteration myself to add a bit of functionality, but I'm happy with that: I don't think the engine should be providing everything we could ever need - that would make the engine bloated.
No apology necessary! It's natural to start with a $$anonymous$$imal description of something like this. If someone can pick up on what you're getting at straight way it'd be a shame to have wasted lots of time on unnecessary detail. And this way you know you're targeting my lack of understanding (which are you doing very well).
I do think I can see what you're getting at now. But I'm struggling to think of a concrete use case, I've certainly never felt any need to create a dummy component in that way. If I want a coroutine to be attached to a particular GameObject then it feels natural to me to have it in a component on that gameobject. Conceptually, it's part of the behaviour of that object, rather than the behaviour of the manager. If I have a coroutine in a manager class, it's generally operating on the manager's data, not on that of a specific object that it's managing.
Put another way: you've got a coroutine that you want to continue when the manager is destroyed. For me, that fact in itself makes it feel inappropriate for that coroutine to be defined in the manager, since it cannot use the manager's data.
So I think in that situation I would define the coroutine in some $$anonymous$$onoBehaviour class, which might contain nothing else apart from that coroutine and a function to start it. In the manager, I would add that component to the "outside object", then get that component to start its coroutine. And I'd be quite happy to do so. Yes, it might be a pretty $$anonymous$$imal $$anonymous$$onoBehaviour class, but that's fine. The behaviour is properly encapsulated and handled by such a class, rather than by the manager.
Interesting stuff, though.
Answer by Bunny83 · Oct 21, 2016 at 03:14 PM
There hasn't been a change of the API. You were never able to run a coroutine on a GameObject. Coroutines run on the MonoBehaviour instance of which you use StartCoroutine. Your conclusions based on your observations are actually wrong.
First of all the "enabled" state of a MonoBehaviour only controls if FixedUpdate, Update, LateUpdate, OnGUI are called or not. All other callbacks still work fine when the script is "disabled". That includes coroutines. So disabling a MonoBehaviour won't have an effect on running coroutines and won't prevent you from starting new coroutines.
Try running a coroutine on a MonoBehaviour instance and destroy only the MonoBehaviour instance. The coroutine will be removed as well, even the gameobject is still there.
Deactivating a GameObject on the other hand will "remove" the gameobject from the whole scene. Not in the sense of wiping out the memory but it will have no interactions with any other gameobject. From the scene's point of view the gameobject doesn't exist. Deactivated gameobjects won't receive any messages by Unity and it does cancel running coroutines. Also if you try starting a coroutine on a script of a deactivated gameobject you will get an error that says:
Coroutine couldn't be started because the the game object 'XXX' is inactive!
So nothing has changed and it still works the same as in Untiy version 2.6 (where i started using Unity).
GameObjects are just containers. They don't have any functionality themselfs. It's the components which adds behaviour and other things. A GameObject is only responsible for providing:
a container for Components
provide fundamental state informations of the object such as: name, tag, layer, containing scene, static flag and the active state.
An API to access and add components
An API to send messages to attached components.
A Gameobject doesn't even have a position. That's handled by the Transform component (which however is always with a GameObject).
See my exchange with Bonfire-boy and you'll begin to get it. You should read that exchange, see the code there and begin to get it.
You could have not understood my question less (but you are the second one that doesn't so it was probably me not wording it right or just having a very unusual use case that rings no bells)
Be fair mate, Bunny does address the "change of API" question (which I didn't). It's just it turned out to be more of an aside than anything else.
There was a change. This used to be possible in older versions. I encountered this problem by not being able to do it one day. Anyone with a pre 4.6 version may confirm? I don't think my brain is making it up and fooling me. (Can't rule it out 100%)
No, you should actually verify your claims which you clearly haven't tested at all. When you add a dummy monobehaviour, start a coroutine and destroy the monobehaviour the coroutine will be gone!!!. I suggest you read my answer carefully.
Again, coroutines do not run on GameObjects they actually run on $$anonymous$$onoBehaviour instances. If you have two $$anonymous$$onoBehaviours attached to the same gameobject and each is running some coroutines, when you call StopAllCoroutines on one of the $$anonymous$$onoBehaviours, only the coroutines which run on that $$anonymous$$onoBehaviour instance will be stopped and not "all on the gameobject".
I worked long enough with Unity and coroutines. I know them inside out and just to be sure as i were writing my answer i actually did some checks in my UnityAnswersTestProject (which i have always open in the background).
Your question asked for "why did the API change" but it didn't change, it always worked that way. The GameObject class never had a StartCoroutine method you always needed and still need a $$anonymous$$onoBehaviour instance to run your coroutine on.