Last active
October 15, 2025 13:26
-
-
Save ophura/bea30c46f7ba59e5a7a640ab653e21f9 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.Collections.Generic; | |
| using System.Linq; | |
| using UnityEditor; | |
| using UnityEngine; | |
| public static class TransformBoneHelpers | |
| { | |
| private static readonly ulong s_boneFlags; | |
| private static readonly int s_boneCount; | |
| // for conversion from HumanBodyBones enum values to bit flags | |
| // stored in s_boneFlags to avoid holding an array reference for | |
| static TransformBoneHelpers() | |
| { | |
| Type type = typeof(HumanBodyBones); | |
| Debug.Assert(type.GetEnumUnderlyingType() == typeof(int)); | |
| Array values = type.GetEnumValues(); | |
| Debug.Assert(values is { Length: > 0 and <= 64 }); | |
| Debug.Assert(values.Cast<int>().Min() >= 0); | |
| Debug.Assert(values.Cast<int>().Max() < 64); | |
| s_boneCount = values.Length - 1; // exclude LastBone | |
| foreach (HumanBodyBones value in (HumanBodyBones[])values) | |
| { | |
| if (value is HumanBodyBones.LastBone) continue; | |
| s_boneFlags |= 1UL << (int)value; | |
| } | |
| } | |
| [MenuItem("Tools/Print Child Bones Of Selected Transform")] | |
| private static void GetChildBonesFromSelectedTransform() | |
| { | |
| Transform parent = Selection.activeTransform; | |
| foreach (Transform bone in GetChildBones(parent)) | |
| { | |
| Debug.Log(bone.name); | |
| } | |
| } | |
| public static IEnumerable<Transform> GetChildBones(Transform parent) | |
| { | |
| Animator animator = parent.GetComponentInParent<Animator>(true); | |
| if (animator != null && animator.isHuman) | |
| { | |
| return GetChildBones(parent, animator); | |
| } | |
| return parent.root.GetComponentsInChildren<SkinnedMeshRenderer>(true) | |
| .SelectMany(renderer => GetChildBones(parent, renderer)) | |
| .Distinct(); | |
| } | |
| public static IEnumerable<Transform> GetChildBones(Transform parent, Animator animator) | |
| { | |
| HashSet<Transform> bones = new(s_boneCount); | |
| ulong flags = s_boneFlags; | |
| HumanBodyBones index = 0; | |
| while (flags != 0) | |
| { | |
| if ((flags & 1) != 0) | |
| { | |
| Transform bone = animator.GetBoneTransform(index); | |
| if (bone != null) // null if unmapped | |
| { | |
| bones.Add(bone); | |
| } | |
| } | |
| flags >>= 1; | |
| index++; | |
| } | |
| // the bones set now contains all mapped human body bones, | |
| // next get all descendants of parent then yield them if | |
| // they're mapped to a human body bone: bones.Contains(child) | |
| foreach (Transform bone in parent.GetComponentsInChildren<Transform>(true)) | |
| { | |
| if (bones.Contains(bone)) | |
| { | |
| yield return bone; | |
| } | |
| } | |
| } | |
| public static IEnumerable<Transform> GetChildBones(Transform parent, SkinnedMeshRenderer renderer) | |
| { | |
| foreach (Transform bone in renderer.bones) | |
| { | |
| if (bone == parent) continue; | |
| if (bone.IsChildOf(parent)) | |
| { | |
| yield return bone; | |
| } | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment