Project_WL/Assets/WL/Look/Toggle/WLLookModeSettings.cs

222 lines
12 KiB
C#
Raw Permalink Normal View History

// ─────────────────────────────────────────────────────────────────────────────
// WLLookModeSettings.cs — 인게임 룩 토글(카메라 모드 · 배경 룩)의 값 단일 출처 (WL-814d · #814)
//
// PD 제안(2026-09-10) 「토글 형태로 카메라를 바꿔서 볼 수 있게」 · 발주서 WL-814d §1.
//
// ■ 왜 이 에셋인가 (C45)
// 프리셋 값(투영 · 궤도 각 · 거리 · 직교 반높이 · 픽셀 높이)과 버튼 좌표·문구를 코드 상수로 두지 않는다.
// 게임이 읽는 값은 `Resources/WL/WLLookModeSettings.asset` 이다(PD 가 인스펙터에서 바꾼다).
// 🔴 여기 있는 값은 **비교용 프리셋**이다. 확정 값은 814b `WLPixelCameraSettings`(직교·픽셀)와
// 811o `WLCameraFramingSettings`(구도)가 계속 소유한다 — 이 파일은 그 둘에 **런타임으로 얹기만** 한다.
//
// ■ 좌표계 (px)
// 813c/813q 와 같은 **세로 1080×1920 기준 화면 픽셀**. px → 캔버스 유닛 환산은
// `WLHudLayoutSettings.UnitsPerPx(canvas)` 하나만 쓴다(산식 중복 0).
//
// ■ C8 롤백
// `enabled_ = 0` → 버튼 0 · 모드 0 고정 · 814b/811o 신규 API 호출 0 = 두 기능 원래 동작 100 %.
//
// 🔴 어셈블리 주의: Assets/WL/Look/ 에 .asmdef 를 만들지 말 것(Assembly-CSharp 단일).
// ─────────────────────────────────────────────────────────────────────────────
using UnityEngine;
namespace WL.Look.Toggle
{
/// <summary>룩 모드 하나 = 카메라 프리셋 1줄(발주서 §1-1 표).</summary>
[System.Serializable]
public sealed class WLLookModePreset
{
[Tooltip("버튼에 찍히는 이름(예: 기본 · 도트A).")]
public string name = "기본";
[Tooltip("켜면 814b 픽셀 카메라 리그(직교 · 저해상도 RT)를 강제로 조립한다. 끄면 원근(리그 해제).")]
public bool pixelRig = false;
[Tooltip("켜면 아래 구도 3값을 쓰지 않고 811o WLCameraFramingSettings 의 SO 값을 그대로 쓴다(= 지금 구도). 0번 기본 모드용.")]
public bool useSoFraming = false;
[Tooltip("811o 궤도 파라미터 cameraHeight(도). 클수록 위에서 내려다본다.")]
public float cameraHeight = 74f;
[Tooltip("811o 궤도 거리(m) — min/max 에 같이 들어간다.")]
public float distance = 3.4f;
[Tooltip("811o 궤도 중심 높이 DistanceUp(m).")]
public float distanceUp = 2.2f;
[Tooltip("814b 직교 반높이 orthoSize(m). pixelRig 가 꺼져 있으면 쓰이지 않는다.")]
public float orthoSize = 3.8f;
[Tooltip("814b 저해상도 RT 세로 픽셀 수. pixelRig 가 꺼져 있으면 쓰이지 않는다.")]
public int pixelHeight = 480;
[Tooltip("envFollowsCameraMode 가 켜져 있을 때 이 모드에서 배경 룩(814c)을 켤지.")]
public bool envLookOn = false;
}
[CreateAssetMenu(fileName = "WLLookModeSettings", menuName = "WL/Look Mode Settings", order = 35)]
public sealed class WLLookModeSettings : ScriptableObject
{
/// <summary>Resources 경로 — 런타임 로드 키.</summary>
public const string ResourcesPath = "WL/WLLookModeSettings";
private static WLLookModeSettings s_instance;
private static bool s_tried;
/// <summary>에셋이 없으면 null(호출부는 null 이면 전부 건너뛴다 = C8 롤백).</summary>
public static WLLookModeSettings Instance
{
get
{
if (s_instance == null && !s_tried)
{
s_tried = true;
s_instance = Resources.Load<WLLookModeSettings>(ResourcesPath);
}
return s_instance;
}
}
/// <summary>에디터에서 에셋을 새로 만들거나 값을 바꾼 뒤 캐시를 버린다.</summary>
public static void ClearCache() { s_instance = null; s_tried = false; }
/// <summary>프로브·QA 용 런타임 스위치(에셋을 건드리지 않고 끈다).</summary>
public static bool RuntimeDisabled;
/// <summary>룩 토글 축이 살아 있는가 = 에셋 존재 · enabled_ · 런타임 스위치 off.</summary>
public static bool Enabled
{
get
{
if (RuntimeDisabled) return false;
var s = Instance;
return s != null && s.enabled_;
}
}
// ── 공통 ──────────────────────────────────────────────────────────────
[Header("공통 (C8 롤백)")]
[Tooltip("끄면 버튼 0 · 모드 0 고정 · 814b/811o 신규 API 호출 0 = 두 기능 원래 동작 100 %.")]
public bool enabled_ = true;
[Tooltip("켜면 모드 전환마다 Console 에 1줄 남긴다(문자열은 이 값으로 막고 만든다 · GC 주의).")]
public bool verboseLog = false;
// ── 모드 ──────────────────────────────────────────────────────────────
[Header("카메라 모드 (0 = 기본 원근 · 1~3 = 도트 프리셋)")]
[Tooltip("앱을 켰을 때 시작 모드. 0 = 지금과 같은 원근.")]
public int startMode = 0;
[Tooltip("맵을 다시 들어가도 같은 모드를 유지한다(앱 세션 동안).")]
public bool keepModeAcrossMaps = true;
[Tooltip("로비·타이틀에서는 카메라를 기본(0)으로 되돌리고, 인게임에 다시 들어가면 고른 모드를 복원한다.")]
public bool defaultInLobby = true;
[Tooltip("도트 모드로 바뀔 때 배경 룩(814c)을 프리셋의 envLookOn 값으로 함께 바꾼다.")]
public bool envFollowsCameraMode = true;
[Tooltip("배경 룩(814c) 시작 상태.")]
public bool envLookOnAtStart = false;
[Tooltip("모드 배열 — 버튼이 0 → 1 → 2 → … → 0 으로 순환한다. 0번은 기본(원근)이어야 한다.")]
public WLLookModePreset[] presets = new WLLookModePreset[]
{
// 0번은 「지금 화면 그대로」 = 811o SO 구도 · 리그 해제(useSoFraming 1 이라 아래 3값은 참고용).
new WLLookModePreset { name = "기본", pixelRig = false, useSoFraming = true, cameraHeight = 76f, distance = 3.0f, distanceUp = 2.4f, orthoSize = 3.8f, pixelHeight = 480, envLookOn = false },
new WLLookModePreset { name = "도트A", pixelRig = true, useSoFraming = false, cameraHeight = 74f, distance = 3.4f, distanceUp = 2.2f, orthoSize = 3.8f, pixelHeight = 480, envLookOn = true },
new WLLookModePreset { name = "도트B", pixelRig = true, useSoFraming = false, cameraHeight = 55f, distance = 4.5f, distanceUp = 1.8f, orthoSize = 4.2f, pixelHeight = 480, envLookOn = true },
new WLLookModePreset { name = "도트C", pixelRig = true, useSoFraming = false, cameraHeight = 45f, distance = 5.5f, distanceUp = 1.5f, orthoSize = 4.6f, pixelHeight = 480, envLookOn = true },
};
// ── HUD 버튼 ──────────────────────────────────────────────────────────
[Header("HUD 버튼 (813q RunHud 방식 · 프리팹 0 · Safe Area 안)")]
[Tooltip("끄면 버튼 노드를 만들지 않는다(시연이 끝나면 여기만 끈다 — 모드 전환은 코드/프로브로 계속 가능).")]
public bool showButtons = true;
[Tooltip("버튼을 붙일 부모 노드의 UI 루트 기준 경로(813y·813q 실측 = IngameUIs/WL_HUD · SafeAreaFitter 아래).")]
public string buttonParentPath = "IngameUIs/WL_HUD";
[Tooltip("전용 Canvas(overrideSorting)로 띄운다 — 813q 결과창(40)·813z 부활 팝업(60)보다 아래.")]
public bool ownCanvas = true;
[Tooltip("전용 Canvas 의 sortingOrder. 결과창 40 · 부활 60 보다 낮게 둔다.")]
public int sortingOrder = 10;
[Tooltip("Safe Area 상단에서 버튼 위쪽까지 여백(px). 813c 보스 바(190~256)·813q 런 HUD(272~394)·813tj 보스 배너(300~450) 아래.")]
public float topMarginPx = 470f;
[Tooltip("Safe Area 오른쪽에서 버튼 오른쪽 끝까지 여백(px).")]
public float rightMarginPx = 24f;
[Tooltip("버튼 하나의 크기(px · 발주서 하한 96×48). 폭 200 이면 런 HUD 폭 560(중앙 ±280) 오른쪽으로 36 px 비켜 있다.")]
public Vector2 buttonSizePx = new Vector2(200f, 64f);
[Tooltip("두 버튼 사이 세로 간격(px).")]
public float buttonGapPx = 12f;
[Tooltip("버튼 글자 크기(px).")]
public float buttonFontPx = 26f;
[Tooltip("버튼 배경색(꺼짐 상태).")]
public Color buttonBgColor = new Color(0.06f, 0.07f, 0.09f, 0.82f);
[Tooltip("버튼 배경색(켜짐 상태 = 도트 모드 · 배경 룩 ON).")]
public Color buttonBgOnColor = new Color(0.16f, 0.42f, 0.86f, 0.9f);
[Tooltip("버튼 글자색.")]
public Color buttonTextColor = new Color(0.95f, 0.96f, 0.98f, 1f);
[Tooltip("카메라 버튼 문구 — {0} 에 프리셋 이름이 들어간다.")]
public string cameraLabelFormat = "카메라: {0}";
[Tooltip("배경 버튼 문구 — {0} 에 아래 두 문구가 들어간다.")]
public string envLabelFormat = "배경: {0}";
[Tooltip("배경 룩 켜짐 문구.")]
public string envOnText = "ON";
[Tooltip("배경 룩 꺼짐 문구.")]
public string envOffText = "OFF";
[Tooltip("버튼 글자 테두리(813y WLTextFxUtil.ApplyEdge 재사용).")]
public WL.UI.WLTextEdge buttonEdge = new WL.UI.WLTextEdge
{
enabled = true,
outlineWidth = 0.24f,
outlineColor = new Color(0.05f, 0.03f, 0.02f, 1f),
shadowOffset = new Vector2(0.4f, -0.4f),
shadowColor = new Color(0f, 0f, 0f, 0.75f),
shadowSoftness = 0.18f,
faceDilate = 0.06f,
};
// ── 조회 ──────────────────────────────────────────────────────────────
/// <summary>모드 개수(배열이 비어 있으면 1 = 기본만).</summary>
public int ModeCount { get { return presets != null && presets.Length > 0 ? presets.Length : 1; } }
/// <summary>모드 번호를 배열 범위로 감싼다(0 → 1 → … → 0 순환).</summary>
public int Wrap(int mode)
{
int n = ModeCount;
int m = mode % n;
return m < 0 ? m + n : m;
}
/// <summary>모드 프리셋(없으면 null).</summary>
public WLLookModePreset Preset(int mode)
{
if (presets == null || presets.Length == 0) return null;
return presets[Wrap(mode)];
}
/// <summary>모드 이름(없으면 번호).</summary>
public string ModeName(int mode)
{
var p = Preset(mode);
return p != null && !string.IsNullOrEmpty(p.name) ? p.name : mode.ToString();
}
}
}