#if UNITY_EDITOR using System.Linq; using System.Text; using UnityEditor; using UnityEngine; public static class MeasureClipMotion { public static string Run() { var sb = new StringBuilder(); foreach (var name in new[] { "Idle", "Walk", "Attack" }) { var clip = AssetDatabase.LoadAssetAtPath($"Assets/Bundles/Characters/Animal/RockBall/Clips/{name}.anim"); if (clip == null) { sb.AppendLine(name + " missing"); continue; } var binds = AnimationUtility.GetCurveBindings(clip); float maxRange = 0f; string maxBind = ""; int moving = 0; foreach (var b in binds) { var c = AnimationUtility.GetEditorCurve(clip, b); if (c == null || c.keys.Length == 0) continue; float mn = float.MaxValue, mx = float.MinValue; foreach (var k in c.keys) { if (k.value < mn) mn = k.value; if (k.value > mx) mx = k.value; } // also sample densely for (float t = 0; t <= clip.length; t += clip.length / 20f) { float v = c.Evaluate(t); if (v < mn) mn = v; if (v > mx) mx = v; } float range = mx - mn; if (range > 1e-4f) moving++; if (range > maxRange) { maxRange = range; maxBind = b.path + "/" + b.propertyName; } } sb.AppendLine($"{name}: len={clip.length:F3} binds={binds.Length} movingCurves={moving} maxRange={maxRange:F5} at {maxBind}"); } // Compare bone rest vs sampled pose at mid Walk on instantiated prefab var prefab = AssetDatabase.LoadAssetAtPath("Assets/Bundles/Characters/Animal/RockBall/RockBall_Rigged.prefab"); var go = (GameObject)PrefabUtility.InstantiatePrefab(prefab); var anim = go.GetComponentInChildren(); anim.SetInteger("Motion", 1); anim.Update(0f); // record bone rotations at t=0 and after scrubbing var bones = go.GetComponentsInChildren().Where(t => t.name.StartsWith("bone_")).ToArray(); var r0 = bones.Select(b => b.localRotation).ToArray(); for (int i = 0; i < 30; i++) anim.Update(0.05f); float maxAng = 0f; string which = ""; for (int i = 0; i < bones.Length; i++) { float ang = Quaternion.Angle(r0[i], bones[i].localRotation); if (ang > maxAng) { maxAng = ang; which = bones[i].name; } } sb.AppendLine($"prefab Walk scrub 1.5s maxBoneDeltaDeg={maxAng:F2} bone={which}"); Object.DestroyImmediate(go); return sb.ToString(); } } #endif