111 lines
4.4 KiB
C#
111 lines
4.4 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Playables;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.Universal;
|
|
|
|
public class LocalManager : MonoBehaviour,ISceneInitializable
|
|
{
|
|
[SerializeField] private string _nextSceneName;
|
|
[SerializeField] private AudioClip _caffebeneBGM;
|
|
|
|
// 흑백 전환에 걸리는 시간(초)
|
|
[SerializeField, Min(0f)] private float _grayscaleFadeTime = 1.5f;
|
|
// BGM 재생 시작 후 씬 전환까지 대기 시간(초)
|
|
[SerializeField, Min(0f)] private float _caffebeneSceneChangeDelay = 13f;
|
|
|
|
// 채도 -100으로 만들어둔 공용 프로파일 (Assets/Settings/GrayscaleVolumeProfile)
|
|
[SerializeField] private VolumeProfile _grayscaleProfile;
|
|
// 씬에 이미 있는 글로벌 볼륨 — 새로 만들지 않고 이 볼륨의 프로파일을 흑백으로 교체한다
|
|
[SerializeField] private Volume _globalVolume;
|
|
[SerializeField] private GameObject CaffebeneImgObj;
|
|
|
|
// 이 씬 전용 스카이박스 (비우면 SceneLoadManager의 기본 스카이박스 사용)
|
|
[SerializeField] private Material _sceneSkybox;
|
|
|
|
public void NextScene(int delay)
|
|
{
|
|
_ = Util.RunDelayed((float)delay,()=>SceneLoadManager.Instance.RequestSceneChange(_nextSceneName));
|
|
}
|
|
|
|
public void CaffebeneNextSceneChange(int delay)
|
|
{
|
|
_ = CaffebeneSequence(delay);
|
|
}
|
|
|
|
// 모든 애니메이션 정지 → 시야 흑백 페이드 → BGM 재생 → 대기 후 씬 전환
|
|
private async Awaitable CaffebeneSequence(int delay)
|
|
{
|
|
try
|
|
{
|
|
await Awaitable.WaitForSecondsAsync((float)delay);
|
|
|
|
PauseAllAnimations();
|
|
CaffebeneImgObj.SetActive(true);
|
|
|
|
await FadeToGrayscale(_grayscaleFadeTime);
|
|
|
|
SoundManager.Instance.PlayOverrideBGM(_caffebeneBGM);
|
|
|
|
await Awaitable.WaitForSecondsAsync(_caffebeneSceneChangeDelay, destroyCancellationToken);
|
|
CaffebeneImgObj.SetActive(false);
|
|
SceneLoadManager.Instance.RequestSceneChange(_nextSceneName);
|
|
}
|
|
catch (System.OperationCanceledException)
|
|
{
|
|
// 씬 전환 등으로 자신이 파괴되어 취소됨
|
|
}
|
|
}
|
|
|
|
// 씬 안의 Animator / 타임라인 / 파티클을 전부 일시정지
|
|
// (Time.timeScale은 건드리지 않는다 — BGM 페이드와 씬 전환 딜레이가 scaled time 기준이라 함께 멈춰버림)
|
|
private static void PauseAllAnimations()
|
|
{
|
|
foreach (var animator in FindObjectsByType<Animator>())
|
|
animator.speed = 0f;
|
|
|
|
foreach (var director in FindObjectsByType<PlayableDirector>())
|
|
director.Pause();
|
|
|
|
foreach (var particle in FindObjectsByType<ParticleSystem>())
|
|
particle.Pause();
|
|
}
|
|
|
|
// 씬의 글로벌 볼륨 프로파일을 흑백으로 교체하고 weight를 0 → 1로 페이드 (완전 흑백)
|
|
// 기존 프로파일(그레인/모션블러 포함)이 통째로 빠지므로 정지 화면에서 지글거림도 없다
|
|
private async Awaitable FadeToGrayscale(float duration)
|
|
{
|
|
if (_grayscaleProfile == null || _globalVolume == null)
|
|
{
|
|
Debug.LogWarning("[LocalManager] _grayscaleProfile 또는 _globalVolume이 비어 있어 흑백 페이드를 건너뜁니다.");
|
|
return;
|
|
}
|
|
|
|
// sharedProfile로 물려야 에셋이 복제·수정되지 않는다 (페이드는 weight로만)
|
|
// 씬 오브젝트의 프로퍼티 변경이라 플레이 종료 시 원래대로 돌아온다
|
|
_globalVolume.sharedProfile = _grayscaleProfile;
|
|
_globalVolume.weight = 0f;
|
|
|
|
// 메인 카메라뿐 아니라 RecordCamera 등 모든 카메라에 포스트 프로세싱을 켠다
|
|
foreach (var cam in FindObjectsByType<Camera>())
|
|
cam.GetUniversalAdditionalCameraData().renderPostProcessing = true;
|
|
|
|
float timer = 0f;
|
|
while (timer < duration)
|
|
{
|
|
timer += Time.deltaTime;
|
|
_globalVolume.weight = Mathf.Clamp01(timer / duration);
|
|
await Awaitable.NextFrameAsync(destroyCancellationToken);
|
|
}
|
|
_globalVolume.weight = 1f;
|
|
}
|
|
|
|
// 씬 로드 시 이 씬 전용 스카이박스를 적용
|
|
// (SceneLoadManager가 기본 스카이박스를 깐 뒤에 호출되므로 그 위에 덮어쓴다)
|
|
public void OnSceneLoaded()
|
|
{
|
|
if (_sceneSkybox != null && SceneLoadManager.Instance != null)
|
|
SceneLoadManager.Instance.SetSceneSkybox(_sceneSkybox);
|
|
}
|
|
|
|
}
|