// WL772_MatFix.cs — PD 지시 #772 : 맵 프리팹의 마젠타(셰이더 깨짐 / null 머티리얼) 진단 + URP 업그레이드 // 진단 : unity command run_script --file AgentScripts/WL772_MatFix.cs --entry WL772_MatFix.Diag --args '["Assets/Res_Addr/Map/WL_Nature03.prefab"]' // 수리 : ... --entry WL772_MatFix.Fix --args '["Assets/Res_Addr/Map/WL_Nature03.prefab"]' // Fix 는 머티리얼 에셋의 셰이더만 Built-in → URP 로 승격한다(MaterialUpgrader). 프리팹은 건드리지 않는다. using System; using System.Collections.Generic; using System.Linq; using System.Text; using UnityEngine; #if UNITY_EDITOR using UnityEditor; using UnityEditor.Rendering; #endif public static class WL772_MatFix { #if UNITY_EDITOR static readonly string[] BuiltIn = { "Standard", "Standard (Specular setup)", "Legacy Shaders/", "Mobile/", "Particles/", "Nature/", "Unlit/Texture", "Unlit/Color", "Unlit/Transparent" }; static List Collect(string prefabPath, StringBuilder sb) { var bad = new List(); var root = PrefabUtility.LoadPrefabContents(prefabPath); try { var seen = new HashSet(); foreach (var r in root.GetComponentsInChildren(true)) foreach (var m in r.sharedMaterials) { if (m == null) { if (seen.Add("NULLSLOT:" + r.name)) sb.AppendLine(" null 머티리얼 슬롯 — " + r.name); continue; } var sn = m.shader != null ? m.shader.name : "(shader null)"; bool broken = m.shader == null || sn == "Hidden/InternalErrorShader" || BuiltIn.Any(b => sn.StartsWith(b)); if (!broken) continue; var path = AssetDatabase.GetAssetPath(m); if (!seen.Add(path + "|" + sn)) continue; sb.AppendLine(string.Format(" [{0}] {1} ← {2}", sn, m.name, path)); if (!string.IsNullOrEmpty(path) && path.StartsWith("Assets/")) bad.Add(m); } } finally { PrefabUtility.UnloadPrefabContents(root); } return bad; } public static object Diag(string prefabPath) { var sb = new StringBuilder("# " + prefabPath + "\n"); var bad = Collect(prefabPath, sb); sb.AppendLine("수리 대상 머티리얼 = " + bad.Count); return sb.ToString(); } public static object Fix(string prefabPath) { var sb = new StringBuilder("# " + prefabPath + "\n"); var bad = Collect(prefabPath, sb); int ok = 0, fail = 0; foreach (var m in bad) { var before = m.shader != null ? m.shader.name : "(null)"; try { MaterialUpgrader.Upgrade(m, new UnityEditor.Rendering.Universal.StandardUpgrader(before), MaterialUpgrader.UpgradeFlags.None); } catch { } if (m.shader == null || m.shader.name == before || m.shader.name == "Hidden/InternalErrorShader") { // 폴백: URP Lit 로 직접 지정하고 주요 값을 옮긴다 var col = m.HasProperty("_Color") ? m.GetColor("_Color") : Color.white; var tex = m.HasProperty("_MainTex") ? m.GetTexture("_MainTex") : null; var lit = Shader.Find("Universal Render Pipeline/Lit"); if (lit == null) { fail++; sb.AppendLine(" FAIL(URP Lit 없음) " + m.name); continue; } m.shader = lit; if (m.HasProperty("_BaseColor")) m.SetColor("_BaseColor", col); if (tex != null && m.HasProperty("_BaseMap")) m.SetTexture("_BaseMap", tex); } EditorUtility.SetDirty(m); ok++; sb.AppendLine(string.Format(" {0} : {1} -> {2}", m.name, before, m.shader != null ? m.shader.name : "(null)")); } AssetDatabase.SaveAssets(); sb.AppendLine(string.Format("업그레이드 {0} · 실패 {1}", ok, fail)); return sb.ToString(); } #endif }