2026-07-14 씬체인지

This commit is contained in:
2026-07-14 11:51:45 +09:00
parent 058783d10d
commit 0e0e12a768
33 changed files with 268 additions and 41 deletions

Binary file not shown.

View File

@@ -0,0 +1,49 @@
using UnityEngine;
public class PlayerController : MonoBehaviour
{
// 시야 가림용 구체(Sight)가 쓰는 머티리얼 — 알파 0(투명)이면 정상 시야, 1(불투명)이면 암전
[SerializeField] private Material _sightMat;
private static readonly int _baseColorId = Shader.PropertyToID("_BaseColor");
private static readonly int _colorId = Shader.PropertyToID("_Color");
private void Awake()
{
// 머티리얼 에셋을 직접 수정하므로 이전 씬에서 암전된 알파가 남아 넘어올 수 있다 — 씬 시작은 항상 투명
SetSightAlpha(0f);
}
// onoff=true: 시야 복구(투명하게), false: 시야 암전(불투명하게). 완료 시점이 필요하면 await 가능.
public async Awaitable FadeSight(bool onoff, float duration)
{
if (_sightMat == null)
{
Debug.LogWarning("[PlayerController] _sightMat이 비어 있어 시야 페이드를 건너뜁니다.");
return;
}
float start = _sightMat.GetColor(AlphaProp).a;
float end = onoff ? 0f : 1f;
float timer = 0f;
while (timer < duration)
{
timer += Time.deltaTime;
SetSightAlpha(Mathf.Lerp(start, end, Mathf.Clamp01(timer / duration)));
await Awaitable.NextFrameAsync(destroyCancellationToken);
}
SetSightAlpha(end);
}
// URP 셰이더는 _BaseColor, 레거시/커스텀 셰이더는 _Color를 쓴다
private int AlphaProp => _sightMat.HasProperty(_baseColorId) ? _baseColorId : _colorId;
private void SetSightAlpha(float alpha)
{
if (_sightMat == null) return;
Color color = _sightMat.GetColor(AlphaProp);
color.a = alpha;
_sightMat.SetColor(AlphaProp, color);
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: a9699c3c85d2cd94fa9f996f93163d59

View File

@@ -1,9 +1,12 @@
using UnityEngine;
public class GameManager : MonoBehaviour
public class GameManager : MonoBehaviour,ISceneInitializable
{
public static GameManager Instance { get; private set; }
public GameObject Player {get; private set;}
public PlayerController PController => Player != null ? Player.GetComponent<PlayerController>() : null;
private void Awake()
{
if (Instance == null)
@@ -15,4 +18,9 @@ private void Awake()
Destroy(gameObject); //이미 인스턴스가 있으면 자신을 파괴
}
}
public void OnSceneLoaded()
{
Player = GameObject.FindWithTag("Player");
}
}

View File

@@ -13,23 +13,28 @@ public class LocalManager : MonoBehaviour
// BGM 재생 시작 후 씬 전환까지 대기 시간(초)
[SerializeField, Min(0f)] private float _caffebeneSceneChangeDelay = 13f;
private VolumeProfile _grayscaleProfile;
// 채도 -100으로 만들어둔 공용 프로파일 (Assets/Settings/GrayscaleVolumeProfile)
[SerializeField] private VolumeProfile _grayscaleProfile;
// 씬에 이미 있는 글로벌 볼륨 — 새로 만들지 않고 이 볼륨의 프로파일을 흑백으로 교체한다
[SerializeField] private Volume _globalVolume;
public void NextScene(int delay)
{
_ = Util.RunDelayed((float)delay,()=>SceneLoadManager.Instance.RequestSceneChange(_nextSceneName));
}
public void CaffebeneNextSceneChange()
public void CaffebeneNextSceneChange(int delay)
{
_ = CaffebeneSequence();
_ = CaffebeneSequence(delay);
}
// 모든 애니메이션 정지 → 시야 흑백 페이드 → BGM 재생 → 대기 후 씬 전환
private async Awaitable CaffebeneSequence()
private async Awaitable CaffebeneSequence(int delay)
{
try
{
await Awaitable.WaitForSecondsAsync((float)delay);
PauseAllAnimations();
await FadeToGrayscale(_grayscaleFadeTime);
@@ -59,38 +64,32 @@ private static void PauseAllAnimations()
particle.Pause();
}
// 런타임 전용 글로벌 볼륨을 만들어 채도를 0 → -100으로 페이드 (완전 흑백)
// 씬의 글로벌 볼륨 프로파일을 흑백으로 교체하고 weight를 0 → 1로 페이드 (완전 흑백)
// 기존 프로파일(그레인/모션블러 포함)이 통째로 빠지므로 정지 화면에서 지글거림도 없다
private async Awaitable FadeToGrayscale(float duration)
{
var volumeObj = new GameObject("Grayscale Volume (Runtime)");
var volume = volumeObj.AddComponent<Volume>();
volume.isGlobal = true;
volume.priority = 100f;
if (_grayscaleProfile == null || _globalVolume == null)
{
Debug.LogWarning("[LocalManager] _grayscaleProfile 또는 _globalVolume이 비어 있어 흑백 페이드를 건너뜁니다.");
return;
}
_grayscaleProfile = ScriptableObject.CreateInstance<VolumeProfile>();
var colorAdjustments = _grayscaleProfile.Add<ColorAdjustments>();
colorAdjustments.saturation.Override(0f);
volume.profile = _grayscaleProfile;
// sharedProfile로 물려야 에셋이 복제·수정되지 않는다 (페이드는 weight로만)
// 씬 오브젝트의 프로퍼티 변경이라 플레이 종료 시 원래대로 돌아온다
_globalVolume.sharedProfile = _grayscaleProfile;
_globalVolume.weight = 0f;
// 카메라에 포스트 프로세싱이 꺼져 있으면 켠다
var mainCam = Camera.main;
if (mainCam != null)
mainCam.GetUniversalAdditionalCameraData().renderPostProcessing = true;
// 메인 카메라뿐 아니라 RecordCamera 등 모든 카메라에 포스트 프로세싱 켠다
foreach (var cam in FindObjectsByType<Camera>())
cam.GetUniversalAdditionalCameraData().renderPostProcessing = true;
float timer = 0f;
while (timer < duration)
{
timer += Time.deltaTime;
colorAdjustments.saturation.value = Mathf.Lerp(0f, -100f, Mathf.Clamp01(timer / duration));
_globalVolume.weight = Mathf.Clamp01(timer / duration);
await Awaitable.NextFrameAsync(destroyCancellationToken);
}
colorAdjustments.saturation.value = -100f;
}
private void OnDestroy()
{
// 런타임 생성한 프로필은 씬 언로드로 자동 파괴되지 않으므로 직접 정리
if (_grayscaleProfile != null)
Destroy(_grayscaleProfile);
_globalVolume.weight = 1f;
}
}

View File

@@ -196,6 +196,11 @@ private async Awaitable SceneChange(string sceneName)
//스카이 박스 페이드 아웃
await FadeSkybox(_skyboxNormalExposure, 0f, _skyboxFadeTime);
// 시야(암전 구체)도 완전히 어두워질 때까지 페이드 — 아래 교체/이동 순간을 씬 오브젝트째로 가린다
var pc = GameManager.Instance != null ? GameManager.Instance.PController : null;
if (pc != null)
await pc.FadeSight(false, 1f);
// 검게 된 상태에서 머티리얼 교체 (교체 순간이 가려져 깜빡임 없음)
RenderSettings.skybox = _runtimeLoadingSkybox;
@@ -203,6 +208,10 @@ private async Awaitable SceneChange(string sceneName)
// (로딩 룸은 카메라를 따라다니므로 함께 이동한다)
MovePlayerToLoadingArea();
// 로딩 룸에 도착했으니 시야를 다시 연다 (스카이박스 페이드 인과 동시 진행)
if (pc != null)
_ = pc.FadeSight(true, 1f);
//스카이 박스 페이드 인
await FadeSkybox(0f, _skyboxNormalExposure, _skyboxFadeTime);

Binary file not shown.

View File

@@ -0,0 +1,141 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!114 &-3068651836705639531
MonoBehaviour:
m_ObjectHideFlags: 11
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 0}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3}
m_Name:
m_EditorClassIdentifier: Unity.RenderPipelines.Universal.Editor::UnityEditor.Rendering.Universal.AssetVersion
version: 10
--- !u!21 &2100000
Material:
serializedVersion: 8
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_Name: SightOnOffMat
m_Shader: {fileID: 4800000, guid: 933532a4fcc9baf4fa0491de14d08ed7, type: 3}
m_Parent: {fileID: 0}
m_ModifiedSerializedProperties: 0
m_ValidKeywords:
- _ALPHAPREMULTIPLY_ON
- _SURFACE_TYPE_TRANSPARENT
m_InvalidKeywords: []
m_LightmapFlags: 4
m_EnableInstancingVariants: 0
m_DoubleSidedGI: 1
m_CustomRenderQueue: 3000
stringTagMap:
RenderType: Transparent
disabledShaderPasses:
- MOTIONVECTORS
- DepthOnly
- SHADOWCASTER
m_LockedProperties:
m_SavedProperties:
serializedVersion: 3
m_TexEnvs:
- _BaseMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _BumpMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailAlbedoMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailMask:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _DetailNormalMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _EmissionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MainTex:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _MetallicGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _OcclusionMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _ParallaxMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- _SpecGlossMap:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_Lightmaps:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_LightmapsInd:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
- unity_ShadowMasks:
m_Texture: {fileID: 0}
m_Scale: {x: 1, y: 1}
m_Offset: {x: 0, y: 0}
m_Ints: []
m_Floats:
- _AddPrecomputedVelocity: 0
- _AlphaClip: 0
- _AlphaToMask: 0
- _Blend: 0
- _BlendModePreserveSpecular: 1
- _BumpScale: 1
- _ClearCoatMask: 0
- _ClearCoatSmoothness: 0
- _Cull: 0
- _Cutoff: 0.5
- _DetailAlbedoMapScale: 1
- _DetailNormalMapScale: 1
- _DstBlend: 10
- _DstBlendAlpha: 10
- _EnvironmentReflections: 1
- _GlossMapScale: 0
- _Glossiness: 0
- _GlossyReflections: 0
- _Metallic: 0
- _OcclusionStrength: 1
- _Parallax: 0.005
- _QueueOffset: 0
- _ReceiveShadows: 1
- _Smoothness: 0
- _SmoothnessTextureChannel: 0
- _SpecularHighlights: 1
- _SrcBlend: 1
- _SrcBlendAlpha: 1
- _Surface: 1
- _WorkflowMode: 1
- _XRMotionVectorsPass: 1
- _ZWrite: 0
m_Colors:
- _BaseColor: {r: 0, g: 0, b: 0, a: 0}
- _Color: {r: 0, g: 0, b: 0, a: 0}
- _EmissionColor: {r: 0, g: 0, b: 0, a: 1}
- _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1}
m_BuildTextureStacks: []
m_AllowLocking: 1

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 15ffa48d980b0f445bb916f36d32e757
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 2100000
userData:
assetBundleName:
assetBundleVariant:

View File

@@ -1884,7 +1884,7 @@ MonoBehaviour:
- rid: 4848514455607443642
type: {class: 'Constant`1[[System.Single, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule}
data:
m_Value: 5
m_Value: 3
- rid: 4848514455607443643
type: {class: 'Constant`1[[System.Boolean, mscorlib]]', ns: Unity.GraphToolkit.Editor, asm: UnityEditor.GraphToolkitModule}
data:

Binary file not shown.

Binary file not shown.

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: cf5e65e890bd2cc4e827a43e9fec7ad9
guid: 8992993370ed3b3418ea511689828415
AudioImporter:
externalObjects: {}
serializedVersion: 8

View File

@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 29f7de3a813ebf245a3fd46e2fced5a1
guid: 555e862bf8e1c714f927be46373a61b2
folderAsset: yes
DefaultImporter:
externalObjects: {}

View File

@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 90f3de47e79624141b4e9dc75652ff18
NativeFormatImporter:
externalObjects: {}
mainObjectFileID: 11400000
userData:
assetBundleName:
assetBundleVariant:

Binary file not shown.