377 lines
21 KiB
C#
377 lines
21 KiB
C#
|
|
// WL-813c3 프로브 — Q7 결함 D-26 「계기는 맞고 화면은 틀리다」 실측(에디트 모드 · Play 0 · 로그인 0 · #813).
|
||
|
|
// unity command run_script --file AgentScripts/WL813c3_Probe.cs --entry WL813c3_Probe.RunAll
|
||
|
|
// 산출물: <worktree>/AgentScripts/WL813c3_PROBE.txt
|
||
|
|
//
|
||
|
|
// 🔴 프리팹 저장 0 — LoadPrefabContents 로 열고 finally 에서 UnloadPrefabContents. SaveAsPrefabAsset 호출 없음.
|
||
|
|
// 파일 크기·수정시각을 전후로 찍어 "저장 0" 을 증거로 남긴다.
|
||
|
|
// 🔴 SO 값은 메모리에서만 바꾸고 finally 에서 원복(AssetDatabase.SaveAssets 0).
|
||
|
|
// 🔴 Gameplay 는 읽기만. Play 진입 0.
|
||
|
|
// 🔴 핵심 계측 = **fillAmount 가 아니라 실제로 생성되는 메쉬 폭**.
|
||
|
|
// uGUI Image.OnPopulateMesh(VertexHelper) 를 리플렉션으로 직접 불러 정점 x 범위를 잰다.
|
||
|
|
// sprite 가 없으면 Graphic.OnPopulateMesh(사각형 전체) 로 빠지므로 fillAmount 와 무관하게 폭 비율 1.000 이 나온다.
|
||
|
|
// = D-26 「화면 만피」 의 기계적 증거.
|
||
|
|
|
||
|
|
using System;
|
||
|
|
using System.Collections.Generic;
|
||
|
|
using System.IO;
|
||
|
|
using System.Reflection;
|
||
|
|
using System.Text;
|
||
|
|
using UnityEngine;
|
||
|
|
using UnityEngine.UI;
|
||
|
|
using UnityEditor;
|
||
|
|
using WL.UI;
|
||
|
|
|
||
|
|
public static class WL813c3_Probe
|
||
|
|
{
|
||
|
|
const string kPrefab = "Assets/Res_Addr/MainUI/NewGameUI.prefab";
|
||
|
|
const string kHudPath = "IngameUIs/WL_HUD";
|
||
|
|
const string kBarName = "WL_BossHpBar";
|
||
|
|
|
||
|
|
static StringBuilder _o;
|
||
|
|
static int _pass, _fail;
|
||
|
|
|
||
|
|
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 string OutPath(string n) { return Path.Combine(Path.Combine(Root, "AgentScripts"), n); }
|
||
|
|
|
||
|
|
// ── 메쉬 실측 ────────────────────────────────────────────────────────────
|
||
|
|
static MethodInfo _opm;
|
||
|
|
/// <summary>Image 가 실제로 만드는 정점의 x 폭 / rect 폭. sprite 없으면 언제나 1.000 이 나온다.</summary>
|
||
|
|
static float MeshWidthRatio(Image img, out int vertexCount, out string note)
|
||
|
|
{
|
||
|
|
vertexCount = 0; note = "";
|
||
|
|
if (img == null) { note = "Image 없음"; return -1f; }
|
||
|
|
if (_opm == null)
|
||
|
|
{
|
||
|
|
_opm = typeof(Image).GetMethod("OnPopulateMesh",
|
||
|
|
BindingFlags.Instance | BindingFlags.NonPublic,
|
||
|
|
null, new Type[] { typeof(VertexHelper) }, null);
|
||
|
|
if (_opm == null)
|
||
|
|
_opm = typeof(Graphic).GetMethod("OnPopulateMesh",
|
||
|
|
BindingFlags.Instance | BindingFlags.NonPublic,
|
||
|
|
null, new Type[] { typeof(VertexHelper) }, null);
|
||
|
|
}
|
||
|
|
if (_opm == null) { note = "OnPopulateMesh 리플렉션 실패"; return -1f; }
|
||
|
|
|
||
|
|
var vh = new VertexHelper();
|
||
|
|
try { _opm.Invoke(img, new object[] { vh }); }
|
||
|
|
catch (Exception e) { note = "invoke 실패: " + e.GetBaseException().Message; vh.Dispose(); return -1f; }
|
||
|
|
|
||
|
|
vertexCount = vh.currentVertCount;
|
||
|
|
if (vertexCount == 0) { vh.Dispose(); note = "정점 0"; return 0f; }
|
||
|
|
|
||
|
|
float minX = float.MaxValue, maxX = float.MinValue;
|
||
|
|
var v = new UIVertex();
|
||
|
|
for (int i = 0; i < vertexCount; i++)
|
||
|
|
{
|
||
|
|
vh.PopulateUIVertex(ref v, i);
|
||
|
|
if (v.position.x < minX) minX = v.position.x;
|
||
|
|
if (v.position.x > maxX) maxX = v.position.x;
|
||
|
|
}
|
||
|
|
vh.Dispose();
|
||
|
|
|
||
|
|
float rectW = img.rectTransform.rect.width;
|
||
|
|
if (rectW <= 0f) { note = "rect 폭 0"; return -1f; }
|
||
|
|
note = "verts=" + vertexCount + " x[" + minX.ToString("F1") + "," + maxX.ToString("F1") + "] rectW=" + rectW.ToString("F1");
|
||
|
|
return (maxX - minX) / rectW;
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── 노드 전수 덤프 ───────────────────────────────────────────────────────
|
||
|
|
static void DumpTree(Transform t, int depth)
|
||
|
|
{
|
||
|
|
var sb = new StringBuilder();
|
||
|
|
sb.Append(new string(' ', 4 + depth * 2)).Append("- ").Append(t.name);
|
||
|
|
sb.Append(" active=").Append(t.gameObject.activeSelf).Append("/").Append(t.gameObject.activeInHierarchy);
|
||
|
|
sb.Append(" parent=").Append(t.parent != null ? t.parent.name : "(없음)");
|
||
|
|
|
||
|
|
var img = t.GetComponent<Image>();
|
||
|
|
if (img != null)
|
||
|
|
{
|
||
|
|
sb.Append(" [Image type=").Append(img.type)
|
||
|
|
.Append(" method=").Append(img.fillMethod)
|
||
|
|
.Append(" origin=").Append(img.fillOrigin)
|
||
|
|
.Append(" amount=").Append(img.fillAmount.ToString("F3"))
|
||
|
|
.Append(" sprite=").Append(img.sprite != null ? img.sprite.name : "🔴없음")
|
||
|
|
.Append(" enabled=").Append(img.enabled)
|
||
|
|
.Append(" color=#").Append(ColorUtility.ToHtmlStringRGBA(img.color))
|
||
|
|
.Append(" raycast=").Append(img.raycastTarget).Append("]");
|
||
|
|
int vc; string note;
|
||
|
|
float r = MeshWidthRatio(img, out vc, out note);
|
||
|
|
sb.Append(" 메쉬폭비=").Append(r < 0f ? "?" : r.ToString("F3")).Append(" (").Append(note).Append(")");
|
||
|
|
}
|
||
|
|
var cg = t.GetComponent<CanvasGroup>();
|
||
|
|
if (cg != null) sb.Append(" [CanvasGroup alpha=").Append(cg.alpha.ToString("F2")).Append("]");
|
||
|
|
var cv = t.GetComponent<Canvas>();
|
||
|
|
if (cv != null) sb.Append(" [Canvas order=").Append(cv.sortingOrder).Append(" override=").Append(cv.overrideSorting)
|
||
|
|
.Append(" enabled=").Append(cv.enabled).Append("]");
|
||
|
|
var tmp = t.GetComponent<TMPro.TextMeshProUGUI>();
|
||
|
|
if (tmp != null) sb.Append(" [TMP \"").Append(tmp.text).Append("\" fs=").Append(tmp.fontSize.ToString("F1")).Append("]");
|
||
|
|
var bhb = t.GetComponent<BossHpBar>();
|
||
|
|
if (bhb != null) sb.Append(" [BossHpBar]");
|
||
|
|
|
||
|
|
_o.AppendLine(sb.ToString());
|
||
|
|
for (int i = 0; i < t.childCount; i++) DumpTree(t.GetChild(i), depth + 1);
|
||
|
|
}
|
||
|
|
|
||
|
|
static void DumpBarNodes(GameObject root, string title)
|
||
|
|
{
|
||
|
|
H(title);
|
||
|
|
var all = root.GetComponentsInChildren<BossHpBar>(true);
|
||
|
|
N("BossHpBar 인스턴스 = " + all.Length + "벌");
|
||
|
|
foreach (var b in all)
|
||
|
|
{
|
||
|
|
// 부모 사슬 + 캔버스
|
||
|
|
var chain = new List<string>();
|
||
|
|
var p = b.transform;
|
||
|
|
Canvas nearest = null;
|
||
|
|
while (p != null)
|
||
|
|
{
|
||
|
|
chain.Add(p.name);
|
||
|
|
if (nearest == null) { var c = p.GetComponent<Canvas>(); if (c != null) nearest = c; }
|
||
|
|
p = p.parent;
|
||
|
|
}
|
||
|
|
chain.Reverse();
|
||
|
|
N("경로 = " + string.Join("/", chain.ToArray()) +
|
||
|
|
" · 소속 캔버스 = " + (nearest != null ? nearest.name + "(order=" + nearest.sortingOrder +
|
||
|
|
" override=" + nearest.overrideSorting + " enabled=" + nearest.enabled + " mode=" + nearest.renderMode + ")" : "🔴없음"));
|
||
|
|
DumpTree(b.transform, 0);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── 본체 ─────────────────────────────────────────────────────────────────
|
||
|
|
public static string RunAll()
|
||
|
|
{
|
||
|
|
_o = new StringBuilder();
|
||
|
|
_pass = _fail = 0;
|
||
|
|
_o.AppendLine("# WL813c3 Probe " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") +
|
||
|
|
" (edit mode · Play " + Application.isPlaying + ")");
|
||
|
|
|
||
|
|
var boss = WLBossUiSettings.Instance;
|
||
|
|
var hudS = WLHudLayoutSettings.Instance;
|
||
|
|
if (boss == null || hudS == null)
|
||
|
|
{
|
||
|
|
_o.AppendLine("🔴 설정 에셋 없음 — boss=" + (boss != null) + " hud=" + (hudS != null));
|
||
|
|
return Flush();
|
||
|
|
}
|
||
|
|
|
||
|
|
// SO 스냅샷(원복용)
|
||
|
|
bool sForce = boss.bossBarForceFilledFill, sWhite = boss.bossBarFillWhiteFallback;
|
||
|
|
bool sBgTick = boss.bossBarSpriteOnBgAndTicks, sDedup = boss.bossBarDedupInstances, sZero = boss.bossBarHideOnZeroHp;
|
||
|
|
|
||
|
|
string abs = Path.Combine(Root, kPrefab.Replace('/', Path.DirectorySeparatorChar));
|
||
|
|
var fi0 = new FileInfo(abs);
|
||
|
|
long len0 = fi0.Length; DateTime mt0 = fi0.LastWriteTimeUtc;
|
||
|
|
N("프리팹 전 = " + len0 + " B · " + mt0.ToString("yyyy-MM-dd HH:mm:ss.fff") + " UTC");
|
||
|
|
|
||
|
|
GameObject root = null;
|
||
|
|
BossHpBar bar = null;
|
||
|
|
GameObject dupGo = null;
|
||
|
|
try
|
||
|
|
{
|
||
|
|
root = PrefabUtility.LoadPrefabContents(kPrefab);
|
||
|
|
|
||
|
|
var hud = root.transform.Find(kHudPath);
|
||
|
|
if (hud == null) { _o.AppendLine("🔴 " + kHudPath + " 없음"); return Flush(); }
|
||
|
|
var barT = hud.Find(kBarName);
|
||
|
|
if (barT == null) { _o.AppendLine("🔴 " + kBarName + " 없음"); return Flush(); }
|
||
|
|
bar = barT.GetComponent<BossHpBar>();
|
||
|
|
if (bar == null) { _o.AppendLine("🔴 BossHpBar 컴포넌트 없음"); return Flush(); }
|
||
|
|
|
||
|
|
// ── ① 저장된 상태 그대로 (수리 전) ──────────────────────────────
|
||
|
|
DumpBarNodes(root, "① 프리팹에 저장된 그대로 (수리 전 · Initialize 호출 0)");
|
||
|
|
var fillT = barT.Find("Bar/Fill");
|
||
|
|
var fillImg = fillT != null ? fillT.GetComponent<Image>() : null;
|
||
|
|
Chk(fillImg != null, "Bar/Fill Image 존재");
|
||
|
|
Chk(fillImg != null && fillImg.sprite == null,
|
||
|
|
"🔴 D-26 원인 재현: 저장된 Fill 의 sprite = 없음 (BossHpBar.cs:499 가 AddComponent 만 했다)");
|
||
|
|
Chk(fillImg != null && fillImg.type == Image.Type.Filled,
|
||
|
|
"저장된 Fill 의 type = Filled (후보 ③ 「type 이 Filled 가 아니다」는 반증됨)");
|
||
|
|
Chk(root.GetComponentsInChildren<BossHpBar>(true).Length == 1,
|
||
|
|
"바 노드 1벌뿐 (후보 ① 「2벌」 반증)");
|
||
|
|
|
||
|
|
// ── ② 메쉬 실측 (수리 전 · fillAmount 0.368 = Q7 캡처 그 값) ────
|
||
|
|
H("② 메쉬 실측 (수리 전) — fillAmount 를 Q7 D-26 그 값 0.368 로 두고 실제 정점을 만든다");
|
||
|
|
if (fillImg != null)
|
||
|
|
{
|
||
|
|
fillImg.fillAmount = 0.368f;
|
||
|
|
int vc; string note;
|
||
|
|
float r = MeshWidthRatio(fillImg, out vc, out note);
|
||
|
|
N("fillAmount=0.368 → 메쉬폭비 = " + r.ToString("F3") + " (" + note + ")");
|
||
|
|
Chk(r > 0.99f,
|
||
|
|
"🔴 D-26 기계적 증거: sprite 가 없어 fillAmount 0.368 인데 메쉬는 폭비 " + r.ToString("F3") + " = 만피 사각형");
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── ③ 수리 = 런타임과 같은 경로(Initialize) ─────────────────────
|
||
|
|
H("③ 수리 — BossHpBar.Initialize() (런타임 OnEnable 과 같은 경로)");
|
||
|
|
boss.bossBarForceFilledFill = true; boss.bossBarFillWhiteFallback = true;
|
||
|
|
string init = bar.Initialize();
|
||
|
|
N("Initialize → " + init);
|
||
|
|
Chk(fillImg != null && fillImg.sprite != null,
|
||
|
|
"고침: Fill.sprite = " + (fillImg != null && fillImg.sprite != null ? fillImg.sprite.name : "(없음)"));
|
||
|
|
Chk(fillImg != null && fillImg.type == Image.Type.Filled &&
|
||
|
|
fillImg.fillMethod == Image.FillMethod.Horizontal &&
|
||
|
|
fillImg.fillOrigin == (int)Image.OriginHorizontal.Left,
|
||
|
|
"고침: type=Filled · method=Horizontal · origin=Left 강제");
|
||
|
|
|
||
|
|
// ── ④ 메쉬 실측 (수리 후) ───────────────────────────────────────
|
||
|
|
H("④ 메쉬 실측 (수리 후) — 같은 fillAmount 0.368");
|
||
|
|
if (fillImg != null)
|
||
|
|
{
|
||
|
|
fillImg.fillAmount = 0.368f;
|
||
|
|
int vc; string note;
|
||
|
|
float r = MeshWidthRatio(fillImg, out vc, out note);
|
||
|
|
N("fillAmount=0.368 → 메쉬폭비 = " + r.ToString("F3") + " (" + note + ")");
|
||
|
|
Chk(Mathf.Abs(r - 0.368f) < 0.01f,
|
||
|
|
"화면 추종: 메쉬폭비 " + r.ToString("F3") + " ≈ fillAmount 0.368 (오차 " +
|
||
|
|
Mathf.Abs(r - 0.368f).ToString("F4") + ")");
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── ⑤ 발주서 §1-2 프로브: 가짜 HP 1800 → 869 → 화면 노드 fill 0.483 ──
|
||
|
|
H("⑤ 가짜 HP 1800 → 869 (발주서 §1-2) — 화면 노드가 따라오는가");
|
||
|
|
BossHpBar.DebugFallbackMaxHp = 1800f;
|
||
|
|
BossHpBar.DebugFakeMaxHp = 1800f;
|
||
|
|
BossHpBar.DebugFakeHp = 1800f;
|
||
|
|
N(BossHpBar.RaiseFakeBossSpawned(1800f));
|
||
|
|
bar.PollBoss(true);
|
||
|
|
N("스폰 직후 ratio=" + bar.LastRatio.ToString("F3") + " fillAmount=" +
|
||
|
|
(fillImg != null ? fillImg.fillAmount.ToString("F3") : "-"));
|
||
|
|
|
||
|
|
BossHpBar.DebugFakeHp = 869f;
|
||
|
|
bar.PollBoss(true);
|
||
|
|
float expect = 869f / 1800f;
|
||
|
|
int vc2; string note2;
|
||
|
|
float r2 = MeshWidthRatio(fillImg, out vc2, out note2);
|
||
|
|
N("HP 869/1800 → LastRatio=" + bar.LastRatio.ToString("F3") +
|
||
|
|
" · fillAmount=" + (fillImg != null ? fillImg.fillAmount.ToString("F3") : "-") +
|
||
|
|
" · 메쉬폭비=" + r2.ToString("F3") + " (" + note2 + ")");
|
||
|
|
Chk(Mathf.Abs(bar.LastRatio - expect) < 0.002f, "계기 LastRatio = " + expect.ToString("F3"));
|
||
|
|
Chk(fillImg != null && Mathf.Abs(fillImg.fillAmount - expect) < 0.002f, "fillAmount = " + expect.ToString("F3"));
|
||
|
|
Chk(Mathf.Abs(r2 - expect) < 0.01f,
|
||
|
|
"🔴 화면 노드 메쉬폭비 = " + r2.ToString("F3") + " (발주서 §1-2 목표 0.483)");
|
||
|
|
Chk(bar.Visible, "바가 보이는 상태(CanvasGroup alpha > 0)");
|
||
|
|
|
||
|
|
// 3칸 구분선(눈금)이 fill 위에 그대로 얹혀 있는가
|
||
|
|
var ticks = barT.Find("Bar/Ticks");
|
||
|
|
int tickN = ticks != null ? ticks.childCount : 0;
|
||
|
|
N("눈금 " + tickN + "개 · Ticks 형제 순서 = " + (ticks != null ? ticks.GetSiblingIndex() : -1) +
|
||
|
|
" / Fill = " + (fillT != null ? fillT.GetSiblingIndex() : -1));
|
||
|
|
Chk(tickN == 2 && ticks != null && fillT != null && ticks.GetSiblingIndex() > fillT.GetSiblingIndex(),
|
||
|
|
"3칸 = fill 위에 얹힌 구분선 2개(Fill 뒤 형제) — 별도 세그먼트 이미지 아님");
|
||
|
|
|
||
|
|
// ── ⑥ C8 롤백 재현 ──────────────────────────────────────────────
|
||
|
|
H("⑥ C8 롤백 — bossBarForceFilledFill = 0 → 813c2 상태(화면 만피)로 복귀");
|
||
|
|
boss.bossBarForceFilledFill = false;
|
||
|
|
if (fillImg != null) fillImg.sprite = null; // 저장된 프리팹과 같은 조건
|
||
|
|
N("EnsureFillRenderable → " + bar.EnsureFillRenderable());
|
||
|
|
if (fillImg != null) fillImg.fillAmount = 0.483f;
|
||
|
|
int vc3; string note3;
|
||
|
|
float r3 = MeshWidthRatio(fillImg, out vc3, out note3);
|
||
|
|
N("롤백 상태 fillAmount=0.483 → 메쉬폭비 = " + r3.ToString("F3") + " (" + note3 + ")");
|
||
|
|
Chk(r3 > 0.99f, "롤백 시 D-26 재현(메쉬폭비 " + r3.ToString("F3") + ") = 스위치 하나로 되돌아간다(C8)");
|
||
|
|
boss.bossBarForceFilledFill = true;
|
||
|
|
N("복원 → " + bar.EnsureFillRenderable());
|
||
|
|
|
||
|
|
// ── ⑦ HP 0 숨김 ────────────────────────────────────────────────
|
||
|
|
H("⑦ 보스 HP 0 → 숨김 예약");
|
||
|
|
int zero0 = bar.ZeroHpHideCount;
|
||
|
|
BossHpBar.DebugFakeHp = 0f;
|
||
|
|
bar.PollBoss(true);
|
||
|
|
N("HP 0 → ratio=" + bar.LastRatio.ToString("F3") + " · ZeroHpHideCount " + zero0 + "→" + bar.ZeroHpHideCount);
|
||
|
|
Chk(bar.LastRatio <= 0.0001f, "HP 0 → ratio 0");
|
||
|
|
Chk(bar.ZeroHpHideCount > zero0, "HP 0 숨김 예약 1회 (bossBarHideDelaySec " + hudS.bossBarHideDelaySec + "s 뒤)");
|
||
|
|
boss.bossBarHideOnZeroHp = false;
|
||
|
|
int zero1 = bar.ZeroHpHideCount;
|
||
|
|
bar.PollBoss(true);
|
||
|
|
Chk(bar.ZeroHpHideCount == zero1, "C8: bossBarHideOnZeroHp=0 → 예약 생략");
|
||
|
|
boss.bossBarHideOnZeroHp = sZero;
|
||
|
|
|
||
|
|
// ── ⑧ 중복 노드 차단 ───────────────────────────────────────────
|
||
|
|
H("⑧ 중복 노드 차단 — 같은 캔버스에 바를 하나 더 만들어 본다");
|
||
|
|
dupGo = new GameObject(kBarName + "_DUP", typeof(RectTransform));
|
||
|
|
dupGo.layer = barT.gameObject.layer;
|
||
|
|
dupGo.hideFlags = HideFlags.HideAndDontSave;
|
||
|
|
((RectTransform)dupGo.transform).SetParent(hud, false);
|
||
|
|
var dup = dupGo.AddComponent<BossHpBar>();
|
||
|
|
string dupInit = dup.Initialize();
|
||
|
|
N("중복 Initialize → " + dupInit);
|
||
|
|
N("원본 BarInstanceCount=" + bar.BarInstanceCount + " · 중복 숨김=" + dup.DedupHidden +
|
||
|
|
" · 중복 Visible=" + dup.Visible);
|
||
|
|
Chk(dup.BarInstanceCount >= 2, "중복 감지 " + dup.BarInstanceCount + "벌");
|
||
|
|
Chk(!dup.Visible, "중복 바는 숨김(alpha 0)");
|
||
|
|
dup.Subscribe(false);
|
||
|
|
boss.bossBarDedupInstances = false;
|
||
|
|
N("C8: bossBarDedupInstances=0 → " + dup.DedupInstances());
|
||
|
|
boss.bossBarDedupInstances = sDedup;
|
||
|
|
|
||
|
|
// ── ⑨ GC ───────────────────────────────────────────────────────
|
||
|
|
H("⑨ GC — 폴링 20,000 회 할당");
|
||
|
|
BossHpBar.DebugFakeHp = 900f;
|
||
|
|
bar.PollBoss(true);
|
||
|
|
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
|
||
|
|
long g0 = GC.GetTotalMemory(true);
|
||
|
|
for (int i = 0; i < 20000; i++) bar.PollBoss(true);
|
||
|
|
long g1 = GC.GetTotalMemory(false);
|
||
|
|
N("GC " + g0 + " → " + g1 + " (Δ " + (g1 - g0) + " B)");
|
||
|
|
Chk(g1 - g0 <= 0, "폴링 20,000회 GC 증가 0 B (Δ " + (g1 - g0) + ")");
|
||
|
|
|
||
|
|
GC.Collect(); GC.WaitForPendingFinalizers(); GC.Collect();
|
||
|
|
long e0 = GC.GetTotalMemory(true);
|
||
|
|
for (int i = 0; i < 20000; i++) bar.EnsureFillRenderable();
|
||
|
|
long e1 = GC.GetTotalMemory(false);
|
||
|
|
N("EnsureFillRenderable 20,000회: GC " + e0 + " → " + e1 + " (Δ " + (e1 - e0) + " B · 문자열 반환 포함)");
|
||
|
|
|
||
|
|
// ── ⑩ 수리 후 노드 전수 재덤프 ─────────────────────────────────
|
||
|
|
BossHpBar.DebugFakeHp = 869f;
|
||
|
|
bar.PollBoss(true);
|
||
|
|
DumpBarNodes(root, "⑩ 수리 후 노드 전수 (HP 869/1800)");
|
||
|
|
N(bar.Dump());
|
||
|
|
}
|
||
|
|
finally
|
||
|
|
{
|
||
|
|
try { if (dupGo != null) UnityEngine.Object.DestroyImmediate(dupGo); } catch { }
|
||
|
|
try { if (bar != null) bar.Subscribe(false); } catch { }
|
||
|
|
BossHpBar.ClearFakeHp();
|
||
|
|
BossHpBar.DebugFallbackMaxHp = 100f;
|
||
|
|
BossHpBar.RunBossHpRatio = -1f; BossHpBar.RunBossSeen = false; BossHpBar.RunResetCount = 0;
|
||
|
|
boss.bossBarForceFilledFill = sForce; boss.bossBarFillWhiteFallback = sWhite;
|
||
|
|
boss.bossBarSpriteOnBgAndTicks = sBgTick; boss.bossBarDedupInstances = sDedup;
|
||
|
|
boss.bossBarHideOnZeroHp = sZero;
|
||
|
|
try { if (root != null) PrefabUtility.UnloadPrefabContents(root); } catch { }
|
||
|
|
}
|
||
|
|
|
||
|
|
// ── ⑪ 저장 0 · 구독 0 ───────────────────────────────────────────────
|
||
|
|
H("⑪ 뒷정리 — 프리팹 저장 0 · 구독 0");
|
||
|
|
var fi1 = new FileInfo(abs); fi1.Refresh();
|
||
|
|
N("프리팹 후 = " + fi1.Length + " B · " + fi1.LastWriteTimeUtc.ToString("yyyy-MM-dd HH:mm:ss.fff") + " UTC");
|
||
|
|
Chk(fi1.Length == len0 && fi1.LastWriteTimeUtc == mt0, "프리팹 파일 무변동 = 저장 0");
|
||
|
|
N("CombatEvents Spawned=" + WL.Combat.Core.CombatEvents.Spawned.Count +
|
||
|
|
" HitConfirmed=" + WL.Combat.Core.CombatEvents.HitConfirmed.Count +
|
||
|
|
" Killed=" + WL.Combat.Core.CombatEvents.Killed.Count +
|
||
|
|
" · RunStarted=" + WL.Combat.Run.RunEvents.RunStarted.Count);
|
||
|
|
N("SO 원복 = force:" + boss.bossBarForceFilledFill + " white:" + boss.bossBarFillWhiteFallback +
|
||
|
|
" bgTick:" + boss.bossBarSpriteOnBgAndTicks + " dedup:" + boss.bossBarDedupInstances +
|
||
|
|
" hp0:" + boss.bossBarHideOnZeroHp);
|
||
|
|
N("DebugFakeHp=" + BossHpBar.DebugFakeHp + " DebugFakeMaxHp=" + BossHpBar.DebugFakeMaxHp +
|
||
|
|
" DebugFallbackMaxHp=" + BossHpBar.DebugFallbackMaxHp);
|
||
|
|
|
||
|
|
return Flush();
|
||
|
|
}
|
||
|
|
|
||
|
|
static string Flush()
|
||
|
|
{
|
||
|
|
_o.AppendLine();
|
||
|
|
_o.AppendLine("== 합계 PASS " + _pass + " / FAIL " + _fail + " ==");
|
||
|
|
string p = OutPath("WL813c3_PROBE.txt");
|
||
|
|
try { File.WriteAllText(p, _o.ToString(), new UTF8Encoding(true)); } catch { }
|
||
|
|
return _o.ToString() + "\n(파일: " + p + ")";
|
||
|
|
}
|
||
|
|
}
|