- Home /
'function Update' vs 'function Start {while(true)}'?
Found this in the Lerpz Tutorial, in the JetPackParticleController.js.
function Start ()
{
// Once every frame update particle emission and lights
while(true)
{
...
yield;
}
}
So is it the same as
function Update() {...}
?
As a side: I don't really get what excatly "yield;" does. (I get it's something like stop/wait with what you're doing, but the exact rules are a mystery to me... :/ )
Thanks & Greetz, Ky.
Yeah Sweet thx for the info will help alot i have been wondering this myself but what about for loops they run differently then while loops. only slightly but they do has anyone tested them as well.
Answer by jashan · Dec 11, 2009 at 10:10 AM
I wouldn't say it's a shortcut: An endless while-loop started in Start() and including a yield should essentially behave very similar as Update(). However, the execution order is very likely a little different. For certain, the first statement in the Start()-construct gets executed before any Update(), so that definitely is a difference.
The best way to find out how exactly the behavior differs would be by experiment:
Have both the Start() method you suggested and an Update() method, putting statements into the log:
function Start() { while(true) { Debug.Log(string.Format("From Start() - time: {0}", Time.time)); yield; } }
function Update() { Debug.Log(string.Format("From Update() - time: {0}", Time.time)); }
You need the timestamps to find out which Start/Update combinations belong together. The time is set once per frame; in other words: when you call Time.time multiple times in the same frame, it gives you the same result. The next frame, it gives you a different result. So, something like:
From Start() - time: 3.432
From Update() - time: 3.432
From Start() - time: 3.623
From Update() - time: 3.623
Would tell you that Start() is called first (which I think to be unlikely), while
From Start() - time: 3.432
From Update() - time: 3.623
From Start() - time: 3.623
From Update() - time: 3.842
Would tell you that Update() comes first (more likely).
Regarding yield and CoRoutines: I found it very helpful to look into C# for this (well, that came natural to me as I prefer C# anyways). "yield" in itself doesn't exist in C# - instead, you need "yield return something;" which makes the code more understandable in my opinion (you could do the same in JavaScript, except that you omit the "new"):
yield return new WaitForEndOfFrame(); // waits until after rendering, before displaying
yield return new WaitForFixedUpdate();
yield return new WaitForSeconds(time); // waits the given amount of seconds
yield return 0; // just waits one frame ... well ... great ;-)
Also, in C# you can't just call yield - instead you always need to have it in a "CoRoutine" and the "CoRoutine" must have IEnumerator as return type. And that's the information I needed: An enumerator is basically something that can be called from a loop (like, a game loop, for instance) and return a different result each time it is called (in simplified terms).
In other words: What happens here is that when you have a "yield return something;" in your code, control is returned from your code to the engine. Depending on what you return the engine decides when to retrieve the next "object from the iteration" and when that happens control is returned back to your code. Most likely, Unity keeps a list of all Iterators (in other words: all your CoRoutines) and at a few "hooks" during the main game loop executes those one after the other.
The concept of CoRoutines is quite similar to cooperative multithreading in a single-processor (or single-core) environment: There, each thread has to give up control for other "threads" to get processor time. The main difference from my point of view is that actual multithreading can make use of multiple processors / cores while CoRoutines can't. The nice thing about that is that you don't have to mess with the complexities of concurrent programming and things remain fully predictable and deterministic (which real threads, especially on multi-processor/multi-core machines, are not) ;-)
Answer by Jaap Kreijkamp · Dec 11, 2009 at 02:17 AM
The construction you see is basically a coroutine, but you're right the ... in above example does about the same as the code in the update. A coroutine can do much more however and is useful for things that happen over a limited amount of time. A coroutine is a function that can suspend execution, using the yield and will go on where it left off after the time specified with the yield (default is next frame, but you can also wait for a number of seconds with WaitForSeconds).
Besides starting a coroutine yourself with StartCoroutine, several functions can act like coroutines just by using a yield somewhere in the code. See again the documentation which functions can or can't be used as a coroutine (I believe basically all the MonoBehaviour
overridable functions except Update
and FixedUpdate
).
In the Unity docs you can find a better explanation and some examples how you can use this.
Answer by SisterKy · Dec 11, 2009 at 02:57 PM
Thank you, Jashan =)
Out of interest I added the other posibilities I could think of to your script
function Awake() { while(true) { Debug.Log("From Awake() - time: " + Time.time); yield; } }
function Start() { while(true) { Debug.Log("From Start() - time: " + Time.time); yield; } }
function Update() { Debug.Log("From Update() - time: " + Time.time); }
function LateUpdate() {
Debug.Log("From LateUpdate() - time: " + Time.time); }
function FixedUpdate() {
Debug.Log("From FixedUpdate() - time: " + Time.time); }
First call is executed in following order:
- Awake creates an error saying it may not contain a coroutine and thus isn't processed at all.
- Start
- FixedUpdate
- Update
- LateUpdate
Following calls:
- FixedUpdate (In my case it was sometimes even called twice or trice before the other functions got called once; but as FixedUpdate isn't framerate-dependend while the other functions are, one will get different results for this depending on the scene one uses this in. So I wouldn't rely on it to get called first. And as FixedUpdate should be used for rigidbodys only anyway, I maybe should have left it out of this experiment altogether)
- Update
- Start
- LateUpdate
So the Start-function with an ever-true while-loop can be used to fit some code between Update and LateUpdate.
Be aware, however, if you use a condition for the while-loop that can become false, the Start-function won't be called anymore, no matter wether the condition becomes true again...
Cheers & Greetz, Ky.
The place you first call a coroutine only matters for the first iteration, after that the yieldinstruction decides when the next iteration will be called.
Also, the error you get in Awake is because you try to run a coroutine inside the function, ins$$anonymous$$d of it getting called from the function. If you do this you'd see it'd be called before the Start-coroutine the first time, and after Update and before the Start one every next frame.
For yield return null, yield WaitForSeconds() and yielding to a coroutine it gets run immediately after Update(), and indeed before FixedUpdate. Yield WaitForFixedUpdate() is run right after FixedUpdate() and yield WaitForEndOfFrame() is called last before the frame ends.
Using Time.time for this is redundant, as you're comparing at what point inside a frame what happens - and Time.time remains the same during that frame. You'll probably want to use System.DateTime.
Edit: Read this article for some more in-depth info, it's a cool read http://altdevblogaday.com/2011/07/07/unity3d-coroutines-in-detail/
cool! Thank you for the additional info =D Greetz, $$anonymous$$y.