- Home /
IEnumerator Stalling Or Stopping
Hi Community!
[Question Solved: See answer comments for demonstrated solution]
I'm truly stumped by this issue. When I supply my IEnumerator yield's WaitForSeconds parameter with 0.1f-0.9f, it works fine. However, when I supply it with an int from another script, hangs up on the WaitForSeconds bit, and just doesn't move past that line of code. I have Debugged and found that it is getting an int (1) as a parameter. Interestingly, if I hard-code the value of 1 into the WaitForSeconds parameter, it does the same thing.
Here's my code:
public IEnumerator Slow(int projLevel) //Accept the level of the projectile as parameter
{
if (slowed) yield break; //If we're already slowed, break out
slowed = true; //Let the script know we're slowed
enemy.speed /= projLevel+1; //Slow down this enemy
yield return new WaitForSeconds(projLevel); //Wait for supplied time (this is where it stops)
enemy.speed *= projLevel+1; //Speed up our enemy to normal speed
slowed = false; //Let the script know we're no longer slowed
}
Still haven't found my issue with this. I tried reformatting my IEnumerator to eli$$anonymous$$ate the yield break; portion since I figured I might be breaking out somehow, but still - none of the characters get past line 5.
Are you sure that you don't stop the coroutine somewhere? Coroutines are stopped / aborted whenever:
you call StopAllCoroutines on the monobehaviour that runs the coroutine.
you call StopCoroutine with a valid handle to that coroutine.
the object that runs the coroutine is disabled or destroyed.
So who's starting the coroutine and on which object is the coroutine running?
Try WaitForSeconds(projLevel * 1.0f) to confirm the issue has to do with int or float, and not something else.
@Bunny83 Thanks for the reply! So there are two components to this. It looks like this:
Projectile is launched with a projectile script component attached
Projectile hits enemy
Enemy detects OnTriggerEnter and reads the type of projectile via boolean on the projectile script (slow, freeze, ignite, damage, etc.)
Enemy starts his/her own coroutine (IEnumerator Slow, for example) based on the data received.
Of course - if the enemy is below 1 health it destroys itself.
The projectile does destroy itself 1 second after hitting the enemy - so perhaps there's a reference lost there? It doesn't make sense that that lost reference would halt a coroutine on another gameObject (the enemy) though.
@Rostam24 I've tried static typecasting, and a number of different solutions including multiplying by a float - and they all give the same result. They only get past the yield if they are less than 1 second. :/ Thanks for the input though!
from what i see waitForSeconds(float) has no overloaded functions so I dont htink it accepts int you could try
float waitTime = projLevel;
yield return new waitForSeconds(waitTime);
you could test this by hard coding a 1f into it as a 1 is an int but 1f is a float
I actually set my parameter projLevel to a float ins$$anonymous$$d of an int in the method parameter - so even if it didn't accept an int, it would be implicitly converted before it was stored (unless I am fundamentally wrong in unboxing theory here).
In other words:
public IEnumerator Slow(float projLevel) //Accept the level of the projectile as parameter
But also, even if WaitForSeconds didn't accept an int as an argument, wouldn't I get a console error and not just a complete abortion of the enumeration?
Answer by Bunny83 · Sep 28, 2015 at 01:10 AM
Are you sure you start the coroutine on the enemy and not the bullet which might get destroyed the next moment? It doesn't depend where the IEnumerator method is defined. It only matters which StartCoroutine method you used to start the coroutine. StartCoroutine is a method of the MonoBehaviour class. The coroutine runs on the MonoBehaviour instance on which you use StartCoroutine.
So without more details about your actual setup we can't help you any further.
@Runalotski @Rostam24:
The data type "int" can be implicitly converted to "float", so that's not a problem here.
Thanks @Bunny83. It appears you got to it a full 60 seconds before @Bored$$anonymous$$ormon, but you both supplied the same information. I incorrectly assumed that the caller of the coroutine was arbitrary and that the actual coroutine lived inside the same script as the IEnumerator was located - but it appears it actually lives inside the caller. Strange and counter-intuitive if you ask me, but I guess that's just one of those things :)
Thanks for the help!
Its actually normal and useful behaviour once you think about it for a while. You just have to get used to the idea.
For some instances perhaps. The reason I ran into this issue in the first place was that I was trying to eli$$anonymous$$ate unnecessary lines of code - which this stipulation seems to discourage. Are there specific advantages to having to write a whole new method just to start a coroutine and not worry about disposing of the caller? Seems like you're having to think backwards ins$$anonymous$$d of forwards as far as code flow. Perhaps a question for the forums I'll admit - but if there's a simple answer lay it on me :P
A lot people get the wrong picture of what a coroutine actually is. First of all it's not a method, it's an object and the actual "coroutine method" just creates that object which implements the IEnumerator interface. Unity now has implemented a scheduler which can "iterate" those objects. You have to pass the object to StartCoroutine which will store the reference to the object internally so the scheduler can actually run / iterate the "object".
You can even declare coroutines in non $$anonymous$$onoBehaviour classes, but to start a coroutine you have to use StartCoroutine which is a method of $$anonymous$$onoBehaviour.
You can run a coroutine on an arbitrary $$anonymous$$onoBehaviour as long as you have a reference to it.
Example:
public class SomeBehaviour : $$anonymous$$onoBehaviour
{
public IEnumerator $$anonymous$$yCoroutine()
{
yield return null;
Debug.Log("done");
}
}
public class SomeOtherClass : $$anonymous$$onoBehaviour
{
public SomeBehaviour other;
void Start()
{
// this will run the coroutine on this monoBehaviour since we use "this.StartCoroutine("
StartCoroutine(other.$$anonymous$$yCoroutine());
// this will run the coroutine on the "other" monobehaviour
other.StartCoroutine(other.$$anonymous$$yCoroutine());
}
}
If you want to start a coroutine of another class and have that coroutine run on that class as well, it's easier to provide a calling method that starts the coroutine.
public class SomeBehaviour : $$anonymous$$onoBehaviour
{
private IEnumerator $$anonymous$$yCoroutine()
{
yield return null;
Debug.Log("done");
}
public Start$$anonymous$$yCoroutine()
{
StartCoroutine($$anonymous$$yCoroutine());
}
}
// [...]
other.Start$$anonymous$$yCoroutine()
To those who may come across this issue:
Here is what my code looked like before:
Script 1 (Projectile That Gets Destroyed At Arbitrary Time)
Script2 s2 = TheTargetWeCollidedWith.GetComponent<Script2>();
StartCoroutine(s2.ExampleCoroutine(ExampleInt));
Script 2 (Target that houses the IEnumerator)
IEnumerator ExampleCoroutine(int SecondsToWait){
yield return new WaitForSeconds(SecondsToWait);
}
Here is what my code looked like after (Corrected):
Script 1 (Projectile That Gets Destroyed At Arbitrary Time)
Script2 s2 = TheTargetWeCollidedWith.GetComponent<Script2>();
s2.Call$$anonymous$$ethodToCallCoroutine(ExampleInt());
Script 2 (Target that houses the IEnumerator)
public void Call$$anonymous$$ethodToCallCoroutine(int waitTime){
StartCoroutine(Wait(waitTime));
}
IEnumerator Wait(int SecondsToWait){
yield return new WaitForSeconds(SecondsToWait);
}
Answer by Kiwasi · Sep 28, 2015 at 01:11 AM
A coroutine stops when the MonoBehaviour that calls StartCoroutine is destroyed. So if your call to StartCoroutine comes from the bullet script, you will stop the coroutine running on the other object when the bullet is destroyed.
Solve this by calling StartCoroutine on the target Component. Either by implementing a wrapper method, or by calling someOtherMonobehaviour.StartCoroutine().
To emphasise: It does not matter where the IEnumerator is located. It matters where StartCoroutine is called.