Project_WL/Assets/WL/Camera/WLCameraFramingSettings.cs

181 lines
12 KiB
C#
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// ─────────────────────────────────────────────────────────────────────────────
// WLCameraFramingSettings.cs — 카메라 가림 해소(구도 값 + 가림 소품 숨김) 값의 단일 출처(SOT · C45)
//
// PD 지시 #813 · 발주서 WL-811o (2026-09-09) · 근거 = qa/W2-4b_재채점_v2.md D-8 · 채점 7-3 = 0
// 「지형 바위가 PC 를 완전히 가림(01_enter_hud ≈40 %) · 대형 이펙트가 ≈70 %(PD_07)」
//
// 에셋 경로(고정): Assets/WL/Camera/Resources/WL/WLCameraFramingSettings.asset
// → Resources.Load<WLCameraFramingSettings>("WL/WLCameraFramingSettings") 로 1회 로드 후 캐시(기존 설정 SO 관례).
// 에셋이 없거나 enabled = false 면 러너 자체가 뜨지 않는다 = 원본 카메라·소품 100 %(C8).
//
// ■ 왜 새 SO 인가 (실측 · 발주서 §1-1 조건)
// 구도 값(거리·궤도 높이·피치 파라미터)은 **원본 프리팹의 arr_cCamData 에만** 있다:
// Assets/ResWork/Prefabs/Ingame/Main Camera.prefab
// arr_cCamData[0](일반) minDistance 3 · maxDistance 3 · DistanceUp 2 · cameraHeight 65
// arr_cCamData[1](보스/락타겟) minDistance 2 · maxDistance 3 · DistanceUp 3.5 · cameraHeight 78
// m_LookAt (0, 1.06, 0) · smooth 4 · FOV 60(세로) · rotateAround = MapZoneData.CamRot_Portal(WL_Nature = 233)
// 기존 WL 카메라 SO 2개에는 이 값이 **없다**:
// · CameraLeadSettings = 이동 리드/후진 피치 **가산분**만(기준 구도 값 없음)
// · WLCombatCameraSettings = 킬캠 배율 + arr_cCamData[**1**] 보스 프리셋만(일반 프리셋 [0] 은 안 건드린다)
// → 발주서 §1-1 의 「WL 설정이 없으면 WL 오버라이드 SO(NEW)」 경로. 프리팹은 수정하지 않는다.
//
// ■ 궤도 수식(RealCamera.Update_Cam 실측) — 이 값들이 화면에서 뭘 바꾸는지
// rotateVector = Quaternion.Euler(h, r, 0) * Vector3.one (|rotateVector| = √3 · h 와 무관)
// 카메라 = 타겟 + up*DistanceUp rotateVector*DistanceAway
// · 타겟 발밑 기준 높이 = DistanceUp + D*(sin h cos h)
// · 수평 거리 = D*√(1 + (sin h + cos h)²)
// · 주시점(0,1.06,0) 내림각 = atan((높이 1.06) / 수평거리)
// 실측 h = 65 → 높이 3.451 m · 수평 4.990 m · **내림각 25.6°** ← 지금(가림 발생)
// 참고 h = 78 → 높이 4.311 m · 수평 4.654 m · 내림각 34.9° ← 원본 커밋 d5df1b2c9 「카메라 수정」 이전 값
// 내림각이 낮을수록 카메라~PC 선분이 지면에 가깝게 깔려 **키 2~3 m 짜리 바위/덤불이 PC 를 덮는다.**
// 선분 높이 y(x) = 1.06 + tan(내림각)·x (x = PC 로부터 카메라 쪽 수평거리)
// h 65 → y(2.5 m) = 2.26 m (3 m 바위가 전 구간에서 가린다)
// h 74 → y(2.5 m) = 2.66 m
//
// 🔴 어셈블리 주의: Assets/WL/Camera/ 에 .asmdef 를 만들지 말 것(RealCamera 가 Assembly-CSharp 에 있다).
// ─────────────────────────────────────────────────────────────────────────────
using UnityEngine;
namespace WL.CameraFraming
{
/// <summary>가림 소품을 숨기는 방식.</summary>
public enum OccluderHideMode
{
/// <summary>머티리얼 큐를 보고 자동 선택(투명 큐 + 색 프로퍼티 → 알파 페이드 · 그 외 → 그림자만).</summary>
Auto = 0,
/// <summary>ShadowCastingMode.ShadowsOnly — 렌더러는 안 보이고 그림자는 남는다(불투명 셰이더 기본 경로 · 머티리얼 무변경).</summary>
ShadowsOnly = 1,
/// <summary>Renderer.enabled = false — 그림자까지 사라진다.</summary>
RendererOff = 2,
/// <summary>MaterialPropertyBlock 알파 페이드 — 셰이더가 투명 큐일 때만 실제로 보인다(새 셰이더·머티리얼 0).</summary>
MpbAlpha = 3,
}
[CreateAssetMenu(fileName = "WLCameraFramingSettings", menuName = "WL/Camera Framing Settings", order = 322)]
public sealed class WLCameraFramingSettings : ScriptableObject
{
public const string ResourcesPath = "WL/WLCameraFramingSettings";
static WLCameraFramingSettings s_cached;
static bool s_lookupDone;
/// <summary>설정 에셋(1회 로드 후 캐시). 없으면 null.</summary>
public static WLCameraFramingSettings Instance
{
get
{
if (!s_lookupDone)
{
s_cached = Resources.Load<WLCameraFramingSettings>(ResourcesPath);
s_lookupDone = true;
}
return s_cached;
}
}
/// <summary>진단·A/B 전용 런타임 스위치(WLHitFeel.RuntimeDisabled 선례). 에셋을 안 건드리고 이 기능만 끈다.</summary>
public static bool RuntimeDisabled;
/// <summary>기능이 살아 있는가 = 에셋 존재 · enabled · 런타임 스위치 off.</summary>
public static bool Enabled
{
get { var i = Instance; return i != null && i.enabled && !RuntimeDisabled; }
}
/// <summary>테스트용: 캐시를 비워 다음 접근 때 다시 Resources.Load 한다.</summary>
public static void ClearCache() { s_cached = null; s_lookupDone = false; }
// ─────────────────────────────────────────── 전체 스위치
[Header("전체 스위치")]
[Tooltip("끄면 구도 오버라이드·가림 소품 숨김이 전부 비활성 — 원본 카메라 값(arr_cCamData) · 소품 100 %. 롤백 1순위.")]
public bool enabled = true;
[Tooltip("적용/복원/숨김 결정을 Console 에 남긴다(검증용 · 기본 off · GC 발생).")]
public bool verboseLog = false;
// ─────────────────────────────────────────── ① 구도 값 오버라이드
[Header("① 구도 오버라이드 — 맵 진입 경계에서 1회 적용(원값 스냅샷 후 복원 가능)")]
[Tooltip("구도 오버라이드 사용 여부. off 면 arr_cCamData 원값(65° · 3 m · 2)을 그대로 쓴다.")]
public bool framingEnabled = true;
[Tooltip("arr_cCamData[0]( ) cameraHeight ().\n" +
"실제 고도각 = asin((sin h cos h)/√3) · 실측 65 = 16.2° · 74 = 22.6° · 78 = 26.4°.\n" +
"올릴수록 더 내려다봐서 지형 소품 위로 시선이 지나간다(= 가림 감소). 너무 올리면 탑뷰가 되어 액션 체감이 떨어진다.")]
[Range(50f, 85f)] public float cameraHeight = 74f;
[Tooltip("arr_cCamData[0].minDistance = maxDistance (m · 3).\n" +
"소폭만 늘린다 — 거리를 늘리면 카메라~PC 선분이 길어져 그 사이에 들어올 수 있는 소품 수도 늘어난다.")]
[Range(2f, 6f)] public float distance = 3.4f;
[Tooltip("arr_cCamData[0].DistanceUp (m · 2). .")]
public float distanceUp = 2.2f;
[Tooltip("맵 진입 경계 검사 주기(초 · unscaled).")]
public float framingPollSeconds = 0.25f;
[Tooltip("로비에서도 적용한다. 기본 off — 로비는 전투가 없어 가림이 문제되지 않고, 연출 구도를 건드리지 않는다.")]
public bool framingInLobby = false;
// ─────────────────────────────────────────── ② 가림 소품 자동 숨김
[Header("② 가림 소품 자동 숨김 — 카메라→PC SphereCast")]
[Tooltip("가림 소품 숨김 사용 여부.")]
public bool occluderFadeEnabled = true;
[Tooltip("카메라→PC 선분 검사 반경(m). PC 실루엣 폭 정도. 0 이면 Linecast.")]
[Range(0f, 2f)] public float sphereRadius = 0.6f;
[Tooltip("검사 주기(초 · unscaled). 0.1 = 10 Hz.")]
public float pollSeconds = 0.1f;
[Tooltip("숨김까지의 시간(초). 불투명 셰이더 경로(ShadowsOnly/RendererOff)에서는 **지연**으로 동작한다\n" +
"— 스쳐 지나가는 소품이 한 프레임 깜빡이는 것을 막는다. MpbAlpha 경로에서는 알파 램프 시간.")]
public float fadeOutSeconds = 0.2f;
[Tooltip("가림이 풀린 뒤 복귀까지 기다리는 시간(초).")]
public float restoreDelaySeconds = 0.3f;
[Tooltip("동시에 숨길 수 있는 소품 수 상한. 넘으면 새 소품은 숨기지 않는다(맵이 통째로 사라지는 사고 방지).")]
[Range(1, 8)] public int maxConcurrent = 4;
[Tooltip("숨김 방식. Auto = 머티리얼이 투명 큐면 알파 페이드, 아니면 그림자만 남기고 숨김.")]
public OccluderHideMode hideMode = OccluderHideMode.Auto;
[Tooltip("검사 레이어 마스크. 실측 레이어: 0 Default · 3 Actor(PC·몹) · 4 Water · 5 UI · 6 Map · 8 GrassFX.\n" +
"기본 = Default + Map (몹·PC·이펙트·UI 제외). 인스펙터에서 바꾼다.")]
public LayerMask occluderMask = (1 << 0) | (1 << 6);
[Tooltip("이 문자열들이 이름에 들어간 오브젝트는 **숨기지 않는다**(바닥·지형·물). 소문자 비교 · 쉼표 구분.")]
public string excludeNameContains = "terrain,ground,floor,water,river,navmesh,plane,road,path";
[Tooltip("렌더러 바운드 높이가 이 값(m) 미만이면 숨기지 않는다(잔디·자갈은 가리지 않는다).")]
public float minOccluderHeight = 0.8f;
[Tooltip("렌더러 바운드 최대 변이 이 값(m)을 넘으면 숨기지 않는다(지형 통짜 메시·배경 산 보호).")]
public float maxOccluderSize = 30f;
[Tooltip("SkinnedMeshRenderer(=캐릭터·몹)는 절대 숨기지 않는다. 안전장치라 켠 채로 둔다.")]
public bool skipSkinnedRenderers = true;
[Tooltip("Actor 컴포넌트를 가진 오브젝트(PC·몹)는 절대 숨기지 않는다. 안전장치라 켠 채로 둔다.")]
public bool skipActors = true;
// ─────────────────────────────────────────── ②-b 바운드 스캔 (콜라이더 없는 소품 대응)
[Header("②-b 바운드 스캔 — 🔴 실측: 가림 후보 소품 979개 중 콜라이더 보유는 121개(12.4 %)")]
[Tooltip("SphereCast 는 콜라이더가 있는 것만 잡는다. WL_Nature 의 나무·덤불·바위는 대부분 콜라이더가 없어\n" +
"물리 검사만으로는 화면을 가리는 소품의 12 % 밖에 못 잡는다(프로브 A 절 실측).\n" +
"켜면 맵 진입 때 한 번 훑어 둔 소품 렌더러의 **바운드**로도 가림을 판정한다(콜라이더 불요).")]
public bool boundsScanEnabled = true;
[Tooltip("바운드 판정에 쓰는 선분 반경(m). PC 실루엣 폭 — sphereRadius 와 같은 뜻이지만 따로 조절할 수 있게 뒀다. 0 이면 sphereRadius 를 쓴다.")]
public float boundsScanRadius = 0f;
[Tooltip("스캔 목록 재구축 주기(초). 0 = 맵/PC 경계에서만 재구축(정상 · 이때만 할당이 있고 폴링은 GC 0).\n" +
"0 보다 크면 그 주기로도 다시 훑는다(런타임에 소품이 생기는 맵용).")]
public float scanRebuildSeconds = 0f;
[Tooltip("스캔 목록 상한(맵 소품 수). WL_Nature 실측 가림 후보 979개.")]
public int scanMax = 2048;
}
}