Project_WL/AgentScripts/WL802b_Probe.cs

140 lines
6.8 KiB
C#
Raw Normal View History

// WL802b_Probe.cs — #802 재단 2차 검증 덤프: CanvasScaler 유닛 스케일 + SafeAreaFitter 부착/앵커 실측 (에디트 모드 · Play 불필요)
// unity command run_script --file AgentScripts/WL802b_Probe.cs --entry WL802b_Probe.Dump
// 산출: ⓑ 파일당 SafeAreaFitter 개수·경로 · ⓒ 부착 RectTransform 앵커/offset + safeArea=전체화면 시뮬레이션 · ⓓ #806 팝업 값 · ⓔ 환산표.
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
public static class WL802b_Probe
{
static readonly string[] Prefabs =
{
"Assets/ResWork/UIPrefabs/Title/TitleInfo.prefab",
"Assets/ResWork/UIPrefabs/Title/SortOrder_5.prefab",
"Assets/Res_Addr/MainUI/NewGameUI.prefab",
};
// Lead 확정 QA 캡처 해상도 5종 (§3)
static readonly Vector2Int[] QaRes =
{
new Vector2Int(1080, 1920), new Vector2Int(1080, 2340), new Vector2Int(1080, 2400),
new Vector2Int(720, 1600), new Vector2Int(1440, 3200),
};
// CanvasScaler.ScreenMatchMode.Expand 의 실제 계산식 (UnityEngine.UI 소스와 동일)
static float ExpandScale(Vector2 refRes, Vector2Int screen)
{
return Mathf.Min(screen.x / refRes.x, screen.y / refRes.y);
}
static string Path(Transform t, Transform stop)
{
var s = t.name;
for (var p = t.parent; p != null && p != stop; p = p.parent) s = p.name + "/" + s;
return s;
}
static string Rt(RectTransform rt)
{
return "aMin=" + rt.anchorMin + " aMax=" + rt.anchorMax + " offMin=" + rt.offsetMin + " offMax=" + rt.offsetMax
+ " size=" + rt.sizeDelta + " pos=" + rt.anchoredPosition + " pivot=" + rt.pivot;
}
// SafeAreaFitter.Apply() 와 동일한 산식 — Play 없이 결과를 재현한다.
static string SimulateFitter(Rect safe, Vector2Int screen)
{
var min = new Vector2(safe.xMin / screen.x, safe.yMin / screen.y);
var max = new Vector2(safe.xMax / screen.x, safe.yMax / screen.y);
return "screen=" + screen.x + "x" + screen.y + " safeArea=" + safe
+ " → anchorMin=" + min + " anchorMax=" + max + " offsetMin=(0,0) offsetMax=(0,0)";
}
static RectTransform FindByName(GameObject root, string name)
{
foreach (var rt in root.GetComponentsInChildren<RectTransform>(true)) if (rt.name == name) return rt;
return null;
}
public static object Dump()
{
var sb = new StringBuilder();
sb.AppendLine("== WL-802b Probe ==");
foreach (var path in Prefabs)
{
var root = PrefabUtility.LoadPrefabContents(path);
try
{
sb.AppendLine();
sb.AppendLine("### " + path + " (root=" + root.name + ")");
// --- CanvasScaler ---
foreach (var cs in root.GetComponentsInChildren<CanvasScaler>(true))
{
sb.AppendLine(" CanvasScaler @" + Path(cs.transform, root.transform.parent)
+ " mode=" + cs.uiScaleMode + " ref=" + cs.referenceResolution
+ " match=" + cs.screenMatchMode + " matchWH=" + cs.matchWidthOrHeight
+ " refPPU=" + cs.referencePixelsPerUnit);
foreach (var r in QaRes)
sb.AppendLine(" scaleFactor " + r.x + "x" + r.y + " = " + ExpandScale(cs.referenceResolution, r).ToString("0.#####")
+ " (1 px = " + (1f / ExpandScale(cs.referenceResolution, r)).ToString("0.####") + " unit)");
}
// --- SafeAreaFitter ---
var fitters = root.GetComponentsInChildren<WL.UI.SafeAreaFitter>(true);
sb.AppendLine(" SafeAreaFitter count = " + fitters.Length);
foreach (var f in fitters)
{
var so = new SerializedObject(f);
var rt = (RectTransform)f.transform;
bool activeChain = true;
for (var t = f.transform; t != null; t = t.parent) if (!t.gameObject.activeSelf) { activeChain = false; break; }
sb.AppendLine(" @" + Path(f.transform, root.transform.parent)
+ " parent=" + (f.transform.parent != null ? f.transform.parent.name : "(none)")
+ " canvasDirectChild=" + (f.transform.parent == root.transform)
+ " activeInPrefab=" + activeChain
+ " applyH=" + so.FindProperty("applyHorizontal").boolValue
+ " applyV=" + so.FindProperty("applyVertical").boolValue);
sb.AppendLine(" RectTransform: " + Rt(rt));
sb.AppendLine(" graphic=" + (rt.GetComponent<Graphic>() != null) + " childCount=" + rt.childCount);
}
// --- #806 팝업 값 (레이아웃 회귀 확인) ---
if (path.EndsWith("SortOrder_5.prefab"))
{
foreach (var n in new[] { "PopupUI", "LoadingUI" })
{
var p = FindByName(root, n);
if (p == null) { sb.AppendLine(" [" + n + "] 없음"); continue; }
sb.AppendLine(" [" + n + "] " + Rt(p));
foreach (var c in p.GetComponentsInChildren<RectTransform>(true))
if (c != p && (c.name == "bg" || c.name == "msg" || c.name == "btn_ok"))
sb.AppendLine(" " + c.name + " " + Rt(c));
}
}
}
finally { PrefabUtility.UnloadPrefabContents(root); }
}
// --- ⓒ safeArea = 전체 화면일 때 회귀 0 ---
sb.AppendLine();
sb.AppendLine("### SafeAreaFitter.Apply 시뮬레이션 (Play 없이 산식 재현)");
foreach (var r in QaRes)
sb.AppendLine(" 미적용기기: " + SimulateFitter(new Rect(0, 0, r.x, r.y), r));
sb.AppendLine(" 노치예시(추정): " + SimulateFitter(new Rect(0, 0, 1080, 2400 - 91), new Vector2Int(1080, 2400)));
// --- ⓔ 환산표 ---
sb.AppendLine();
sb.AppendLine("### 유닛 ↔ px 환산 (세로 1080 폭 기준 · Expand)");
sb.AppendLine(" ref 1080x1920: scale 1.0 → 1 unit = 1 px");
sb.AppendLine(" ref 1920x1080: scale 0.5625 → 1 px = 1.7778 unit");
foreach (var px in new[] { 44f, 88f, 130f, 340f })
sb.AppendLine(" " + px + " px = " + px + " unit(1080기준) = " + (px / 0.5625f).ToString("0.##") + " unit(1920기준)");
sb.AppendLine();
sb.AppendLine("Screen(batch) = " + Screen.width + "x" + Screen.height + " safeArea=" + Screen.safeArea + " (배치모드 값 — 실기 아님)");
return sb.ToString();
}
}