Project_WL/Assets/WL/Camera/CameraLeadSettings.cs

144 lines
11 KiB
C#
Raw 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.

using UnityEngine;
/// <summary>
/// 이동 방향 기반 카메라 리드/피치 수치의 단일 출처(SOT · 조직 C45).
/// 전진(카메라에서 멀어짐) = 궤도 중심·주시점을 진행 방향으로 밀어 앞을 더 보여준다.
/// 후진(카메라 쪽으로 옴) = 궤도 피치를 올려 더 내려다보게 하고, 주시점을 카메라 쪽으로 당겨
/// 캐릭터가 화면에서 위로 올라가게 한다.
///
/// 에셋 경로(고정): Assets/WL/Settings/Resources/WL/CameraLeadSettings.asset
/// → CameraMoveLead 가 Resources.Load&lt;CameraLeadSettings&gt;("WL/CameraLeadSettings") 로 1회 로드한다.
/// 에셋이 없거나 enabled = false 면 RealCamera 는 100% 기존 동작을 한다.
///
/// 【힘민지 RealCamera 실측 기준값 · 2026-09-06 · Assets/ResWork/Prefabs/Ingame/Main Camera.prefab】
/// arr_cCamData[0](일반) minDistance 3 · maxDistance 3 · DistanceUp 2 · cameraHeight 78
/// m_LookAt (0, 1.06, 0) · smooth 4 · FOV 60 · rotateAround = 맵별 MapZoneData.CamRot_Portal(27~270)
/// → 궤도 중심 = 발밑 + up*2 · 반지름 = √3 × 3 = 5.196 m
/// → 카메라 = 발밑 +4.311 m / 수평 4.654 m · 주시점까지 5.677 m · 실제 내림각 34.93°
///
/// 【구 WL FollowCameraSettings 실측값 → 이 에셋 기본값의 근거】
/// lookAheadMaxDistance 1.5 m(모드2 distance 15) · lookAheadSmoothTime 0.7 · lookAheadDirSmoothTime 0.35
/// lookAheadSpeedReference 3.85 · followMoveSpeedThreshold 0.15 · pitchBlendSmoothTime 0.4
/// screenAnchorY 0.3 ↔ screenAnchorYTowardCamera 0.5 · anchorSmoothTime 0.4
/// usePitchMoving = false ← PD #719 ④ "세로 출렁임" 으로 이동 중 피치 변경을 껐던 이력 (아래 backwardPitchAdd 주의)
///
/// 【2026-09-06 PM 결정(PD 지시 #755 2단계) — 아래 기본값의 확정 근거】
/// ① 후진 = 절충안 C : backwardPitchAdd 6° + backwardLeadDistance 0.4 m (피치 주 · 앵커 보조)
/// ② leadPerpendicularBlend = 1 (구 WL 앵커 방식 동등 · 깊이 변화 0)
/// ③ leadDistance = 1.5 m ④ leadSmoothTime = 0.45 (RealCamera 자체 Lerp τ≈0.25 s 보정)
/// </summary>
[CreateAssetMenu(fileName = "CameraLeadSettings", menuName = "WL/Camera Lead Settings")]
public class CameraLeadSettings : ScriptableObject
{
[Header("전체 스위치")]
[Tooltip("끄면 리드·피치 가산이 전부 0 이 되어 RealCamera 가 100% 기존 동작을 한다. 롤백 1순위 스위치.")]
public bool enabled = true;
// ─────────────────────────────────────────── 전진 리드
[Header("전진 리드 — 궤도 중심 + 주시점을 진행 방향으로 밀기")]
[Tooltip("전진 성분이 최대일 때 궤도 중심과 주시점을 진행 방향(수평)으로 미는 거리(m).\n" +
"궤도 중심과 주시점을 같은 값으로 함께 밀기 때문에 카메라는 회전 없이 통째로 평행 이동한다.\n" +
"【환산】 화면 세로 이동 비율 = lead × sin(내림각) / (2 × 주시거리 × tan(FOV/2))\n" +
" 힘민지 실측(내림각 34.93° · 주시거리 5.677 m · FOV 60) → 화면 세로의 8.73 %/m\n" +
" 0.6 m = 구 WL 룩어헤드만 거리비 환산 · 2.9 m = 룩어헤드+앵커 총합 환산(과함)\n" +
" 1.5 m(기본 · PM 결정 ③) = PD 가 '카메라가 앞으로 이동' 을 체감하도록 1.2 보다 크게\n" +
" → 화면 세로 13.1 % · 캐릭터 화면 y 0.50 → 0.37 【추정】")]
[Range(0f, 3f)] public float leadDistance = 1.5f;
[Tooltip("후진 성분이 최대일 때 **카메라 수평 전방(camFwd) 축** 으로 주시점을 미는 거리(m). 부호가 중요하다.\n" +
" 음수(기본 0.4 · PM 결정 ①) = 주시점이 카메라 쪽으로 당겨진다 → 캐릭터가 화면에서 **위로** 올라간다.\n" +
" 구 WL 의 screenAnchorY 0.3 ↔ screenAnchorYTowardCamera 0.5 앵커 전환과 같은 방향의 효과다.\n" +
" 양수 = 반대(캐릭터가 화면 아래로). 0 = 후진 시 리드 없음(피치 가산만).\n" +
" 0.4 m → 화면 세로 3.5 % 상승 【추정】. 후진 연출의 주역은 backwardPitchAdd 이고 이 값은 보조다.")]
[Range(-2f, 2f)] public float backwardLeadDistance = -0.4f;
[Tooltip("좌/우(측면) 이동 성분이 최대일 때 카메라 수평 우측(camRight) 축으로 주시점을 미는 거리(m) · PD #759 (2026-09-06).\n" +
"이동 방향 쪽 화면이 더 보인다(캐릭터는 반대쪽으로 밀림). 전진 리드와 같은 시선-수직 처리를 받는다.\n" +
"0 = 측면 리드 없음(이전 동작). 기본 1.0 【추정 · 전진 1.5 의 2/3】")]
[Range(0f, 3f)] public float lateralLeadDistance = 1.0f;
[Tooltip("리드를 현재 카메라 거리(DistanceAway)에 비례시킨다. 구 WL lookAheadScaleByDistance = true 와 동일 취지.\n" +
"실내(Set_InsideBuilding · DistanceAway 2)·보스(2~3)에서 리드가 과해지는 것을 자동으로 눌러 준다.\n" +
"전진 리드와 후진 리드에 모두 적용된다.")]
public bool scaleLeadByDistance = true;
[Tooltip("리드 거리비의 기준이 되는 DistanceAway. 0 이면 타겟이 바인드된 시점의 DistanceAway 를 자동으로 쓴다\n" +
"(일반 카메라 실측 3 · 코드에 숫자를 박지 않기 위해 런타임 캡처).\n" +
"실효 리드 = leadDistance × (현재 DistanceAway / 이 값)")]
public float leadReferenceDistance = 0f;
[Tooltip("리드 오프셋의 감쇠 시간(초).\n" +
"구 WL lookAheadSmoothTime 실측은 0.7 이지만, RealCamera 는 그 위에\n" +
"Vector3.Lerp(smooth 4 · 시정수 약 0.25 s)를 또 건다. 두 저역통과가 겹쳐 체감이 늦어지므로\n" +
"PM 결정 ④ 로 0.45 로 낮춘다(합성 90% 도달 약 1.1 s 【추정】).")]
public float leadSmoothTime = 0.45f;
[Tooltip("리드가 향하는 **방향** 자체의 감쇠 시간(초). 급반전 때 오프셋이 화면을 가로질러 휘두르는 것을 막는다.\n" +
"구 WL lookAheadDirSmoothTime 실측 0.35")]
public float leadDirSmoothTime = 0.35f;
[Tooltip("리드에서 **시선 방향 성분을 제거하는 비율**.\n" +
"힘민지 카메라는 34.93° 로 내려다본다 → 수평 리드 1 m 중 0.82 m 가 시선 방향(=줌인), 0.57 m 만 화면 세로로 간다.\n" +
" 0 = 브리프 사양 그대로의 수평 리드. 리드 1.5 m 면 피사체가 1.23 m 가까워진다(깊이 22 %).\n" +
" 1(기본 · PM 결정 ②) = 시선에 수직인 성분만 남긴다 → 깊이 변화 0 · 줌인 0.\n" +
" 구 WL 의 screenAnchorY 방식(FocusShift)과 동일. 화면 세로 이동량은 0 일 때와 같다.")]
[Range(0f, 1f)] public float leadPerpendicularBlend = 1f;
// ─────────────────────────────────────────── 후진 피치
[Header("후진 피치 — 궤도 각을 올려 더 내려다보기")]
[Tooltip("후진 성분이 최대일 때 RealCamera.cameraHeight 에 더하는 각(도).\n" +
"cameraHeight 는 실제 고도각이 아니라 Quaternion.Euler(h, yaw, 0) * Vector3.one 의 파라미터다.\n" +
"【환산 · 힘민지 실측 78°】 실제 고도각 = asin((sin h cos h)/√3) → 78° = 26.40°\n" +
" 파라미터 +1° ≈ 실제 고도각 +0.76°\n" +
" +6°(기본 · PM 결정 ① 절충안 C) → 고도각 26.40° → 30.9°, 내림각 34.93° → 39.5°\n" +
" 카메라는 0.35 m 올라가고 수평으로 0.20 m 붙는다(궤도 반지름 5.196 m 는 불변).\n" +
" 구 WL 에 직접 대응하는 실측값이 없다(WL 은 usePitchMoving = false 로 껐다) → 이 값은 【추정】.")]
[Range(0f, 30f)] public float backwardPitchAdd = 6f;
[Tooltip("피치 가산/복귀 감쇠 시간(초). 구 WL pitchBlendSmoothTime 실측 0.4.\n" +
"🔴 WL 은 '이동 중 피치 하향'이 세로 출렁임을 만들어 PD #719 ④ 로 기본 off 했다.\n" +
"여기서는 '후진할 때만' 이라 발생 빈도가 낮지만, 출렁임이 보이면 이 값을 먼저 키운다.")]
public float pitchSmoothTime = 0.4f;
[Tooltip("피치 가산의 각속도 상한(도/초). 0 = 무제한.\n" +
"PD #724 교훈에 따라 속도 제한은 이 한 곳에만 둔다(위치 상한은 두지 않는다).")]
public float pitchMaxSpeed = 0f;
[Tooltip("cameraHeight + 가산의 상한(도). 파라미터가 90° 를 넘으면 궤도 수식이 뒤집히므로 안전 상한을 둔다.")]
[Range(60f, 89f)] public float pitchTotalMax = 89f;
[Tooltip("피치를 올리면 궤도 수식 특성상 카메라의 수평 방위도 함께 돈다(78° 기준 파라미터 +1° 당 +0.32°).\n" +
"켜면 그만큼 rotateAround 가산으로 정확히 상쇄해 카메라가 옆으로 밀리지 않는다.\n" +
"상쇄량 = (atan2(1, sin(h+add) + cos(h+add)) atan2(1, sin h + cos h))")]
public bool compensatePitchYawCoupling = true;
// ─────────────────────────────────────────── 판정
[Header("전진/후진 판정")]
[Tooltip("이 속도(m/s) 미만이면 정지로 보고 리드·피치를 0 으로 되돌린다. 구 WL followMoveSpeedThreshold 실측 0.15")]
public float moveSpeedThreshold = 0.15f;
[Tooltip("리드·피치가 최대가 되는 기준 속도(m/s). 0 이면 캐릭터의 Actor.Get_MoveSpeed() 를 매 프레임 그대로 쓴다.\n" +
"0 권장 — 이동속도는 테이블/버프에서 오는 데이터라 코드·에셋에 숫자로 박지 않는다(C45).\n" +
"참고: 구 WL lookAheadSpeedReference 실측 3.85")]
public float speedReference = 0f;
[Tooltip("측면 이동 무반응 반각(도). 속도 방향이 카메라 수평 전방과 이루는 각이\n" +
"(90° 이 값) 을 넘으면 가중치 0, 0° 면 1 로 선형 보간한다.\n" +
"20 = ±70° 안쪽만 전/후진으로 본다.")]
[Range(0f, 45f)] public float sideDeadAngle = 20f;
[Tooltip("한 프레임 이동 거리가 이 값(m)을 넘으면 순간이동(포탈·맵 이동·리스폰)으로 보고\n" +
"속도 계산을 버리고 리드·피치를 즉시 0 으로 스냅한다.")]
public float teleportDistance = 3f;
// ─────────────────────────────────────────── 진단
[Header("진단")]
[Tooltip("CameraMoveLead.LastSample 을 매 프레임 채운다. 검증용 CSV 를 뽑을 때만 켠다.\n" +
"끄더라도 LeadOffset · PitchAdd 프로퍼티는 항상 읽을 수 있다.")]
public bool recordSample = false;
}