using UnityEditor; using UnityEditor.Animations; using UnityEngine; namespace ProjectM.EditorTools { /// /// Shared editor-only helpers for the player/enemy Rukhanka rig tools (PlayerRigTools / EnemyRigTools): /// AnimatorController parameter probing + the "overlay clip = full idle pose (every bone keyed, minus Root) /// plus a Root-yaw twist" build. A Root-ONLY clip makes Rukhanka collapse every un-keyed bone to identity for /// the state's duration -> the body sinks into the floor (writeDefaultValues does NOT prevent it), so both the /// player swing and the enemy attack-windup base their clip on idle and drive only the Root yaw. The twist /// keyframes differ per clip, so they are passed in. /// public static class AnimRigUtil { /// True if the controller already declares a parameter named . public static bool HasParam(AnimatorController ac, string name) { foreach (var p in ac.parameters) if (p.name == name) return true; return false; } /// /// Rebuild as the full pose (all bindings except Root) with /// the supplied Root localEulerAnglesRaw.y curve on top; sets loopTime=false and /// saves. Height-preserving (avoids the un-keyed-bone collapse). /// public static void BuildRootYawOverlayClip(AnimationClip clip, AnimationClip idle, AnimationCurve rootYaw) { clip.ClearCurves(); foreach (var b in AnimationUtility.GetCurveBindings(idle)) { if (b.path == "Root") continue; // the Root is driven below AnimationUtility.SetEditorCurve(clip, b, AnimationUtility.GetEditorCurve(idle, b)); } AnimationUtility.SetEditorCurve(clip, EditorCurveBinding.FloatCurve("Root", typeof(Transform), "localEulerAnglesRaw.y"), rootYaw); var s = AnimationUtility.GetAnimationClipSettings(clip); s.loopTime = false; AnimationUtility.SetAnimationClipSettings(clip, s); EditorUtility.SetDirty(clip); AssetDatabase.SaveAssets(); } } }