Integrate a function between two IEnumerator

In fact, I have a coroutine FadeIn() (which opacifies an image), then I launch a function which changes according to my needs, then finally, I call my second coroutine FadeOut() (which makes transparent my image).

I thought of two ways to do this:

  • Create a unique coroutine at once: which brings together the FadeIn(), DoSomething() and FadeOut().
  • A system with booleans: which check when a fade is played and when the FadeIn() is finished.

There must be a cleaner way of doing that (less spaghetti)?

Thanks!

Edit: It looks like something like that for the first point

private IEnumerator FadeTransition()
        {
            // Enabled the panel
            m_fade.SetActive(true);
            // Increase opacity
            float opacity = 0;
            while (m_fade.GetComponent<UnityEngine.UI.Image>().color.a < 1)
            {
                m_fade.GetComponent<UnityEngine.UI.Image>().color = new Color(0, 0, 0, opacity);
                opacity += Time.deltaTime * 3;
                yield return null;
            }
            // Do something (which changes according to my needs)
            DoSomething();
            // Decrease opacity
            opacity = 1f;
            while (m_fade.GetComponent<UnityEngine.UI.Image>().color.a > 0)
            {
                m_fade.GetComponent<UnityEngine.UI.Image>().color = new Color(0, 0, 0, opacity);
                opacity -= Time.deltaTime * 3;
                yield return null;
            }
            // Disabled the panel
            m_fade.SetActive(false);
        }

For the second point, it’s about the same, simply that the FadeTransition coroutine is divided into two coroutines, and the DoSomething() function is integrated into a Update(), for example.

Edit 2: Finally, one of the solutions that could be envisaged, can be simplified by “passing a function (here, DoSomething()) as an argument to my coroutine FadeTransition ()

My Fade functions are made so I take a graphic element, and I have a FadeMultiple function which just foreaches the Graphic array and calls coroutines. The way I check if they’re done, for an example the FadeIn function, is with :

while(element.color.a < 0.99f) yield return Constants.time_250ms;

where Constants.time_250ms is a static WaitForSeconds(0.25f).

The whole waiting is wrapped in an IEnumerator. No bools needed. But then again, I also use a system of static lists with a custom class containing the transform of the object edited and the IEnumerator of the coroutine called, and every time I call fade, if the object I want to edit has the same transform as an object in the list (which holds all coroutine and object transfom pairs which weren’t stopped), then it stops the paired coroutine and removes that class instance from the list. This prevents the object color being stuck if two opposite coriutines are called at once. One will always kill the other instance even if there are thousands of instances of the same IEnumerator running.

You can’t change opacity directly, get color value and do change it then assign to the image. And always cache preferences you call frequently (avoid calling GetComponent every frame).

public float transitionSpeed = 0.5f;

    IEnumerator FadeInAndOut()
    {
        // Enabled the panel
        m_fade.SetActive(true);
        UnityEngine.UI.Image image = m_fade.GetComponent<UnityEngine.UI.Image>();
        Color color = image.color;
        float opacity = 0;
        color.a = opacity;
        image.color = color;

        // Increase opacity
        while (opacity < 1)
        {
            opacity += Time.deltaTime * transitionSpeed;
            color.a = opacity;
            image.color = color;
            yield return null;
        }
        // Do something (which changes according to my needs)
        Debug.Log("opacity is full");
        // Decrease opacity
        opacity = 1f;
        while (opacity > 0)
        {
            opacity -= Time.deltaTime * transitionSpeed;
            color.a = opacity;
            image.color = color;
            yield return null;
        }
        // Disabled the panel
        m_fade.SetActive(false);
    }

EDIT:
U can use animation events, it would be much easier, the only problem is that animation won’t wait if you “DoSomething” takes more time…

You can do the fading in one loop for both fade in and out. By adding an opacity parameter to the coroutine you can then customise your fade when you call it

    private IEnumerator FadeTransition(float targetOpacity)
    {
        // Enabled the panel
        m_fade.SetActive(true);
        // Increase opacity

        var image = m_fade.GetComponent<UnityEngine.UI.Image>();
        var originalOpacity = image.color.a;

        while(true)
        {
            var currentOpacity = image.color.a;
            if (targetOpacity < originalOpacity)
            {
                currentOpacity -= Time.deltaTime * 3; ;
            }
            else if (targetOpacity > originalOpacity)
            {
                currentOpacity += Time.deltaTime * 3; ;
            }

            image.color = new Color(0, 0, 0, currentOpacity);

            if (Math.Abs(image.color.a - targetOpacity) < 0.1f) break;
            yield return null;
        }

        m_fade.SetActive(false);
    }

I haven’t tested it, but this code should fade to the targetOpacity you set. So you only have to do one loop.

Is it really important to you to do this in code? It seems like you are trying to animate the alpha channel. I wonder if just using an animation would be the way to go.

Your coroutine could then look something like this:

private IEnumerator Routine()
{
    targetAnimation.SetTrigger("FadeIn");
    yield return new WaitForSeconds(targetAnimation.GetCurrentAnimatorStateInfo(0).length);
    foreach (var signal in DoSomething()) yield return signal;
    targetAnimation.SetTrigger("FadeOut");
}

I’ve attached a full example.