// WL842_StatusPrefabs.cs — #842 상태이상 VFX 준비(에디터 전용 · Play 밖에서) // ① ConvertLegacy : Master Stylized FX 안에서 아직 내장(Built-in) 셰이더(Particles/Standard Unlit 211 · Surface 210 · Standard 46)를 쓰는 머티리얼을 URP 로 변환. // 동봉 MSFURP 패키지가 46장을 빠뜨린다(DizzyStar·SleepSymbol·BubbleSymbol·Spike3D 등 = 우리가 쓰는 것들). _Mode(2 Fade·4 Additive) 와 블렌드 값을 그대로 옮긴다. // ② Build : Prefabs/Buffs·Hit 의 원본 프리팹을 복제해 Resources/WL/Status/.prefab 으로 저장(독은 보라 틴트). 원본 에셋 무수정. // unity command run_script --file AgentScripts/WL842_StatusPrefabs.cs --entry WL842_StatusPrefabs.ConvertLegacy // unity command run_script --file AgentScripts/WL842_StatusPrefabs.cs --entry WL842_StatusPrefabs.Build --args '[]' using System.Collections.Generic; using System.Text; using UnityEditor; using UnityEngine; public static class WL842_StatusPrefabs { const string AssetRoot = "Assets/ResWork/VFX/MasterStylizedFX"; const string OutDir = "Assets/WL/Combat/Status/Resources/WL/Status"; const string BuiltinGuid = "0000000000000000f000000000000000"; // 출력 이름 → (원본 프리팹 경로 · 틴트(없으면 null)) static readonly (string name, string src, Color? tint)[] Map = { ("WL_Status_Stun", "Prefabs/Buffs/Dizziness.prefab", null), ("WL_Status_Sleep", "Prefabs/Buffs/Sleeping.prefab", null), ("WL_Status_SpeedUp", "Prefabs/Buffs/EnhanceBuff.prefab", null), // 꺽새(^) 상승 ("WL_Status_SpeedDown", "Prefabs/Buffs/Debuff.prefab", null), // 꺽새 하락 ("WL_Status_StatUp", "Prefabs/Buffs/EnhanceBuff2.prefab", null), // 화살표 상승 ("WL_Status_Poison", "Prefabs/Buffs/DrunkBuff.prefab", new Color(0.72f, 0.30f, 1f, 1f)), // 방울+연기 → 보라 ("WL_Status_Bleed", "Prefabs/Hit/OrangeSlashHit.prefab", null), // 스크린샷 대조 후 교체 가능(SetBleedSource) }; public static string ConvertLegacy() { if (EditorApplication.isPlaying) return "skipped: playing"; var sb = new StringBuilder(); int n = 0, skipped = 0; var unlit = Shader.Find("Universal Render Pipeline/Particles/Unlit"); var lit = Shader.Find("Universal Render Pipeline/Lit"); if (unlit == null || lit == null) return "URP shaders not found"; foreach (var guid in AssetDatabase.FindAssets("t:Material", new[] { AssetRoot + "/Materials" })) { string path = AssetDatabase.GUIDToAssetPath(guid); var txt = System.IO.File.ReadAllText(path); var m = System.Text.RegularExpressions.Regex.Match(txt, @"m_Shader: \{fileID: (\d+), guid: " + BuiltinGuid); if (!m.Success) { skipped++; continue; } int fid = int.Parse(m.Groups[1].Value); var mat = AssetDatabase.LoadAssetAtPath(path); if (mat == null) continue; var mainTex = mat.HasProperty("_MainTex") ? mat.GetTexture("_MainTex") : null; var scale = mat.HasProperty("_MainTex") ? mat.GetTextureScale("_MainTex") : Vector2.one; var offset = mat.HasProperty("_MainTex") ? mat.GetTextureOffset("_MainTex") : Vector2.zero; var color = mat.HasProperty("_Color") ? mat.GetColor("_Color") : Color.white; if (fid == 46) { // Standard → Lit mat.shader = lit; mat.SetTexture("_BaseMap", mainTex); mat.SetColor("_BaseColor", color); sb.AppendLine("lit " + path.Substring(AssetRoot.Length + 1)); EditorUtility.SetDirty(mat); n++; continue; } // Particles/Standard Unlit(211)·Surface(210) → Particles/Unlit · 블렌드 값(_Mode·_SrcBlend·_DstBlend·_ZWrite·_Cull·_ColorMode) 승계 int mode = mat.HasProperty("_Mode") ? (int)mat.GetFloat("_Mode") : 2; float src = mat.HasProperty("_SrcBlend") ? mat.GetFloat("_SrcBlend") : 5f, dst = mat.HasProperty("_DstBlend") ? mat.GetFloat("_DstBlend") : 10f; float zw = mat.HasProperty("_ZWrite") ? mat.GetFloat("_ZWrite") : 0f, cull = mat.HasProperty("_Cull") ? mat.GetFloat("_Cull") : 2f; float colorMode = mat.HasProperty("_ColorMode") ? mat.GetFloat("_ColorMode") : 0f; float soft = mat.HasProperty("_SoftParticlesEnabled") ? mat.GetFloat("_SoftParticlesEnabled") : 0f; mat.shader = unlit; mat.SetTexture("_BaseMap", mainTex); mat.SetTextureScale("_BaseMap", scale); mat.SetTextureOffset("_BaseMap", offset); mat.SetColor("_BaseColor", color); bool opaque = mode == 0 || mode == 1; mat.SetFloat("_Surface", opaque ? 0f : 1f); mat.SetFloat("_Blend", mode == 4 ? 2f : mode == 3 ? 1f : mode == 6 ? 3f : 0f); mat.SetFloat("_AlphaClip", mode == 1 ? 1f : 0f); mat.SetFloat("_SrcBlend", src); mat.SetFloat("_DstBlend", dst); if (mat.HasProperty("_SrcBlendAlpha")) { mat.SetFloat("_SrcBlendAlpha", src); mat.SetFloat("_DstBlendAlpha", dst); } mat.SetFloat("_ZWrite", zw); mat.SetFloat("_Cull", cull); mat.SetFloat("_ColorMode", colorMode); mat.SetFloat("_SoftParticlesEnabled", soft); if (opaque) { mat.DisableKeyword("_SURFACE_TYPE_TRANSPARENT"); mat.SetOverrideTag("RenderType", mode == 1 ? "TransparentCutout" : "Opaque"); mat.renderQueue = mode == 1 ? 2450 : 2000; } else { mat.EnableKeyword("_SURFACE_TYPE_TRANSPARENT"); mat.SetOverrideTag("RenderType", "Transparent"); mat.renderQueue = 3000; } if (mode == 3) mat.EnableKeyword("_ALPHAPREMULTIPLY_ON"); else mat.DisableKeyword("_ALPHAPREMULTIPLY_ON"); if (mode == 6) mat.EnableKeyword("_ALPHAMODULATE_ON"); else mat.DisableKeyword("_ALPHAMODULATE_ON"); if (mode == 1) mat.EnableKeyword("_ALPHATEST_ON"); else mat.DisableKeyword("_ALPHATEST_ON"); if (soft > 0f) mat.EnableKeyword("_SOFTPARTICLES_ON"); else mat.DisableKeyword("_SOFTPARTICLES_ON"); mat.SetShaderPassEnabled("ShadowCaster", false); EditorUtility.SetDirty(mat); n++; sb.AppendLine("unlit " + path.Substring(AssetRoot.Length + 1) + " mode=" + mode + " blend=" + src + "/" + dst); } AssetDatabase.SaveAssets(); return "converted " + n + " (already URP " + skipped + ")\n" + sb; } public static string Build() { if (EditorApplication.isPlaying) return "skipped: playing"; if (!AssetDatabase.IsValidFolder(OutDir)) System.IO.Directory.CreateDirectory(OutDir); AssetDatabase.Refresh(); var sb = new StringBuilder(); foreach (var e in Map) { string srcPath = AssetRoot + "/" + e.src; var src = AssetDatabase.LoadAssetAtPath(srcPath); if (src == null) { sb.AppendLine("missing " + srcPath); continue; } var inst = (GameObject)PrefabUtility.InstantiatePrefab(src); PrefabUtility.UnpackPrefabInstance(inst, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction); inst.name = e.name; inst.transform.position = Vector3.zero; inst.transform.rotation = Quaternion.identity; inst.transform.localScale = Vector3.one; int ps = 0; foreach (var p in inst.GetComponentsInChildren(true)) { ps++; { var main = p.main; main.scalingMode = ParticleSystemScalingMode.Hierarchy; } // 트랜스폼 스케일이 파티클 크기에도 먹게(기본 Shape 는 방출 범위만 줄어 별이 원래 크기로 남는다 · 2026-10-11 실측) if (e.tint.HasValue) { var main = p.main; var sc = main.startColor; sc.mode = ParticleSystemGradientMode.Color; sc.color = e.tint.Value; main.startColor = sc; var col = p.colorOverLifetime; if (col.enabled) { var g = col.color; if (g.mode == ParticleSystemGradientMode.Gradient && g.gradient != null) { var grad = g.gradient; var keys = grad.colorKeys; for (int i = 0; i < keys.Length; i++) keys[i].color = e.tint.Value; grad.SetKeys(keys, grad.alphaKeys); g.gradient = grad; col.color = g; } } } } foreach (var l in inst.GetComponentsInChildren(true)) Object.DestroyImmediate(l); // 포인트 라이트는 치비 씬에서 과함 string outPath = OutDir + "/" + e.name + ".prefab"; PrefabUtility.SaveAsPrefabAsset(inst, outPath); Object.DestroyImmediate(inst); sb.AppendLine(e.name + " ← " + e.src + " ps=" + ps + (e.tint.HasValue ? " tint" : "")); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return sb.ToString(); } // ── #843 임팩트 프리팹 사본 (Resources/WL/Impact/) ─────────────────────────── const string ImpactDir = "Assets/WL/Combat/Impact/Resources/WL/Impact"; static readonly (string name, string src)[] ImpactMap = { ("WL_Impact_Hit_Blue", "Prefabs/Hit/SlashHitBlue.prefab"), // 검 피격 — 파란 별 + 긴 베기 줄 + 불꽃(PD 스크린샷) ("WL_Impact_Hit_Orange", "Prefabs/Hit/OrangeSlashHit.prefab"), // 해머 피격 — 주황 타격 폭발 ("WL_Impact_Swing_Orange", "Prefabs/Slash/OrangeSlash.prefab"), // 해머 궤적 — 주황 회전 링(SlashHead·DustRing) ("WL_Impact_Ground", "Prefabs/GroundBlast/ExplosionBlast.prefab"), // 지면 충격파 — 주황 폭발 + 검은 파편 + 불꽃 }; public static string BuildImpact() { if (EditorApplication.isPlaying) return "skipped: playing"; if (!AssetDatabase.IsValidFolder(ImpactDir)) System.IO.Directory.CreateDirectory(ImpactDir); AssetDatabase.Refresh(); var sb = new StringBuilder(); foreach (var e in ImpactMap) { var src = AssetDatabase.LoadAssetAtPath(AssetRoot + "/" + e.src); if (src == null) { sb.AppendLine("missing " + e.src); continue; } var inst = (GameObject)PrefabUtility.InstantiatePrefab(src); PrefabUtility.UnpackPrefabInstance(inst, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction); inst.name = e.name; inst.transform.position = Vector3.zero; inst.transform.rotation = Quaternion.identity; inst.transform.localScale = Vector3.one; int ps = 0; foreach (var p in inst.GetComponentsInChildren(true)) { ps++; var main = p.main; main.scalingMode = ParticleSystemScalingMode.Hierarchy; } foreach (var l in inst.GetComponentsInChildren(true)) Object.DestroyImmediate(l); PrefabUtility.SaveAsPrefabAsset(inst, ImpactDir + "/" + e.name + ".prefab"); Object.DestroyImmediate(inst); sb.AppendLine(e.name + " ← " + e.src + " ps=" + ps); } AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return sb.ToString(); } /// 출혈 원본 프리팹 교체(스크린샷 대조 결과 반영). 예: SetBleedSource("Prefabs/Slash/OrangeSlash.prefab") public static string SetBleedSource(string relPrefab) { if (EditorApplication.isPlaying) return "skipped: playing"; var src = AssetDatabase.LoadAssetAtPath(AssetRoot + "/" + relPrefab); if (src == null) return "missing " + relPrefab; var inst = (GameObject)PrefabUtility.InstantiatePrefab(src); PrefabUtility.UnpackPrefabInstance(inst, PrefabUnpackMode.Completely, InteractionMode.AutomatedAction); inst.name = "WL_Status_Bleed"; inst.transform.position = Vector3.zero; inst.transform.rotation = Quaternion.identity; inst.transform.localScale = Vector3.one; foreach (var p in inst.GetComponentsInChildren(true)) { var main = p.main; main.scalingMode = ParticleSystemScalingMode.Hierarchy; } foreach (var l in inst.GetComponentsInChildren(true)) Object.DestroyImmediate(l); PrefabUtility.SaveAsPrefabAsset(inst, OutDir + "/WL_Status_Bleed.prefab"); Object.DestroyImmediate(inst); AssetDatabase.SaveAssets(); return "bleed ← " + relPrefab; } }