简体   繁体   English

即使从更新调用,如何使用 StartCoroutine 使协程每次仅启动一次,如何淡入/淡出屏幕?

[英]How can I fade in/out the screen using StartCoroutine making the Coroutine to start each time only once even if calling from Update?

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

public class SceneFader : MonoBehaviour
{
    #region FIELDS
    public GameObject fadeOutUIGameobjectImage;
    public float fadeSpeed = 3f;
    //public SaveLoad saveLoad;

    private Image fadeOutUIImage;

    private void Start()
    {
        SceneManager.sceneLoaded += SceneManager_sceneLoaded;
    }

    public enum FadeDirection
    {
        In, //Alpha = 1
        Out // Alpha = 0
    }
    #endregion

    #region FADE
    public IEnumerator Fade(FadeDirection fadeDirection)
    {
        fadeOutUIGameobjectImage.SetActive(true);

        float alpha = (fadeDirection == FadeDirection.Out) ? 1 : 0;
        float fadeEndValue = (fadeDirection == FadeDirection.Out) ? 0 : 1;
        if (fadeDirection == FadeDirection.Out)
        {
            while (alpha >= fadeEndValue)
            {
                SetColorImage(ref alpha, fadeDirection);
                yield return null;
            }
            fadeOutUIGameobjectImage.SetActive(false);
        }
        else
        {
            fadeOutUIGameobjectImage.SetActive(true);
            while (alpha <= fadeEndValue)
            {
                SetColorImage(ref alpha, fadeDirection);
                yield return null;
            }
        }
    }
    #endregion

    #region HELPERS
    public IEnumerator FadeAndLoadScene(FadeDirection fadeDirection, string sceneToLoad)
    {
        yield return Fade(fadeDirection);

        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 SetColorImage(ref float alpha, FadeDirection fadeDirection)
    {
        if(fadeOutUIImage == null)
        {
            fadeOutUIImage = fadeOutUIGameobjectImage.GetComponent<Image>();
        }

        fadeOutUIImage.color = new Color(fadeOutUIImage.color.r, fadeOutUIImage.color.g, fadeOutUIImage.color.b, alpha);
        alpha += Time.deltaTime * (1.0f / fadeSpeed) * ((fadeDirection == FadeDirection.Out) ? -1 : 1);
    }
    #endregion
}

Using it in other scripts like this :在其他脚本中使用它,如下所示:

At the top :在顶部 :

public SceneFader sceneFader; 

Then :然后 :

StartCoroutine(sceneFader.Fade(SceneFader.FadeDirection.In));

And

StartCoroutine(sceneFader.Fade(SceneFader.FadeDirection.Out));

The problem is if I need to fade in or out at some point in the game and call it from Update in some script/s.问题是我是否需要在游戏中的某个时刻淡入或淡出,并在某些脚本/秒中从更新中调用它。 Then it will start the Coroutine all the time over again.然后它会一直重新启动 Coroutine。

I can use a flag to set it to false and true so it will start the Coroutine once but it's a bit annoying to use a flag each time in scripts.我可以使用一个标志将它设置为 false 和 true,这样它就会启动协程一次,但是每次在脚本中使用一个标志有点烦人。 Maybe there is a way in the SceneFader script to make sure that no matter if I call the Coroutine from other script/s in Update and it will know to start the Coroutine once only?也许 SceneFader 脚本中有一种方法可以确保无论我是否从 Update 中的其他脚本调用协程,它都会知道只启动一次协程? In the Update, it will call it each frame but in the SceneFader script, it will know to start the Coroutine only once when need to start it.在更新中,它会在每一帧调用它,但在 SceneFader 脚本中,它会知道只在需要启动协程时启动它一次。

Still not working.还是行不通。 I changed the SceneFader to this :我将 SceneFader 更改为:

Added the flag isFading but still the fade out is interrupting the fade in. It's not that the fade in or the fade out start a lot of time in the Update they start once but the fade out start before the fade in finish.添加了标志 isFading 但淡出仍然会中断淡入。并不是说淡入或淡出在更新中开始很多时间,而是在淡入结束之前开始淡出。

using System.Collections;
using System.Collections.Generic;
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;
    }

    public enum FadeDirection
    {
        In, //Alpha = 1
        Out // Alpha = 0
    }
    #endregion

    #region FADE
    public IEnumerator Fade(FadeDirection fadeDirection)
    {
        if(isFading)
        {
            yield return null;
        }

        isFading = true;

        fadeOutUIGameobjectImage.SetActive(true);

        float alpha = (fadeDirection == FadeDirection.Out) ? 1 : 0;
        float fadeEndValue = (fadeDirection == FadeDirection.Out) ? 0 : 1;
        if (fadeDirection == FadeDirection.Out)
        {
            while (alpha >= fadeEndValue)
            {
                SetColorImage(ref alpha, fadeDirection);
                yield return null;
            }

            fadeOutUIGameobjectImage.SetActive(false);
        }
        else
        {
            fadeOutUIGameobjectImage.SetActive(true);

            while (alpha <= fadeEndValue)
            {
                SetColorImage(ref alpha, fadeDirection);
                yield return null;
            }
        }

        isFading = false;
    }
    #endregion

    #region HELPERS

    public IEnumerator FadeAndLoadScene(FadeDirection fadeDirection, string sceneToLoad)
    {
        yield return Fade(fadeDirection);

        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 SetColorImage(ref float alpha, FadeDirection fadeDirection)
    {
        if (fadeOutUIImage == null)
        {
            fadeOutUIImage = fadeOutUIGameobjectImage.GetComponent<Image>();
        }

        fadeOutUIImage.color = new Color(fadeOutUIImage.color.r, fadeOutUIImage.color.g, fadeOutUIImage.color.b, alpha);
        alpha += Time.deltaTime * (1.0f / fadeSpeed) * ((fadeDirection == FadeDirection.Out) ? -1 : 1);
    }
    #endregion
}

In another script where I'm using the fade in/out I did something like this:在我使用淡入/淡出的另一个脚本中,我做了这样的事情:

I'm starting another new Coroutine :我正在开始另一个新的协程:

StartCoroutine(WaitBeforeOut());

Then inside there : I'm waiting 5 seconds before starting the fade out.然后在里面:我在开始淡出之前等待 5 秒钟。 This 5 seconds is enough time to let the fade in to finish and then to start the fade out :这 5 秒足以让淡入完成,然后开始淡出:

but it's not a good solution.但这不是一个好的解决方案。 I don't want in any script that I'm using the fade in/out to do it this way.我不希望在我使用淡入/淡出的任何脚本中这样做。 It's working but not so good a solution.它正在工作,但不是很好的解决方案。

IEnumerator WaitBeforeOut()
    {
        yield return new WaitForSeconds(5f);

        StartCoroutine(sceneFader.Fade(SceneFader.FadeDirection.Out));
    }

the code for fading is incredibly simple.淡入淡出的代码非常简单。 in pseudocode,在伪代码中,

boolean alreadyFading = false

function doFade
   if alreadyFading == true return
   alreadyFading = true
   start coroutine _fade

coroutine _fade
   if alpha > 0.01
     alpha = alpha - 0.01
     loop
   alreadyFading = false

Yes, of course you need a local flag there, a boolean, to see if you are already animating.是的,当然你需要一个本地标志,一个布尔值,看看你是否已经在制作动画。 Note that this is true of every animation of any type (on any system!)请注意,这适用于任何类型的每个动画(在任何系统上!)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM