using UnityEngine; using UnityEditor; using UnityEngine.Animations.Rigging; using System.Collections.Generic; using System.Linq; namespace EditorTools { public static class BoneRendererContextMenu { [MenuItem("CONTEXT/BoneRenderer/휴머노이드 본 자동 추가")] private static void AddHumanoidBones(MenuCommand command) { BoneRenderer boneRenderer = command.context as BoneRenderer; if (boneRenderer == null) return; Animator animator = boneRenderer.GetComponent(); if (animator == null || !animator.isHuman) { EditorUtility.DisplayDialog("오류", "휴머노이드 Animator가 필요합니다.", "확인"); return; } Undo.RecordObject(boneRenderer, "Add Humanoid Bones"); List humanoidBones = GetAvailableHumanoidBones(animator); boneRenderer.transforms = humanoidBones.ToArray(); EditorUtility.SetDirty(boneRenderer); Debug.Log($"{humanoidBones.Count}개의 휴머노이드 본이 BoneRenderer에 추가되었습니다."); } [MenuItem("CONTEXT/BoneRenderer/본 목록 초기화")] private static void ClearBones(MenuCommand command) { BoneRenderer boneRenderer = command.context as BoneRenderer; if (boneRenderer == null) return; if (!EditorUtility.DisplayDialog("확인", "본 목록을 초기화하시겠습니까?", "예", "아니오")) return; Undo.RecordObject(boneRenderer, "Clear Bones"); boneRenderer.transforms = new Transform[0]; EditorUtility.SetDirty(boneRenderer); Debug.Log("BoneRenderer의 본 목록이 초기화되었습니다."); } private static readonly HumanBodyBones[] HumanoidBoneTypes = new HumanBodyBones[] { HumanBodyBones.Hips, HumanBodyBones.Spine, HumanBodyBones.Chest, HumanBodyBones.UpperChest, HumanBodyBones.Neck, HumanBodyBones.Head, HumanBodyBones.LeftShoulder, HumanBodyBones.LeftUpperArm, HumanBodyBones.LeftLowerArm, HumanBodyBones.LeftHand, HumanBodyBones.LeftThumbProximal, HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal, HumanBodyBones.LeftIndexProximal, HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal, HumanBodyBones.LeftMiddleProximal, HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal, HumanBodyBones.LeftRingProximal, HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal, HumanBodyBones.LeftLittleProximal, HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal, HumanBodyBones.RightShoulder, HumanBodyBones.RightUpperArm, HumanBodyBones.RightLowerArm, HumanBodyBones.RightHand, HumanBodyBones.RightThumbProximal, HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal, HumanBodyBones.RightIndexProximal, HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal, HumanBodyBones.RightMiddleProximal, HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal, HumanBodyBones.RightRingProximal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal, HumanBodyBones.RightLittleProximal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal, HumanBodyBones.LeftUpperLeg, HumanBodyBones.LeftLowerLeg, HumanBodyBones.LeftFoot, HumanBodyBones.LeftToes, HumanBodyBones.RightUpperLeg, HumanBodyBones.RightLowerLeg, HumanBodyBones.RightFoot, HumanBodyBones.RightToes }; private static List GetAvailableHumanoidBones(Animator animator) { List humanoidBones = new List(); foreach (HumanBodyBones boneType in HumanoidBoneTypes) { Transform bone = animator.GetBoneTransform(boneType); if (bone != null) { humanoidBones.Add(bone); } } return humanoidBones; } } [CustomEditor(typeof(BoneRenderer))] [CanEditMultipleObjects] public class BoneRendererEditorExtension : Editor { static readonly GUIContent k_BoneSizeLabel = new GUIContent("Bone Size"); static readonly GUIContent k_BoneColorLabel = new GUIContent("Color"); static readonly GUIContent k_BoneShapeLabel = new GUIContent("Shape"); static readonly GUIContent k_TripodSizeLabel = new GUIContent("Tripod Size"); SerializedProperty m_DrawBones; SerializedProperty m_BoneShape; SerializedProperty m_BoneSize; SerializedProperty m_BoneColor; SerializedProperty m_DrawTripods; SerializedProperty m_TripodSize; SerializedProperty m_Transforms; // 휴머노이드 본 타입 정의 private static readonly HumanBodyBones[] HumanoidBoneTypes = new HumanBodyBones[] { // 스파인 체인 HumanBodyBones.Hips, HumanBodyBones.Spine, HumanBodyBones.Chest, HumanBodyBones.UpperChest, HumanBodyBones.Neck, HumanBodyBones.Head, // 왼쪽 팔 HumanBodyBones.LeftShoulder, HumanBodyBones.LeftUpperArm, HumanBodyBones.LeftLowerArm, HumanBodyBones.LeftHand, // 왼쪽 손가락 HumanBodyBones.LeftThumbProximal, HumanBodyBones.LeftThumbIntermediate, HumanBodyBones.LeftThumbDistal, HumanBodyBones.LeftIndexProximal, HumanBodyBones.LeftIndexIntermediate, HumanBodyBones.LeftIndexDistal, HumanBodyBones.LeftMiddleProximal, HumanBodyBones.LeftMiddleIntermediate, HumanBodyBones.LeftMiddleDistal, HumanBodyBones.LeftRingProximal, HumanBodyBones.LeftRingIntermediate, HumanBodyBones.LeftRingDistal, HumanBodyBones.LeftLittleProximal, HumanBodyBones.LeftLittleIntermediate, HumanBodyBones.LeftLittleDistal, // 오른쪽 팔 HumanBodyBones.RightShoulder, HumanBodyBones.RightUpperArm, HumanBodyBones.RightLowerArm, HumanBodyBones.RightHand, // 오른쪽 손가락 HumanBodyBones.RightThumbProximal, HumanBodyBones.RightThumbIntermediate, HumanBodyBones.RightThumbDistal, HumanBodyBones.RightIndexProximal, HumanBodyBones.RightIndexIntermediate, HumanBodyBones.RightIndexDistal, HumanBodyBones.RightMiddleProximal, HumanBodyBones.RightMiddleIntermediate, HumanBodyBones.RightMiddleDistal, HumanBodyBones.RightRingProximal, HumanBodyBones.RightRingIntermediate, HumanBodyBones.RightRingDistal, HumanBodyBones.RightLittleProximal, HumanBodyBones.RightLittleIntermediate, HumanBodyBones.RightLittleDistal, // 왼쪽 다리 HumanBodyBones.LeftUpperLeg, HumanBodyBones.LeftLowerLeg, HumanBodyBones.LeftFoot, HumanBodyBones.LeftToes, // 오른쪽 다리 HumanBodyBones.RightUpperLeg, HumanBodyBones.RightLowerLeg, HumanBodyBones.RightFoot, HumanBodyBones.RightToes }; public void OnEnable() { m_DrawBones = serializedObject.FindProperty("drawBones"); m_BoneSize = serializedObject.FindProperty("boneSize"); m_BoneShape = serializedObject.FindProperty("boneShape"); m_BoneColor = serializedObject.FindProperty("boneColor"); m_DrawTripods = serializedObject.FindProperty("drawTripods"); m_TripodSize = serializedObject.FindProperty("tripodSize"); m_Transforms = serializedObject.FindProperty("m_Transforms"); } public override void OnInspectorGUI() { serializedObject.Update(); // 기본 BoneRenderer UI EditorGUILayout.BeginHorizontal(); EditorGUILayout.PropertyField(m_DrawBones, k_BoneSizeLabel); using (new EditorGUI.DisabledScope(!m_DrawBones.boolValue)) EditorGUILayout.PropertyField(m_BoneSize, GUIContent.none); EditorGUILayout.EndHorizontal(); using (new EditorGUI.DisabledScope(!m_DrawBones.boolValue)) { EditorGUI.indentLevel++; EditorGUILayout.PropertyField(m_BoneShape, k_BoneShapeLabel); EditorGUILayout.PropertyField(m_BoneColor, k_BoneColorLabel); EditorGUI.indentLevel--; } EditorGUILayout.BeginHorizontal(); EditorGUILayout.PropertyField(m_DrawTripods, k_TripodSizeLabel); using (new EditorGUI.DisabledScope(!m_DrawTripods.boolValue)) EditorGUILayout.PropertyField(m_TripodSize, GUIContent.none); EditorGUILayout.EndHorizontal(); bool isDragPerformed = Event.current.type == EventType.DragPerform; EditorGUI.BeginChangeCheck(); EditorGUILayout.PropertyField(m_Transforms, true); bool boneRendererDirty = EditorGUI.EndChangeCheck(); boneRendererDirty |= Event.current.type == EventType.ValidateCommand && Event.current.commandName == "UndoRedoPerformed"; boneRendererDirty |= Event.current.type == EventType.Used && isDragPerformed; // 휴머노이드 본 자동 설정 섹션 EditorGUILayout.Space(); EditorGUILayout.LabelField("휴머노이드 본 자동 설정", EditorStyles.boldLabel); BoneRenderer boneRenderer = target as BoneRenderer; GameObject targetObject = boneRenderer.gameObject; // Animator 확인 및 상태 표시 Animator animator = targetObject.GetComponent(); if (animator == null) { EditorGUILayout.HelpBox("Animator 컴포넌트가 필요합니다.", MessageType.Warning); } else if (!animator.isHuman) { EditorGUILayout.HelpBox("휴머노이드 아바타로 설정되지 않았습니다.", MessageType.Warning); } else { // 사용 가능한 본 수 표시 List availableBones = GetAvailableHumanoidBones(animator); EditorGUILayout.HelpBox($"사용 가능한 휴머노이드 본: {availableBones.Count}개", MessageType.Info); // 버튼들 EditorGUILayout.BeginHorizontal(); if (GUILayout.Button("휴머노이드 본 추가")) { AddHumanoidBones(boneRenderer, animator); } if (GUILayout.Button("본 목록 초기화")) { ClearBones(boneRenderer); } EditorGUILayout.EndHorizontal(); } serializedObject.ApplyModifiedProperties(); if (boneRendererDirty) { for (int i = 0; i < targets.Length; i++) { var renderer = targets[i] as BoneRenderer; renderer.ExtractBones(); } } } private List GetAvailableHumanoidBones(Animator animator) { List humanoidBones = new List(); foreach (HumanBodyBones boneType in HumanoidBoneTypes) { Transform bone = animator.GetBoneTransform(boneType); if (bone != null) { humanoidBones.Add(bone); } } return humanoidBones; } private void AddHumanoidBones(BoneRenderer boneRenderer, Animator animator) { Undo.RecordObject(boneRenderer, "Add Humanoid Bones"); List humanoidBones = GetAvailableHumanoidBones(animator); boneRenderer.transforms = humanoidBones.ToArray(); EditorUtility.SetDirty(boneRenderer); Debug.Log($"{humanoidBones.Count}개의 휴머노이드 본이 BoneRenderer에 추가되었습니다."); } private void ClearBones(BoneRenderer boneRenderer) { Undo.RecordObject(boneRenderer, "Clear Bones"); boneRenderer.transforms = new Transform[0]; EditorUtility.SetDirty(boneRenderer); Debug.Log("BoneRenderer의 본 목록이 초기화되었습니다."); } } }