#if UNITY_EDITOR using System.Linq; using System.Text; using UnityEditor; using UnityEngine; public static class DiagnoseRockBallOrient { static string Fmt(Vector3 v) => $"({v.x:F3},{v.y:F3},{v.z:F3})"; static string FmtE(Quaternion q) => $"({q.eulerAngles.x:F1},{q.eulerAngles.y:F1},{q.eulerAngles.z:F1})"; static Bounds MeshBounds(GameObject go) { var rs = go.GetComponentsInChildren() .Where(r => r.name != "ForwardMinusZ" && r.GetComponent() == null) .ToArray(); var b = rs[0].bounds; for (int i = 1; i < rs.Length; i++) b.Encapsulate(rs[i].bounds); return b; } public static string Run() { var sb = new StringBuilder(); var staticP = AssetDatabase.LoadAssetAtPath("Assets/Bundles/Characters/Animal/RockBall/RockBall.prefab"); var rigP = AssetDatabase.LoadAssetAtPath("Assets/Bundles/Characters/Animal/RockBall/RockBall_Rigged.prefab"); var a = (GameObject)PrefabUtility.InstantiatePrefab(staticP); a.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity); var ba = MeshBounds(a); sb.AppendLine($"STATIC bounds min={Fmt(ba.min)} size={Fmt(ba.size)}"); Object.DestroyImmediate(a); var b = (GameObject)PrefabUtility.InstantiatePrefab(rigP); b.transform.SetPositionAndRotation(new Vector3(3, 0, 0), Quaternion.identity); var anim = b.GetComponentInChildren(); var bone0 = b.GetComponentsInChildren().FirstOrDefault(t => t.name == "bone_0"); var arm = b.GetComponentsInChildren().FirstOrDefault(t => t.name == "Armature"); anim.Rebind(); anim.Update(0f); var br = MeshBounds(b); sb.AppendLine($"RIGGED rebind bounds min={Fmt(br.min)} size={Fmt(br.size)}"); if (arm != null) sb.AppendLine($" Armature up={Fmt(arm.up)} fwd={Fmt(arm.forward)} euler={FmtE(arm.rotation)}"); if (bone0 != null) sb.AppendLine($" bone0 up={Fmt(bone0.up)} fwd={Fmt(bone0.forward)} euler={FmtE(bone0.rotation)}"); foreach (var motion in new[] { 0, 1, 2 }) { anim.Rebind(); anim.SetInteger("Motion", motion); anim.Update(0f); for (int i = 0; i < 15; i++) anim.Update(0.05f); var bm = MeshBounds(b); string name = motion == 0 ? "Idle" : motion == 1 ? "Walk" : "Attack"; sb.AppendLine($"{name}: bounds min={Fmt(bm.min)} size={Fmt(bm.size)} extentY/X={(bm.size.y / Mathf.Max(0.001f, bm.size.x)):F2}"); if (bone0 != null) sb.AppendLine($" bone0 up={Fmt(bone0.up)} euler={FmtE(bone0.rotation)}"); } // clip: does Armature/bone_0 leave bind orientation? foreach (var name in new[] { "Idle", "Walk", "Attack" }) { var clip = AssetDatabase.LoadAssetAtPath($"Assets/Bundles/Characters/Animal/RockBall/Clips/{name}.anim"); if (clip == null) continue; sb.AppendLine($"clip {name}:"); foreach (var path in new[] { "Armature", "Armature/bone_0" }) { var qx = AnimationUtility.GetEditorCurve(clip, new EditorCurveBinding { path = path, propertyName = "m_LocalRotation.x", type = typeof(Transform) }); var qy = AnimationUtility.GetEditorCurve(clip, new EditorCurveBinding { path = path, propertyName = "m_LocalRotation.y", type = typeof(Transform) }); var qz = AnimationUtility.GetEditorCurve(clip, new EditorCurveBinding { path = path, propertyName = "m_LocalRotation.z", type = typeof(Transform) }); var qw = AnimationUtility.GetEditorCurve(clip, new EditorCurveBinding { path = path, propertyName = "m_LocalRotation.w", type = typeof(Transform) }); if (qx == null) continue; var q0 = new Quaternion(qx.Evaluate(0), qy.Evaluate(0), qz.Evaluate(0), qw.Evaluate(0)); var q1 = new Quaternion(qx.Evaluate(clip.length * 0.5f), qy.Evaluate(clip.length * 0.5f), qz.Evaluate(clip.length * 0.5f), qw.Evaluate(clip.length * 0.5f)); sb.AppendLine($" {path} t0 euler={FmtE(q0)} tMid euler={FmtE(q1)} angleDelta={Quaternion.Angle(q0, q1):F1}"); } } Object.DestroyImmediate(b); return sb.ToString(); } } #endif