SetActive(false) stops the routine
A coroutine is not a separate thread. It stops when the owner GameObject's activeSelf becomes false or when the MonoBehaviour is destroyed. Disabling the component with enabled = false does not stop its coroutines by itself. That difference explains many silent fades, timers, and wait operations.
using System.Collections;
using UnityEngine;
public class FadeController : MonoBehaviour
{
[SerializeField] private CanvasGroup target;
public void Begin()
{
StartCoroutine(FadeOut());
}
private IEnumerator FadeOut()
{
while (target.alpha > 0f)
{
target.alpha = Mathf.MoveTowards(target.alpha, 0f, Time.deltaTime);
yield return null;
}
}
}Separate work from visuals
If the whole controller is disabled just to hide a panel, keep the controller alive and hide the CanvasGroup or a child object instead. When you do want to stop the work, keep the Coroutine handle and call StopCoroutine explicitly.
private Coroutine running;
public void Begin()
{
if (running != null)
StopCoroutine(running);
running = StartCoroutine(FadeOut());
}
private void OnDisable()
{
if (running != null)
StopCoroutine(running);
}A coroutine is still on the main thread
File I/O or heavy computation inside a coroutine still runs on the main thread. Coroutines spread work across frames, but they do not move CPU work to another core. Profile the cost first, then decide whether a Job System or async/await is appropriate.