Hyun's Wonderwall

유니티 페이드 인&페이드 아웃 효과 주기 - Panel의 불투명도를 조절 본문

Study/Unity, C#

유니티 페이드 인&페이드 아웃 효과 주기 - Panel의 불투명도를 조절

Hyun_! 2024. 1. 21. 01:43

유니티 2D 게임의 씬 전환 등에서 페이드 인/페이드 아웃 효과를 주고자 한다. 체크박스 on/off를 통해 페이드인과 페이드아웃 효과를 모두 줄 수 있는 스크립트를 원해서 공부하며 만들어 보았다. 덕분에 Coroutine을 조금 더 잘 이해하게 된 것 같다.

FadeController.cs

효과를 줄 오브젝트(GameObject panel)와 페이드인여부(bool isFadeIn)를 인스펙터에서 지정하면, 페이드인과 페이드아웃 중 하나를 적용할 수 있는 스크립트이다. 

 

(1) 효과를 주고자 하는 씬에서 UI > Panel 오브젝트를 하나 만들어 Image 색을 검정으로 지정했다.

(2) 작성한 FadeController.cs를 Canvas에 붙였다.

- IsFadeIn을 체크하면 페이드인, 체크를 하지 않으면 페이드아웃.

- Panel에는 위의 오브젝트 FadePanel을 넣었다. Panel에 직접 붙이지 않은 이유는 Panel을 활성화/비활성화하는 코드가 있기 때문이다. (-> Panel을 통해 전환 효과 중에 클릭을 막기 위해)

 

isFadeIn이 true이면 해당 씬을 플레이하는 즉시 페이드인이 된다.

isFadeIn이 false이면 다른 외부 스크립트에서 FadeOut()을 호출하면 페이드아웃이 된다.

 

(3) 콜백을 고려하기: 외부 스크립트에서 페이드 효과를 호출하는데 그 이후에 다른 동작을 해야 하는 경우가 있다. 

(예: 씬을 전환하는 외부 스크립트에서 FadeOut()을 호출하는 경우, 씬 전환 동작이 페이드아웃 효과가 종료된 이후에 일어나야 함)

외부 스크립트에서 씬에 FadeController가 있는지 찾고, 있는 경우 FadeController의 RegisterCallback 메소드를 호출해 다음 동작을 저장한 후에 페이드 효과 메소드를 호출하도록 한다. (->효과를 끝마친 후 액션을 필드에서 확인하고 invoke됨)

(외부 스크립트 코드는 생략)

*참고한 블로그들: https://blog.naver.com/bestmic/221335432969https://makerejoicegames.tistory.com/87

 

(4) FadeController 스크립트 코드

using System;
using System.Collections;
using UnityEngine;

public class FadeController : MonoBehaviour // Panel 불투명도 조절해 페이드인 or 페이드아웃
{
    public bool isFadeIn; // true=FadeIn, false=FadeOut
    public GameObject panel; // 불투명도를 조절할 Panel 오브젝트
    private Action onCompleteCallback; // FadeIn 또는 FadeOut 다음에 진행할 함수

    void Start()
    {
        if (!panel)
        {
            Debug.LogError("Panel 오브젝트를 찾을 수 없습니다.");
            throw new MissingComponentException();
        }

        if (isFadeIn) // Fade In Mode -> 바로 코루틴 시작
        {
            panel.SetActive(true); // Panel 활성화
            StartCoroutine(CoFadeIn());
        }
        else
        {
            panel.SetActive(false); // Panel 비활성화
        }
    }

    public void FadeOut()
    {
        panel.SetActive(true); // Panel 활성화
        Debug.Log("FadeCanvasController_ Fade Out 시작");
        StartCoroutine(CoFadeOut());
        Debug.Log("FadeCanvasController_ Fade Out 끝");
    }

    IEnumerator CoFadeIn()
    {
        float elapsedTime = 0f; // 누적 경과 시간
        float fadedTime = 0.5f; // 총 소요 시간

        while (elapsedTime <= fadedTime)
        {
            panel.GetComponent<CanvasRenderer>().SetAlpha(Mathf.Lerp(1f, 0f, elapsedTime / fadedTime));
            
            elapsedTime += Time.deltaTime;
            Debug.Log("Fade In 중...");
            yield return null;
        }
        Debug.Log("Fade In 끝");
        panel.SetActive(false); // Panel을 비활성화
        onCompleteCallback?.Invoke(); // 이후에 해야 하는 다른 액션이 있는 경우(null이 아님) 진행한다
        yield break;
    }

    IEnumerator CoFadeOut()
    {
        float elapsedTime = 0f; // 누적 경과 시간
        float fadedTime = 0.5f; // 총 소요 시간

        while (elapsedTime <= fadedTime)
        {
            panel.GetComponent<CanvasRenderer>().SetAlpha(Mathf.Lerp(0f, 1f, elapsedTime / fadedTime));

            elapsedTime += Time.deltaTime;
            Debug.Log("Fade Out 중...");
            yield return null;
        }

        Debug.Log("Fade Out 끝");
        onCompleteCallback?.Invoke(); // 이후에 해야 하는 다른 액션이 있는 경우(null이 아님) 진행한다
        yield break;
    }

    public void RegisterCallback(Action callback) // 다른 스크립트에서 콜백 액션 등록하기 위해 사용
    {
        onCompleteCallback = callback;
    }
}

위의 코드가 가장 깔끔한 페이드인&아웃 방법이고

아래는 시행착오 코드들이다.

 

(1) 이 코드는 Canvas에 Canvas Group 컴포넌트를 추가해 하위 UI들의 투명도를 조절 가능하는 코드이다.

