169 lines
4.9 KiB
C#
169 lines
4.9 KiB
C#
using System;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
public class SceneLoadManager : MonoBehaviour
|
|
{
|
|
public static SceneLoadManager Instance;
|
|
|
|
[SerializeField] private GameObject _loadingRoot;
|
|
[SerializeField] private Camera _loadingCam;
|
|
[SerializeField] private Transform _loadingCamTargetTransform;
|
|
|
|
private bool _isChangingScene = false;
|
|
|
|
public bool IsChangingScene => _isChangingScene;
|
|
|
|
private void Awake()
|
|
{
|
|
if (Instance == null)
|
|
{
|
|
Instance = this; // 만들어진 자신을 인스턴스로 설정
|
|
DontDestroyOnLoad(gameObject);
|
|
}
|
|
else
|
|
{
|
|
Destroy(gameObject); // 이미 인스턴스가 있으면 자신을 파괴
|
|
}
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
SceneManager.sceneLoaded += OnSceneLoaded;
|
|
OnSceneLoaded(SceneManager.GetActiveScene(), LoadSceneMode.Single);
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
if (Instance == this)
|
|
{
|
|
SceneManager.sceneLoaded -= OnSceneLoaded;
|
|
}
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (_loadingRoot != null && _loadingCamTargetTransform != null)
|
|
{
|
|
_loadingRoot.transform.position = _loadingCamTargetTransform.position;
|
|
}
|
|
}
|
|
|
|
// 씬이 로드되었을때 호출
|
|
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
|
{
|
|
MonoBehaviour[] allObjs = UnityEngine.Object.FindObjectsByType<MonoBehaviour>(FindObjectsSortMode.None);
|
|
|
|
foreach (var obj in allObjs)
|
|
{
|
|
if (obj is ISceneInitializable initializable)
|
|
{
|
|
// 씬에서 ISceneInitializable 인터페이스를 가진 오브젝트의 초기화 로직을 실행
|
|
initializable.OnSceneLoaded();
|
|
}
|
|
}
|
|
}
|
|
|
|
public void SetSceneLoadingProgressValue(float value)
|
|
{
|
|
// 여기에 로딩바 UI 연결 예정
|
|
}
|
|
|
|
public void RequestSceneChange(string sceneName)
|
|
{
|
|
if (_isChangingScene)
|
|
{
|
|
Debug.Log("이미 씬 전환 중입니다.");
|
|
return;
|
|
}
|
|
|
|
if (string.IsNullOrEmpty(sceneName))
|
|
{
|
|
Debug.LogWarning("이동할 씬 이름이 비어있습니다.");
|
|
return;
|
|
}
|
|
|
|
_ = SceneChange(sceneName);
|
|
}
|
|
|
|
private async Awaitable SceneChange(string sceneName)
|
|
{
|
|
try
|
|
{
|
|
_isChangingScene = true;
|
|
|
|
// 로딩바 수치 0으로 설정
|
|
SetSceneLoadingProgressValue(0f);
|
|
|
|
if (_loadingRoot != null)
|
|
{
|
|
_loadingRoot.SetActive(true);
|
|
}
|
|
|
|
AsyncOperation op = SceneManager.LoadSceneAsync(sceneName);
|
|
|
|
if (op == null)
|
|
{
|
|
Debug.LogError($"씬 로드 실패: {sceneName}");
|
|
_isChangingScene = false;
|
|
return;
|
|
}
|
|
|
|
// 자동 전환을 하고 싶지 않을 경우 false로 두었다가 true로 바꾸면 그 때 전환됨
|
|
op.allowSceneActivation = false;
|
|
|
|
// 화면에 보여줄 로딩 수치
|
|
float displayProgress = 0f;
|
|
|
|
// op.progress 0.9가 데이터 로딩이 끝난 기준
|
|
while (op.progress < 0.9f)
|
|
{
|
|
// 실제 로딩 수치
|
|
float realProgress = Mathf.Clamp01(op.progress / 0.9f);
|
|
|
|
// 보여줄 값을 실제값을 향해 부드럽게 이동
|
|
displayProgress = Mathf.MoveTowards(displayProgress, realProgress, Time.deltaTime * 0.5f);
|
|
|
|
// 로딩바 UI에 값 적용
|
|
SetSceneLoadingProgressValue(displayProgress);
|
|
|
|
// 자기자신이 파괴될때 토큰에 취소요청을 보냄
|
|
await Awaitable.NextFrameAsync(this.destroyCancellationToken);
|
|
}
|
|
|
|
// 로딩바 수치 1(100%)로 설정
|
|
SetSceneLoadingProgressValue(1f);
|
|
|
|
// 잠시 대기했다가 전환
|
|
await Awaitable.WaitForSecondsAsync(1.0f, this.destroyCancellationToken);
|
|
|
|
// 다음씬으로 넘어가도 됨을 알림
|
|
op.allowSceneActivation = true;
|
|
|
|
// 씬 활성화가 완전히 끝날 때까지 대기
|
|
while (!op.isDone)
|
|
{
|
|
await Awaitable.NextFrameAsync(this.destroyCancellationToken);
|
|
}
|
|
|
|
// VR용 로직
|
|
// 트래킹이 중단되면 안되기 때문에 카메라를 유지해야 한다
|
|
if (Camera.main != null)
|
|
{
|
|
_loadingCamTargetTransform = Camera.main.transform;
|
|
}
|
|
|
|
if (_loadingRoot != null)
|
|
{
|
|
_loadingRoot.SetActive(false);
|
|
}
|
|
|
|
_isChangingScene = false;
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
Debug.Log("씬 전환 작업이 취소됨");
|
|
_isChangingScene = false;
|
|
}
|
|
}
|
|
} |