메인 화면에서 다른 씬으로 이동할 때, 게임이 멈춘 상태로 씬을 로딩하는 것보단 움직이는 로딩 화면을 보고 있는 것이 나을 것이다.
이 로딩 화면 기능을 만들어보자
유니티 에디터에서 준비
필자가 만들 로딩 화면은 Scene 1 -> Scene 2 로 넘어갈 때, 중간에 로딩 Scene으로 넘어가는 것이 아닌 Canvas를 띄워 안보이는 뒷편에서 Scene2를 로딩하고 바로 이동하는 식으로 진행할 것이다.
기본 씬 하이어라키에서인 화면에서 다른 씬으로 이동할 때, 게임이 멈춘 상태로 씬을 로딩하는 것보단 움직이는 로딩 화면을 보고 있는 것이 나을 것이다.
이 로딩 화면 기능을 만들어보자
유니티 에디터에서 준비
필자가 만들 로딩 화면은 Scene 1 -> Scene 2 로 넘어갈 때, 중간에 로딩 Scene으로 넘어가는 것이 아닌 Canvas를 띄워 안보이는 뒷편에서 Scene2를 로딩하고 바로 이동하는 식으로 진행할 것이다.

일단 이 UI의 기능을 만들 Script를 하나 생성해준다. (유니티 6은 MonoBehaviour Script를 만들면 된다)
이름은 LoadingUIManager로 설정하였다.

기본 씬 하이어라키에서 우클릭 -> UI -> Canvas 를 하나 생성해주고, Add Component로 Canvas Group과 방금 만든 Script를 추가해준다.
Canvas Group은 Canvas 아래에 있는 모든 것들을 한번에 제어해줄 수 있는 Component이다.

캔버스 아래에는 Background 이미지 및 로딩 텍스트, 로딩 바를 넣어주었다.

로딩바 이미지는 넣은 뒤 ImageType을 Filled, Fill Method를 Horizontal, Fill Amout를 0으로 만들어 로딩이 될 때 채워질 수 있도록 설정한다.
이 캔버스는 스크립트를 전부 제작한 후 다시 수정하여 프리팹으로 만들 것이기 때문에 두고 넘어가도록 하겠다.
Script 작성
로딩화면은 당장의 하나의 씬만 넘어갈 때 사용할 뿐만이 아닌 다른 씬을 넘어갈 때에도 사용할 것이다.
또한 여러 인스턴스를 관리할 필요 없이 이 하나만 관리하여 안정성을 향상시키기 위해서 '싱글톤' 패턴으로 제작할 것이다.

현재 인스턴스를 저장해줄 변수 및 Canvas 내용, 넘어갈 씬 이름을 저장할 변수를 선언해준다.
싱글톤 패턴의 인스턴스를 받아올 때 씬 내에 존재하면 FindObjectOfType으로 찾아오고, 그렇지 않으면 Create 함수를 사용하여 LoadingUI 프리팹을 불러오게 할 것이다.

Create 함수를 생성하여 Resources폴더 내에 있는 LoadingUI를 불러오게 한다.
(Resources 폴더 코드를 사용하는 것이기 때문에 스크립트를 전부 만들고, 프리팹을 저장할 때 Resources 폴더를 생성하여 그 아래에 저장하여야 한다.)

만약 어떠한 문제로 인스턴스가 자기 자신이 아닐 수 있기 때문에 이를 검사하여 아닐 경우 Destroy하거나, 맞을 경우에는 씬을 넘어갈 때 파괴되지 않도록 DontDestroyOnLoad로 파괴를 방지하여 준다.
이제 씬을 넘어가도록 하기 위해 함수를 작성해줄 것이다.

LoadScene이라는 함수를 만들어 받아 온 sceneName을 저장하여주고, 다음 씬이 로딩되었는지 확인하기 위해 OnSceneLoaded를 SceneManager 콜백에 함수를 추가하여 준다.
Coroutine을 사용하여 LoadSceneProgress를 실행해준다.


LoadSceneProgress는 다음과 같은 구조로 이루어져 있다.
맨 처음 fillProgress바를 0.0f으로 설정해주어 로딩바를 초기화 해주고, Fade를 진행하여 준다.
Fade는 isFadeIn으로 받아온 매개변수를 통해 Canvas의 Alpha값을 조절하여 Fade In 애니메이션을 실행하게 된다.
Fade In 애니메이션이 끝나면 AsyncOperation을 통해 비동기적으로 _loadSceneName을 가진 Scene을 불러온다.
이 때, asyncOperation.allowSceneActivation을 통해 바로 씬 이동이 되게 하지 않고, progressBar를 조절하게 한다.
asyncOperation.progress을 통해 값을 받아와 0.9f 미만이면 fillAmount 를 통해 채우지만, 그 이상이 넘어가면 Mathf.Lerp 를 이용하여 자연스럽게 채운 뒤 allowSceneActivation 를 true 값으로 바꿔주어 씬을 이동하게 된다.

