首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >协程如何与OnTriggerEnter2D协同工作?

协程如何与OnTriggerEnter2D协同工作?
EN

Stack Overflow用户
提问于 2020-06-12 18:02:27
回答 2查看 201关注 0票数 0

我正在学习C#和unity,我最近的一个问题是让我的OnTriggerEnter2D()函数工作。经过大量的测试,我设法解决了我的问题,但我不能百分之百地确定我是否理解。

所以基本上我是检查一个物体是否接触另一个物体,如果不是这样,我旋转这个物体:

代码语言:javascript
复制
for (var i = 0; i < 4; i++)
        {
            if (!door.connected)
            {
                go.transform.Rotate(0f, 0f, 90f, Space.Self);
            }
        }

这个不起作用,经过大量的调试,我决定在go.transform.Rotate(0f, 0f, 90f, Space.Self);和这项工作之后立即使用一个延迟为0.5s的协程。

如果我是对的,这是因为延迟让我的OnTriggerEnter2D()函数有足够的时间来检测冲突,还是这与OnTriggerEnter2D()无关?如果是这种情况,那么每次我要检查碰撞时,我都需要使用协程?

编辑1:我在另一个脚本中有这段代码

代码语言:javascript
复制
public void OnTriggerEnter2D(Collider2D other)
{
    if (other.CompareTag("Door"))
    {
        this.connected = true;
    }
}

因此,前面的循环确实会让我的对象旋转,但永远不会将我的公共变量"connected“设置为true,就像碰撞永远不会发生一样,除非是在正确的位置产生的碰撞。但是,当我使用协程时,它确实可以工作,为什么?

EN

回答 2

Stack Overflow用户

发布于 2020-06-12 18:30:50

在OnTriggerEnter2D中旋转对象绝对不需要协程。

我想象一下,根据你展示的代码片段,它几乎立即将物体旋转了90度4次(这是一个完整的360度旋转),因此看起来它根本没有移动。

编辑(响应编辑1):

当你添加一个协程时,你的代码能够工作的原因是因为四个90度的旋转并不都发生在一个帧中。发生的情况是:

  1. ,它将门旋转90度,然后启动一个协程,这个协程持续几帧。
  2. 由于门已在帧之间旋转,因此另一个脚本有机会检测任何与门的冲突。
  3. 如果检测到与门的冲突,则可以将connected标志设置为true,从而防止在任何后续帧中调用go.transform.Rotate

将其与不带协程的序列进行比较,该序列将为:

它将门旋转90度,然后再旋转一次,然后,它移动到下一个frame.

  • Since,,就所有其他GameObjects而言,门没有在帧之间移动,没有检测到新的冲突,并且您的connected标志仍为false。

然而,问题的解决方案并不是使用协程。解决方案是删除您的for循环。在Update方法中,如果门未连接,则将其旋转一度。在下一次更新时,如果门仍然没有连接,请重新连接。最终,在4次更新中,它将连接在一起,并且不再旋转每一帧。

票数 2
EN

Stack Overflow用户

发布于 2020-06-12 18:43:47

如果我是对的,这项工作是因为延迟让我的OnTriggerEnter2D()函数有足够的时间来检测冲突,还是这与OnTriggerEnter2D()无关?

不,这和OnTriggerEnter2D没有任何关系。正如你所看到的,你的碰撞正在工作并被触发。

发生的是,你旋转4次,大约90°,在一帧内加起来是360°,=>,你会注意到没有区别。

Unity中的Coroutine基本上就是一个IEnumerator。在某些情况下,每个IEnumerator都使用yield关键字。所以Unity中的Coroutine基本上做的是:

每一帧调用给定IEnumerator上的MoveNext,直到它终止或到达下一条yield语句。(有一些特殊情况,例如WaitUntilEndOfFrame...)

所以翻译过来,你可以说yield的意思是

从这里开始,在下一帧中中断“routine

  • Render”this frame

  • Continue

顺便说一句。你正在做什么,例如

代码语言:javascript
复制
yield return new WaitForseconds(3f);

基本上等同于做像这样的事情

代码语言:javascript
复制
var timePassed = 0f;
while(timePassed < 3f)
{
    yield return null;
    timePassed += Time.deltaTime;
}

所以:不,对于碰撞检测,你不需要使用需要使用协程;而是,当你想要在多个帧上“扩展”一些代码执行时,你应该使用一个。

当您将OnTriggerEnter2D声明为IEnumerator时,Unity在内部所做的基本上等于以下内容

代码语言:javascript
复制
private void OnTriggerEnter2D(Collider2D other)
{
    StartCoroutine(RotationAnimation());
}

private IEnumerator RotationAnimation()
{
    // ...
}

所以你还是要非常小心,不要得到并发的例程!

如果你想要的实际上是一个平滑的旋转,你可以使用

代码语言:javascript
复制
private bool alreadyRotating;

// adjust this via the Inspector
[SerializeField] float rotationAnglePerSecond = 90f;

private void OnTriggerEnter2D(Collider2D other)
{
    // avoid concurrent routines
    if(!alreadyRotating) StartCoroutine(RotationAnimation());
}

private IEnumerator RotationAnimation()
{
     // block concurrent routine
     if(alreadyRotating) yield break;
     alreadyRotating = true;

     var initialOrientation = go.transform.rotation;;

     // rotate until you reach 360°
     var rotated = 0f;
     while(rotated < 360f)
     {
         // get amount of rotation within this frame
         var rotate = Time.deltaTime * rotationAnglePerSecond;
         go.transform.Rotate(0f, 0f, 90f);           
         rotated += rotate;

         yield return null;
     }

     // reset to initial orientation
     go.transform.rotation = initialOrientation;    

     // when done allow the next routine
     alreadyRotating = false;
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/62341901

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档