yield return someMonoBehaviourScript.StartCoroutine(name, parameter);
If I call:
someMonoBehaviourScript.StopCoroutine(name);
The coroutine “name” stops properly, but the yield never returns
Long version:
Consider the two scripts at the bottom of this post.
Human_Behaviour.cs is added to a GameObject
The problem is that StartSingleYieldingCoroutine never returns from the:
“yield return callingScript.StartCoroutine(name, parameter);”
line. BUT Test() coroutine is properly STOPPED and RESTARTED.
Functionally, it’s not a problem, but I guess every call to StartSingleYieldingCoroutine will leave a junk entry in the stack while the application is running.
StartSingleYieldingCoroutine (INSTANCE 2) stops Test
StartSingleYieldingCoroutine (INSTANCE 1) naturally returns from “yield return callingScript.StartCoroutine” since Test is now stopped. <<< THIS NEVER HAPPENS
StartSingleYieldingCoroutine (INSTANCE 2) starts Test
etc…
As additional information, if I remove the “callingScript.StopCoroutine(name);” line from StartSingleYieldingCoroutine, the “yield return callingScript.StartCoroutine…” returns properly.
Please, keep in mind the posted code is just a very simplified version of what I’m trying to do, to make the problem I’m facing as evident as possible. I KNOW that if I were trying to just accomplish restarting Test() on every frame, there are easier ways to do so.
CoroutineHandler.cs is a handler that makes using coroutines easier, and I’m trying to make this work in this particular way since it will help simplify this classes’ use.
Human_Behaviour.cs
using UnityEngine;
using System;
using System.Collections;
using System.Collections.Generic;
public class Human_Behaviour : MonoBehaviour {
CoroutineHandler coroutineHandler = new CoroutineHandler();
void Awake(){
coroutineHandler.Initialize(this);
}
void Update () {
coroutineHandler.StartSingleCoroutine("Test", null, true);
}
int debugTest = 0;
int debugStartRun = 0;
IEnumerator Test(){
debugStartRun++;
yield return new WaitForSeconds(5F);
++debugTest;
}
}
CoroutineHandler.cs
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class CoroutineHandler {
MonoBehaviour callingScript;
public void Initialize(MonoBehaviour script){
callingScript = script;
}
public void StartSingleCoroutine(string name, object parameter = null, bool stopOtherInstance = false){
// Take advantage of the calling script's MonoBehaviour to run StartSingleYieldingCoroutine
callingScript.StartCoroutine(StartSingleYieldingCoroutine(name, parameter, stopOtherInstance));
}
public IEnumerator StartSingleYieldingCoroutine(string name, object parameter = null, bool stopOtherInstance = false){
callingScript.StopCoroutine(name);
yield return callingScript.StartCoroutine(name, parameter);
debug.Log("NEVER GETS HERE!!!");
}
}
You should avoid executing named coroutine in that case. In 4.6 they added a StopCoroutine(Coroutine) function that should be nicer to use. (Use a Coroutine object rather than a string).
Executing a coroutine in the Update function doesn’t seems the best idea. Update is launched each frame, that mean you try to exec/Stop a coroutine each frame. Your coroutine can’t finish, it wait for 5 sec while a frame is 1/30 sec.
For your test i will suggest you to add keyInput in ur update, in order to exec your coroutine statement only when a key is pressed.
Woha! I did not know that, thanks! I’m still on 4.5.
This is just for testing purposes, and that is exactly the intended behavior. If the coroutine were to end, I’d have known StopCoroutine wasn’t working.
I’d still like to see a way to have this working in 4.5 without having to resort to upgrading to have access to the new StopCoroutine. I’ve read some people have seen performance degrade in the new version.
Definitely not the case. Each StartCoroutine is called inside the MonoBehaviour (callingScript) where the coroutines are located. As I mentioned, the coroutine does run properly (as evidenced by the “debugStartRun” variable changing).
Y’know you can have multiple versions of Unity on your computer, so you can test performance yourself pretty easily. I haven’t noticed any performance issues that’d make me give up uGUI and various other nifty upgrades.
Personally I wrote my own implementation for Coroutines (that works in tandem with unity Coroutines), that gives cancel/pause capabilities, as well as implementing your own custom yield instructions.
Sorry I didn’t respond sooner, I’m not getting e-mail notifications for some reason.
As I mentioned, this is part of a Coroutine handling class that is already working great in my project, with the upside that it has almost no impact on garbage or performance. The problem itself is the result of me trying to make the class completely seamless; as it is, I have to call a method at the end of every Coroutine to inform the class that is has ended naturally… no bueno.
Nope; unfortunately, I found that if you StopCoroutine on a Coroutine that is being yielded, it’ll keep yielding indefinitely.
I’ve found no workaround either, so it’s one thing I keep my eyes peeled for. I can’t say how it fares on newer Unity versions, since I’m still on 4.5.
ok, thanks, i did a small workaround, not as capable as my initial idea but breakable from the outside. But you can only break/transit to another State once per frame.
Dumbed down example if anyone is interested:
using UnityEngine;
using System.Collections;
public class wasd : MonoBehaviour {
public enum State { Approach,BackOff,Charge,MeleeCombat,RangedCombat,Focus,Jumping,Melee,Patrol,Staggered,Unalarmed,Commanded,Dead,None }
bool statebreak = false;
private IEnumerator FSM(){
while (true){
switch (state) {
case State.Approach: yield return StartCoroutine( StateApproach() );break;
case State.BackOff: yield return StartCoroutine( StateBackOff() );break;
case State.Charge: yield return StartCoroutine( StateCharge() );break;
case State.MeleeCombat: yield return StartCoroutine( StateMeleeCombat() );break;
case State.Melee: yield return StartCoroutine( StateMelee() );break;
case State.Patrol: yield return StartCoroutine( StatePatrol() );break;
case State.Staggered: yield return StartCoroutine( StateStaggered() );break;
case State.Unalarmed: yield return StartCoroutine( StateUnalarmed() );break;
case State.Commanded: yield return StartCoroutine( StateCommanded() );break;
case State.Dead: yield return StartCoroutine( StateDead() );yield break; break;
default: yield return null; break;
}
}
}
private IEnumerator StatePatrol(){
//coroutine local variables here
while(true){
//stuff happens here, also state Transition are declared here
//End block of any State
yield return new WaitForEndOfFrame(); //end of current frame, all Coroutines and Update()/LateUpdate() calls that may set stateBreak have finished by now
if(stateBreak){ stateBreak = false; yield break; } //break State, the new "state" is already set by the function that has set "stateBreak to true"
yield return null; //continue after next Update()
}
}
public void Hit(int damage){
health -= damage;
state = State.Staggered;
stateBreak = true; //current state will be broken at the end of this Frame, next State will be running after Update() of the next frame
}
//Info, Unity Timing: 1.Update - 2.Coroutines - 3.LateUpdate, then WaitForEndOfFrame();
}
I usually don’t promote my own Asset Store packages. In this case, it seems to be fit perfectly, especially because I can’t see another direct solution.
I was having some comparable issues and created a custom coroutine solution to overcome this and other issues. You can find the link in my signature.