结论
当MonoBehaviour组件被禁用时(enabled = false),已经启动的协程会继续运行。
这与很多开发者的直觉相反,因此常导致难以排查的bug。让我深入解释这一行为并提供验证方法。
组件禁用 vs 游戏对象禁用
首先,必须区分两种不同类型的"禁用":
- 组件禁用:component.enabled = false
- 协程继续运行
- Update、FixedUpdate等生命周期方法停止调用
- OnDisable被调用
- 游戏对象禁用:gameObject.SetActive(false)
- 协程立即停止
- 所有生命周期方法停止调用
- OnDisable被调用
验证代码示例
以下是一个简单测试来验证这一行为:
using System.Collections;
using UnityEngine;public class CoroutineTest : MonoBehaviour
{private void Start(){StartCoroutine(TestCoroutine());}private IEnumerator TestCoroutine(){int counter = 0;while (true){counter++;Debug.Log($"Coroutine tick: {counter}, Component enabled: {enabled}");yield return new WaitForSeconds(1f);}}// 用UI按钮调用此方法public void DisableComponent(){Debug.Log("Disabling component...");enabled = false;}// 用UI按钮调用此方法public void DisableGameObject(){Debug.Log("Disabling GameObject...");gameObject.SetActive(false);}private void OnDisable(){Debug.Log("Component Disabled.");}
}
运行此代码并调用DisableComponent(),你会看到协程仍在每秒记录消息,尽管组件已被禁用。
而调用DisableGameObject()会立即停止协程的执行。
因此仅仅禁用 (enabled = false
) 一个 MonoBehaviour
组件不会停止它已经启动的协程。协程会继续运行,直到完成、被手动停止、或者其所属的 GameObject 被禁用或销毁。
如果你希望在组件禁用时停止协程,你需要在 OnDisable()
方法中显式调用 StopCoroutine()
或 StopAllCoroutines()
。