// ───────────────────────────────────────────────────────────────────────────── // WLCombatCameraSettings.cs — 전투 연출 카메라(킬캠 · 보스 등장 캠 · 3타 리드) 값의 단일 출처(SOT · C45) // // PD 지시 #813 · 발주서 WL-811def §1-1 · 설계안 WL-811a §C 811d 행 · 기준서 v1 §B 요소 6·7 · §F-4 (2026-09-09) // // 에셋 경로(고정): Assets/WL/Camera/Resources/WL/WLCombatCameraSettings.asset // → Resources.Load("WL/WLCombatCameraSettings") 로 1회 로드 후 캐시(기존 설정 SO 관례). // 에셋이 없거나 enabled = false 또는 RuntimeDisabled 면 CombatCameraDirector 의 모든 구독자가 즉시 반환 = 원본 동작 100%(C8). // // 코어 SO(WLCombatCoreSettings)와 나눠 갖는 값 — 811b 가 811d 소비자용으로 예약해 둔 필드는 여기서 **다시 두지 않는다**(SOT 1곳): // · 킬캠 슬로모 배율 = WLCombatCoreSettings.cinematicDefaultScale (기획안 제안값 0.35) // · 킬캠 최소 간격(쿨) = WLCombatCoreSettings.killCamCooldownSeconds (기획안 F-0A 제안값 8 s) // // RealCamera(원본 · S 소유)는 수정하지 않는다 — 공개 필드 minDistance/maxDistance/DistanceUp · Add_Rot · Set_Target · arr_cCamData 만 쓴다. // ───────────────────────────────────────────────────────────────────────────── using UnityEngine; namespace WL.CombatCamera { [CreateAssetMenu(fileName = "WLCombatCameraSettings", menuName = "WL/Combat Camera Settings", order = 321)] public sealed class WLCombatCameraSettings : ScriptableObject { public const string ResourcesPath = "WL/WLCombatCameraSettings"; static WLCombatCameraSettings s_cached; static bool s_lookupDone; /// 설정 에셋(1회 로드 후 캐시). 없으면 null. public static WLCombatCameraSettings Instance { get { if (!s_lookupDone) { s_cached = Resources.Load(ResourcesPath); s_lookupDone = true; } return s_cached; } } /// 진단·A/B 전용 런타임 스위치(WLHitFeel.RuntimeDisabled 선례). 에셋을 건드리지 않고 이 연출만 끈다. Play 종료 시 자동 false. public static bool RuntimeDisabled; /// 연출 카메라가 살아 있는가 = 에셋 존재 · enabled · 런타임 스위치 off. public static bool Enabled { get { var i = Instance; return i != null && i.enabled && !RuntimeDisabled; } } /// 테스트용: 캐시를 비워 다음 접근 때 다시 Resources.Load 한다. public static void ClearCache() { s_cached = null; s_lookupDone = false; } // ─────────────────────────────────────────── 전체 스위치 [Header("전체 스위치")] [Tooltip("끄면 킬캠·보스 캠·3타 리드가 전부 비활성 — RealCamera 는 100% 기존 동작. 롤백 1순위.")] public bool enabled = true; [Tooltip("연출 결정(발동·스킵 사유·복원)을 Console 에 남긴다(검증용 · 기본 off · GC 발생).")] public bool verboseLog = false; // ─────────────────────────────────────────── 킬캠 [Header("킬캠 — Killed(엘리트/보스) → 줌인 + 슬로모(중재자 Cinematic 층)")] [Tooltip("킬캠 사용 여부.")] public bool killCamEnabled = true; [Tooltip("엘리트(eSubRol.Elite) 처치에 발동.")] public bool killCamOnElite = true; [Tooltip("보스(eSubRol.Boss) 처치에 발동.")] public bool killCamOnBoss = true; [Tooltip("일반 몹 처치에도 발동(기본 off · 존 마지막 신호는 813e 뒤에 붙는다).")] public bool killCamOnNormal = false; [Tooltip("직접 타격 처치(byDirectHit)에만 발동. off 면 도트·연쇄 피해 처치도 발동.")] public bool killCamDirectHitOnly = false; [Tooltip("줌 배율 — RealCamera 궤도 거리(minDistance/maxDistance)에 곱한다. 기준서 §B 요소 7 제안값 0.75.")] public float killCamZoomScale = 0.75f; [Tooltip("궤도 중심 높이(DistanceUp)도 같은 배율로 줄인다 = 시야각을 유지한 균일 줌. off 면 거리만 줄어 내려다보는 각이 조금 가팔라진다.")] public bool killCamScaleDistanceUp = true; [Tooltip("줌인 램프 시간(초 · unscaled). RealCamera 자체 Lerp(smooth 4 · τ≈0.25 s)가 더해지므로 짧게.")] public float killCamZoomInSeconds = 0.12f; [Tooltip("줌 유지 시간(초 · unscaled). 줌인+유지 ≈ 기준서 0.25~0.4 s.")] public float killCamHoldSeconds = 0.28f; [Tooltip("줌아웃 복원 시간(초 · unscaled).")] public float killCamZoomOutSeconds = 0.35f; [Tooltip("슬로모 유지 시간(초 · unscaled). 배율은 코어 SO cinematicDefaultScale(0.35). 기준서 §B 요소 6 = 0.35 · 0.4 s.")] public float killCamSlowMoSeconds = 0.35f; [Tooltip("세로 하단 HUD 영역 보호 — 처치 위치의 뷰포트 y 가 이 값 미만(화면 아래쪽)이면 줌은 건너뛰고 슬로모만 건다(줌인이 하단으로 더 밀어 넣는 것을 막는다). 0 = 검사 안 함.")] public float killCamBottomGuardViewportY = 0.4f; [Tooltip("한 런(씬 로드 사이)당 킬캠 최대 횟수. 기준서 「런당 ≤6회」 제안값. 0 = 무제한.")] public int killCamMaxPerRun = 6; // ─────────────────────────────────────────── 보스 등장 캠 [Header("보스 등장 캠 — Spawned(isBoss) → 오빗")] [Tooltip("보스 등장 캠 사용 여부.")] public bool bossCamEnabled = true; [Tooltip("오빗 길이(초 · unscaled). 기준서 §B 요소 6 = 1.2 s.")] public float bossCamSeconds = 1.2f; [Tooltip("오빗 동안 카메라 추적 대상을 보스로 바꾼다(Set_Target) — 끝나면 원래 대상으로 복원(외부가 바꿨으면 복원하지 않음).")] public bool bossCamRetarget = true; [Tooltip("오빗 요(yaw) 진폭(도). sin 곡선으로 0 → +진폭 → 0 이라 끝에서 원래 각으로 돌아온다(점프 0).")] public float bossCamOrbitDegrees = 35f; [Tooltip("오빗 동안 궤도 거리 배율(1 = 그대로 · >1 = 멀리서 보스 전신).")] public float bossCamDistanceScale = 1.15f; [Tooltip("보스 스폰 때 RealCamera.arr_cCamData[1](보스/락타겟 프리셋)을 아래 값으로 덮어쓴다. 기준서 §F-4 「보스전 3.5~4 m · 65~70°」 제안값. 프리셋은 플레이어가 보스를 락타겟할 때 적용된다.")] public bool bossPresetApply = true; [Tooltip("arr_cCamData[1].minDistance = maxDistance 로 쓸 거리(m).")] public float bossPresetDistance = 3.75f; [Tooltip("arr_cCamData[1].cameraHeight(궤도 파라미터 · 원본 78).")] public float bossPresetCameraHeight = 67f; [Tooltip("arr_cCamData[1].DistanceUp. 음수면 프리팹 값을 유지한다.")] public float bossPresetDistanceUp = -1f; // ─────────────────────────────────────────── 3타 리드 [Header("3타 카메라 리드 — ComboStage(isLast) → 이동 리드 배율")] [Tooltip("콤보 마지막 타에 카메라 이동 리드(CameraMoveLead)를 잠시 키운다.")] public bool comboLeadEnabled = true; [Tooltip("리드 배율(CameraMoveLead.LeadMultiplier). 설계안 §C 811d = ×1.3.")] public float comboLeadMultiplier = 1.3f; [Tooltip("배율 유지 시간(초 · unscaled). 3타 클립 길이(0.42~0.47 s)에 맞춘 값.")] public float comboLeadSeconds = 0.45f; } }