You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.29 19:32:53