- Home /
Cannot duplicate Coroutines and Yield Instruction in the Unity Manual... Is the manual WRONG?
I cannot seem to get the results as explained in the examples in the Unity Manual http://docs.unity3d.com/Manual/Coroutines.html- particularly the last example with DoCheck() and ProximityCheck().
The Manual says: "Many tasks in a game need to be carried out periodically and the most obvious way to do this is to include them in the Update function. However, this function will typically be called many times per second. When a task doesn’t need to be repeated quite so frequently, you can put it in a coroutine to get an update regularly but not every single frame. An example of this might be an alarm that warns the player if an enemy is nearby."
Here is a simple code that should, according to the manual then, print and advance the variable STEP every 0.1 seconds, however, the output shows that the coroutine and the content of the 'for' loop is actually called every frame, and the value of STEP is not sequentially reduced in the output every 0.1 seconds. Moreover, it appears the routine is duplicated each time it is called to wait, only to be resumed 01s later. This means STEP is 5 at the start, and after STEP reduced from say 4 to 3 at a certain time, STEP then goes back to being 5 (On my machine STEP sequence 5, 4, 3, 3, 5 happened with STEP back to 5 at Time.time = 0.315489s.). This back and forth keeps happening as the old yield calls catch up with more recent ones.
I tried it both in C# and in JS and in neither case can I duplicate what the manual suggests in http://docs.unity3d.com/Manual/Coroutines.html.
Any ideas?
C#:
public class TestCoroutinesinC : MonoBehaviour {
private bool TEST = false;
void Update () {
if (!TEST) {
StartCoroutine("TestCoroutine");
}
if (Time.time >= 0.6) TEST = true;
}
IEnumerator TestCoroutine() {
for (float STEP = 5; STEP >= 0; STEP -= 1) {
Debug.Log ("The value of STEP: " + STEP + " at time: " + Time.time);
yield return new WaitForSeconds(0.1f);
}
}
}
My output: (partial)
The value of f: 5 at time: 0
The value of f: 5 at time: 0.02
The value of f: 5 at time: 0.19133
The value of f: 4 at time: 0.19133
The value of f: 4 at time: 0.19133
The value of f: 5 at time: 0.248696
The value of f: 5 at time: 0.265611
The value of f: 5 at time: 0.282157
The value of f: 5 at time: 0.298827
The value of f: 4 at time: 0.298827
The value of f: 3 at time: 0.298827
The value of f: 3 at time: 0.298827
The value of f: 5 at time: 0.315489
...this output continues on my machine up until...
The value of f: 5 at time: 0.315489
Answer by GerardQ · Feb 22, 2016 at 01:49 PM
The OP is correct - the manual is confusing and wrong on that page.
maccabe and flashfame: you are both right as well, however you missed the OP's point. He was putting the last example in practice, not the Fade() one. Both your solutions do not apply to that example, because the example is about a function that must be called after each wait, not one that is called only once.
Confusing perhaps, but not wrong.
In the last example, the documentation explains how to use a coroutine to reduce the amount of times you are calling a custom function. They demonstrate the code for the custom function and the coroutine, but they don't show you how to start the coroutine.
To complete their example:
function ProximityCheck() {
for (int i = 0; i < enemies.Length; i++) {
if (Vector3.Distance(transform.position, enemies[i].transform.position) < dangerDistance) {
return true;
}
}
return false;
}
IEnumerator DoCheck() {
for(;;) {
ProximityCheck;
yield return new WaitForSeconds(.1f);
}
}
void Update() {
if (!TEST) {
StartCoroutine("DoCheck");
TEST = true;
}
}
This will start the DoCheck coroutine on the first frame, which will then run until you tell it to stop. The coroutine will yield every 0.1 seconds indefinitely.
Yes, flashframe, that is the best explanation. Thank you.
I still feel that the manual is wrong and confusing.
coroutines may be easy to understand for programmers, but in this case leaving out important information in a manual doesn't help.
I think the manual ought to include the Update code that you added, state that a coroutine should only be called once in an update function and also your last sentence: "This will start the DoCheck coroutine on the first frame, which will then run until you tell it to stop. The coroutine will yield every 0.1 seconds indefinitely."
The manual didn't say all that, flashframe. Your code and your description may be correct, but none of that is in the manual. I agree with dexon that it is too confusing and the manual is misleading.
Answer by DeXoN · Feb 22, 2016 at 04:17 PM
Yeah, it's misleading. It neglects to mention pertinent info.
Unless off course the Unity manual is written for people who already know how everything works. As far as I know, that is not the purpose of a manual. It is written for people to look up things that they don't yet understand.
Answer by maccabbe · Feb 22, 2016 at 12:03 PM
Update() is called every frame so your code
void Update () {
if (!TEST) {
StartCoroutine("TestCoroutine");
}
if (Time.time >= 0.6) TEST = true;
}
is starting a new coroutine every frame for 0.6 seconds.
The code shown in the docs
void Update () {
if (Input.GetKeyDown("f")) {
StartCoroutine("Fade");
}
}
only starts a coroutine on the frame when a button is pressed.
I'm guessing you meant to start a new coroutine every 0.6 seconds (not by a coroutine) in which case the following will work
float time = 0;
void Update () {
time += Time.deltaTime;
if(time >= 0.6f)
{
StartCoroutine("TestCoroutine");
time -= 0.6f;
}
}
If you do it like you suggest maccabe, then you don't have to use a coroutine just to reduce the frequency of the call. You can call a normal function.
I understand the Input.Get$$anonymous$$eyDown function. That is not the problem. That portion of the example so happens to work because Get$$anonymous$$eyDown returns true only once as a key is pressed.
The problem is the claim of the last section of the manual page about the function DoCheck().
The $$anonymous$$anual says: "$$anonymous$$any tasks in a game need to be carried out periodically and the most obvious way to do this is to include them in the Update function. However, this function will typically be called many times per second. When a task doesn’t need to be repeated quite so frequently, you can put it in a coroutine to get an update regularly but not every single frame. An example of this might be an alarm that warns the player if an enemy is nearby." and then "This would greatly reduce the number of checks carried out without any noticeable effect on gameplay."
That is the functionality that I'm testing.
Clearly that is not the case. I submit that the manual is wrong, that it forgets to mention that it is up to the programmer to ensure the coroutine doesn't get called each update, and that the code in the last manual example should be replaced by, for example:
public class TestCoroutineinC : $$anonymous$$onoBehaviour {
private bool TEST = false;
private bool IsReadyToDoCoroutine = true;
void Update () {
if (!TEST && IsReadyToDoCoroutine) {
StartCoroutine("TestCoroutine");
}
if (Time.time >= 0.6) TEST = true;
}
IEnumerator TestCoroutine() {
for (;;) {
IsReadyToDoCoroutine = false;
Debug.Log ("The value of STEP: + STEP + at time: " + Time.time + " IsReadyToDoCoroutine: " + IsReadyToDoCoroutine);
yield return new WaitForSeconds(1.0f);
IsReadyToDoCoroutine = true;
Debug.Log ("The value of STEP: + STEP + at time: " + Time.time + " IsReadyToDoCoroutine: " + IsReadyToDoCoroutine);
}
}
}
$$anonymous$$accabe is right. You are starting new coroutines on every frame until "Test" = true.
If you only want your coroutine to run once, then you have to set Test to true immediately after starting it.
if (!TEST) {
StartCoroutine("TestCoroutine");
TEST = true;
}
Answer by JoshuaMcKenzie · Feb 22, 2016 at 03:15 PM
No, the Manual is not wrong, everything supplied its factually correct. Nowhere in the manual does it tell you to create a new coroutine on every frame for 0.6 seconds. Its not the fault of the manual when it doesn't work as expected because it was misued. This all sounds to me like any line from Weird Al's "I'll Sue Ya". The intent of the page was to teach you on the possible uses of coroutines, not delve deep into writing coroutines properly (as that's a broad topic)
try this, Start can be a coroutine and you're having Monobehavior manage that for you:
public class Test : MonoBehaviour
{
IEnumerator Start()
{
for (float step= 5; step>= 0; step--)
{
Debug.Log ("The value of STEP: " + step + " at time: " + Time.time);
yield return new WaitForSeconds(0.1f);
}
}
}
does the code work as expected? and its strikingly similar to what you wrote, the main difference here is how the coroutine is getting invoked
Joshua, your code is correct, but in this case I do agree with the OP that the manual is too vague on the subject matter. Even if it is 'factually correct, it won't be helpful to someone who is learning if it is this incomplete. This manual page makes sense to those of us who already understand coroutines. Based on all the confusion that I've seen about coroutines and how people interpret the manual, I think the manual is what is lacking to be honest.
The manual section is Scripting ---> Scripting overview --> Coroutines.
Not "Scripting --> Scripting Overview -> The possible uses of Coroutines for people who are already expects in writing coroutines."
Thank you GerardQ, maccabbe, flashrider, Joshua$$anonymous$$c$$anonymous$$enzie for taking time to look at this and explain.
Your answer
Follow this Question
Related Questions
coroutine not running after yield new WaitForSeconds 1 Answer
Lerping blinks 1 Answer
Coroutine Will NOT Start 3 Answers
Coroutoutine doesnt work properly. 3 Answers
Coroutines /WaitForSecond ain't working 2 Answers