// WL785_Setup.cs — PD 지시 #785 무기 궤적 리본 집행 (에디트 모드 · Assets 수정) // unity command run_script --file AgentScripts/WL785_Setup.cs --entry WL785_Setup.MakeMaterial // unity command run_script --file AgentScripts/WL785_Setup.cs --entry WL785_Setup.ApplySettings // unity command run_script --file AgentScripts/WL785_Setup.cs --entry WL785_Setup.Dump // // 왜 스크립트로 만드는가: // Gradient 는 .asset YAML 로 손으로 쓰기 위험하다(colorKeys/alphaKeys 직렬화가 버전마다 // 미묘하게 다르다). 머티리얼도 셰이더 fileID 를 손으로 넣어야 해 오타 1글자가 곧 분홍 머티리얼이다. // → 공개 API 로 만들고 SetDirty/SaveAssets 로 굽는다. // // 값의 SOT 는 이 스크립트가 아니라 **SlashTrailSettings.asset** 이다. 이 스크립트는 // PD 참고 이미지에 맞춘 "기본값 1회 주입"만 한다. 이후 튜닝은 인스펙터에서 한다(C45). using System.Text; using UnityEditor; using UnityEngine; public static class WL785_Setup { const string ShaderPath = "Assets/WL/Combat/Shaders/WL_BladeRibbon.shader"; const string MatPath = "Assets/WL/Combat/Materials/M_WL_BladeRibbon.mat"; const string SettingsPath = "Assets/WL/Settings/Resources/WL/SlashTrailSettings.asset"; public static object MakeMaterial() { if (EditorApplication.isPlaying) return "ABORT: Play 중"; var sh = AssetDatabase.LoadAssetAtPath(ShaderPath); if (sh == null) return "ABORT: 셰이더 로드 실패 " + ShaderPath; var mat = AssetDatabase.LoadAssetAtPath(MatPath); bool created = false; if (mat == null) { mat = new Material(sh); AssetDatabase.CreateAsset(mat, MatPath); created = true; } else mat.shader = sh; // 기본값 = 프리멀티플라이 알파(One / OneMinusSrcAlpha). 양면. 텍스처 없음(흰색). // 순수 가산(One/One)은 WL_Nature 의 밝은 모래 위에서 색이 전부 흰색으로 날아갔다(실측). mat.SetFloat("_SrcBlend", (float)UnityEngine.Rendering.BlendMode.One); mat.SetFloat("_DstBlend", (float)UnityEngine.Rendering.BlendMode.OneMinusSrcAlpha); mat.SetFloat("_Cull", (float)UnityEngine.Rendering.CullMode.Off); mat.SetFloat("_EdgeSoftness", 0.24f); mat.SetFloat("_HeadSoftness", 0.03f); mat.SetFloat("_TailSoftness", 0.16f); mat.SetFloat("_Intensity", 1.0f); mat.SetFloat("_AlphaCutoff", 0.008f); mat.renderQueue = (int)UnityEngine.Rendering.RenderQueue.Transparent; EditorUtility.SetDirty(mat); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return (created ? "생성 " : "갱신 ") + MatPath + " shader=" + mat.shader.name + " pass=" + mat.passCount + " queue=" + mat.renderQueue; } public static object ApplySettings() { if (EditorApplication.isPlaying) return "ABORT: Play 중"; var s = AssetDatabase.LoadAssetAtPath(SettingsPath); if (s == null) return "ABORT: 설정 에셋 로드 실패 " + SettingsPath; var mat = AssetDatabase.LoadAssetAtPath(MatPath); if (mat == null) return "ABORT: 머티리얼이 아직 없다 — MakeMaterial 먼저"; var sb = new StringBuilder(); // ── 리본 ON · 크레센트 프리팹 경로 폐기 (#761/#777 방식 종료) ────────── s.drawRibbon = true; s.burstMode = WL.Combat.BurstMode.None; // ShowEffect 프리팹 스폰 안 함 = 이중 연출 제거 s.suppressMode = WL.Combat.SuppressMode.CrescentOnly; // 억제할 프리팹 자체가 없다 // ── 형상 : 폭 = 손잡이~검끝 전체(PD "검 끝~손잡이 폭") ──────────────── s.widthScale = 1f; s.tailWidthRatio = 1f; // 테이퍼 없음 — 폭 일정 s.tipExtendRatio = 0f; s.maxSamples = 64; // PD 예산 상한 = 세그먼트 64 s.minSampleDistance = 0.010f; s.maxSegmentDistance = 0.22f; s.maxSubSteps = 3; // UV : 새 리본 셰이더는 v 로 가장자리를 페이드한다. 구 시트 텍스처용 창(0.667~1)이 // 남아 있으면 칼끝 절반이 통째로 지워진다 → 0~1 전체로 되돌린다. s.uvUMin = 0f; s.uvUMax = 1f; s.uvVMin = 0f; s.uvVMax = 1f; // ── 수명 : PD 지시 0.15~0.25 s ──────────────────────────────────────── // 0.22 는 스윙이 빠를 때 꼬리가 화면 밖까지 길어져(실측) 참고 이미지의 짧은 호와 달랐다. s.fadeSeconds = 0.20f; s.fadeHoldRatio = 0.55f; // ── 머티리얼 : 리본 전용 1장 = 드로우 1회 ───────────────────────────── s.materialAdditive = mat; s.materialAlpha = null; s.alsoApplyColorViaPropertyBlock = false; s.writeCustomDataChannels = false; // ── 색 : 참고 이미지 (안쪽·머리 주황/노랑 → 바깥·꼬리 파랑/보라) ────── s.useGradients = true; // 0.5(반반)는 잔상축의 보라와 칼날축의 노랑을 반반 섞어 **회색**을 만든다 — 리본이 // 하얗게 보이던 실측 원인이었다. 색조는 칼날축(안쪽 주황 → 바깥 파랑)이 주도하게 둔다. s.bladeGradientWeight = 0.8f; s.colorIntensity = 1.0f; var gSwing = new Gradient(); gSwing.SetKeys( new[] { new GradientColorKey(new Color(0.42f, 0.16f, 0.90f), 0.00f), // 꼬리 = 보라 new GradientColorKey(new Color(0.20f, 0.45f, 1.00f), 0.32f), // 파랑 new GradientColorKey(new Color(1.00f, 0.52f, 0.14f), 0.72f), // 주황 new GradientColorKey(new Color(1.00f, 0.85f, 0.42f), 1.00f), // 머리 = 노랑 }, new[] { new GradientAlphaKey(0.00f, 0.00f), new GradientAlphaKey(0.68f, 0.18f), new GradientAlphaKey(0.95f, 0.55f), new GradientAlphaKey(0.95f, 1.00f), }); s.gradientAlongSwing = gSwing; var gBlade = new Gradient(); gBlade.SetKeys( new[] { new GradientColorKey(new Color(1.00f, 0.72f, 0.22f), 0.00f), // 손잡이(안쪽) = 주황/노랑 new GradientColorKey(new Color(1.00f, 0.42f, 0.18f), 0.45f), new GradientColorKey(new Color(0.34f, 0.30f, 1.00f), 1.00f), // 칼끝(바깥) = 파랑/보라 }, new[] { new GradientAlphaKey(1.00f, 0.00f), new GradientAlphaKey(1.00f, 0.60f), new GradientAlphaKey(0.85f, 1.00f), }); s.gradientAlongBlade = gBlade; // ── 클래스 오버라이드 : 색은 전역 그라디언트로 통일, 폭만 전체로 ────── if (s.classOverrides != null) foreach (var ov in s.classOverrides) { if (ov == null) continue; ov.overrideLook = false; // 전역 그라디언트를 그대로 쓴다 ov.widthScale = 1f; sb.AppendLine(" class " + ov.classId + " overrideLook=0 widthScale=1"); } EditorUtility.SetDirty(s); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); sb.Insert(0, "ApplySettings OK drawRibbon=1 burstMode=None widthScale=1 tailWidthRatio=1 " + "life=" + s.fadeSeconds + "s maxSamples=" + s.maxSamples + " mat=" + mat.name + "\n"); return sb.ToString(); } public static object Dump() { var s = AssetDatabase.LoadAssetAtPath(SettingsPath); if (s == null) return "설정 에셋 없음"; var sb = new StringBuilder(); sb.AppendLine("drawRibbon=" + s.drawRibbon + " burstMode=" + s.burstMode + " useGradients=" + s.useGradients + " intensity=" + s.colorIntensity); sb.AppendLine("widthScale=" + s.widthScale + " tailWidthRatio=" + s.tailWidthRatio + " maxSamples=" + s.maxSamples + " life=" + s.fadeSeconds + " hold=" + s.fadeHoldRatio); sb.AppendLine("maxSegmentDistance=" + s.maxSegmentDistance + " maxSubSteps=" + s.maxSubSteps + " minSampleDistance=" + s.minSampleDistance); sb.AppendLine("matAdd=" + (s.materialAdditive != null ? s.materialAdditive.name + " / " + s.materialAdditive.shader.name : "(null)") + " matAlpha=" + (s.materialAlpha != null ? s.materialAlpha.name : "(null)")); if (s.gradientAlongSwing != null) for (float t = 0f; t <= 1.001f; t += 0.25f) sb.AppendLine(" swing t=" + t.ToString("0.00") + " -> " + s.gradientAlongSwing.Evaluate(t)); if (s.gradientAlongBlade != null) for (float t = 0f; t <= 1.001f; t += 0.5f) sb.AppendLine(" blade t=" + t.ToString("0.00") + " -> " + s.gradientAlongBlade.Evaluate(t)); return sb.ToString(); } }