[Unity] UniTask로 씬 이동하기
![[Unity] UniTask로 씬 이동하기](/_next/image?url=https%3A%2F%2Fdata.develog.develrocket.com%2Fupload%2Fdevelog%2Fuser_1777093328305%2F1781529944310-7pysou%2F_____2026-06-15_222406.png&w=3840&q=75)
본문 로딩 중...
댓글 0
댓글을 작성하려면 로그인이 필요합니다.
아직 댓글이 없습니다. 첫 번째 댓글을 작성해보세요!
흐름
버튼 클릭
│
▼
① 페이드 아웃 (화면 까매짐, 완료까지 대기) ─ 약 1초
│ ← 이 시점엔 아직 씬 A, 로드 시작 안 함
▼
③ 씬 B 비동기 로드 시작 (까만 화면 뒤에서)
│ progress 0.9 도달 + 최소시간 대기
▼
allowSceneActivation = true → 씬 A 내려가고 씬 B 활성화
│ ← 화면은 여전히 까맣게 덮여 있음
▼
④ 페이드 인 (씬 B가 서서히 드러남)
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.InputSystem;
public class FadeController : MonoBehaviour
{
public CanvasGroup fadeCanvasGroup;
public float fadeDuration = 1f;
private static FadeController instance;
private void Awake()
{
if (instance != null && instance != this)
{
Destroy(gameObject);
return;
}
instance = this;
DontDestroyOnLoad(gameObject);
fadeCanvasGroup.alpha = 0f;
}
private void OnDestroy()
{
if (instance == this)
{
instance = null;
}
}
public async UniTask FadeOut(float duration = -1f)
{
if (duration < 0f)
duration = fadeDuration;
await FadeAsync(fadeCanvasGroup, 0f, 1f, duration);
}
public async UniTask FadeIn(float duration = -1f)
{
if (duration < 0f)
duration = fadeDuration;
await FadeAsync(fadeCanvasGroup, 1f, 0f, duration);
}
private async UniTask FadeAsync(CanvasGroup canvasGroup, float from, float to, float duration)
{
canvasGroup.alpha = from;
float elapsed = 0f;
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = elapsed / duration;
canvasGroup.alpha = Mathf.Lerp(from, to, t);
await UniTask.Yield();
}
canvasGroup.alpha = to;
}
}
while (elapsed < duration)
{
elapsed += Time.deltaTime;
float t = elapsed / duration;
canvasGroup.alpha = Mathf.Lerp(from, to, t);
await UniTask.Yield();
}
여기서 await UniTask.Yield()는 코루틴의 yield return null 과 같다
using System;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;
using TMPro;
using UnityEngine;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
public class SceneLoadController : MonoBehaviour
{
public Button loadSceneButton;
public Slider progressSlider;
public TMP_Text progressText;
public FadeController fadeController;
public string targetSceneName;
public float minimumLoadTime = 2f;
private bool isLoading = false;
private void Start()
{
loadSceneButton.onClick.AddListener(OnLoadSceneClicked);
fadeController = FindFirstObjectByType<FadeController>();
gameObject.SetActive(false);
}
private void OnLoadSceneClicked()
{
OnLoadSceneWithFadeAsync().Forget();
}
private async UniTask OnLoadSceneWithFadeAsync()
{
progressSlider.value = 0f;
progressText.text = "로딩 중... 0%";
await fadeController.FadeOut();
gameObject.SetActive(true);
isLoading = true;
loadSceneButton.interactable = false;
await LoadSceneAsync();
await fadeController.FadeIn();
isLoading = false;
// loadSceneButton.interactable = true;
// gameObject.SetActive(false);
}
private async UniTask LoadSceneAsync()
{
var progress = Progress.Create<float>(p =>
{
progressSlider.value = p;
progressText.text = $"로딩 중... {(int)(p * 100)}%";
Debug.Log($"로딩 중... {(int)(p * 100)}%");
});
var asyncOp = SceneManager.LoadSceneAsync(targetSceneName);
asyncOp.allowSceneActivation = false; // 얘를 true로 하면 씬이 바로 넘어감 나중에 바꿀때 true 전환
float startTime = Time.time;
while (asyncOp.progress < 0.9f)
{
progress.Report(Mathf.Clamp01(asyncOp.progress / 0.9f));
await UniTask.Yield();
}
float elapsed = Time.time - startTime;
if (elapsed < minimumLoadTime)
{
await UniTask.Delay(TimeSpan.FromSeconds(minimumLoadTime - elapsed));
}
progress.Report(1.0f);
asyncOp.allowSceneActivation = true;
await asyncOp.ToUniTask();
}
}
allowSceneActivation = false일 때 asyncOp.progress는 0.9에서 멈춰서 나머지 0.1은 활성화에 쓰이는 구간이라 false면 도달을 안함


잘 보이진 않지만 씬 A에서 페이드 아웃 되면서 로딩창이 뜨고 씬 B로 이동하는 것을 볼 수 있다.
UniTask핵심 API 종류
유니티 3rd-Party 라이브러리 UniTask
서로 다른 객체가 서로의 상태를 알아야하는 상황은 굉장히 자주 나오고, 어찌보면 객체지향 프로그래밍의 대부분의 고민이 여기서 비롯되는 것도 같다. 내가 지금껏 개발하며 사용한 패턴이나 전략들의 장단점을 모았다. 싱글턴 클래스 개념 단 한 개의 인스턴스만 존재하도록 하면서, static 으로 전역적인 접근이 가능하도록 하는 클래스. 장점 구현이 매우 빠르다.