Unity中如何通过StartCoroutine实现屏幕淡入淡出,避免Update重复调用并解决淡入未完成时被淡出打断的问题
解决Unity SceneFader重复启动协程与操作打断问题
我来帮你搞定这个SceneFader的两个核心问题——Update里重复启动协程、淡入淡出互相打断。问题根源在于你直接把协程方法暴露给外部调用,而且isFading的判断逻辑有漏洞:外部每帧调用StartCoroutine(Fade(...))时,哪怕isFading是true,新的协程实例还是会被创建,等一帧后就继续执行,直接覆盖状态并打断当前的淡入淡出。
下面是完整的修复方案,不需要外部加延迟,所有逻辑都封装在SceneFader内部:
using System.Collections; using UnityEngine; using UnityEngine.SceneManagement; using UnityEngine.UI; public class SceneFader : MonoBehaviour { #region FIELDS public GameObject fadeOutUIGameobjectImage; public float fadeSpeed = 3f; private Image fadeOutUIImage; private bool isFading = false; private void Start() { SceneManager.sceneLoaded += SceneManager_sceneLoaded; // 提前获取Image组件,避免重复查找 fadeOutUIImage = fadeOutUIGameobjectImage.GetComponent<Image>(); } public enum FadeDirection { In, // 最终Alpha = 1,淡入(屏幕从透明到不透明) Out // 最终Alpha = 0,淡出(屏幕从不透明到透明) } #endregion #region FADE CONTROL // 外部调用这个方法来启动淡入淡出,不要直接调用协程 public void StartFade(FadeDirection fadeDirection) { // 如果正在淡入淡出,直接忽略新请求 if (isFading) return; StartCoroutine(_Fade(fadeDirection)); } // 私有协程,封装实际的淡入淡出逻辑 private IEnumerator _Fade(FadeDirection fadeDirection) { isFading = true; fadeOutUIGameobjectImage.SetActive(true); float alpha = fadeDirection == FadeDirection.Out ? 1f : 0f; float fadeEndValue = fadeDirection == FadeDirection.Out ? 0f : 1f; if (fadeDirection == FadeDirection.Out) { while (alpha >= fadeEndValue) { UpdateFadeAlpha(ref alpha, fadeDirection); yield return null; } fadeOutUIGameobjectImage.SetActive(false); } else { while (alpha <= fadeEndValue) { UpdateFadeAlpha(ref alpha, fadeDirection); yield return null; } } isFading = false; } #endregion #region HELPERS public IEnumerator FadeAndLoadScene(FadeDirection fadeDirection, string sceneToLoad) { // 这里也改用StartFade的逻辑,确保状态正确 StartFade(fadeDirection); // 等待当前fade完成 while (isFading) yield return null; SceneManager.LoadSceneAsync(sceneToLoad); } private void SceneManager_sceneLoaded(Scene arg0, LoadSceneMode arg1) { if (MenuController.LoadSceneForSavedGame == true) { var saveLoad = GameObject.Find("Save System").GetComponent<SaveLoad>(); saveLoad.Load(); } } private void UpdateFadeAlpha(ref float alpha, FadeDirection fadeDirection) { fadeOutUIImage.color = new Color(fadeOutUIImage.color.r, fadeOutUIImage.color.g, fadeOutUIImage.color.b, alpha); alpha += Time.deltaTime * (1f / fadeSpeed) * (fadeDirection == FadeDirection.Out ? -1 : 1); } #endregion }
关键改动说明:
- 封装协程调用:新增
StartFade公共方法,外部脚本统一调用这个方法,而不是直接启动协程。方法内部先检查isFading,确保同一时间只有一个fade操作在运行。 - 私有协程逻辑:把原
Fade协程改名为_Fade并设为私有,避免外部直接调用导致的混乱。 - 优化组件查找:在
Start里提前获取Image组件,避免每帧重复查找,提升性能。 - 完善状态锁定:
isFading在协程启动时设为true,直到整个fade过程完全结束才设为false,彻底阻止新请求打断当前操作。 - 修复冗余代码:移除淡入逻辑里重复的
SetActive(true)调用。
现在,不管外部脚本在Update里怎么调用sceneFader.StartFade(...),都只会在当前没有fade操作时启动一次,而且淡入淡出过程不会互相打断,完全不需要外部加延迟等待的临时方案。
内容的提问来源于stack exchange,提问作者Daniel Lip
相关产品推荐
相关产品推荐

