Error stating Coroutines can only be started from a MonoBehaviour

Started by
28 comments, last by ethancodes 5 years, 9 months ago

I have a BasePickUp : MonoBehaviour, ICloneable class. Then I have subclasses such as FastBall : BasePickUp. My FastBall class looks like this:


public class FastBall : BasePickUp {

	public override void ActivatePickUp ()
	{
		mainBall.GetComponent<Ball>().Speed = 2.0f; //increase ball speed...
		StartCoroutine(timer); //...set time that power up is active
	}

}

So I'm getting an error because of the StartCoroutine I guess, but I'm wondering if there is a way around this because I would really like to keep my current layout.

Advertisement

As a workaround, you could create a class that is derived from MonoBehavior and have that create your coroutine for you. But really that's a workaround for a bad solution in the first place.

Unity Coroutines are iterator functions called after the active object's Update() is run. Coroutines are great for spreading a bit of processing across frames or queuing a delayed action. Timer comparisons like you briefly describe are often best set as time stamps which you can directly test against yourself in your object's Update() function. Timers like you describe have no need to be a coroutine at all, and they tend to have subtle bugs with the coroutine scheduler which can run even when the object itself is disabled.  if you've got a collection of them you can iterate across the collection and compare them against the current time..

 

So I had used the Coroutine because I need the pick up to be activated, run for a certain amount of time, and then destroy itself. So if I'm understanding you correctly, what I should do is get rid of the coroutine and put a timer function in there (between the activate and destroy) and it will just iterate through the timer function for however long I set it to. Is that correct? If so do you have an example of a timer function? All the ones that I have looked up are to fire something every Tick of the Timer function. But that isn't what I want. I basically want it to wait until the timer is done, then continue with the method the timer is in.

Not a specific timer function. Changing a bit of functionality or activating an object's feature is often done during Update().

For the timer that I see in the code, during update() check if the time has passed. Once time has passed you take the action.  There is probably no need for a specialized function.

If you have a separate game object being created, and if you need deferred processing on itself such as self-destruction, that is different functionality than what I saw in the code above. For that, sure, a coroutine on the new game object is appropriate, have the object register the co-routine on itself when it is created.

I'm not sure I entirely understand what you're saying. I'll try to explain better what is I'm doing. I have a pickup game object moving around in game space. If the pick up gets hit by the ball or paddle (this is an arkanoid style game btw) it creates a clone of itself (script only, not the gameobject) and adds it to the pickup queue. It then destroys itself. The instance in the queue calls ActivatePickUp(). For the FastBall, the ActivatePickUp means we find the ball in the game world, modify its speed, and then activate the coroutine so that it only stays at this increased speed until the coroutine is finished. Once it is done and ActivatePickUp() has been fully executed control is given back to a PickUpManager, which is where the PickUpQueue list is, and destroys the FastBall object, and removes that index from the list. So, if I'm understanding you correctly, it sounds like I need a coroutine because I don't want control to be given back to the PickUpManager too soon, otherwise it will destroy my FastBall object before I want it to. Am I correct?

3 hours ago, ethancodes said:

I'm not sure I entirely understand what you're saying.

I think what @frob is saying you can just add a float to the mono script that gets reduced every update() tick.

For example:


float Time = 10;

void Update(){
  
  if (Time > 0){
    Time -= Time.deltaTime;
  }else{
    //Do something when time is 0 or less
  }
}

This way you don't need a class and can just use the mono class you have

Unfortunately I don't think that will work for what I'm trying to do. Unless i just completely don't understand what you mean, but I tried setting something up like this and it didn't make any sense to me and I couldn't find a way to make it work that way.

Classes derived from MonoBehaviour can start coroutines (and classes deriving from those classes, etc).

In your original post, you say that you have a class derived from a class derived from a MonoBehaviour.  Therefore StartCoroutine should work.

Post your actual error message.

You probably need to do something that looks like this:


public class FastBall : BasePickUp
{
	public override void ActivatePickUp ()
	{
		mainBall.GetComponent<Ball>().Speed = 2.0f; //increase ball speed...
		StartCoroutine(DoStuff); //...set time that power up is active
	}
	
    IEnumerator DoStuff()
    {
        // do something here.
		yield return new WaitForSeconds(5);
        // do something else here.
	}
}

 

That's exactly what I thought. Here is the error:

 

ArgumentException: Coroutines can only be started from a MonoBehaviour
UnityEngine.MonoBehaviour.StartCoroutine (IEnumerator routine) (at C:/buildslave/unity/build/artifacts/generated/common/runtime/MonoBehaviourBindings.gen.cs:62)
BasePickUp.StartCoroutine (Int32 delay) (at Assets/Scripts/BasePickUp.cs:86)
FastBall.ActivatePickUp () (at Assets/Scripts/FastBall.cs:11)
PickUpManager.Update () (at Assets/Scripts/PickUpManager.cs:29)
 

3 minutes ago, ethancodes said:

That's exactly what I thought. Here is the error:

BasePickUp.StartCoroutine (Int32 delay) (at Assets/Scripts/BasePickUp.cs:86)

Paste that function.  It's the one with a problem.

Well, either that or you used 'new' to make an instance of your MonoBehaviour, in which case it won't work because it hasn't been instantiated properly.  You have to use AddComponent on a gameobject to get a valid MonoBehaviour.

This topic is closed to new replies.

Advertisement