// WL825_SpearSetup.cs — 창 계열 자산 조립 (에디터 · 1회) (#825 · 2026-10-09 · PD 「창을 넣어서 6종」) // ① 한손검 컨트롤러 복제 → pcanim_spear.controller · 공격 3상태(attack/Attack2/Attack3)의 모션을 Shinabro 창 클립으로 교체 // ② PurePoly 창 모델 → Assets/Res_Addr/Weapon/PP_Theme_01_Spear_001.prefab (Make_Weapon 이 이 경로를 Addressables 로 읽는다) // ③ 두 자산을 Addressables 에 등록(주소 = 경로 · 한손검 컨트롤러와 같은 그룹) // 실행: unity command run_script --file AgentScripts/WL825_SpearSetup.cs --entry WL825_SpearSetup.Run --timeout 120 using System.Text; using UnityEditor; using UnityEditor.Animations; using UnityEditor.AddressableAssets; using UnityEditor.AddressableAssets.Settings; using UnityEngine; public static class WL825_SpearSetup { const string SrcCtrl = "Assets/Res_Addr/Animations/pcanim_onehand.controller"; const string DstCtrl = "Assets/Res_Addr/Animations/pcanim_spear.controller"; const string ClipDir = "Assets/Shinabro/Platform_Animation/Animation/07_Spear/"; const string SrcFbx = "Assets/ResWork/PurePoly/Fantasy RPG Weapons/Meshes/PP_Theme_01_Spear_001.fbx"; const string DstPrefab = "Assets/Res_Addr/Weapon/PP_Theme_01_Spear_001.prefab"; static AnimationClip LoadClip(string fbx) { foreach (var o in AssetDatabase.LoadAllAssetsAtPath(fbx)) { var c = o as AnimationClip; if (c != null && !c.name.StartsWith("__preview__")) return c; } return null; } public static string Run() { var sb = new StringBuilder(); // ① 컨트롤러 if (AssetDatabase.LoadAssetAtPath(DstCtrl) == null) { if (!AssetDatabase.CopyAsset(SrcCtrl, DstCtrl)) return "컨트롤러 복제 실패 " + SrcCtrl; sb.Append("컨트롤러 복제 · "); } var ctrl = AssetDatabase.LoadAssetAtPath(DstCtrl); if (ctrl == null) return "컨트롤러 로드 실패"; var map = new[] { new[] { "attack", "Stander@Spear_Attack1.FBX" }, new[] { "Attack2", "Stander@Spear_Attack2.FBX" }, new[] { "Attack3", "Stander@Spear_Attack3.FBX" } }; int swapped = 0; var sm = ctrl.layers[0].stateMachine; foreach (var pair in map) { var clip = LoadClip(ClipDir + pair[1]); if (clip == null) { sb.Append("클립 없음 ").Append(pair[1]).Append(" · "); continue; } foreach (var cs in sm.states) if (cs.state.name == pair[0]) { cs.state.motion = clip; swapped++; sb.Append(pair[0]).Append('→').Append(clip.name).Append('(').Append(clip.length.ToString("0.00")).Append("s) · "); } } EditorUtility.SetDirty(ctrl); // ② 무기 프리팹 if (AssetDatabase.LoadAssetAtPath(DstPrefab) == null) { var fbx = AssetDatabase.LoadAssetAtPath(SrcFbx); if (fbx == null) return sb + "창 모델 없음 " + SrcFbx; var inst = (GameObject)PrefabUtility.InstantiatePrefab(fbx); inst.name = "PP_Theme_01_Spear_001"; PrefabUtility.SaveAsPrefabAsset(inst, DstPrefab); Object.DestroyImmediate(inst); sb.Append("무기 프리팹 생성 · "); } // ③ Addressables var settings = AddressableAssetSettingsDefaultObject.Settings; if (settings == null) sb.Append("Addressables 설정 없음(등록 생략) · "); else { var srcEntry = settings.FindAssetEntry(AssetDatabase.AssetPathToGUID(SrcCtrl)); var group = srcEntry != null ? srcEntry.parentGroup : settings.DefaultGroup; foreach (var p in new[] { DstCtrl, DstPrefab }) { var guid = AssetDatabase.AssetPathToGUID(p); var e = settings.CreateOrMoveEntry(guid, group, false, false); e.address = p; sb.Append("주소 ").Append(p).Append(" @").Append(group.Name).Append(" · "); } settings.SetDirty(AddressableAssetSettings.ModificationEvent.EntryMoved, null, true, true); } AssetDatabase.SaveAssets(); return sb + "교체 " + swapped + "/3"; } }