259 lines
8.2 KiB
C#

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<HumanPoseClip> poseClips = new List<HumanPoseClip>();
private string searchText = "";
private bool showCreateNew = false;
private string newPoseName = "New Pose";
[MenuItem("Window/Animation/Human Pose Manager")]
static void ShowWindow()
{
var window = GetWindow<HumanPoseWindow>("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<HumanPoseClip>(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<HumanPoseClip>();
// 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);
}
}
}
}