243 lines
8.4 KiB
C#
243 lines
8.4 KiB
C#
using System;
|
|
using System.Threading.Tasks;
|
|
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;
|
|
[SerializeField] private LoadingScreen _loadingScreen;
|
|
|
|
[SerializeField] private Material _supermarketStartSkybox;
|
|
[SerializeField] private Material _supermarketLoadingSkybox;
|
|
|
|
[SerializeField, Min(0f)] private float _skyboxFadeTime = 1f;
|
|
// 정상(밝음) 상태에서의 스카이박스 _Exposure 값. 페이드 인의 도착 지점.
|
|
[SerializeField, Min(0f)] private float _skyboxNormalExposure = 1f;
|
|
|
|
// 공유 에셋이 더러워지지 않도록 런타임 인스턴스로 복제해 사용
|
|
private Material _runtimeStartSkybox;
|
|
private Material _runtimeLoadingSkybox;
|
|
|
|
private void Awake()
|
|
{
|
|
if (Instance == null)
|
|
{
|
|
Instance = this; //만들어진 자신을 인스턴스로 설정
|
|
}
|
|
else
|
|
{
|
|
Destroy(gameObject); //이미 인스턴스가 있으면 자신을 파괴
|
|
}
|
|
|
|
_loadingScreen = _loadingCam.GetComponentInChildren<LoadingScreen>();
|
|
|
|
if (_supermarketStartSkybox != null)
|
|
_runtimeStartSkybox = new Material(_supermarketStartSkybox);
|
|
if (_supermarketLoadingSkybox != null)
|
|
_runtimeLoadingSkybox = new Material(_supermarketLoadingSkybox);
|
|
|
|
if (_runtimeStartSkybox != null)
|
|
{
|
|
RenderSettings.skybox = _runtimeStartSkybox;
|
|
DynamicGI.UpdateEnvironment();
|
|
}
|
|
}
|
|
|
|
private void OnDestroy()
|
|
{
|
|
// 런타임 복제 인스턴스는 직접 정리
|
|
if (_runtimeStartSkybox != null) Destroy(_runtimeStartSkybox);
|
|
if (_runtimeLoadingSkybox != null) Destroy(_runtimeLoadingSkybox);
|
|
}
|
|
|
|
private void Start()
|
|
{
|
|
SceneManager.sceneLoaded += OnSceneLoaded;
|
|
OnSceneLoaded(SceneManager.GetActiveScene(), LoadSceneMode.Single);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if(_loadingCamTargetTransform != null)
|
|
{
|
|
_loadingCam.transform.position = _loadingCamTargetTransform.position;
|
|
_loadingCam.transform.rotation = _loadingCamTargetTransform.rotation;
|
|
}
|
|
}
|
|
|
|
//씬이 로드되었을때 호출
|
|
private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
|
|
{
|
|
Debug.Log("씬 로드됨");
|
|
|
|
if(scene.name == "GameScene")
|
|
{
|
|
MonoBehaviour[] allObjs = UnityEngine.Object.FindObjectsByType<MonoBehaviour>(FindObjectsSortMode.None);
|
|
|
|
foreach (var obj in allObjs)
|
|
{
|
|
if (obj is ITransScenePossible itsp)
|
|
{
|
|
itsp.OnSceneLoaded();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public async Awaitable FadeLoadingCanvas(bool isOut,float fadeTime)
|
|
{
|
|
float startAlpha = isOut ? 1f : 0f;
|
|
float endAlpha = isOut ? 0f : 1f;
|
|
|
|
float timer = 0;
|
|
_loadingScreen.LoadingScreenCanvasGroup.alpha = startAlpha;
|
|
|
|
while(timer < fadeTime)
|
|
{
|
|
timer += Time.deltaTime;
|
|
_loadingScreen.LoadingScreenCanvasGroup.alpha = Mathf.Lerp(startAlpha, endAlpha, timer / fadeTime);
|
|
await Awaitable.NextFrameAsync(this.destroyCancellationToken);
|
|
}
|
|
|
|
_loadingScreen.LoadingScreenCanvasGroup.alpha = endAlpha;
|
|
}
|
|
|
|
// 현재 RenderSettings.skybox의 _Exposure를 from→to로 보간
|
|
// Skybox/Procedural, /Cubemap, /Panoramic 셰이더 공통 프로퍼티
|
|
private async Awaitable FadeSkybox(float from, float to, float duration)
|
|
{
|
|
var sky = RenderSettings.skybox;
|
|
if (sky == null || !sky.HasFloat("_Exposure")) return;
|
|
|
|
float timer = 0f;
|
|
while (timer < duration)
|
|
{
|
|
timer += Time.deltaTime;
|
|
float k = Mathf.Clamp01(timer / duration);
|
|
sky.SetFloat("_Exposure", Mathf.Lerp(from, to, k));
|
|
DynamicGI.UpdateEnvironment();
|
|
await Awaitable.NextFrameAsync(this.destroyCancellationToken);
|
|
}
|
|
sky.SetFloat("_Exposure", to);
|
|
DynamicGI.UpdateEnvironment();
|
|
}
|
|
|
|
public async Awaitable SetSceneLoadingActive(bool isActive,float alphaTime)
|
|
{
|
|
if (isActive)
|
|
_loadingRoot.SetActive(true);
|
|
|
|
if (alphaTime > 0f)
|
|
{
|
|
await FadeLoadingCanvas(!isActive,alphaTime);
|
|
}
|
|
|
|
if (!isActive)
|
|
_loadingRoot.SetActive(false);
|
|
}
|
|
|
|
public void SetSceneLoadingActive(bool isActive)
|
|
{
|
|
_ = SetSceneLoadingActive(isActive, 0f);
|
|
}
|
|
|
|
public void SetSceneLoadingProgressValue(float value)
|
|
{
|
|
_loadingScreen.LoadingImage.fillAmount = value;
|
|
_loadingScreen.LoadingTextMeshProUGUI.text = $"{(value * 100):F0}% 로딩 중...";
|
|
}
|
|
public void SetSceneLoadingProgressValue(float value,string loadingText)
|
|
{
|
|
_loadingScreen.LoadingImage.fillAmount = value;
|
|
_loadingScreen.LoadingTextMeshProUGUI.text = loadingText;
|
|
}
|
|
|
|
public void RequestSceneChange(string sceneName)
|
|
{
|
|
_ = SceneChange(sceneName);
|
|
}
|
|
|
|
private async Awaitable SceneChange(string sceneName)
|
|
{
|
|
try
|
|
{
|
|
SetSceneLoadingProgressValue(0f);
|
|
|
|
//스카이 박스 페이드 아웃 로직
|
|
await FadeSkybox(_skyboxNormalExposure, 0f, _skyboxFadeTime);
|
|
|
|
// 검게 된 상태에서 머티리얼 교체 (교체 순간이 가려져 깜빡임 없음)
|
|
RenderSettings.skybox = _runtimeLoadingSkybox;
|
|
|
|
//스카이 박스 페이드 인 로직
|
|
await FadeSkybox(0f, _skyboxNormalExposure, _skyboxFadeTime);
|
|
|
|
await SetSceneLoadingActive(true,1f);
|
|
|
|
AsyncOperation op = SceneManager.LoadSceneAsync(sceneName);
|
|
|
|
//자동 전환을 하고 싶지 않을 경우 해당값을 false로 두었다가 true로 바꾸면 그 때 전환됨
|
|
op.allowSceneActivation = false;
|
|
|
|
//화면에 보여줄 로딩 수치
|
|
float displayProgress = 0f;
|
|
|
|
//op.progress 0.9가 데이터 로딩이 끝난 기준 allowSceneActivation이 트루면 바로 다음으로 넘어가면서 op.isDone이 true가 된다.
|
|
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); //자기자신이 파괴될때 토큰에 취소요청을 보냄
|
|
}
|
|
|
|
SetSceneLoadingProgressValue(1);
|
|
|
|
// 잠시 대기했다가 전환
|
|
await Awaitable.WaitForSecondsAsync(1.0f, this.destroyCancellationToken);
|
|
|
|
await SetSceneLoadingActive(false,1f);
|
|
|
|
//스카이 박스 페이드 아웃
|
|
await FadeSkybox(_skyboxNormalExposure, 0f, _skyboxFadeTime);
|
|
|
|
// 검게 된 상태에서 정상 스카이박스로 교체
|
|
RenderSettings.skybox = _runtimeStartSkybox;
|
|
|
|
//로딩 끝
|
|
op.allowSceneActivation = true;
|
|
|
|
// 씬 활성화가 완전히 끝날 때까지 대기
|
|
// allowSceneActivation가 true가 되고 완전히 전환되기까지는 몇프레임 걸림. op.isDone 은 이 과정이 끝난 뒤에 true가 됨.
|
|
while(!op.isDone)
|
|
{
|
|
await Awaitable.NextFrameAsync(this.destroyCancellationToken);
|
|
}
|
|
|
|
//VR용 로직
|
|
//트래킹이 중단되면 안되기 때문에 카메라를 유지해야 한다
|
|
_loadingCamTargetTransform = Camera.main.transform; // 새로운 씬의 메인카메라를 따라가게끔 설정
|
|
//-------------------------------------------------------------------------------
|
|
|
|
//스카이 박스 페이드 인
|
|
await FadeSkybox(0f, _skyboxNormalExposure, _skyboxFadeTime);
|
|
|
|
Debug.Log("씬 전환됨");
|
|
}
|
|
catch (OperationCanceledException)
|
|
{
|
|
Debug.Log("씬 전환 작업이 취소됨");
|
|
}
|
|
}
|
|
}
|