82 lines
5.8 KiB
C#
82 lines
5.8 KiB
C#
// WL882_Anim.cs — PD 지시 #882 무기 기술 이동용 애니메이터 도구 (에디터 · Play 아님)
|
|
// unity command run_script --file AgentScripts/WL882_Anim.cs --entry WL882_Anim.Describe --args '["Assets/Res_Addr/Animations/pcanim_onehand.controller","fatalblow"]'
|
|
// unity command run_script --file AgentScripts/WL882_Anim.cs --entry WL882_Anim.CopyState --args '[src컨트롤러, src상태, dst컨트롤러, 새 상태, 전이 본보기 상태]'
|
|
using System.Linq;
|
|
using System.Text;
|
|
using UnityEditor;
|
|
using UnityEditor.Animations;
|
|
using UnityEngine;
|
|
|
|
public static class WL882_Anim
|
|
{
|
|
static AnimatorState Find(UnityEditor.Animations.AnimatorController c, string name, out AnimatorStateMachine owner, out int layer)
|
|
{
|
|
owner = null; layer = -1;
|
|
for (int li = 0; li < c.layers.Length; li++)
|
|
{
|
|
var st = FindIn(c.layers[li].stateMachine, name, out owner);
|
|
if (st != null) { layer = li; return st; }
|
|
}
|
|
return null;
|
|
}
|
|
|
|
static AnimatorState FindIn(AnimatorStateMachine sm, string name, out AnimatorStateMachine owner)
|
|
{
|
|
foreach (var cs in sm.states) if (cs.state.name == name) { owner = sm; return cs.state; }
|
|
foreach (var sub in sm.stateMachines) { var r = FindIn(sub.stateMachine, name, out owner); if (r != null) return r; }
|
|
owner = null; return null;
|
|
}
|
|
|
|
public static object Describe(string ctrlPath, string stateName)
|
|
{
|
|
var c = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController>(ctrlPath);
|
|
if (c == null) return "컨트롤러 없음 " + ctrlPath;
|
|
var sb = new StringBuilder();
|
|
sb.Append("layers=").Append(c.layers.Length).Append(' ');
|
|
foreach (var l in c.layers) sb.Append('[').Append(l.name).Append(" states=").Append(l.stateMachine.states.Length).Append(" any=").Append(l.stateMachine.anyStateTransitions.Length).Append("] ");
|
|
AnimatorStateMachine owner; int layer;
|
|
var s = Find(c, stateName, out owner, out layer);
|
|
if (s == null) return sb.Append("\n상태 없음 ").Append(stateName).ToString();
|
|
var clip = s.motion as AnimationClip;
|
|
sb.Append("\n").Append(stateName).Append(" layer=").Append(layer).Append(" sm=").Append(owner.name).Append(" motion=").Append(s.motion != null ? s.motion.name + "(" + AssetDatabase.GetAssetPath(s.motion) + ")" : "null")
|
|
.Append(" len=").Append(clip != null ? clip.length.ToString("F2") : "-").Append(" speed=").Append(s.speed).Append(" speedParam=").Append(s.speedParameterActive ? s.speedParameter : "-")
|
|
.Append(" tag=").Append(s.tag).Append(" wd=").Append(s.writeDefaultValues).Append(" mirror=").Append(s.mirror).Append(" iks=").Append(s.iKOnFeet)
|
|
.Append(" behaviours=").Append(s.behaviours.Length);
|
|
if (clip != null) foreach (var e in AnimationUtility.GetAnimationEvents(clip)) sb.Append(" ev[").Append(e.functionName).Append('@').Append(e.time.ToString("F2")).Append(']');
|
|
foreach (var t in s.transitions)
|
|
sb.Append("\n → ").Append(t.destinationState != null ? t.destinationState.name : (t.isExit ? "EXIT" : "?")).Append(" exit=").Append(t.hasExitTime).Append('@').Append(t.exitTime.ToString("F2"))
|
|
.Append(" dur=").Append(t.duration.ToString("F2")).Append(t.hasFixedDuration ? "s" : "n").Append(" off=").Append(t.offset).Append(" cond=").Append(string.Join(",", t.conditions.Select(x => x.parameter + " " + x.mode + " " + x.threshold).ToArray()));
|
|
return sb.ToString();
|
|
}
|
|
|
|
/// <summary>src 컨트롤러의 상태 모션을 dst 컨트롤러에 새 상태로 넣는다 — 속도 · 태그 · 전이는 dst 의 본보기 상태(like)를 따른다. 이미 있으면 모션만 맞춘다.</summary>
|
|
public static object CopyState(string srcCtrl, string srcState, string dstCtrl, string newState, string likeState)
|
|
{
|
|
var src = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController>(srcCtrl);
|
|
var dst = AssetDatabase.LoadAssetAtPath<UnityEditor.Animations.AnimatorController>(dstCtrl);
|
|
if (src == null || dst == null) return "컨트롤러 없음";
|
|
AnimatorStateMachine so, lo; int sl, ll;
|
|
var s = Find(src, srcState, out so, out sl);
|
|
var like = Find(dst, likeState, out lo, out ll);
|
|
if (s == null || s.motion == null) return "원본 상태/모션 없음 " + srcState;
|
|
if (like == null) return "본보기 상태 없음 " + likeState;
|
|
AnimatorStateMachine eo; int el;
|
|
var exist = Find(dst, newState, out eo, out el);
|
|
if (exist != null) { exist.motion = s.motion; EditorUtility.SetDirty(dst); AssetDatabase.SaveAssets(); return "이미 있음 → 모션만 " + s.motion.name; }
|
|
var pos = lo.states.First(x => x.state == like).position + new Vector3(0f, 60f, 0f);
|
|
var ns = lo.AddState(newState, pos);
|
|
ns.motion = s.motion; ns.speed = like.speed; ns.tag = like.tag; ns.writeDefaultValues = like.writeDefaultValues; ns.iKOnFeet = like.iKOnFeet;
|
|
ns.speedParameterActive = like.speedParameterActive; ns.speedParameter = like.speedParameter;
|
|
foreach (var t in like.transitions)
|
|
{
|
|
AnimatorStateTransition nt = t.isExit ? ns.AddExitTransition() : ns.AddTransition(t.destinationState);
|
|
nt.hasExitTime = t.hasExitTime; nt.exitTime = t.exitTime; nt.duration = t.duration; nt.hasFixedDuration = t.hasFixedDuration; nt.offset = t.offset;
|
|
nt.interruptionSource = t.interruptionSource; nt.orderedInterruption = t.orderedInterruption; nt.canTransitionToSelf = t.canTransitionToSelf;
|
|
foreach (var cnd in t.conditions) nt.AddCondition(cnd.mode, cnd.threshold, cnd.parameter);
|
|
}
|
|
EditorUtility.SetDirty(dst);
|
|
AssetDatabase.SaveAssets();
|
|
return "추가 " + newState + " ← " + s.motion.name + " (전이 " + like.transitions.Length + " · 본보기 " + likeState + ")";
|
|
}
|
|
}
|