Files
Project-M/Assets/_Project/Scripts/Editor/AnimRigUtil.cs
T
kronic 21a4c71413 Hygiene B4c: clean dedup helpers (knockback, bakers, editor rig)
- KnockbackUtil.Stamp: shared knockback-stamp used by AbilityFireSystem + MeleeComboSystem (guard + planar normalize + write; speed passed in).
- BakerStructureExt: AddPlacedStructure/AddDamageable extensions on Baker<T>; rewired Turret/Structure/Harvester/Conveyor/Fabricator bakers (baked data + serialized fields unchanged).
- EnemyPrefabBakeExt.AddEnemyPrefabPool: shared prefab-buffer bake for ZoneEnemyDirector/WaveDirector authoring (fields stay flat — no serialization change).
- Editor AnimRigUtil (HasParam + root-yaw overlay clip) shared by PlayerRigTools/EnemyRigTools; EnemyRigTools.MakeMat now GUID-preserving (CopySerialized over existing) — the composite AnimatorController copy left as-is with a caveat comment.

466/466 EditMode tests pass, 0 warnings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 21:26:23 -07:00

46 lines
2.3 KiB
C#

using UnityEditor;
using UnityEditor.Animations;
using UnityEngine;
namespace ProjectM.EditorTools
{
/// <summary>
/// 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.
/// </summary>
public static class AnimRigUtil
{
/// <summary>True if the controller already declares a parameter named <paramref name="name"/>.</summary>
public static bool HasParam(AnimatorController ac, string name)
{
foreach (var p in ac.parameters) if (p.name == name) return true;
return false;
}
/// <summary>
/// Rebuild <paramref name="clip"/> as the full <paramref name="idle"/> pose (all bindings except Root) with
/// the supplied Root localEulerAnglesRaw.y <paramref name="rootYaw"/> curve on top; sets loopTime=false and
/// saves. Height-preserving (avoids the un-keyed-bone collapse).
/// </summary>
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();
}
}
}