Project_WL/AgentScripts/WL800_Apply.cs

406 lines
22 KiB
C#
Raw Normal View History

WL 리뉴얼 세션 2026-09-06~07 (#760~#800) — 전투·배경·UI·타격감 (조직 PD 로그 #746~#800 · 대화로그 §14~§24) - 전투: Knight@Attack1~3_S 3콤보(4 컨트롤러·클래스별 서브클립) · 타겟팅 4규칙(정면 우선·재타겟·즉시 공격·어그로 금지 · WLTargetingSettings) · 대쉬 후 공격(Stander@Chase_Start · DashDriver · 5 m · 사정거리+0.8 · 근거리 적 우선) · 공격 이동 FrameTable(발 접지 실측 표 · AttackRootMotion) · 충돌 반경/투사체 0.3 배(WLCollisionTuning) · 무적(임시)·펫 금지(WLGameplaySettings) - 검기: NamuFX Slash_B 배리언트 Effect_WLSwingArc 원 피팅 정합 배치 + 캘리브레이션(SlashArcMeasure · SlashTrailSettings) · 찌르기 Effect_WLStab 대기 · 램프 리본(BladeTrail · WL_BladeRibbon.shader · T_WL_BladeRibbonRamp) 보존(drawRibbon 0) - 타격감: Assets/WL/Feel(WLHitFeel · 히트스톱 0.03 · 셰이크 0.10 m · 몹 펀치 1.12 · Actor.Get_Damage 훅 1줄 · 원본 RealCamera 셰이크 결함 대체) - 배경/맵: LMHPOLY Demo_01~10 → WL_Nature01~10(프리팹·씬·NavMesh·스포너·BattleMapConfig) · 물(ToonWaterU) · 포스트 블룸 0.9/0.3 · 잔디(BruteForce·드레싱) 제거 · 마젠타 머티리얼 URP 변환 - UI: 세로 HUD(WL_HUD · 하단 5메뉴 폭 전체 · 채팅/전투 패드 숨김 · WLIngameUiOverride) · Title/TitleInfo 1080×1920 Expand + 배경 높이 fit(WLBackgroundFit) · 로딩 SortOrder_5(WLRawImageAspectSync · 초점표 8장) · Loading1~8 ASTC 6×6 - 도구: AgentScripts/*(LightProbe · WL_MapSwitch · WL760~WL800 프로브/집행/검증 · 상단 사용법 주석) · 에디터 락 프로토콜 파일(staging) - 제외(별도 커밋 예정): Assets/LMHPOLY(703 MB) · Assets/Feel(422 MB) · Assets/Shinabro(300 MB) — 에셋 스토어/구 WL 팩 원본 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-06 23:24:25 +00:00
// WL800_Apply.cs — PD 지시 #800 로딩 화면 비율 어긋남 집행 (에디트 모드 · Assets 수정)
//
// unity command run_script --file AgentScripts/WL800_Apply.cs --entry WL800_Apply.ApplyPrefab --args '["0.68,0.70,0.50,0.29,0.70,0.27,0.29,0.71"]'
// unity command run_script --file AgentScripts/WL800_Apply.cs --entry WL800_Apply.ApplyTextures
// unity command run_script --file AgentScripts/WL800_Apply.cs --entry WL800_Apply.TuneCluster --args '[28,1080]'
// unity command run_script --file AgentScripts/WL800_Apply.cs --entry WL800_Apply.SetFocusX --args '[0.5]'
// unity command run_script --file AgentScripts/WL800_Apply.cs --entry WL800_Apply.Dump // 읽기 전용 확인
//
// 초점표(focusX_perImage): Loading1~8 은 주인공이 서 있는 가로 위치가 제각각이다(0.27~0.71).
// 세로 화면에서는 원본 가로의 31.6% 만 보이므로, 단일 초점 0.5 로는 8장 중 6장에서
// 인물이 아예 화면 밖으로 밀려난다. 그래서 장별 초점을 LoadingUI 에 데이터로 들고 간다.
//
// 진단(실측 근거 · #800):
// SortOrder_5.prefab / LoadingUI/child/RawImage 가 앵커 전체 스트레치다.
// CanvasScaler = 참조 1920x1080 · Expand 이므로 1080x1920 단말에서 캔버스가 1920x3413 로 열리고,
// 1920x1080 원본(Loading1~8, 8장 모두 1.7778)이 세로로 3.16배 늘어난다. = PD 가 본 "비율 어긋남".
// 타이틀(#795)과 동일 원인·동일 해법.
//
// 해법(타이틀과 같은 방식):
// LoadingUI/child : Image(검정) 추가 = 백드롭 겸 입력 차단 (현 RawImage 의 raycast 역할 승계)
// LoadingUI/child/RawImage : 세로 스트레치 + AspectRatioFitter(HeightControlsWidth)
// + WLBackgroundFit(초점 클램프) + WLRawImageAspectSync(런타임 텍스처 추종)
// => 높이 = 화면 높이, 폭 = 높이 x 원본 종횡비. 넘치면 초점 크롭(대칭), 좁으면 좌우 대칭 검정.
//
// 캔버스 기준(참조 해상도)은 건드리지 않는다. 근거는 WL800_Shot.MeasureCanvas 실측 참조:
// Expand + 참조 1920x1080 은 세로 단말에서 캔버스 폭이 정확히 1920 유닛 = 모든 자식이 작성된 폭 기준과 일치.
// 1080x1920 로 바꾸면 캔버스 폭이 1080 유닛이 되어 Slider_(1510) · tip(1000) · 팝업류가 전부 화면 밖으로 넘친다.
//
// 값의 SOT 는 이 스크립트가 아니라 프리팹이다. 여기서는 "기본값 1회 주입"만 하고 이후는 인스펙터에서 튜닝한다(C45).
using System.Text;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;
public static class WL800_Apply
{
public const string PrefabPath = "Assets/ResWork/UIPrefabs/Title/SortOrder_5.prefab";
public const string LoadingRoot = "LoadingUI";
public const string BackdropPath = "LoadingUI/child"; // LoadingUI.go (SetActive 대상)
public const string BgPath = "LoadingUI/child/RawImage"; // LoadingUI.loadingimg
const string TexDir = "Assets/Res_Addr/Loading";
const int TexCount = 8; // LoadingUI.MaxImage 와 동일 (Loading1~8)
// ---------- 1) 프리팹 ----------
/// focusCsv = Loading1~8 각 장의 초점 x (예 "0.68,0.70,0.50,0.29,0.70,0.27,0.29,0.71").
/// 빈 문자열이면 전부 0.5(정중앙). 값은 이 스크립트가 아니라 프리팹이 SOT 다 — 여기서는 1회 주입만 한다.
public static object ApplyPrefab(string focusCsv)
{
if (EditorApplication.isPlaying) return "ABORT: Play 중";
var sb = new StringBuilder();
sb.AppendLine("=== SortOrder_5.prefab / LoadingUI ===");
float[] focus = ParseFocus(focusCsv, sb);
float focusX = focus != null && focus.Length > 0 ? focus[0] : 0.5f;
var root = PrefabUtility.LoadPrefabContents(PrefabPath);
if (root == null) return "ABORT: 프리팹 로드 실패 " + PrefabPath;
try
{
// 캔버스 기준은 그대로 둔다 — 현재 값을 기록만 한다(변경 없음 증빙).
var cs = root.GetComponent<CanvasScaler>();
if (cs != null)
sb.AppendLine(" CanvasScaler(변경 없음): ref " + cs.referenceResolution
+ " / " + cs.screenMatchMode + " / match " + cs.matchWidthOrHeight);
var backdropTr = root.transform.Find(BackdropPath) as RectTransform;
var bgTr = root.transform.Find(BgPath) as RectTransform;
if (backdropTr == null) return "ABORT: 경로 없음 " + BackdropPath;
if (bgTr == null) return "ABORT: 경로 없음 " + BgPath;
var bgRaw = bgTr.GetComponent<RawImage>();
if (bgRaw == null) return "ABORT: RawImage 없음 " + BgPath;
// --- 1) 부모 = 검정 백드롭 (여백을 확실히 채우고, 원래 RawImage 가 하던 입력 차단을 승계) ---
bool hadRaycast = bgRaw.raycastTarget;
var backdrop = backdropTr.GetComponent<Image>();
if (backdrop == null)
{
backdrop = backdropTr.gameObject.AddComponent<Image>();
sb.AppendLine(" child: Image 신규 추가 (검정 백드롭)");
}
else sb.AppendLine(" child: 기존 Image 재사용");
backdrop.sprite = null;
backdrop.color = Color.black;
backdrop.raycastTarget = hadRaycast; // 로딩 중 하위 UI 입력 차단 유지
backdrop.maskable = false;
// 백드롭은 전체 스트레치를 유지한다
backdropTr.anchorMin = Vector2.zero;
backdropTr.anchorMax = Vector2.one;
backdropTr.pivot = new Vector2(0.5f, 0.5f);
backdropTr.anchoredPosition = Vector2.zero;
backdropTr.sizeDelta = Vector2.zero;
sb.AppendLine(" color=black raycastTarget=" + backdrop.raycastTarget + " (RawImage 에서 승계)");
// --- 2) 배경 = 세로 맞춤 · 종횡비 유지 · 초점 크롭 ---
bgRaw.raycastTarget = false; // 입력은 부모가 받는다 (레이캐스트 대상 1개로 축소)
bgRaw.color = Color.white;
bgTr.anchorMin = new Vector2(0.5f, 0f); // 세로 스트레치 + 가로 자유
bgTr.anchorMax = new Vector2(0.5f, 1f); // => AspectRatioFitter 가 폭을 계산할 수 있는 유일한 구성
bgTr.pivot = new Vector2(0.5f, 0.5f);
bgTr.anchoredPosition = Vector2.zero;
bgTr.sizeDelta = Vector2.zero;
bgTr.localScale = Vector3.one;
// 원본 종횡비는 텍스처에서 유도한다(하드코딩 아님). 런타임 교체분은 Sync 컴포넌트가 추종.
float ratio = SourceRatio(sb);
var arf = bgTr.GetComponent<AspectRatioFitter>() ?? bgTr.gameObject.AddComponent<AspectRatioFitter>();
arf.aspectMode = AspectRatioFitter.AspectMode.HeightControlsWidth;
arf.aspectRatio = ratio;
var fit = bgTr.GetComponent<WLBackgroundFit>() ?? bgTr.gameObject.AddComponent<WLBackgroundFit>();
fit.focusX = focusX;
fit.applyVertical = false;
var sync = bgTr.GetComponent<WLRawImageAspectSync>() ?? bgTr.gameObject.AddComponent<WLRawImageAspectSync>();
sync.fallbackRatio = ratio;
sb.AppendLine(" RawImage: 전체 스트레치 -> 세로 맞춤(HeightControlsWidth) ratio=" + ratio.ToString("F4")
+ " focusX=" + focusX.ToString("F2") + " raycast=false");
sb.AppendLine(" + AspectRatioFitter / WLBackgroundFit / WLRawImageAspectSync(런타임 텍스처 추종)");
// --- 3) 이미지 번호별 초점표를 LoadingUI 에 주입 ---
// 그림마다 주인공 위치가 달라 단일 초점으로는 6/8 장에서 인물이 화면 밖으로 밀려난다.
var lui = root.transform.Find(LoadingRoot)?.GetComponent<LoadingUI>();
if (lui == null) sb.AppendLine(" !! LoadingUI 컴포넌트를 못 찾음 — 초점표 미주입");
else
{
lui.bgFit = fit;
if (focus != null && focus.Length > 0)
{
lui.focusX_perImage = focus;
sb.AppendLine(" LoadingUI.focusX_perImage = [" + string.Join(", ", System.Array.ConvertAll(focus, v => v.ToString("F2"))) + "]"
+ " (MaxImage=" + lui.MaxImage + ")");
if (focus.Length != lui.MaxImage)
sb.AppendLine(" !! 초점 개수 " + focus.Length + " != MaxImage " + lui.MaxImage + " — 남는 장은 0.5 로 처리된다");
}
sb.AppendLine(" LoadingUI.bgFit = " + fit.name);
}
PrefabUtility.SaveAsPrefabAsset(root, PrefabPath);
sb.AppendLine(" 저장 완료");
}
finally { PrefabUtility.UnloadPrefabContents(root); }
AssetDatabase.SaveAssets();
return sb.ToString();
}
/// Loading1~8 이 모두 같은 종횡비인지 확인하고 그 값을 돌려준다. 다르면 첫 장 기준 + 경고.
static float SourceRatio(StringBuilder sb)
{
float first = 0f; bool mixed = false;
for (int i = 1; i <= TexCount; i++)
{
var t = AssetDatabase.LoadAssetAtPath<Texture2D>(TexPath(i));
if (t == null) continue;
float r = t.width / (float)t.height;
if (first == 0f) first = r;
else if (Mathf.Abs(r - first) > 0.001f) mixed = true;
}
if (first == 0f) { sb.AppendLine(" !! 로딩 텍스처를 하나도 못 읽음 -> 16:9 로 가정"); return 16f / 9f; }
sb.AppendLine(" 원본 종횡비(Loading1~" + TexCount + "): " + first.ToString("F4")
+ (mixed ? " !! 장마다 다름 -> Sync 컴포넌트가 런타임에 보정" : " (8장 동일)"));
return first;
}
static string TexPath(int i) { return TexDir + "/Loading" + i + ".jpg"; }
static float[] ParseFocus(string csv, StringBuilder sb)
{
if (string.IsNullOrEmpty(csv)) { sb.AppendLine(" 초점표 미지정 -> 전부 0.5(정중앙)"); return null; }
var parts = csv.Split(',');
var v = new float[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
float f;
if (!float.TryParse(parts[i].Trim(), System.Globalization.NumberStyles.Float,
System.Globalization.CultureInfo.InvariantCulture, out f))
{ sb.AppendLine(" !! 초점 파싱 실패 '" + parts[i] + "' -> 0.5"); f = 0.5f; }
v[i] = Mathf.Clamp01(f);
}
return v;
}
// ---------- 2) 텍스처 임포트 (Loading4 기준으로 8장 통일) ----------
public static object ApplyTextures()
{
if (EditorApplication.isPlaying) return "ABORT: Play 중";
var sb = new StringBuilder();
sb.AppendLine("=== Loading1~" + TexCount + ".jpg 임포트 통일 (기준 = Loading4 / #795) ===");
int changed = 0;
AssetDatabase.StartAssetEditing();
try
{
for (int i = 1; i <= TexCount; i++)
{
var path = TexPath(i);
var ti = AssetImporter.GetAtPath(path) as TextureImporter;
if (ti == null) { sb.AppendLine(" !! TextureImporter 없음 " + path); continue; }
bool dirty = false;
var before = new StringBuilder();
var def = ti.GetDefaultPlatformTextureSettings();
if (def.maxTextureSize != 2048 || def.textureCompression != TextureImporterCompression.Compressed)
{
before.Append(" Default[" + def.maxTextureSize + "/" + def.textureCompression + "]");
def.maxTextureSize = 2048;
def.textureCompression = TextureImporterCompression.Compressed;
ti.SetPlatformTextureSettings(def);
dirty = true;
}
var and = ti.GetPlatformTextureSettings("Android");
if (!and.overridden || and.format != TextureImporterFormat.ASTC_6x6
|| and.compressionQuality != 100 || and.maxTextureSize != 2048 || and.crunchedCompression)
{
before.Append(" Android[ovr=" + and.overridden + "/" + and.format + "/q" + and.compressionQuality + "]");
and.overridden = true;
and.maxTextureSize = 2048;
and.format = TextureImporterFormat.ASTC_6x6;
and.compressionQuality = 100;
and.crunchedCompression = false;
ti.SetPlatformTextureSettings(and);
dirty = true;
}
if (ti.mipmapEnabled) { before.Append(" mipmap[on]"); ti.mipmapEnabled = false; dirty = true; }
if (ti.isReadable) { before.Append(" isReadable[on]"); ti.isReadable = false; dirty = true; }
if (ti.filterMode != FilterMode.Bilinear) { before.Append(" filter[" + ti.filterMode + "]"); ti.filterMode = FilterMode.Bilinear; dirty = true; }
if (dirty)
{
EditorUtility.SetDirty(ti);
ti.SaveAndReimport();
changed++;
sb.AppendLine(" Loading" + i + ": 변경" + before);
}
else sb.AppendLine(" Loading" + i + ": 이미 기준 충족 (변경 없음)");
}
}
finally { AssetDatabase.StopAssetEditing(); }
AssetDatabase.Refresh();
AssetDatabase.SaveAssets();
sb.AppendLine(" 변경 " + changed + " / " + TexCount + " 장");
for (int i = 1; i <= TexCount; i++)
{
var t = AssetDatabase.LoadAssetAtPath<Texture2D>(TexPath(i));
if (t != null)
sb.AppendLine(string.Format(" Loading{0}: {1}x{2} format={3} mip={4}", i, t.width, t.height, t.format, t.mipmapCount));
}
return sb.ToString();
}
// ---------- 3) 로딩 하단 클러스터 가독성 (세로 기준 재배치) ----------
//
// 캔버스가 Expand + 참조 1920x1080 이므로 캔버스 폭은 어떤 단말에서도 정확히 1920 유닛이다.
// => 1 유닛 = (화면폭 / 1920) px. 1080 폭 단말에서 1 유닛 = 0.5625 px.
// 현재 퍼센트/팁 글자는 20 유닛 = 11.3 px 로, 1080 폭 단말에서 사실상 판독 불가다.
// 목표 글자 px 하나만 받아 필요한 유닛을 역산한다(매직넘버 없음).
// 구도(가로 폭·앵커·mark/botimg 배치)는 건드리지 않는다 — 아트 결정 영역.
public static object TuneCluster(float targetTextPx, float refScreenWidth)
{
if (EditorApplication.isPlaying) return "ABORT: Play 중";
var sb = new StringBuilder();
var root = PrefabUtility.LoadPrefabContents(PrefabPath);
if (root == null) return "ABORT: 프리팹 로드 실패";
try
{
var cs = root.GetComponent<CanvasScaler>();
float unitPerPx = cs.referenceResolution.x / refScreenWidth; // 유닛/px
float fontUnit = Mathf.Round(targetTextPx * unitPerPx);
sb.AppendLine("=== 로딩 하단 클러스터 (목표 글자 " + targetTextPx.ToString("F0")
+ "px @ 화면폭 " + refScreenWidth.ToString("F0") + ") ===");
sb.AppendLine(" 캔버스 폭 " + cs.referenceResolution.x + " 유닛 / 화면폭 " + refScreenWidth
+ "px => 1 유닛 = " + (1f / unitPerPx).ToString("F4") + "px, 목표 글자 = " + fontUnit + " 유닛");
var t = root.transform;
float lineH = Mathf.Round(fontUnit * 1.4f); // 글자 1줄이 들어갈 최소 높이
// 퍼센트 텍스트
SetFont(t, "LoadingUI/child/Slider_/t_proc", fontUnit, new Vector2(fontUnit * 6f, lineH), sb);
// 슬라이더 바 = 글자 높이의 약 88% (바가 글자보다 두꺼워 보이지 않도록)
SetSize(t, "LoadingUI/child/Slider_", null, Mathf.Round(fontUnit * 0.88f), sb);
SetSize(t, "LoadingUI/child/Slider_/handle", Mathf.Round(fontUnit * 1.76f), Mathf.Round(fontUnit * 1.76f), sb);
// 팁 바 + 팁 텍스트 (텍스트 폭은 바 안쪽으로 제한 = 잘림 방지)
var tipBar = t.Find("LoadingUI/child/tip") as RectTransform;
float tipW = tipBar != null ? tipBar.sizeDelta.x : 1000f;
SetSize(t, "LoadingUI/child/tip", null, lineH, sb);
SetFont(t, "LoadingUI/child/tip/tip", fontUnit, new Vector2(tipW - fontUnit, lineH), sb);
// 커진 바끼리 겹치지 않도록 팁 바만 아래로 내린다 (슬라이더/마크/botimg 위치는 유지)
var slider = t.Find("LoadingUI/child/Slider_") as RectTransform;
if (tipBar != null && slider != null)
{
float sliderBottom = slider.anchoredPosition.y - slider.sizeDelta.y * 0.5f;
float wantTop = sliderBottom - Mathf.Round(fontUnit * 0.3f); // 여백 = 글자의 30%
float newY = wantTop - tipBar.sizeDelta.y * 0.5f;
if (newY < tipBar.anchoredPosition.y)
{
sb.AppendLine(" tip 위치 y " + tipBar.anchoredPosition.y + " -> " + newY + " (슬라이더와 겹침 방지)");
tipBar.anchoredPosition = new Vector2(tipBar.anchoredPosition.x, newY);
}
}
PrefabUtility.SaveAsPrefabAsset(root, PrefabPath);
sb.AppendLine(" 저장 완료");
}
finally { PrefabUtility.UnloadPrefabContents(root); }
AssetDatabase.SaveAssets();
return sb.ToString();
}
static void SetFont(Transform root, string path, float size, Vector2 rect, StringBuilder sb)
{
var rt = root.Find(path) as RectTransform;
if (rt == null) { sb.AppendLine(" !! 경로 없음 " + path); return; }
var tmp = rt.GetComponent<TMPro.TextMeshProUGUI>();
if (tmp == null) { sb.AppendLine(" !! TMP 없음 " + path); return; }
sb.AppendLine(" " + path + ": font " + tmp.fontSize + " -> " + size
+ ", rect " + rt.sizeDelta + " -> " + rect);
tmp.fontSize = size;
tmp.enableAutoSizing = false;
rt.sizeDelta = rect;
}
static void SetSize(Transform root, string path, float? w, float? h, StringBuilder sb)
{
var rt = root.Find(path) as RectTransform;
if (rt == null) { sb.AppendLine(" !! 경로 없음 " + path); return; }
var before = rt.sizeDelta;
rt.sizeDelta = new Vector2(w ?? before.x, h ?? before.y);
sb.AppendLine(" " + path + ": size " + before + " -> " + rt.sizeDelta);
}
// ---------- 4) 초점만 재조정 (A/B 비교용) ----------
public static object SetFocusX(float focusX)
{
if (EditorApplication.isPlaying) return "ABORT: Play 중";
var root = PrefabUtility.LoadPrefabContents(PrefabPath);
try
{
var f = root.transform.Find(BgPath)?.GetComponent<WLBackgroundFit>();
if (f == null) return "ABORT: WLBackgroundFit 없음 (ApplyPrefab 먼저)";
f.focusX = focusX;
PrefabUtility.SaveAsPrefabAsset(root, PrefabPath);
}
finally { PrefabUtility.UnloadPrefabContents(root); }
AssetDatabase.SaveAssets();
return "SortOrder_5.prefab " + BgPath + " focusX = " + focusX.ToString("F2");
}
// ---------- 4) 읽기 전용 확인 ----------
public static object Dump()
{
var sb = new StringBuilder();
var root = PrefabUtility.LoadPrefabContents(PrefabPath);
try
{
var cs = root.GetComponent<CanvasScaler>();
sb.AppendLine("CanvasScaler: mode=" + cs.uiScaleMode + " ref=" + cs.referenceResolution
+ " match=" + cs.screenMatchMode + "/" + cs.matchWidthOrHeight);
var bd = root.transform.Find(BackdropPath) as RectTransform;
var img = bd != null ? bd.GetComponent<Image>() : null;
sb.AppendLine("child: active=" + (bd != null && bd.gameObject.activeSelf)
+ " Image=" + (img != null ? (img.color + " raycast=" + img.raycastTarget) : "없음"));
var bg = root.transform.Find(BgPath) as RectTransform;
if (bg != null)
{
var raw = bg.GetComponent<RawImage>();
var arf = bg.GetComponent<AspectRatioFitter>();
var fit = bg.GetComponent<WLBackgroundFit>();
var sync = bg.GetComponent<WLRawImageAspectSync>();
sb.AppendLine("RawImage: anc " + bg.anchorMin + "~" + bg.anchorMax + " piv " + bg.pivot
+ " pos " + bg.anchoredPosition + " size " + bg.sizeDelta
+ " raycast=" + (raw != null && raw.raycastTarget));
sb.AppendLine(" AspectRatioFitter=" + (arf != null ? (arf.aspectMode + "/" + arf.aspectRatio.ToString("F4")) : "없음"));
sb.AppendLine(" WLBackgroundFit=" + (fit != null ? ("focusX " + fit.focusX.ToString("F2")) : "없음"));
sb.AppendLine(" WLRawImageAspectSync=" + (sync != null ? ("fallback " + sync.fallbackRatio.ToString("F4")) : "없음"));
}
var lui = root.transform.Find(LoadingRoot)?.GetComponent<LoadingUI>();
if (lui != null)
sb.AppendLine("LoadingUI: MaxImage=" + lui.MaxImage
+ " bgFit=" + (lui.bgFit != null ? lui.bgFit.name : "null")
+ " focusX_perImage=" + (lui.focusX_perImage == null ? "null"
: "[" + string.Join(", ", System.Array.ConvertAll(lui.focusX_perImage, v => v.ToString("F2"))) + "]"));
}
finally { PrefabUtility.UnloadPrefabContents(root); }
return sb.ToString();
}
}