Remember last June, when I first started the internship, I was asked to write the structure of the network layer, the use of the association, at that time a bit of a confused, do not know the unity of the implementation mechanism of the process is what, just know that the return value of the function is the IEnumerator type, the function uses yield return, Can be called through the startcoroutine. Later also has been the use of the Internet, Google some of the basic is an example, rarely can help to understand the principle of unity process.
This article is only from the perspective of unity to analyze understanding of the internal workings of the process, rather than from the C # underlying syntax implementation to introduce (follow-up needs to be introduced), is divided into three parts:
threads (thread) and co-processes (Coroutine)
how unity performs in the process
IEnumerator & Coroutine
Previously wrote a "Unity Process (coroutine) Management class--taskmanager tool sharing" is mainly introduced TaskManager implementation of the state control of the association, there is no unity background implementation of the principle of the process to dig into. Although I had a little understanding of the process, but the unity of how to implement the process is still a blank, in unitygems.com see two explain coroutine, very familiar, when I see advanced coroutine hijack class behind, Suddenly feel very delicate, a bright, then moved to write the text to share.
threads (thread) and co-processes (Coroutine)
D.s.qiu that the use of the role of a total of two points: 1) delay (wait) for a period of time to execute code, 2) and so on after an operation is completed before executing the subsequent code. Summing up is a sentence: control code at a specific time to execute.
Many beginners, subconsciously, will feel that the process is asynchronous execution, will feel that the association is a C # thread of the alternative, unity is not the use of threading solutions.
So first of all, remember that the process is not a thread, nor is it executed asynchronously. The Monobehaviour and the update function are also executed in Mainthread. Using the coprocessor you don't have to worry about syncing and locking issues.
How unity performs in the process
Unitygems.com gives the definition of the co-process:
A coroutine is a function this is executed partially and presuming suitable conditions was met, would be resumed at some p Oint in the "future until" it is done.
That is, the process is a partial execution, and the condition (yield return statement) is suspended until the condition is met before it wakes up to execute the subsequent code.
Unity handles the threads on the object at each frame. Unity is primarily the process of processing after the update (checking that the conditions of the Association are met), but there are also write exceptions:
From the analysis, it is understood that the co-process is the same as update (), which is the function (if any) that the unity will handle for each frame. If the monobehaviour is in the active state and the yield condition is met, the code behind the method will be followed. It can also be found that if you call the co-process at the beginning of an object, the co-process will immediately run to the first yield return statement, and if yield return null, it will be awakened again at the same frame. If you do not consider this detail there will be some strange question "1".
"1" note Drawing and conclusion are obtained from the unitygems.com, after the following verification found inconsistent with the actual, D.s.qiu with unity 4.3.4f1 for testing. After testing, the co-process is at least lateupdate () after each frame to run.
Use yield return new Waitforseconds (1f) below; Test separately in Start,update and lateupdate:
Using Unityengine;
Using System.Collections;
public class Testcoroutine:monobehaviour {
private bool Isstartcall = false; MakeSure Update () and Lateupdate () Log only once
private bool Isupdatecall = false;
private bool Islateupdatecall = false;
Use this for initialization
void Start () {
if (!isstartcall)
{
Debug.Log ("Start call Begin");
Startcoroutine (Startcoutine ());
Debug.Log ("Start call End");
Isstartcall = true;
}
}
IEnumerator Startcoutine ()
{
Debug.Log ("This is the Start coroutine call before");
Yield return new waitforseconds (1f);
Debug.Log ("This was Start Coroutine call after");
}
Update is called once per frame
void Update () {
if (!isupdatecall)
{
Debug.Log ("Update call Begin");
Startcoroutine (Updatecoutine ());
Debug.Log ("Update call End");
Isupdatecall = true;
}
}
IEnumerator Updatecoutine ()
{
Debug.Log ("This was Update Coroutine call before");
Yield return new waitforseconds (1f);
Debug.Log ("This was Update Coroutine call after");
}
void Lateupdate ()
{
if (!islateupdatecall)
{
Debug.Log ("Lateupdate call Begin");
Startcoroutine (Latecoutine ());
Debug.Log ("Lateupdate call End");
Islateupdatecall = true;
}
}
IEnumerator Latecoutine ()
{
Debug.Log ("This was late Coroutine call before");
Yield return new waitforseconds (1f);
Debug.Log ("This was late Coroutine call after");
}
}
Get log input results as follows:
Then yield return to new Waitforseconds (1f), and yield return null; found that the log input results are the same as above, did not appear above the situation:
Using Unityengine;
Using System.Collections;
public class Testcoroutine:monobehaviour {
private bool Isstartcall = false; MakeSure Update () and Lateupdate () Log only once
private bool Isupdatecall = false;
private bool Islateupdatecall = false;
Use this for initialization
void Start () {
if (!isstartcall)
{
Debug.Log ("Start call Begin");
Startcoroutine (Startcoutine ());
Debug.Log ("Start call End");
Isstartcall = true;
}
}
IEnumerator Startcoutine ()
{
Debug.Log ("This is the Start coroutine call before");
yield return null;
Debug.Log ("This was Start Coroutine call after");
}
Update is called once per frame
void Update () {
if (!isupdatecall)
{
Debug.Log ("Update call Begin");
Startcoroutine (Updatecoutine ());
Debug.Log ("Update call End");
Isupdatecall = true;
}
}
IEnumerator Updatecoutine ()
{
Debug.Log ("This was Update Coroutine call before");
yield return null;
Debug.Log ("This was Update Coroutine call after");
}
void Lateupdate ()
{
if (!islateupdatecall)
{
Debug.Log ("Lateupdate call Begin");
Startcoroutine (Latecoutine ());
Debug.Log ("Lateupdate call End");
Islateupdatecall = true;
}
}
IEnumerator Latecoutine ()
{
Debug.Log ("This was late Coroutine call before");
yield return null;
Debug.Log ("This was late Coroutine call after");
}
}
"Today, I accidentally discovered Monobehaviour's function execution sequence diagram, and found out that the co-operation was actually after lateupdate, attached below:"
In the previous introduction of the TaskManager tool, it was said that Monobehaviour did not provide a stop method for a particular association, but it could be monobehaviour enabled = False or gameobject.active = False to stop the execution of the process "2".
It is verified that the "2" conclusion is also wrong, and the correct conclusion is that the monobehaviour.enabled = False coprocessor will run as usual, but the gameobject.setactive (false) is all stopped, even if the inspector Gameobject activated or did not continue to execute:
Using Unityengine;
Using System.Collections;
public class Testcoroutine:monobehaviour {
private bool Isstartcall = false; MakeSure Update () and Lateupdate () Log only once
private bool Isupdatecall = false;
private bool Islateupdatecall = false;
Use this for initialization
void Start () {
if (!isstartcall)
{
Debug.Log ("Start call Begin");
Startcoroutine (Startcoutine ());
Debug.Log ("Start call End");
Isstartcall = true;
}
}
IEnumerator Startcoutine ()
{
Debug.Log ("This is the Start coroutine call before");
Yield return new waitforseconds (1f);
Debug.Log ("This was Start Coroutine call after");
}
Update is called once per frame
void Update () {
if (!isupdatecall)
{
Debug.Log ("Update call Begin");
Startcoroutine (Updatecoutine ());
Debug.Log ("Update call End");
Isupdatecall = true;
this.enabled = false;
This.gameObject.SetActive (FALSE);
}
}
IEnumerator Updatecoutine ()
{
Debug.Log ("This was Update Coroutine call before");
Yield return new waitforseconds (1f);
Debug.Log ("This was Update Coroutine call after");
Yield return new waitforseconds (1f);
Debug.Log ("This was Update Coroutine call Second");
}
void Lateupdate ()
{
if (!islateupdatecall)
{
Debug.Log ("Lateupdate call Begin");
Startcoroutine (Latecoutine ());
Debug.Log ("Lateupdate call End");
Islateupdatecall = true;
}
}
IEnumerator Latecoutine ()
{
Debug.Log ("This was late Coroutine call before");
yield return null;
Debug.Log ("This was late Coroutine call after");
}
}
Call this.enabled = False in update first; The results obtained:
Then put this.enabled = false; Comment out, change to this.gameObject.SetActive (false); The results are as follows:
collation : By setting the Monobehaviour script's enabled, there is no effect on the coprocessor, but if gameobject.setactive (false) is already started, the co-process is completely stopped. Even if the inspector activates Gameobject, it does not continue to execute. It is said that although the process is initiated in the Monobehvaviour (Startcoroutine), but the position of the function is completely with monobehaviour is a level, not affected by the state of Monobehaviour, However, as with the Monobehaviour script is controlled by Gameobject, it should be the same as the Monobehaviour script each frame "poll" yield conditions are satisfied.
An expression that can be followed by yield:
A) Null-the Coroutine executes the next time it is eligible
b) Waitforendofframe-the Coroutine executes on the frame, after all of the rendering and GUI are complete
c) waitforfixedupdate-causes this coroutine to execute on the next physics step, after all physics are calculated
d) waitforseconds-causes the Coroutine not-to-execute for a given game time period
e) Www-waits for a Web request to complete (resumes as if waitforseconds or null)
f) Another coroutine-in which case the new coroutine would run to completion before the Yielder is resumed
It is noteworthy that waitforseconds () is affected by Time.timescale, and when time.timescale = 0f, yield return new Waitforsecond (x) will not be satisfied.
IEnumerator & Coroutine
The process is actually a IEnumerator (iterator), the IEnumerator interface has two methods current and MoveNext (), the TaskManager described above is the user two methods to manage the association, only when the MoveNext () Return true to access current, otherwise an error will be encountered. When the iterator method runs to the yield return statement, it returns an expression and retains the current position in the code. The next time the iterator function is called, execution restarts from that location.
Unity does what it does at every frame: Call the MoveNext (iterator) method and, if true, proceed from the current position.
Hijack
Here is an introduction to the Cross call class Hijack of a co-process (see attachment):
Using System;
Using System.Collections.Generic;
Using System.Linq;
Using Unityengine;
Using System.Collections;
[Requirecomponent (typeof (Guitext))]
public class Hijack:monobehaviour {
This'll hold the counting up coroutine
IEnumerator _countup;
This is the counting down coroutine
IEnumerator _countdown;
The coroutine we are currently
Hijacking
IEnumerator _current;
A value that would be updated by the Coroutine
That's currently running
int value = 0;
void Start ()
{
Create Our count up coroutine
_countup = Countup ();
Create Our count down Coroutine
_countdown = Countdown ();
Start our own coroutine for the hijack
Startcoroutine (Dohijack ());
}
void Update ()
{
Show the current value on the screen
Guitext.text = value. ToString ();
}
void Ongui ()
{
Switch between the different functions
if (Guilayout.button ("Switch functions"))
{
if (_current = = _countup)
_current = _countdown;
Else
_current = _countup;
}
}
IEnumerator Dohijack ()
{
while (true)
{
Check If we have a current coroutine and MoveNext on it if we do
if (_current! = null && _current. MoveNext ())
{
Return whatever the coroutine yielded, so we'll yield the
Same thing
Yield return _current. Current;
}
Else
Otherwise wait for the next frame
yield return null;
}
}
IEnumerator Countup ()
{
We have a local increment so the routines
Get independently faster depending on how
Long they has been active
float increment = 0;
while (true)
{
Exit If the Q button is pressed
if (Input.getkey (KEYCODE.Q))
Break
Increment+=time.deltatime;
Value + = Mathf.roundtoint (increment);
yield return null;
}
}
IEnumerator Countdown ()
{
float increment = 0f;
while (true)
{
if (Input.getkey (KEYCODE.Q))
Break
Increment+=time.deltatime;
Value-= Mathf.roundtoint (increment);
This coroutine returns a yield instruction
Yield return new waitforseconds (0.1f);
}
}
}
The above code implementation is a two-way alternate call, which is too subtle for this requirement.
Summary:
Today carefully read the next unitygems.com about coroutine two articles, although the first (reference ①) Now verify the results have a lot of errors, but for the understanding of the process is good, especially when I found hijack this script, I can't wait to share to everyone.
Originally did not think there will be unitygems.com on the article will be wrong, no intention to test the discovery or there is a great discrepancy, of course, this is not to say that the original author has not been verified to speculate, D.s.qiu think it is possible that unity inside the implementation mechanism has changed, this thing can be changed completely, unity although developed a very years, but actually in the actual development still have a lot of pits, more and feel unity's weakness, although easy to get started, but pits is also indispensable.
It seems that a lot of conclusions are to pass their own verification, the hasty copy and paste difficult to make the truth, remember!
This article was reproduced in: http://dsqiu.iteye.com/blog/2029701
10:47:19
The Unity Process (coroutine) principle in-depth analysis (reprint)