Skip to content

Instantly share code, notes, and snippets.

@ophura
Last active October 15, 2025 13:26
Show Gist options
  • Select an option

  • Save ophura/bea30c46f7ba59e5a7a640ab653e21f9 to your computer and use it in GitHub Desktop.

Select an option

Save ophura/bea30c46f7ba59e5a7a640ab653e21f9 to your computer and use it in GitHub Desktop.
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