8

Unity之"诡异"的协程 - M-Anonymous

 1 year ago
source link: https://www.cnblogs.com/M-Anonymous/p/16883465.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Unity之"诡异"的协程

为什么说是诡异的协程呢?首先从一个案例说起吧,示例如下:

游戏目标:让小车进入到对应颜色屋子里,即可获得一分。(转弯的道路可控)

1321357-20221112103116774-2126767956.png

 

1321357-20221112103440031-1699061885.png

为了让小车能够平滑转弯,小车的前进方向需要和车子的位置与圆心组成的连线垂直。

1321357-20221112104827602-1663559017.png

首先想到的就是在车子进入到碰撞体和在碰撞体里面都是上述运动方式,离开碰撞体后相当于旋转了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 调用中保持一致,所以这些局部作用域变量将被保存到上一级的生成的它们的类中,从而保证在协程的存活期内保留在堆上的地址分配。

好了,现在回到我们的案例,发生了什么问题呢?

1321357-20221112114119501-1208586988.png
1321357-20221112114445857-549382436.png

当其中一个小车进入到房子后,其中有一个小车没有正常转弯了,并且发生报错,如上图标红的地方,这是为什么呢?

房子的碰撞代码如下:

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) 

这是不是很匪夷所思呢?

因为当小车进入直线轨道时协程已经结束了,怎么协程还在运行呢?

此时你可以想起上面那句话,应该可以猜到了为什么会发生这样。

1321357-20221112122225812-671249472.png

当Car1进入House碰撞体时,也将Collider存放在同一个地方,之后将这个小车摧毁。

此时小车3正好在执行协程,当从yield 之后的语句开始执行时,由于小车已经被摧毁,所以就报错了。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK