259 lines
8.2 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
} |