씬이 로딩이 다 되었을 때 위에서 콜백 함수를 설정하였기 때문에 OnSceneLoaded가 실행되게 되고, Fade를 통해 Fade Out 애니메이션 실행과 함께 콜백을 제거하게 된다.
이를 제거하지 않으면 씬을 이동할 때 콜백 함수가 중첩되어 문제가 발생할 수 있기 때문에 제거하는 것이다.
총 스크립트는 다음과 같다.
using System;
using System.Collections;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class LoadingUIManager : MonoBehaviour
{
private static LoadingUIManager _instance;
[SerializeField]
private CanvasGroup _loadingCanvas;
[SerializeField]
private Image _progressBar;
private string _loadSceneName;
public static LoadingUIManager Instance
{
get
{
if (_instance == null)
{
var obj = FindObjectOfType<LoadingUIManager>();
if (obj != null)
_instance = obj;
else
_instance = Create();
}
return _instance;
}
}
private static LoadingUIManager Create()
{
return Instantiate(Resources.Load<LoadingUIManager>("LoadingUI"));
}
private void Awake()
{
if (Instance != this)
{
Destroy(gameObject);
return;
}
DontDestroyOnLoad(gameObject);
}
public void LoadScene(string sceneName)
{
gameObject.SetActive(true);
SceneManager.sceneLoaded += OnSceneLoaded;
_loadSceneName = sceneName;
StartCoroutine(LoadSceneProgress());
}
private IEnumerator LoadSceneProgress()
{
_progressBar.fillAmount = 0.0f;
yield return StartCoroutine(Fade(true));
AsyncOperation asyncOperation = SceneManager.LoadSceneAsync(_loadSceneName);
asyncOperation.allowSceneActivation = false;
float timer = 0.0f;
while (!asyncOperation.isDone)
{
yield return null;
if (asyncOperation.progress < 0.9f)
_progressBar.fillAmount = asyncOperation.progress;
else
{
timer += Time.unscaledDeltaTime;
_progressBar.fillAmount = Mathf.Lerp(0.9f, 1.0f, timer);
if (_progressBar.fillAmount >= 1.0f)
{
asyncOperation.allowSceneActivation = true;
yield break;
}
}
}
}
private IEnumerator Fade(bool isFadeIn)
{
float timer = 0.0f;
while (timer <= 1.0f)
{
yield return null;
timer += Time.unscaledDeltaTime * 3.0f;
_loadingCanvas.alpha = isFadeIn ? Mathf.Lerp(0.0f, 1.0f, timer) : Mathf.Lerp(1.0f, 0.0f, timer);
}
if (!isFadeIn)
gameObject.SetActive(false);
}
private void OnSceneLoaded(Scene arg0, LoadSceneMode arg1)
{
if (arg0.name == _loadSceneName)
{
StartCoroutine(Fade(false));
SceneManager.sceneLoaded -= OnSceneLoaded;
}
}
}
프리팹으로 제작
위에서 스크립트를 작성하면서 기능을 전부 설정하였다.

LoadingUIManager 에서 이미 만들어 둔 Group 과 Progress Bar 를 드래그를 통해 연결한다.

이 후 하이어라키에 있는 LoadingUI Canvas를 Assets / Resources 에 드래그 해주면 Prefab이 생성되어 이 후에 사용할 때 편리하게 적용할 수 있게 된다.
완성

이미 만들어두었던 다른 Canvas 의 Button에서 클릭 이벤트를 생성해주었다.

Instance를 통해 씬을 불러오는 함수를 실행시키게 되면 로딩 화면을 통해 자연스럽게 씬 이동이 실행되게 된다.
결과는 다음과 같다.
'Unity > 공부' 카테고리의 다른 글
| [Unity C#] Photon Fusion2 환경에서 Photon Voice2 사용하기 (1) | 2025.12.11 |
|---|---|
| [Unity C#] 자연스럽게 등장하는 UI 애니메이션 구현 (0) | 2025.04.19 |
| [Unity C#] Dynamic Menu에 따른 카메라 움직임 구현 (0) | 2025.04.19 |
CSE & GAME 개발 블로그
포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 부탁드립니다!