Canvas 하위 Sprite Render 오브젝트들의 투명도가 조절 안되어.. 동작은 하지만 원하는 것은 이루지 못했다.

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class FadeController : MonoBehaviour
{
    public bool isFadeIn; // true=FadeIn, false=FadeOut

    public CanvasGroup canvasGroup;
    private float elapsedTime = 0f; // 경과 시간
    private float fadedTime = 1f; // 총 소요 시간
    private Action onCompleteCallback; // FadeIn 또는 FadeOut 다음에 진행할 함수

    void Start()
    {
        canvasGroup = GetComponent<CanvasGroup>();
        if (!canvasGroup)
        {
            Debug.LogError("canvasGroup 컴포넌트를 찾을 수 없습니다.");
            throw new MissingComponentException();
        }

        // Fade In Mode
        if (isFadeIn)
        {
            canvasGroup.alpha = 0f;
            StartCoroutine(CoFadeIn());
        }

        // Fade Out Mode
        else
        {
            canvasGroup.alpha = 1f;
            // FadeOut 함수 호출 시 코루틴 시작
        }
    }

    public void RegisterCallback(Action callback) // 다른 스크립트에서 콜백 액션 등록하기 위해 사용
    {
        onCompleteCallback = callback;
    }

    public void FadeOut()
    {
        StartCoroutine(CoFadeOut());
    }

    IEnumerator CoFadeIn()
    {
        while (elapsedTime <= fadedTime)
        {
            canvasGroup.alpha = Mathf.Lerp(0f, 1f, elapsedTime / fadedTime);
            elapsedTime += Time.deltaTime;
            yield return null;
        }
    }

    IEnumerator CoFadeOut()
    {
        while (elapsedTime <= fadedTime)
        {
            canvasGroup.alpha = Mathf.Lerp(1f, 0f, elapsedTime / fadedTime);
            elapsedTime += Time.deltaTime;
            yield return null;
        }

        if (onCompleteCallback != null)
        {
            onCompleteCallback();
        }
    }
}

 

(2) 이 코드는 (1) 코드에서 Canvas 자식들 오브젝트들의 Sprite Render 불투명도를 조절하는 코드이다.

이와 같이 하면 겹쳐있는 스프라이트들이 비쳐보이게 되어 실패... 그러나 나중에 쓸 일이 있을 수도 있을 것 같아 기록해둔다.

using System;
using System.Collections;
using UnityEngine;

public class FadeCanvasController : MonoBehaviour // 캔버스 전체를 페이드인 or 페이드아웃
{
    public bool isFadeIn; // true=FadeIn, false=FadeOut
    private Action onCompleteCallback; // FadeIn 또는 FadeOut 다음에 진행할 함수

    void Start()
    {

        canvasGroup = gameObject.GetComponent<CanvasGroup>();
        if (!canvasGroup)
        {
            Debug.LogError("CanvasGroup 컴포넌트를 찾을 수 없습니다.");
            throw new MissingComponentException();
        }

        if (isFadeIn) // Fade In Mode -> 바로 코루틴 시작
        {
            canvasGroup.alpha = 0f;
            StartCoroutine(CoFadeIn());
        }
        else // Fade Out Mode -> FadeOut 함수 호출 시 코루틴 시작
        {
            canvasGroup.alpha = 1f;
        }
    }

    public void FadeOut()
    {
        Debug.Log("FadeCanvasController_ Fade Out 시작");
        StartCoroutine(CoFadeOut());
        Debug.Log("FadeCanvasController_ Fade Out 끝");
    }

    IEnumerator CoFadeIn()
    {
        float elapsedTime = 0f; // 누적 경과 시간
        float fadedTime = 0.5f; // 총 소요 시간

        while (elapsedTime <= fadedTime)
        {
            canvasGroup.alpha = Mathf.Lerp(0f, 1f, elapsedTime / fadedTime); // Canvas가 투명 -> 불투명
            SetChildRenderersAlpha(Mathf.Lerp(0f, 1f, elapsedTime / fadedTime));
            
            elapsedTime += Time.deltaTime;
            Debug.Log("Fade In 중...");
            yield return null;
        }
        Debug.Log("Fade In 끝");
        onCompleteCallback?.Invoke(); // 이후에 해야 하는 다른 액션이 있는 경우(null이 아님) 진행한다
        yield break;
    }

    IEnumerator CoFadeOut()
    {
        float elapsedTime = 0f; // 누적 경과 시간
        float fadedTime = 0.5f; // 총 소요 시간

        while (elapsedTime <= fadedTime)
        {
            canvasGroup.alpha = Mathf.Lerp(1f, 0f, elapsedTime / fadedTime); // Canvas가 불투명 -> 투명
            SetChildRenderersAlpha(Mathf.Lerp(1f, 0f, elapsedTime / fadedTime));
            
            elapsedTime += Time.deltaTime;
            Debug.Log("Fade Out 중...");
            yield return null;
        }

        Debug.Log("Fade Out 끝");
        onCompleteCallback?.Invoke(); // 이후에 해야 하는 다른 액션이 있는 경우(null이 아님) 진행한다
        yield break;
    }

    void SetChildRenderersAlpha(float alpha)
    {
        // Canvas 하위의 모든 Renderer를 찾아서 투명도를 조절
        Renderer[] renderers = gameObject.GetComponentsInChildren<Renderer>(true);
        foreach (Renderer renderer in renderers)
        {
            renderer.material.color = new Color(renderer.material.color.r, renderer.material.color.g, renderer.material.color.b, alpha);
        }
    }
    public void RegisterCallback(Action callback) // 다른 스크립트에서 콜백 액션 등록하기 위해 사용
    {
        onCompleteCallback = callback;
    }
}