543 lines
34 KiB
C#
543 lines
34 KiB
C#
// WL-814d 프로브 — 인게임 룩 토글(카메라 모드 순환 + 배경 룩 훅) 실측 (에디트 모드 · Play 0 · 로그인 0).
|
||
// unity command run_script --file AgentScripts/WL814d_Probe.cs --entry WL814d_Probe.Edit --project-path E:\NerdNavis\WL_wt\ui
|
||
// 산출물: <worktree>/AgentScripts/WL814d_PROBE.txt
|
||
// 🔴 NewGameUI.prefab 은 LoadPrefabContents 로 열고 **저장하지 않는다**(UnloadPrefabContents(false)).
|
||
// 🔴 씬에 만드는 것은 전부 HideFlags.DontSave + DestroyImmediate · 씬 저장 0 · 에셋 쓰기 0.
|
||
|
||
using System;
|
||
using System.IO;
|
||
using System.Text;
|
||
using TMPro;
|
||
using UnityEditor;
|
||
using UnityEngine;
|
||
using UnityEngine.Rendering.Universal;
|
||
using UnityEngine.UI;
|
||
using WL.CameraFraming;
|
||
using WL.Look.Toggle;
|
||
using WL.PixelArt;
|
||
using WL.UI;
|
||
|
||
public static class WL814d_Probe
|
||
{
|
||
const string kUiPrefab = "Assets/Res_Addr/MainUI/NewGameUI.prefab";
|
||
const string kCamPrefab = "Assets/ResWork/Prefabs/Ingame/Main Camera.prefab";
|
||
const string kOut = "AgentScripts/WL814d_PROBE.txt";
|
||
|
||
static StringBuilder _o;
|
||
static int _pass, _fail;
|
||
|
||
static void L(string s) { _o.AppendLine(s); }
|
||
static void H(string s) { _o.AppendLine(); _o.AppendLine("── " + s); }
|
||
static void N(string s) { _o.AppendLine(" " + s); }
|
||
static bool Chk(bool ok, string what)
|
||
{
|
||
if (ok) { _pass++; _o.AppendLine(" [PASS] " + what); }
|
||
else { _fail++; _o.AppendLine(" [FAIL] " + what); }
|
||
return ok;
|
||
}
|
||
static string Root { get { return Directory.GetParent(Application.dataPath).FullName; } }
|
||
|
||
// 프로브가 만든 것
|
||
static GameObject _uiRoot, _camGo, _uiCamGo, _infoGo;
|
||
static Camera _srcCam, _uiCam;
|
||
static RealCamera _realCam;
|
||
|
||
public static string Edit()
|
||
{
|
||
_o = new StringBuilder(); _pass = _fail = 0;
|
||
L("# WL814d Probe " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") + " (edit mode · Play 0 · 로그인 0)");
|
||
L("playMode=" + Application.isPlaying);
|
||
var scene = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
||
L("현재 씬 = '" + scene.name + "' dirty(before)=" + scene.isDirty + " rootCount=" + scene.rootCount);
|
||
|
||
var savedRt = WLLookModeSettings.RuntimeDisabled;
|
||
try
|
||
{
|
||
A_Asset();
|
||
B_Scene();
|
||
C_Nodes();
|
||
D_ModeCycle();
|
||
E_Session();
|
||
F_Gc();
|
||
G_C8();
|
||
}
|
||
catch (Exception e)
|
||
{
|
||
_fail++;
|
||
L(" [FAIL] 예외 " + e.GetType().Name + " : " + e.Message);
|
||
L(e.StackTrace);
|
||
}
|
||
finally
|
||
{
|
||
WLLookModeSettings.RuntimeDisabled = savedRt;
|
||
Z_Cleanup();
|
||
}
|
||
|
||
L("");
|
||
L("RESULT " + (_fail == 0 ? "PASS" : "FAIL") + " (pass=" + _pass + " fail=" + _fail + ")");
|
||
var after = UnityEngine.SceneManagement.SceneManager.GetActiveScene();
|
||
L("씬 정리 후: dirty=" + after.isDirty + " rootCount=" + after.rootCount + " (저장 0)");
|
||
string path = Path.Combine(Root, kOut);
|
||
File.WriteAllText(path, _o.ToString(), new UTF8Encoding(false));
|
||
return _o.ToString() + "\n(파일: " + path + ")";
|
||
}
|
||
|
||
// ─────────────────────────────────────────── A. 에셋 · 계약
|
||
static void A_Asset()
|
||
{
|
||
H("A. 에셋 · 계약 (814d SO · 814b SO · 811o SO)");
|
||
WLLookModeSettings.ClearCache();
|
||
var s = WLLookModeSettings.Instance;
|
||
Chk(s != null, "WLLookModeSettings.asset 로드(Resources/WL/WLLookModeSettings)");
|
||
if (s == null) return;
|
||
Chk(s.enabled_ && WLLookModeSettings.Enabled, "enabled_ = 1");
|
||
Chk(s.ModeCount == 4, "모드 4개 — 실측 " + s.ModeCount);
|
||
L(" | 모드 | 이름 | 리그 | SO구도 | cameraHeight | distance | distanceUp | orthoSize | pixelHeight | envLookOn |");
|
||
L(" |---|---|---|---|---|---|---|---|---|---|");
|
||
for (int i = 0; i < s.ModeCount; i++)
|
||
{
|
||
var p = s.Preset(i);
|
||
L(" | " + i + " | " + p.name + " | " + (p.pixelRig ? "직교" : "원근") + " | " + (p.useSoFraming ? "O" : "-") +
|
||
" | " + p.cameraHeight + " | " + p.distance + " | " + p.distanceUp +
|
||
" | " + p.orthoSize + " | " + p.pixelHeight + " | " + (p.envLookOn ? "1" : "0") + " |");
|
||
}
|
||
Chk(!s.Preset(0).pixelRig && s.Preset(0).useSoFraming, "0번 = 원근 + 811o SO 구도 그대로(리그 해제)");
|
||
Chk(s.Preset(1).pixelRig && s.Preset(2).pixelRig && s.Preset(3).pixelRig, "1~3번 = 도트(직교 리그)");
|
||
Chk(s.Wrap(4) == 0 && s.Wrap(-1) == 3, "순환 0→1→2→3→0 (Wrap(4)=" + s.Wrap(4) + " Wrap(-1)=" + s.Wrap(-1) + ")");
|
||
|
||
var px = WLPixelCameraSettings.Instance;
|
||
N("814b WLPixelCameraSettings: enabled=" + (px != null ? px.enabled.ToString() : "?") +
|
||
" orthoSize=" + (px != null ? px.orthoSize.ToString() : "?") +
|
||
" pixelHeight=" + (px != null ? px.pixelHeight.ToString() : "?") +
|
||
" prefab=" + (px != null && px.pixelCameraSystemPrefab != null));
|
||
Chk(px != null && !px.enabled, "🔴 814b SO enabled = 0(F-23) — 강제 조립 API 가 필요한 이유(실측)");
|
||
Chk(px != null && px.pixelCameraSystemPrefab != null, "814b 리그 프리팹 배선됨(URP 패키지 해제 확인)");
|
||
|
||
var fr = WLCameraFramingSettings.Instance;
|
||
N("811o WLCameraFramingSettings: enabled=" + (fr != null ? fr.enabled.ToString() : "?") +
|
||
" framingEnabled=" + (fr != null ? fr.framingEnabled.ToString() : "?") +
|
||
" h=" + (fr != null ? fr.cameraHeight.ToString() : "?") +
|
||
" d=" + (fr != null ? fr.distance.ToString() : "?") +
|
||
" up=" + (fr != null ? fr.distanceUp.ToString() : "?") +
|
||
" poll=" + (fr != null ? fr.framingPollSeconds.ToString() : "?") + "s");
|
||
Chk(fr != null && fr.enabled && fr.framingEnabled, "811o 구도 오버라이드가 켜져 있다(런타임 프리셋의 전제)");
|
||
}
|
||
|
||
// ─────────────────────────────────────────── B. 씬(원본 카메라 · UI 카메라)
|
||
static void B_Scene()
|
||
{
|
||
H("B. 임시 씬 — 원본 Main Camera(RealCamera 포함) + UICamera(스택 오버레이)");
|
||
var pf = AssetDatabase.LoadAssetAtPath<GameObject>(kCamPrefab);
|
||
Chk(pf != null, "원본 카메라 프리팹 로드 " + kCamPrefab);
|
||
if (pf == null) return;
|
||
|
||
_camGo = UnityEngine.Object.Instantiate(pf);
|
||
_camGo.name = "__WL814d_MainCamera";
|
||
_camGo.hideFlags = HideFlags.DontSave; // 씬 저장 0 · Camera.main 은 찾을 수 있어야 한다
|
||
_srcCam = _camGo.GetComponent<Camera>();
|
||
_realCam = _camGo.GetComponent<RealCamera>();
|
||
Chk(_srcCam != null && _realCam != null, "Camera + RealCamera 보유");
|
||
Chk(_camGo.tag == "MainCamera", "tag = MainCamera");
|
||
Chk(Camera.main == _srcCam, "Camera.main 이 이 카메라로 해석된다(리그 강제 조립의 전제) — 실측 " +
|
||
(Camera.main != null ? Camera.main.name : "null"));
|
||
|
||
// 원본 프리팹 실측값을 arr_cCamData 에 확인/보강(에디트 모드는 Awake 미실행 → 부작용 0)
|
||
if (_realCam.arr_cCamData == null || _realCam.arr_cCamData.Length < 2)
|
||
{
|
||
_realCam.arr_cCamData = new cCamData[2];
|
||
_realCam.arr_cCamData[0] = new cCamData { minDistance = 3f, maxDistance = 3f, DistanceUp = 2f, cameraHeight = 65f };
|
||
_realCam.arr_cCamData[1] = new cCamData { minDistance = 2f, maxDistance = 3f, DistanceUp = 3.5f, cameraHeight = 78f };
|
||
}
|
||
N("프리팹 arr_cCamData[0] h=" + _realCam.arr_cCamData[0].cameraHeight +
|
||
" min=" + _realCam.arr_cCamData[0].minDistance + " max=" + _realCam.arr_cCamData[0].maxDistance +
|
||
" up=" + _realCam.arr_cCamData[0].DistanceUp +
|
||
" · [1](보스 프리셋) h=" + _realCam.arr_cCamData[1].cameraHeight);
|
||
N("원본 카메라 orthographic=" + _srcCam.orthographic + " fov=" + _srcCam.fieldOfView +
|
||
" cullingMask=" + _srcCam.cullingMask + " depth=" + _srcCam.depth);
|
||
|
||
// UICamera 를 스택에 넣는다 — 원본 NewGameUI.cs:38 이 런타임에 하는 일과 같다(UI 소실 검증용)
|
||
_uiCamGo = new GameObject("__WL814d_UICamera", typeof(Camera));
|
||
_uiCamGo.hideFlags = HideFlags.DontSave;
|
||
_uiCam = _uiCamGo.GetComponent<Camera>();
|
||
_uiCam.orthographic = true; _uiCam.depth = 6f; _uiCam.cullingMask = 32; _uiCam.clearFlags = CameraClearFlags.Nothing;
|
||
var uiData = _uiCam.GetUniversalAdditionalCameraData();
|
||
if (uiData != null) uiData.renderType = CameraRenderType.Overlay;
|
||
var srcData = _srcCam.GetUniversalAdditionalCameraData();
|
||
if (srcData != null && srcData.cameraStack != null) { srcData.cameraStack.Clear(); srcData.cameraStack.Add(_uiCam); }
|
||
Chk(srcData != null && srcData.cameraStack.Count == 1 && srcData.cameraStack[0] == _uiCam,
|
||
"원본 스택 = [UICamera] (NewGameUI.cs:38 재현)");
|
||
|
||
_bossPreset = _realCam.arr_cCamData[1].cameraHeight;
|
||
|
||
// 가짜 InGameInfo — 로비/인게임 판정만 바꾼다(에디트 모드 Awake 미실행 · Ins 는 순수 static 필드)
|
||
_infoGo = new GameObject("__WL814d_InGameInfo");
|
||
_infoGo.hideFlags = HideFlags.DontSave;
|
||
_info = _infoGo.AddComponent<InGameInfo>();
|
||
InGameInfo.Ins = _info;
|
||
_modeField = typeof(InGameInfo).GetField("m_GameMode",
|
||
System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic);
|
||
Chk(_modeField != null, "InGameInfo.m_GameMode 리플렉션(프로브 전용 · 게임 코드 0)");
|
||
if (_modeField != null) _modeField.SetValue(_info, eGameMode.Stage);
|
||
Chk(!LookModeHub.InLobby, "인게임(Stage) 판정 — LookModeHub.InLobby=" + LookModeHub.InLobby);
|
||
}
|
||
|
||
static float _bossPreset;
|
||
static InGameInfo _info;
|
||
static System.Reflection.FieldInfo _modeField;
|
||
|
||
// ─────────────────────────────────────────── C. 버튼 노드 · 좌표
|
||
static void C_Nodes()
|
||
{
|
||
H("C. HUD 버튼 — NewGameUI 프리팹(저장 0) · Safe Area · 좌표 표");
|
||
_uiRoot = PrefabUtility.LoadPrefabContents(kUiPrefab);
|
||
Chk(_uiRoot != null, "LoadPrefabContents(" + kUiPrefab + ")");
|
||
if (_uiRoot == null) return;
|
||
|
||
var ingame = WLVignetteUtil.FindUiPath(_uiRoot.transform, "IngameUIs");
|
||
var hud = WLVignetteUtil.FindUiPath(_uiRoot.transform, "IngameUIs/WL_HUD");
|
||
Chk(hud != null, "IngameUIs/WL_HUD 경로 실측");
|
||
if (hud != null)
|
||
Chk(hud.GetComponentInParent<SafeAreaFitter>(true) != null || hud.GetComponent<SafeAreaFitter>() != null,
|
||
"WL_HUD 가 SafeAreaFitter 아래 = Safe Area 안 (802b/813y 실측 재확인)");
|
||
|
||
N("Bind: " + WLLookToggleUi.Bind(_uiRoot.transform));
|
||
Chk(WLLookToggleUi.Bound, "WL_LookToggle 노드 생성(HideFlags.DontSave)");
|
||
var s = WLLookModeSettings.Instance;
|
||
Chk(WLLookToggleUi.SortingOrder == s.sortingOrder,
|
||
"전용 Canvas sortingOrder=" + WLLookToggleUi.SortingOrder + " — 813q2 결과창 40 · 813z 부활 60 보다 아래");
|
||
// 🔴 Unity 는 부모 캔버스 계층이 비활성이면 overrideSorting 을 되돌린다.
|
||
// 프리팹 저장 상태의 IngameUIs 는 active=False 라 조립 시점에는 false 다 → 인게임(활성)에서 켜지는지 실측한다.
|
||
N("조립 직후 overrideSorting=" + WLLookToggleUi.OverrideSorting + " (IngameUIs 활성=" +
|
||
(ingame != null ? ingame.gameObject.activeSelf.ToString() : "?") + ")");
|
||
if (ingame != null)
|
||
{
|
||
bool wasOn = ingame.gameObject.activeSelf;
|
||
ingame.gameObject.SetActive(true); // 프리팹 미리보기 안에서만 · 저장 0
|
||
WLLookToggleUi.Tick();
|
||
Chk(WLLookToggleUi.OverrideSorting && WLLookToggleUi.SortingOrder == s.sortingOrder,
|
||
"IngameUIs 활성 → overrideSorting=" + WLLookToggleUi.OverrideSorting +
|
||
" sortingOrder=" + WLLookToggleUi.SortingOrder + "(중첩 캔버스로 인정 · Tick 이 다시 건다)");
|
||
N("활성 첫 프레임 재배치: " + WLLookToggleUi.Dump());
|
||
ingame.gameObject.SetActive(wasOn);
|
||
}
|
||
N("Layout: " + WLLookToggleUi.ApplyLayout());
|
||
N(WLLookToggleUi.Dump());
|
||
// px → 캔버스 유닛 계수는 NewGameUI 루트 캔버스(CanvasScaler)에서 와야 한다(813q 실측 1.7778).
|
||
var rootCanvas = _uiRoot.GetComponent<Canvas>();
|
||
var scaler = rootCanvas != null ? rootCanvas.GetComponent<CanvasScaler>() : null;
|
||
float u0 = WLVignetteUtil.UnitsPerPx(rootCanvas);
|
||
N("루트 캔버스 = " + (rootCanvas != null ? rootCanvas.name : "없음") +
|
||
" scaler=" + (scaler != null ? scaler.uiScaleMode + " ref=" + scaler.referenceResolution + " match=" + scaler.screenMatchMode : "없음") +
|
||
" → unitsPerPx=" + u0.ToString("F4"));
|
||
Chk(Mathf.Abs(u0 - 1.7778f) < 0.001f, "unitsPerPx = 1.7778 (813q·802b 실측과 같은 계수) — 실측 " + u0.ToString("F4"));
|
||
var lookRt = FindNode(_uiRoot.transform, "WL_LookToggle") as RectTransform;
|
||
Chk(lookRt != null && Mathf.Abs(lookRt.sizeDelta.x - s.buttonSizePx.x * u0) < 0.01f,
|
||
"노드 폭 = 200 px × " + u0.ToString("F4") + " = " + (s.buttonSizePx.x * u0).ToString("F2") +
|
||
" 유닛 — 실측 " + (lookRt != null ? lookRt.sizeDelta.x.ToString("F2") : "-"));
|
||
|
||
// 터치 통과 0 — 버튼 배경은 raycastTarget · 라벨은 아님
|
||
var node = FindNode(_uiRoot.transform, "WL_LookToggle");
|
||
Chk(node != null, "WL_LookToggle 노드 탐색");
|
||
if (node != null)
|
||
{
|
||
var bgs = node.GetComponentsInChildren<Image>(true);
|
||
int rc = 0; for (int i = 0; i < bgs.Length; i++) if (bgs[i].raycastTarget) rc++;
|
||
Chk(bgs.Length == 2 && rc == 2, "버튼 배경 2개 전부 raycastTarget = 터치 통과 0 — 실측 " + rc + "/" + bgs.Length);
|
||
var lbs = node.GetComponentsInChildren<TextMeshProUGUI>(true);
|
||
int lrc = 0; for (int i = 0; i < lbs.Length; i++) if (lbs[i].raycastTarget) lrc++;
|
||
Chk(lrc == 0, "라벨은 raycastTarget 0(버튼이 먹는다) — 실측 " + lrc);
|
||
Chk(node.gameObject.hideFlags == HideFlags.DontSave, "노드 hideFlags = DontSave(프리팹에 굽지 않는다)");
|
||
}
|
||
|
||
// ── 좌표 표 (px · 세로 1080×1920 Safe Area 기준 · 좌상단 원점)
|
||
var hs = WLHudLayoutSettings.Instance;
|
||
var rs = WLRunUiSettings.Instance;
|
||
var bs = WLBossUiSettings.Instance;
|
||
float barTop = hs != null ? hs.bossBarTopMarginPx : 190f;
|
||
float barBottom = hs != null ? barTop + hs.bossBarSizePx.y + hs.bossBarNameFontPx + hs.bossBarNameGapPx : 256f;
|
||
float barHalfW = hs != null ? hs.bossBarSizePx.x * 0.5f : 440f;
|
||
float runTop = rs != null ? rs.runHudTopMarginPx : 272f;
|
||
float runH = rs != null ? rs.timerFontPx + rs.zoneGaugeTopGapPx + rs.zoneCellHeightPx + rs.phaseTopGapPx + rs.phaseFontPx : 122f;
|
||
float runHalfW = rs != null ? rs.runHudWidthPx * 0.5f : 280f;
|
||
float banTop = bs != null ? bs.bannerTopMarginPx : 300f;
|
||
float banBottom = bs != null ? banTop + bs.bannerSizePx.y : 450f;
|
||
float banHalfW = bs != null ? bs.bannerSizePx.x * 0.5f : 430f;
|
||
|
||
var r0 = WLLookToggleUi.ButtonRectPx(0);
|
||
var r1 = WLLookToggleUi.ButtonRectPx(1);
|
||
L("");
|
||
L(" | 요소 | 소유 | 상단 y(px) | 하단 y(px) | 좌 x(px) | 우 x(px) |");
|
||
L(" |---|---|---|---|---|---|");
|
||
L(" | 상단 재화 바 | 프리팹(#729) | 130 | 176 | (프리팹) | (프리팹) |");
|
||
L(" | 보스 HP 바 | 813c3 | " + barTop + " | " + barBottom + " | " + (540f - barHalfW) + " | " + (540f + barHalfW) + " |");
|
||
L(" | 런 HUD | 813q | " + runTop + " | " + (runTop + runH) + " | " + (540f - runHalfW) + " | " + (540f + runHalfW) + " |");
|
||
L(" | 보스 배너(1.2 s) | 813tj | " + banTop + " | " + banBottom + " | " + (540f - banHalfW) + " | " + (540f + banHalfW) + " |");
|
||
L(" | **[카메라] 버튼** | **814d** | **" + r0.y + "** | **" + (r0.y + r0.height) + "** | **" + r0.x + "** | **" + (r0.x + r0.width) + "** |");
|
||
L(" | **[배경] 버튼** | **814d** | **" + r1.y + "** | **" + (r1.y + r1.height) + "** | **" + r1.x + "** | **" + (r1.x + r1.width) + "** |");
|
||
L("");
|
||
Chk(r0.width >= 96f && r0.height >= 48f, "버튼 크기 ≥ 96×48 px — 실측 " + r0.width + "×" + r0.height);
|
||
Chk(r0.y >= barBottom, "보스 HP 바(≤" + barBottom + ")와 겹침 0 · 여유 " + (r0.y - barBottom) + " px");
|
||
Chk(r0.y >= runTop + runH, "런 HUD(≤" + (runTop + runH) + ")와 겹침 0 · 여유 " + (r0.y - (runTop + runH)) + " px");
|
||
Chk(r0.y >= banBottom, "보스 배너(≤" + banBottom + ")와 겹침 0 · 여유 " + (r0.y - banBottom) + " px");
|
||
Chk(r0.x >= 540f + runHalfW, "런 HUD 폭(우 " + (540f + runHalfW) + ")보다 오른쪽 · 가로 여유 " + (r0.x - (540f + runHalfW)) + " px");
|
||
Chk(r1.y + r1.height <= 1920f, "두 버튼이 화면 안 — 아래끝 " + (r1.y + r1.height) + " px");
|
||
N("결과창(813q2 sortingOrder 40) · 부활 팝업(813z 60) 은 이 버튼(" + WLLookToggleUi.SortingOrder +
|
||
")보다 위 = 팝업이 뜨면 버튼이 가려진다(겹침 대신 덮임).");
|
||
|
||
// 프리팹 상단 노드 실측 덤프(참고)
|
||
L("");
|
||
L(" [참고] NewGameUI 캔버스 직속 노드(이름 · active):");
|
||
var canvasT = _uiRoot.transform;
|
||
for (int i = 0; i < canvasT.childCount; i++)
|
||
{
|
||
var c = canvasT.GetChild(i);
|
||
L(" - " + c.name + " (active=" + c.gameObject.activeSelf + " children=" + c.childCount + ")");
|
||
}
|
||
}
|
||
|
||
static Transform FindNode(Transform root, string name)
|
||
{
|
||
var all = root.GetComponentsInChildren<Transform>(true);
|
||
for (int i = 0; i < all.Length; i++) if (all[i].name == name) return all[i];
|
||
return null;
|
||
}
|
||
|
||
// ─────────────────────────────────────────── D. 모드 순환
|
||
static void D_ModeCycle()
|
||
{
|
||
H("D. 모드 0→1→2→3→0 — 리그 · arr_cCamData · 원본 카메라 · 버튼 라벨");
|
||
var s = WLLookModeSettings.Instance;
|
||
if (s == null || _srcCam == null || _realCam == null) { Chk(false, "전제 준비 실패"); return; }
|
||
|
||
LookModeHub.ResetAll();
|
||
CameraFramingOverride.ResetDiagnostics();
|
||
WLLookToggleUi.ResetState();
|
||
WLLookToggleUi.Tick();
|
||
|
||
L("");
|
||
L(" | 모드 | 이름 | 리그조립 | 원본 ortho | orthoSize | RT | arr[0] h/min/max/up | 라이브 h/max/up | 내림각 | 스택 | 카메라 라벨 | 배경 라벨 |");
|
||
L(" |---|---|---|---|---|---|---|---|---|---|---|---|");
|
||
|
||
for (int step = 0; step <= 4; step++)
|
||
{
|
||
int want = step % 4;
|
||
if (step > 0) WLLookToggleUi.ClickCamera(); // 실제 버튼 onClick 경로
|
||
Chk(LookModeHub.CameraMode == want, "step " + step + " → 모드 " + want + " (실측 " + LookModeHub.CameraMode + ")");
|
||
|
||
// 811o 는 폴링으로 적용한다 — 에디트 모드에는 러너가 없으므로 경계 1회를 강제한다(런타임과 같은 Apply 경로).
|
||
CameraFramingOverride.ProbeApply(_realCam);
|
||
WLLookToggleUi.Tick();
|
||
|
||
var p = s.Preset(want);
|
||
var d = _realCam.arr_cCamData[0];
|
||
var srcData = _srcCam.GetUniversalAdditionalCameraData();
|
||
int stackN = srcData != null && srcData.cameraStack != null ? srcData.cameraStack.Count : -1;
|
||
var mgr = PixelCameraRig.Manager;
|
||
string rt = mgr != null ? mgr.GameResolution.x + "×" + mgr.GameResolution.y : "—";
|
||
L(" | " + want + " | " + p.name + " | " + (PixelCameraRig.IsAssembled ? "O" : "-") +
|
||
" | " + _srcCam.orthographic + " | " + _srcCam.orthographicSize.ToString("0.###") +
|
||
" | " + rt +
|
||
" | " + d.cameraHeight + "/" + d.minDistance + "/" + d.maxDistance + "/" + d.DistanceUp +
|
||
" | " + _realCam.cameraHeight + "/" + _realCam.maxDistance + "/" + _realCam.DistanceUp +
|
||
" | " + CameraFramingOverride.DownAngleDeg(d.cameraHeight, d.maxDistance, d.DistanceUp).ToString("0.0") + "°" +
|
||
" | " + stackN +
|
||
" | " + WLLookToggleUi.CameraLabel + " | " + WLLookToggleUi.EnvLabel + " |");
|
||
|
||
Chk(PixelCameraRig.IsAssembled == p.pixelRig,
|
||
"모드 " + want + " 리그 조립 = " + p.pixelRig + " (실측 " + PixelCameraRig.IsAssembled + ")");
|
||
Chk(_srcCam.orthographic == p.pixelRig,
|
||
"모드 " + want + " 원본 카메라 orthographic = " + p.pixelRig + " (실측 " + _srcCam.orthographic + ")");
|
||
if (p.pixelRig)
|
||
{
|
||
Chk(Mathf.Abs(_srcCam.orthographicSize - p.orthoSize) < 0.001f,
|
||
"모드 " + want + " orthographicSize = " + p.orthoSize + " (실측 " + _srcCam.orthographicSize.ToString("0.####") + ")");
|
||
Chk(mgr != null && mgr.GameResolution.y == p.pixelHeight,
|
||
"모드 " + want + " RT 세로 = pixelHeight " + p.pixelHeight + " (실측 " + (mgr != null ? mgr.GameResolution.y : -1) + ")");
|
||
Chk(Mathf.Abs(d.cameraHeight - p.cameraHeight) < 0.001f && Mathf.Abs(d.maxDistance - p.distance) < 0.001f &&
|
||
Mathf.Abs(d.DistanceUp - p.distanceUp) < 0.001f,
|
||
"모드 " + want + " 구도 = h " + p.cameraHeight + " · d " + p.distance + " · up " + p.distanceUp +
|
||
" (실측 " + d.cameraHeight + "/" + d.maxDistance + "/" + d.DistanceUp + ")");
|
||
Chk(stackN == 2, "모드 " + want + " URP 스택 = [ViewCamera, UICamera] 2개(UI 소실 0) — 실측 " + stackN);
|
||
Chk(srcData != null && srcData.cameraStack[stackN - 1] == _uiCam, "모드 " + want + " UICamera 가 스택 맨 뒤(UI 가 맨 위에 그려진다)");
|
||
}
|
||
else
|
||
{
|
||
var fr = WLCameraFramingSettings.Instance;
|
||
Chk(!CameraFramingOverride.RuntimePresetOn, "모드 0 런타임 프리셋 해제 = 811o SO 값으로 복귀");
|
||
Chk(fr != null && Mathf.Abs(d.cameraHeight - fr.cameraHeight) < 0.001f && Mathf.Abs(d.maxDistance - fr.distance) < 0.001f,
|
||
"모드 0 구도 = 811o SO(h " + (fr != null ? fr.cameraHeight : 0f) + " · d " + (fr != null ? fr.distance : 0f) +
|
||
") — 실측 " + d.cameraHeight + "/" + d.maxDistance);
|
||
Chk(stackN == 1, "모드 0 URP 스택 = [UICamera] 1개(리그 해제 후 원복) — 실측 " + stackN);
|
||
}
|
||
Chk(WLLookToggleUi.CameraLabel == string.Format(s.cameraLabelFormat, p.name),
|
||
"모드 " + want + " 카메라 라벨 = \"" + string.Format(s.cameraLabelFormat, p.name) + "\" (실측 \"" + WLLookToggleUi.CameraLabel + "\")");
|
||
Chk(_realCam.arr_cCamData[1].cameraHeight == _bossPreset,
|
||
"보스 프리셋 arr_cCamData[1] 무변경 — h " + _realCam.arr_cCamData[1].cameraHeight + "(시작 " + _bossPreset + ")");
|
||
}
|
||
|
||
// 배경 룩 버튼
|
||
H("D-2. 배경 룩 버튼(814c 연동 훅) — 플래그 + 이벤트만");
|
||
int evt = 0; bool last = false;
|
||
System.Action<bool> sub = v => { evt++; last = v; };
|
||
LookModeHub.EnvLookChanged += sub;
|
||
try
|
||
{
|
||
bool before = LookModeHub.EnvLookOn;
|
||
WLLookToggleUi.ClickEnv(); WLLookToggleUi.Tick();
|
||
Chk(LookModeHub.EnvLookOn != before, "[배경] 탭 → 플래그 뒤집힘 " + before + " → " + LookModeHub.EnvLookOn);
|
||
Chk(evt == 1 && last == LookModeHub.EnvLookOn, "EnvLookChanged 1회 발행(814c 구독 지점) — 실측 " + evt + "회 값=" + last);
|
||
Chk(WLLookToggleUi.EnvLabel == string.Format(WLLookModeSettings.Instance.envLabelFormat,
|
||
LookModeHub.EnvLookOn ? WLLookModeSettings.Instance.envOnText : WLLookModeSettings.Instance.envOffText),
|
||
"배경 라벨 = \"" + WLLookToggleUi.EnvLabel + "\"");
|
||
WLLookToggleUi.ClickEnv(); WLLookToggleUi.Tick();
|
||
Chk(LookModeHub.EnvLookOn == before && evt == 2, "다시 탭 → 원래대로 · 이벤트 2회");
|
||
N("클릭 카운트: 카메라 " + WLLookToggleUi.CameraClicks + " · 배경 " + WLLookToggleUi.EnvClicks +
|
||
" · 라벨 쓰기 " + WLLookToggleUi.LabelWrites + "회");
|
||
|
||
// envFollowsCameraMode — 도트 모드로 바꾸면 배경 룩이 따라 켜진다
|
||
evt = 0;
|
||
LookModeHub.SetCameraMode(0); CameraFramingOverride.ProbeApply(_realCam);
|
||
LookModeHub.SetCameraMode(1); CameraFramingOverride.ProbeApply(_realCam);
|
||
Chk(WLLookModeSettings.Instance.envFollowsCameraMode && LookModeHub.EnvLookOn,
|
||
"envFollowsCameraMode 1 → 도트 모드에서 배경 룩 자동 ON (실측 " + LookModeHub.EnvLookOn + " · 이벤트 " + evt + "회)");
|
||
LookModeHub.SetCameraMode(0); CameraFramingOverride.ProbeApply(_realCam);
|
||
Chk(!LookModeHub.EnvLookOn, "기본 모드로 돌아오면 배경 룩 자동 OFF (실측 " + LookModeHub.EnvLookOn + ")");
|
||
}
|
||
finally { LookModeHub.EnvLookChanged -= sub; }
|
||
}
|
||
|
||
// ─────────────────────────────────────────── E. 세션 유지 · 로비 복귀
|
||
static void E_Session()
|
||
{
|
||
H("E. 세션 유지 — 로비 강등 · 인게임 복원 · 맵 재진입 재조립");
|
||
if (_modeField == null || _info == null) { Chk(false, "가짜 InGameInfo 준비 실패 — 건너뜀"); return; }
|
||
var info = _info; var fi = _modeField;
|
||
|
||
fi.SetValue(info, eGameMode.Stage);
|
||
Chk(!LookModeHub.InLobby, "인게임(Stage) 판정");
|
||
LookModeHub.SetCameraMode(2);
|
||
LookModeHub.Tick(); CameraFramingOverride.ProbeApply(_realCam);
|
||
var d = _realCam.arr_cCamData[0];
|
||
Chk(LookModeHub.AppliedMode == 2 && PixelCameraRig.IsAssembled,
|
||
"인게임 모드 2 적용 — applied=" + LookModeHub.AppliedMode + " 리그=" + PixelCameraRig.IsAssembled +
|
||
" h=" + d.cameraHeight + " ortho=" + _srcCam.orthographicSize.ToString("0.###"));
|
||
|
||
// 로비로 — 고른 모드는 유지, 적용만 0
|
||
fi.SetValue(info, eGameMode.Lobby);
|
||
LookModeHub.Tick(); CameraFramingOverride.ProbeApply(_realCam);
|
||
d = _realCam.arr_cCamData[0];
|
||
Chk(LookModeHub.CameraMode == 2 && LookModeHub.AppliedMode == 0 && !PixelCameraRig.IsAssembled,
|
||
"로비 → 카메라 기본 복귀(적용 0 · 리그 해제) · 고른 모드 2 유지 — 실측 mode=" + LookModeHub.CameraMode +
|
||
" applied=" + LookModeHub.AppliedMode + " 리그=" + PixelCameraRig.IsAssembled +
|
||
" ortho=" + _srcCam.orthographic + " h=" + d.cameraHeight);
|
||
|
||
// 인게임 재진입 — 저절로 복원
|
||
fi.SetValue(info, eGameMode.Stage);
|
||
LookModeHub.Tick(); CameraFramingOverride.ProbeApply(_realCam);
|
||
d = _realCam.arr_cCamData[0];
|
||
var p2 = WLLookModeSettings.Instance.Preset(2);
|
||
Chk(LookModeHub.AppliedMode == 2 && PixelCameraRig.IsAssembled &&
|
||
Mathf.Abs(_srcCam.orthographicSize - p2.orthoSize) < 0.001f && Mathf.Abs(d.cameraHeight - p2.cameraHeight) < 0.001f,
|
||
"인게임 재진입 → 모드 2 자동 복원 — ortho=" + _srcCam.orthographicSize.ToString("0.###") +
|
||
" h=" + d.cameraHeight + " (로비강등 " + LookModeHub.LobbyDemotes + "회)");
|
||
|
||
// 맵 재진입(리그가 걷혔다가 다시 조립되는 경로) — 814b PollBoundary 가 하는 일을 손으로
|
||
PixelCameraRig.Teardown("맵 이탈(프로브)");
|
||
Chk(!PixelCameraRig.IsAssembled && PixelCameraRig.RuntimeOverrideOn,
|
||
"맵 이탈 → 리그 해제되지만 오버라이드 플래그는 유지(다음 진입에서 같은 값으로 재조립) — 실측 " +
|
||
PixelCameraRig.RuntimeOverrideOn);
|
||
Chk(PixelCameraRig.ProbeAssemble(_srcCam) && Mathf.Abs(_srcCam.orthographicSize - p2.orthoSize) < 0.001f,
|
||
"맵 재진입 → 같은 값으로 재조립 ortho=" + _srcCam.orthographicSize.ToString("0.###"));
|
||
N(LookModeHub.Dump());
|
||
}
|
||
|
||
// ─────────────────────────────────────────── F. GC
|
||
static void F_Gc()
|
||
{
|
||
H("F. GC — 전환 외 프레임당 할당 0");
|
||
LookModeHub.UiTick = null; // 러너 훅 대신 직접 부른다
|
||
WLLookToggleUi.Tick();
|
||
LookModeHub.Tick(); // 안정화 1회
|
||
|
||
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
|
||
long before = GC.GetTotalMemory(false);
|
||
int g0 = GC.CollectionCount(0);
|
||
int writes = WLLookToggleUi.LabelWrites, applies = LookModeHub.Applies;
|
||
for (int i = 0; i < 200; i++) { LookModeHub.Tick(); WLLookToggleUi.Tick(); }
|
||
long after = GC.GetTotalMemory(false);
|
||
Chk(after - before == 0, "허브 Tick + UI Tick ×200 → Δ" + (after - before) + " B (gen0 " + g0 + "→" + GC.CollectionCount(0) + ")");
|
||
Chk(WLLookToggleUi.LabelWrites == writes, "라벨 쓰기 0회(값이 그대로면 안 쓴다) — 실측 " + (WLLookToggleUi.LabelWrites - writes));
|
||
Chk(LookModeHub.Applies == applies, "적용 0회(모드가 그대로면 두 기능을 건드리지 않는다) — 실측 " + (LookModeHub.Applies - applies));
|
||
|
||
// 대조군 — 계기 감도 확인
|
||
long b2 = GC.GetTotalMemory(false);
|
||
var junk = new byte[16 * 1024 * 1024];
|
||
junk[0] = 1;
|
||
long a2 = GC.GetTotalMemory(false);
|
||
Chk(a2 - b2 > 15000000, "대조군 16 MB 할당 감지 Δ" + (a2 - b2) + " B = 계기 감도 확인");
|
||
junk = null;
|
||
}
|
||
|
||
// ─────────────────────────────────────────── G. C8
|
||
static void G_C8()
|
||
{
|
||
H("G. C8 — WLLookModeSettings 를 끄면 814b/811o 가 원래대로");
|
||
var fr = WLCameraFramingSettings.Instance;
|
||
LookModeHub.SetCameraMode(1);
|
||
LookModeHub.Tick(); CameraFramingOverride.ProbeApply(_realCam);
|
||
Chk(PixelCameraRig.RuntimeOverrideOn && CameraFramingOverride.RuntimePresetOn, "끄기 전 = 도트 모드 · 두 오버라이드 on");
|
||
|
||
WLLookModeSettings.RuntimeDisabled = true;
|
||
Chk(!WLLookModeSettings.Enabled, "WLLookModeSettings.Enabled = false");
|
||
LookModeHub.Tick();
|
||
CameraFramingOverride.ProbeApply(_realCam);
|
||
WLLookToggleUi.Tick();
|
||
var d = _realCam.arr_cCamData[0];
|
||
Chk(!PixelCameraRig.RuntimeOverrideOn && !PixelCameraRig.IsAssembled, "814b 리그 오버라이드 0 · 리그 해제 = SO(enabled 0) 원래 동작");
|
||
Chk(!CameraFramingOverride.RuntimePresetOn, "811o 런타임 프리셋 0 = SO 값 경로 그대로");
|
||
Chk(fr != null && Mathf.Abs(d.cameraHeight - fr.cameraHeight) < 0.001f && Mathf.Abs(d.maxDistance - fr.distance) < 0.001f &&
|
||
Mathf.Abs(d.DistanceUp - fr.distanceUp) < 0.001f,
|
||
"arr_cCamData[0] = 811o SO 값 h " + (fr != null ? fr.cameraHeight : 0f) + " / d " + (fr != null ? fr.distance : 0f) +
|
||
" / up " + (fr != null ? fr.distanceUp : 0f) + " — 실측 " + d.cameraHeight + "/" + d.maxDistance + "/" + d.DistanceUp);
|
||
Chk(!_srcCam.orthographic, "원본 카메라 원근 복귀 — orthographic=" + _srcCam.orthographic);
|
||
Chk(!WLLookToggleUi.Visible, "버튼 숨김 — visible=" + WLLookToggleUi.Visible);
|
||
Chk(LookModeHub.AppliedMode == 0, "적용 모드 0 고정 — 실측 " + LookModeHub.AppliedMode);
|
||
|
||
// showButtons 만 끄기 — 모드는 그대로 두고 버튼만 숨긴다(시연 뒤 off)
|
||
WLLookModeSettings.RuntimeDisabled = false;
|
||
var s = WLLookModeSettings.Instance;
|
||
bool saved = s.showButtons;
|
||
s.showButtons = false;
|
||
WLLookToggleUi.Tick();
|
||
Chk(!WLLookToggleUi.Visible, "showButtons = 0 → 버튼만 숨김(모드 전환은 그대로) — visible=" + WLLookToggleUi.Visible);
|
||
s.showButtons = saved;
|
||
WLLookToggleUi.Tick();
|
||
Chk(WLLookToggleUi.Visible, "되돌리면 다시 보인다");
|
||
}
|
||
|
||
// ─────────────────────────────────────────── Z. 정리
|
||
static void Z_Cleanup()
|
||
{
|
||
H("Z. 정리 — 오버라이드 해제 · 노드 파괴 · 프리팹 저장 0");
|
||
try
|
||
{
|
||
LookModeHub.ResetAll();
|
||
LookModeHub.UiTick = null;
|
||
PixelCameraRig.Teardown("probe end");
|
||
CameraFramingOverride.ProbeRestore();
|
||
CameraFramingOverride.ResetDiagnostics();
|
||
N("리그 조립=" + PixelCameraRig.IsAssembled + " 오버라이드=" + PixelCameraRig.RuntimeOverrideOn +
|
||
" · 구도 런타임프리셋=" + CameraFramingOverride.RuntimePresetOn + " 적용=" + CameraFramingOverride.IsApplied);
|
||
N(WLLookToggleUi.Teardown());
|
||
if (_uiRoot != null) { PrefabUtility.UnloadPrefabContents(_uiRoot); _uiRoot = null; }
|
||
if (_infoGo != null) { InGameInfo.Ins = null; UnityEngine.Object.DestroyImmediate(_infoGo); _infoGo = null; }
|
||
if (_uiCamGo != null) { UnityEngine.Object.DestroyImmediate(_uiCamGo); _uiCamGo = null; }
|
||
if (_camGo != null) { UnityEngine.Object.DestroyImmediate(_camGo); _camGo = null; }
|
||
var strays = UnityEngine.Object.FindObjectsByType<Camera>(FindObjectsInactive.Include, FindObjectsSortMode.None);
|
||
N("남은 카메라 " + strays.Length + "개 · InGameInfo.Ins=" + (InGameInfo.Ins != null));
|
||
N("NewGameUI.prefab 저장 0(UnloadPrefabContents(false) 기본) · 에셋 쓰기 0");
|
||
}
|
||
catch (Exception e) { N("정리 중 예외 " + e.GetType().Name + " " + e.Message); }
|
||
}
|
||
}
|