[Skill] 로딩 화면 구현하기 (UI 방식)
in Unity
전에 썼던 글과는 다르게 이번에는 로딩 화면을 구현하는데 UI를 써보도록하고 싱글톤 패턴까지 적용해보겠다.
우선 canvas 오브젝트를 만들고 LoadingUI로 이름을 바꾼 뒤 Canvas Group 컴포넌트를 추가하자.
Canvas Group 컴포넌트에 프로퍼티 중 Alpha는 LoadingUI의 하위 오브젝트의 알파값까지 영향을 준다.
그리고 로딩바 이미지와, 로딩바 프레임 이미지를 만들어주고 아래의 스크립트를 작성하자.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using System;
public class LoadingSceneController : MonoBehaviour
{
//기존 씬을 UI로 가리고 다음 씬을 불러오는 역할
//싱글톤패턴으로 구현하여 하나뿐인 로딩 UI를 어디서든지 호출할 수 있게 만듬
private static LoadingSceneController instance;
public static LoadingSceneController Instance
{
get
{
if (instance == null)
{
var obj=FindObjectOfType<LoadingSceneController>();
if(obj != null)
{
instance = obj;
}
else
{
//새로운 인스턴스를 만들 때 미리 만들어둔 배경UI이나 로딩바의 셋팅이 있어야하므로
//리소스폴더에 있는 UI들을 인스턴화 한다.
instance = Create();
}
}
return instance;
}
}
private static LoadingSceneController Create()
{
//Resources폴더에서 LoadingSceneController타입의 LoadingUI를 불러옴
return Instantiate(Resources.Load<LoadingSceneController>("LoadingUI"));
}
private void Awake()
{
if(instance != this)
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
}
[SerializeField]
private CanvasGroup CanvasGroup; //로딩 UI에서 사용됨
[SerializeField]
private Image progressBar;
private string loadSceneName;
public void LoadScene(string sceneName)
{
//비활성화 되어있던 LoadingSceneController오브젝트를 활성화 시키고
//씬을 로딩하는 과정 처리
gameObject.SetActive(true);
//씬의 로딩이 끝나는 시점을 알려줌( SceneManager.sceneLoaded)
SceneManager.sceneLoaded += OnSceneLoaded;
loadSceneName = sceneName;
StartCoroutine(LoadSceneProcess()); //씬을 로딩하는 과정을 처리함
}
private IEnumerator LoadSceneProcess()
{
progressBar.fillAmount = 0;
//yield return 뒤에 StartCoroutine이 있으면 StartCoroutine으로 호출한 코루틴이 종료될 때 까지 멈춤
yield return StartCoroutine(Fade(true));//FadeIn으로 자연스러움 연출
//FadeIn이 끝나면 비동기로 씬을 불러오기 시작
AsyncOperation op = SceneManager.LoadSceneAsync(loadSceneName);
op.allowSceneActivation = false;//씬의 로딩이 끝나도 자동으로 넘어가지 않음
float timer = 0f;
while (!op.isDone)
{
yield return null;
if (op.progress < 0.9f)
{
progressBar.fillAmount=op.progress;
}
else
{
timer += Time.unscaledDeltaTime;
progressBar.fillAmount = Mathf.Lerp(0.9f,1.0f,timer);
if (progressBar.fillAmount >= 1.0f)
{
op.allowSceneActivation = true;
yield break;
}
}
}
}
private void OnSceneLoaded(Scene arg0, LoadSceneMode arg1)
{
//매개변수 Scene을 통해 어떤 씬이 로딩 되었는지 알 수 있음
//씬의 로딩이 끝나면 유니티 엔진이 SceneManager.sceneLoaded에 등록해둔
//OnSceneLoaded를 자동으로 호출해서 씬로딩이 끝난 시점을 우리에게 알려준다.
if (arg0.name == loadSceneName)
{
//불러오고자 하는 씬이 모두 불려와 진 것
StartCoroutine(Fade(false));
SceneManager.sceneLoaded -= OnSceneLoaded;
}
}
private IEnumerator Fade(bool isFadeIn)
{
float timer = 0f;
while (timer <= 1f)
{
yield return null;
timer += Time.unscaledDeltaTime*3f;
CanvasGroup.alpha = isFadeIn ? Mathf.Lerp(0f, 1f, timer) : Mathf.Lerp(1f, 0f, timer);
}
if (!isFadeIn)
{
gameObject.SetActive(false);
}
}
}
코드가 상당히 길다. 하지만 이해하기 어려운 부분은 그다지 없다. 설명 또한 코드에 바로 적어두었다.
그나마 바로 해석하기 어려운 것은 SceneManager.sceneLoaded += OnSceneLoaded; 이 부분이다. 처음 봤을 때 저게 뭐지 했다가 이벤트라는걸 알고 c#에 이벤트 체인이 떠올랐다. 그리고 마지막으로 유니티에서는 Resoueces폴더를 만들면 런타임중에 Resources폴더안에 있는 에셋을 불러올 수 있게 만들어 놨다.
예시로는 Resources.Load<LoadingSceneController(“LoadingUI”)이 있다.
직역하면 리소스폴더에서 불러와라 LoadingSceneController타입의 LoadingUI를 이런식으로 해석이 가능하다.