爲什麼說是詭異的協程呢?首先從一個案例說起吧,示例如下:
遊戲目標:讓小車進入到對應顏色屋子裏,即可獲得一分。(轉彎的道路可控)
爲了讓小車能夠平滑轉彎,小車的前進方向需要和車子的位置與圓心組成的連線垂直。
首先想到的就是在車子進入到碰撞體和在碰撞體裏面都是上述運動方式,離開碰撞體後相當於旋轉了90度。
但是當車子在轉彎的道路上時,此時將左轉彎的路變成右轉彎的路,車子就會失控,因爲碰撞體消失後對應的事件就不會執行了。
所以想到讓車子持續轉彎的方法放進協程裏面做,小車前進代碼和轉彎代碼如下:
小車前進代碼:
public class CarMove : MonoBehaviour { public float Speed = 1f; // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { transform.Translate(Vector3.forward * Speed * Time.deltaTime); } }
小車轉彎代碼:
public class CurveCollider : MonoBehaviour { // Start is called before the first frame update void Start() { } // Update is called once per frame void Update() { } private void OnCollisionEnter(Collision collision) { if (this.CompareTag("TurnLeft")) { StartCoroutine(CarTurnLeft(collision)); } } IEnumerator CarTurnLeft(Collision collision) { while (true) { Vector3 worldUp = Vector3.up; Vector3 targetPos = collision.transform.position; Vector3 direction = transform.position - targetPos; Vector3 forwardDir = new Vector3(-1.0f, 1.0f, 1.0f); Vector3.OrthoNormalize(ref worldUp, ref direction, ref forwardDir); Quaternion quaternion = Quaternion.identity; quaternion.SetLookRotation(forwardDir); collision.transform.rotation = quaternion; yield return null; Debug.Log(collision.transform.eulerAngles.y); if (collision.transform.eulerAngles.y >= 270 && (collision.transform.eulerAngles.y - 270) <= 1) { quaternion = Quaternion.identity; quaternion.eulerAngles = new Vector3(0.0f, -90.0f, 0.0f); collision.transform.rotation = quaternion; yield break; } } } }
下面先簡單介紹一下協程的基本概念:Unity手冊:協程
UnityEngine所提供的SDK都只能在單線程中調用,而協程也是單線程的,不同於多線程。
Unity中只代碼只要有一個地方代碼出現死循環或者運行時間較長,遊戲就會卡死。
關於協程其中有這麼一句話,很重要:協程優化
因爲協程中的局部作用域變量必須在 yield
調用中保持一致,所以這些局部作用域變量將被保存到上一級的生成的它們的類中,從而保證在協程的存活期內保留在堆上的地址分配。
好了,現在回到我們的案例,發生了什麼問題呢?
當其中一個小車進入到房子後,其中有一個小車沒有正常轉彎了,並且發生報錯,如上圖標紅的地方,這是爲什麼呢?
房子的碰撞代碼如下:
private void OnCollisionEnter(Collision collision) { if (this.CompareTag(collision.gameObject.tag)) { GameController.Score++; } Destroy(collision.gameObject); }
當小車進入到房子後就會摧毀小車,如果進入的車是對的,就加一分。
報錯的行43行是如下代碼:
if (collision.transform.eulerAngles.y >= 270 && (collision.transform.eulerAngles.y - 270) <= 1)
這是不是很匪夷所思呢?
因爲當小車進入直線軌道時協程已經結束了,怎麼協程還在運行呢?
此時你可以想起上面那句話,應該可以猜到了爲什麼會發生這樣。
當Car1進入House碰撞體時,也將Collider存放在同一個地方,之後將這個小車摧毀。
此時小車3正好在執行協程,當從yield 之後的語句開始執行時,由於小車已經被摧毀,所以就報錯了。