using UnityEngine; using UnityEditor; using System.Collections.Generic; using System.Linq; namespace UniHumanoid { public class HumanPoseWindow : EditorWindow { private Vector2 scrollPosition; private Animator targetAnimator; private HumanPoseHandler poseHandler; private Animator lastAnimator; private List poseClips = new List(); private string searchText = ""; private bool showCreateNew = false; private string newPoseName = "New Pose"; [MenuItem("Window/Animation/Human Pose Manager")] static void ShowWindow() { var window = GetWindow("Human Pose Manager"); window.Show(); } void OnEnable() { RefreshPoseList(); } void RefreshPoseList() { // Find all HumanPoseClip assets in the project string[] guids = AssetDatabase.FindAssets("t:HumanPoseClip"); poseClips = guids .Select(guid => AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(guid))) .Where(clip => clip != null) .ToList(); } void OnGUI() { EditorGUILayout.Space(10); // Target Animator Selection using (new EditorGUILayout.HorizontalScope()) { targetAnimator = (Animator)EditorGUILayout.ObjectField("Target Humanoid", targetAnimator, typeof(Animator), true); if (targetAnimator != lastAnimator) { poseHandler = null; lastAnimator = targetAnimator; } } if (targetAnimator != null && !targetAnimator.isHuman) { EditorGUILayout.HelpBox("Selected animator must be humanoid!", MessageType.Warning); return; } EditorGUILayout.Space(10); // Search Bar using (new EditorGUILayout.HorizontalScope()) { searchText = EditorGUILayout.TextField("Search", searchText, EditorStyles.toolbarSearchField); if (GUILayout.Button("Refresh", GUILayout.Width(60))) { RefreshPoseList(); } } EditorGUILayout.Space(10); // Create New Pose Section using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) { showCreateNew = EditorGUILayout.Foldout(showCreateNew, "Create New Pose", true); if (showCreateNew) { EditorGUI.indentLevel++; newPoseName = EditorGUILayout.TextField("Pose Name", newPoseName); using (new EditorGUI.DisabledScope(targetAnimator == null || !targetAnimator.isHuman)) { if (GUILayout.Button("Capture Current Pose")) { CreateNewPoseFromCurrent(); } } EditorGUI.indentLevel--; } } EditorGUILayout.Space(10); // Pose List EditorGUILayout.LabelField("Available Poses", EditorStyles.boldLabel); scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); var filteredPoses = poseClips .Where(p => string.IsNullOrEmpty(searchText) || p.name.ToLower().Contains(searchText.ToLower())) .ToList(); foreach (var pose in filteredPoses) { DrawPoseEntry(pose); } EditorGUILayout.EndScrollView(); } void DrawPoseEntry(HumanPoseClip pose) { using (new EditorGUILayout.VerticalScope(EditorStyles.helpBox)) { // Pose name and buttons using (new EditorGUILayout.HorizontalScope()) { EditorGUILayout.LabelField(pose.name, EditorStyles.boldLabel); using (new EditorGUI.DisabledScope(targetAnimator == null || !targetAnimator.isHuman)) { if (GUILayout.Button("Apply", GUILayout.Width(60))) { ApplyPose(pose); } if (GUILayout.Button("Update", GUILayout.Width(60))) { UpdatePose(pose); } } if (GUILayout.Button("Select", GUILayout.Width(60))) { Selection.activeObject = pose; } } } } void CreateNewPoseFromCurrent() { if (targetAnimator == null || !targetAnimator.isHuman) return; // Ensure animator is properly initialized PrepareAnimator(); // Create new pose asset var newPose = CreateInstance(); // Capture current pose CreatePoseHandler(); var pose = new HumanPose(); poseHandler.GetHumanPose(ref pose); newPose.ApplyPose(ref pose); // Save asset string path = EditorUtility.SaveFilePanel( "Save Pose Asset", "Assets", newPoseName + ".asset", "asset" ); if (string.IsNullOrEmpty(path)) return; path = FileUtil.GetProjectRelativePath(path); AssetDatabase.CreateAsset(newPose, path); AssetDatabase.SaveAssets(); RefreshPoseList(); newPoseName = "New Pose"; } void ApplyPose(HumanPoseClip clip) { if (targetAnimator == null || !targetAnimator.isHuman) return; // Ensure animator is properly initialized PrepareAnimator(); // Record undo before applying Undo.RegisterFullObjectHierarchyUndo(targetAnimator.gameObject, "Apply Pose"); CreatePoseHandler(); var pose = clip.GetPose(); // Apply the pose poseHandler.SetHumanPose(ref pose); // Force update the animator ForceAnimatorUpdate(); EditorUtility.SetDirty(targetAnimator.gameObject); SceneView.RepaintAll(); } void UpdatePose(HumanPoseClip clip) { if (targetAnimator == null || !targetAnimator.isHuman) return; // Ensure animator is properly initialized PrepareAnimator(); CreatePoseHandler(); var pose = new HumanPose(); poseHandler.GetHumanPose(ref pose); Undo.RecordObject(clip, "Update Pose"); clip.ApplyPose(ref pose); EditorUtility.SetDirty(clip); AssetDatabase.SaveAssets(); } void PrepareAnimator() { if (targetAnimator == null) return; // Ensure the animator is enabled and active targetAnimator.enabled = true; // Force animator to update targetAnimator.Rebind(); targetAnimator.Update(0); } void CreatePoseHandler() { if (poseHandler == null) { poseHandler = new HumanPoseHandler(targetAnimator.avatar, targetAnimator.transform); } } void ForceAnimatorUpdate() { if (targetAnimator == null) return; // Force the animator to update its internal state targetAnimator.Update(0); // Also update the transform hierarchy targetAnimator.transform.hasChanged = true; // Force a physics update if needed if (targetAnimator.updateMode == AnimatorUpdateMode.Fixed) { targetAnimator.Update(Time.fixedDeltaTime); } } } }