如何在特定条件下统一退出协程?

How to exit from coroutine in unity in specific condition?

我在统一协程方面遇到了一些问题。我希望我的播放器在到达门时(发生碰撞时)在 3 秒后进入塔内,并且如果他在距离门不到 3 秒的时间内移动以关闭门并且不加载新场景。我尝试了大部分事情,但没有用。有人可以帮忙或给我一些提示吗?

这是我的代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Door : MonoBehaviour
{
[SerializeField] private Animator animator;
bool open = false;
// Start is called before the first frame update
void Start()
{
    animator = GetComponent<Animator>();
}

// Update is called once per frame
void Update()
{

}

public void OnTriggerStay2D(Collider2D collider)
{
    if (collider.gameObject.tag == "Player")
    {
        animator.SetBool("opening", true);
        StartCoroutine("LoadLevelTowerAfterDelay");
    }
    else
    {
        animator.SetBool("opening", false);
        StopCoroutine("LoadLevelTowerAfterDelay");
    }
}
IEnumerator LoadLevelTowerAfterDelay()
{

    if (GameManager.sharedInstance != null)
    {
        yield return new WaitForSeconds(3);
        GameManager.sharedInstance.LoadLevelTower();
    }
}

}

一个选项是:如果发生某些事情让您想取消操作,请在某处设置一个布尔值并在协程的 LoadLevelTower() 行之前立即检查它。

另一种选择是保留对协程的引用(例如Coroutine c = StartCoroutine("LoadLevelTowerAfterDelay");),然后如果发生某些事情让您想中止它,请使用StopCoroutine(c);.

我认为您更大的问题可能在于您对 OnTriggerStay2D 的使用。对我来说,当玩家在对撞机内时,您似乎每帧都在调用 StartCoroutine,而当 玩家 以外的其他东西在对撞机内时,您每帧都在调用 StopCoroutine对撞机。可能您反而想使用 OnTriggerEnter2D 来检查玩家是否进入,并使用 OnTriggerExit2D 来检查玩家是否退出?

OnTriggerStay2D 被调用 每个物理更新帧 !

您肯定不想在每个物理帧启动一个新协程!

您更想做的是使用 OnTriggerEnter2DOnTriggerExit2D,例如

private void OnTriggerEnter2D(Collider2D collider)
{
    if (!collider.gameObject.CompareTag("Player")) return;
    
    animator.SetBool("opening", true);
    StartCoroutine(LoadLevelTowerAfterDelay());
}

private void OnTriggerExit2D(Collider2D collider)
{
    if (!collider.gameObject.CompareTag("Player")) return;
    
    animator.SetBool("opening", false);
    StopCoroutine(LoadLevelTowerAfterDelay());
}

一般来说,这应该已经可以工作了,但为了保存,您可以存储已启动的例程,如此处也提到的那样

private Coroutine currentRoutine;

private void OnTriggerEnter2D(Collider2D collider)
{
    if (!collider.gameObject.CompareTag("Player")) return;
    
    // This should actually not happen
    if(currentRoutine != null)
    {
        Debug.LogError("Huh?!", this);
        return;
    }

    animator.SetBool("opening", true);
    currentRoutine = StartCoroutine(LoadLevelTowerAfterDelay());
}

private void OnTriggerExit2D(Collider2D collider)
{
    if (!collider.gameObject.CompareTag("Player")) return;
    
    // This should actually not happen
    if(currentRoutine == null)
    {
        Debug.LogError("Huh?!", this);
        return;
    }

    animator.SetBool("opening", false);
    StopCoroutine(currentRoutine);
    currentRoutine = null;
}