Unity中MonoBehaviour组件禁用时协程的行为
结论
当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()
。