public IEnumerator TurnEnemy(float waitForTurning) {
Debug.Log("Start");
justTurned = true;
yield return new WaitForSeconds(waitForTurning);
transform.localScale = tempEnemyScale;
Debug.Log("Stop");
StopCoroutine(turnEnemyCoroutine);
yield break;
I also randomly (after reading something around) added that “yield break” but watching at that Debug Start and Stop keeps running and … of course my trick does not work!
My idea was to wait for 2 seconds then turn enemy stopping this routine waiting for the next time!
// field declaration
private Coroutine turnEnemyCo;
// when you start the coroutine
turnEnemyCo = StartCoroutine( TurnEnemy( 2f ) );
// elsewhere when you want to stop the coroutine
if( turnEnemyCo != null ) StopCoroutine( turnEnemyCo );
Thank you Madgvox!
I changed my script as you suggested but It does not stop! On starting it works … enemy waits two seconds then he turns, but … then it turns as soon I change my position (left or right of him)
Sometimes it might be handy to have an ability to perform stop actions from inside of coroutine. For example, you might want stop some sfx/vfx created inside that coroutine before finishing it. You cant use try…finally within coroutine. Here’s another way to stop coroutines
It stops if you say stop. But! If you start playing sound or animation in that coroutine, that sound or animation will not stop when you call StopCoroutine. The code I posted allows you to stop something else when coroutine is about to exit.
I suggest avoiding coroutines when possible. They will give more headache to new developers than it is worth.
Coroutines are not magic tools. Most things can be achieved without them. Don’t fall into trap, of spamming coroutines, whenever possible.
@Antypodish Your suggestion isn’t quite a good one. You suggest “avoid coroutines when possible” without giving any details, reasoning, proper evidence, or at least suggesting an alternative. Even three years later, your reply brings no value for many people that have googled to this point, including myself.
Well, how do you know if the suggestion is a good one or not without understanding the reason? ^^. Yes, he could have explained why, but than this post would be a lot longer because it’s not as simple as it may seem.
Don’t get me wrong, I do like coroutines a lot and I use them a lot, so I don’t think the general advice to avoid them at all cost as some people put is a good advice. Though he specifically said for “new developers” and I partially agree here. It’s very easy to use coroutines the wrong way if you don’t understand how they work. In this example here the OP most likely starts a new coroutine every frame as long as the condition is true. Manually stopping a coroutine at the end of the coroutine is also completely pointless.
If you want to know more about how coroutines work, I can suggest you read through my coroutine crash course. Reading it carefully should tell you almost everything there is to know about coroutines.
The reasons why beginners may want to avoid using coroutines is:
They may not understand the basic concept of the coroutine scheduler. Some think they have to call StartCoroutine every frame to keep it going, this is not the case. Whenever you call StartCoroutine you start a new coroutine.
Many do not understand the relationship between the concept of a coroutine and the IEnumerator statemachine. You can not simply re-use an IEnumerator instance as it represents a statemachine. Passing the same IEnumerator instance two or more times to StartCoroutine would result in pure chaos as the scheduler would advance the statemachine two times seperately from each other.
Generator methods (methods with a yield statement in them) do allocate memory as they actually create a statemachine object. That’s why it’s not a good idea to call StartCoroutine every frame or quite often in general. Coroutines are most memory friendly if they just keep running.
Coroutines seem like they work like independent threads of execution, but they are not. Coroutines run synchronously but can be paused at the yield statements and resumed at a later point in time. This later point is determined by the coroutine scheduler based on the value that was yielded.
So the code provided by the OP clearly shows he didn’t know anything about coroutines at all and therefore got confused how to use them. Though from the OP it’s not even clear what this coroutine should do, so it’s quite difficult to suggest an “alternative” when the actual question is not clear. This is again kind of an XY problem which appears quite often here on the forum.
He talks about delaying the turning of his enemy. It’s not clear what he actually means by that and based on what event that turning should happen. Since the coroutine sets the localScale and doesn’t seem to have anything to do with actual turning in the sense or rotation, I would guess he talks about a 2d game and he wants to mirror the character so its facing in the opposite direction. To me that sounds more like he wants the enemy to only evaluate a new direction to move every couple of seconds so when it went past the player it does not immediately follow the player. Though this is all just guesswork based on the fragments we see. The current approach of checking if the enemy went past the player every frame and somehow delaying the reaction is just a bad approach to begin with. What if that state changes within the timeframe? Maybe even a couple of times. Fos such mechanics it makes more sense to simply evaluate where to go in a fix timeinterval. This would not only reduce the amount of checks you have to do each frame, but also gets rid of all those issues. This is essentially quite similar how the AI of minecraft zombies work. They choose a target and move towards it and it takes a bit of time until they change direction when the target moved away.
It’s still possible to use a coroutine for this. As I mentioned above, an endless running coroutine that is started once in Start. Though it’s also possible to use InvokeRepeating or just an ordinary timer (float variable that is incremented or decremented each frame by deltaTime).
I hope this has cleared up a bit of the confusion and why the best answer is often: Well, it’s complicated.