// WL762_Apply.cs — PD 지시 #762 집행 (에디트 모드) // unity command run_script --file AgentScripts/WL762_Apply.cs --entry WL762_Apply.CreateSettings // unity command run_script --file AgentScripts/WL762_Apply.cs --entry WL762_Apply.AddMoveEvents // unity command run_script --file AgentScripts/WL762_Apply.cs --entry WL762_Apply.RemoveMoveEvents (롤백) // ① WLCombatMotionSettings.asset 생성 ② 9개 서브클립에 Move 애니메이션 이벤트 추가. // 거리 = M1 실측 루트 전진량(RootT.z max) × 0.4, 0.05 반올림. 시각 = Paladin 원본 Move 시각(실측). using System.Collections.Generic; using System.Linq; using System.Text; using UnityEditor; using UnityEngine; public static class WL762_Apply { const string SettingsDir = "Assets/WL/Settings/Resources/WL"; const string SettingsPath = SettingsDir + "/WLCombatMotionSettings.asset"; public static object CreateSettings() { var existing = AssetDatabase.LoadAssetAtPath(SettingsPath); if (existing != null) return "already exists: " + SettingsPath; var so = ScriptableObject.CreateInstance(); AssetDatabase.CreateAsset(so, SettingsPath); AssetDatabase.SaveAssets(); AssetDatabase.Refresh(); return "created: " + SettingsPath; } // fbx 경로 → (클립이름, Move 시각, Move 거리) static readonly object[][] Targets = { new object[]{ "Assets/Res_Addr/Animations/Animation/Paladin/attack.FBX", new[]{"Sword&Shield_Attack1_10101","Sword&Shield_Attack1_10102","Sword&Shield_Attack1_10501"}, 0.11957f, 0.30f }, new object[]{ "Assets/Res_Addr/Animations/Animation/Paladin/Stander@Sword&Shield_Attack2.FBX", new[]{"Sword&Shield_Attack2_10101","Sword&Shield_Attack2_10102","Sword&Shield_Attack2_10501"}, 0.06759f, 0.70f }, new object[]{ "Assets/Res_Addr/Animations/Animation/Paladin/Stander@Sword&Shield_Attack3.FBX", new[]{"Sword&Shield_Attack3_10101","Sword&Shield_Attack3_10102","Sword&Shield_Attack3_10501"}, 0.05169f, 0.65f }, }; public static object AddMoveEvents() { return Edit(true); } public static object RemoveMoveEvents() { return Edit(false); } static object Edit(bool add) { var sb = new StringBuilder(); foreach (var row in Targets) { string fbx = (string)row[0]; var names = (string[])row[1]; float time = (float)row[2]; float dist = (float)row[3]; var imp = AssetImporter.GetAtPath(fbx) as ModelImporter; if (imp == null) { sb.AppendLine("importer 없음: " + fbx); continue; } var clips = imp.clipAnimations; bool dirty = false; for (int i = 0; i < clips.Length; i++) { if (!names.Contains(clips[i].name)) continue; var evs = new List(clips[i].events ?? new AnimationEvent[0]); int removed = evs.RemoveAll(e => e.functionName == "Move"); if (add) { evs.Add(new AnimationEvent { functionName = "Move", time = time, floatParameter = dist, intParameter = 0, stringParameter = "" }); } evs.Sort((a, b) => a.time.CompareTo(b.time)); clips[i].events = evs.ToArray(); dirty = true; sb.AppendLine(string.Format(" {0}: Move {1} (기존 {2}개 제거) time={3:F5} f={4:F2} → 총 {5}개 이벤트", clips[i].name, add ? "추가" : "삭제", removed, time, dist, evs.Count)); } if (!dirty) { sb.AppendLine("대상 클립 없음: " + fbx); continue; } imp.clipAnimations = clips; EditorUtility.SetDirty(imp); imp.SaveAndReimport(); sb.AppendLine("reimported: " + fbx); } AssetDatabase.Refresh(); return sb.ToString(); } /// 현재 9개 서브클립의 이벤트를 다시 읽어 검증한다. public static object Verify() { var sb = new StringBuilder(); foreach (var row in Targets) { string fbx = (string)row[0]; var imp = AssetImporter.GetAtPath(fbx) as ModelImporter; if (imp == null) continue; foreach (var ca in imp.clipAnimations) { var evs = ca.events ?? new AnimationEvent[0]; sb.AppendLine(string.Format(" {0,-32} : {1}", ca.name, string.Join(" | ", evs.Select(e => string.Format("{0}@{1:F5}(f={2:F2})", e.functionName, e.time, e.floatParameter)).ToArray()))); } } return sb.ToString(); } }