Permalink
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Can now build project without touching the editor. :)
377 lines (303 sloc)
10.2 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#if UNITY_EDITOR | |
using UnityEngine; | |
using UnityEditor; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Text.RegularExpressions; | |
public class AnimationHierarchyEditor : EditorWindow { | |
private static int columnWidth = 300; | |
private Animator animatorObject; | |
private List<AnimationClip> animationClips; | |
private ArrayList pathsKeys; | |
private Hashtable paths; | |
Dictionary<string, string> tempPathOverrides; | |
private Vector2 scrollPos = Vector2.zero; | |
[MenuItem("Window/Animation Hierarchy Editor")] | |
static void ShowWindow() { | |
EditorWindow.GetWindow<AnimationHierarchyEditor>(); | |
} | |
public AnimationHierarchyEditor(){ | |
animationClips = new List<AnimationClip>(); | |
tempPathOverrides = new Dictionary<string, string>(); | |
} | |
void OnSelectionChange() { | |
if (Selection.objects.Length > 1 ) | |
{ | |
Debug.Log ("Length? " + Selection.objects.Length); | |
animationClips.Clear(); | |
foreach ( Object o in Selection.objects ) | |
{ | |
if ( o is AnimationClip ) animationClips.Add((AnimationClip)o); | |
} | |
} | |
else if (Selection.activeObject is AnimationClip) { | |
animationClips.Clear(); | |
animationClips.Add((AnimationClip)Selection.activeObject); | |
FillModel(); | |
} else { | |
animationClips.Clear(); | |
} | |
this.Repaint(); | |
} | |
private string sOriginalRoot = "Root"; | |
private string sNewRoot = "SomeNewObject/Root"; | |
void OnGUI() { | |
if (Event.current.type == EventType.ValidateCommand) { | |
switch (Event.current.commandName) { | |
case "UndoRedoPerformed": | |
FillModel(); | |
break; | |
} | |
} | |
if (animationClips.Count > 0 ) { | |
scrollPos = GUILayout.BeginScrollView(scrollPos, GUIStyle.none); | |
EditorGUILayout.BeginHorizontal(); | |
GUILayout.Label("Referenced Animator (Root):", GUILayout.Width(columnWidth)); | |
animatorObject = ((Animator)EditorGUILayout.ObjectField( | |
animatorObject, | |
typeof(Animator), | |
true, | |
GUILayout.Width(columnWidth)) | |
); | |
EditorGUILayout.EndHorizontal(); | |
EditorGUILayout.BeginHorizontal(); | |
GUILayout.Label("Animation Clip:", GUILayout.Width(columnWidth)); | |
if ( animationClips.Count == 1 ) | |
{ | |
animationClips[0] = ((AnimationClip)EditorGUILayout.ObjectField( | |
animationClips[0], | |
typeof(AnimationClip), | |
true, | |
GUILayout.Width(columnWidth)) | |
); | |
} | |
else | |
{ | |
GUILayout.Label("Multiple Anim Clips: " + animationClips.Count, GUILayout.Width(columnWidth)); | |
} | |
EditorGUILayout.EndHorizontal(); | |
GUILayout.Space(20); | |
EditorGUILayout.BeginHorizontal(); | |
sOriginalRoot = EditorGUILayout.TextField(sOriginalRoot, GUILayout.Width(columnWidth)); | |
sNewRoot = EditorGUILayout.TextField(sNewRoot, GUILayout.Width(columnWidth)); | |
if (GUILayout.Button("Replace Root")) { | |
Debug.Log("O: "+sOriginalRoot+ " N: "+sNewRoot); | |
ReplaceRoot(sOriginalRoot, sNewRoot); | |
} | |
EditorGUILayout.EndHorizontal(); | |
EditorGUILayout.BeginHorizontal(); | |
GUILayout.Label("Reference path:", GUILayout.Width(columnWidth)); | |
GUILayout.Label("Animated properties:", GUILayout.Width(columnWidth*0.5f)); | |
GUILayout.Label("(Count)", GUILayout.Width(60)); | |
GUILayout.Label("Object:", GUILayout.Width(columnWidth)); | |
EditorGUILayout.EndHorizontal(); | |
if (paths != null) | |
{ | |
foreach (string path in pathsKeys) | |
{ | |
GUICreatePathItem(path); | |
} | |
} | |
GUILayout.Space(40); | |
GUILayout.EndScrollView(); | |
} else { | |
GUILayout.Label("Please select an Animation Clip"); | |
} | |
} | |
void GUICreatePathItem(string path) { | |
string newPath = path; | |
GameObject obj = FindObjectInRoot(path); | |
GameObject newObj; | |
ArrayList properties = (ArrayList)paths[path]; | |
string pathOverride = path; | |
if ( tempPathOverrides.ContainsKey(path) ) pathOverride = tempPathOverrides[path]; | |
EditorGUILayout.BeginHorizontal(); | |
pathOverride = EditorGUILayout.TextField(pathOverride, GUILayout.Width(columnWidth)); | |
if ( pathOverride != path ) tempPathOverrides[path] = pathOverride; | |
if (GUILayout.Button("Change", GUILayout.Width(60))) { | |
newPath = pathOverride; | |
tempPathOverrides.Remove(path); | |
} | |
EditorGUILayout.LabelField( | |
properties != null ? properties.Count.ToString() : "0", | |
GUILayout.Width(60) | |
); | |
Color standardColor = GUI.color; | |
if (obj != null) { | |
GUI.color = Color.green; | |
} else { | |
GUI.color = Color.red; | |
} | |
newObj = (GameObject)EditorGUILayout.ObjectField( | |
obj, | |
typeof(GameObject), | |
true, | |
GUILayout.Width(columnWidth) | |
); | |
GUI.color = standardColor; | |
EditorGUILayout.EndHorizontal(); | |
try { | |
if (obj != newObj) { | |
UpdatePath(path, ChildPath(newObj)); | |
} | |
if (newPath != path) { | |
UpdatePath(path, newPath); | |
} | |
} catch (UnityException ex) { | |
Debug.LogError(ex.Message); | |
} | |
} | |
void OnInspectorUpdate() { | |
this.Repaint(); | |
} | |
void FillModel() { | |
paths = new Hashtable(); | |
pathsKeys = new ArrayList(); | |
foreach ( AnimationClip animationClip in animationClips ) | |
{ | |
FillModelWithCurves(AnimationUtility.GetCurveBindings(animationClip)); | |
FillModelWithCurves(AnimationUtility.GetObjectReferenceCurveBindings(animationClip)); | |
} | |
} | |
private void FillModelWithCurves(EditorCurveBinding[] curves) { | |
foreach (EditorCurveBinding curveData in curves) { | |
string key = curveData.path; | |
if (paths.ContainsKey(key)) { | |
((ArrayList)paths[key]).Add(curveData); | |
} else { | |
ArrayList newProperties = new ArrayList(); | |
newProperties.Add(curveData); | |
paths.Add(key, newProperties); | |
pathsKeys.Add(key); | |
} | |
} | |
} | |
string sReplacementOldRoot; | |
string sReplacementNewRoot; | |
void ReplaceRoot(string oldRoot, string newRoot) | |
{ | |
float fProgress = 0.0f; | |
sReplacementOldRoot = oldRoot; | |
sReplacementNewRoot = newRoot; | |
AssetDatabase.StartAssetEditing(); | |
for ( int iCurrentClip = 0; iCurrentClip < animationClips.Count; iCurrentClip++ ) | |
{ | |
AnimationClip animationClip = animationClips[iCurrentClip]; | |
Undo.RecordObject(animationClip, "Animation Hierarchy Root Change"); | |
for ( int iCurrentPath = 0; iCurrentPath < pathsKeys.Count; iCurrentPath ++) | |
{ | |
string path = pathsKeys[iCurrentPath] as string; | |
ArrayList curves = (ArrayList)paths[path]; | |
for (int i = 0; i < curves.Count; i++) | |
{ | |
EditorCurveBinding binding = (EditorCurveBinding)curves[i]; | |
if ( path.Contains(sReplacementOldRoot) ) | |
{ | |
if ( !path.Contains(sReplacementNewRoot) ) | |
{ | |
string sNewPath = Regex.Replace(path, "^"+sReplacementOldRoot, sReplacementNewRoot ); | |
AnimationCurve curve = AnimationUtility.GetEditorCurve(animationClip, binding); | |
if ( curve != null ) | |
{ | |
AnimationUtility.SetEditorCurve(animationClip, binding, null); | |
binding.path = sNewPath; | |
AnimationUtility.SetEditorCurve(animationClip, binding, curve); | |
} | |
else | |
{ | |
ObjectReferenceKeyframe[] objectReferenceCurve = AnimationUtility.GetObjectReferenceCurve(animationClip, binding); | |
AnimationUtility.SetObjectReferenceCurve(animationClip, binding, null); | |
binding.path = sNewPath; | |
AnimationUtility.SetObjectReferenceCurve(animationClip, binding, objectReferenceCurve); | |
} | |
} | |
} | |
} | |
// Update the progress meter | |
float fChunk = 1f / animationClips.Count; | |
fProgress = (iCurrentClip * fChunk) + fChunk * ((float) iCurrentPath / (float) pathsKeys.Count); | |
EditorUtility.DisplayProgressBar( | |
"Animation Hierarchy Progress", | |
"How far along the animation editing has progressed.", | |
fProgress); | |
} | |
} | |
AssetDatabase.StopAssetEditing(); | |
EditorUtility.ClearProgressBar(); | |
FillModel(); | |
this.Repaint(); | |
} | |
void UpdatePath(string oldPath, string newPath) | |
{ | |
if (paths[newPath] != null) { | |
throw new UnityException("Path " + newPath + " already exists in that animation!"); | |
} | |
AssetDatabase.StartAssetEditing(); | |
for ( int iCurrentClip = 0; iCurrentClip < animationClips.Count; iCurrentClip++ ) | |
{ | |
AnimationClip animationClip = animationClips[iCurrentClip]; | |
Undo.RecordObject(animationClip, "Animation Hierarchy Change"); | |
//recreating all curves one by one | |
//to maintain proper order in the editor - | |
//slower than just removing old curve | |
//and adding a corrected one, but it's more | |
//user-friendly | |
for ( int iCurrentPath = 0; iCurrentPath < pathsKeys.Count; iCurrentPath ++) | |
{ | |
string path = pathsKeys[iCurrentPath] as string; | |
ArrayList curves = (ArrayList)paths[path]; | |
for (int i = 0; i < curves.Count; i++) { | |
EditorCurveBinding binding = (EditorCurveBinding)curves[i]; | |
AnimationCurve curve = AnimationUtility.GetEditorCurve(animationClip, binding); | |
ObjectReferenceKeyframe[] objectReferenceCurve = AnimationUtility.GetObjectReferenceCurve(animationClip, binding); | |
if ( curve != null ) | |
AnimationUtility.SetEditorCurve(animationClip, binding, null); | |
else | |
AnimationUtility.SetObjectReferenceCurve(animationClip, binding, null); | |
if (path == oldPath) | |
binding.path = newPath; | |
if ( curve != null ) | |
AnimationUtility.SetEditorCurve(animationClip, binding, curve); | |
else | |
AnimationUtility.SetObjectReferenceCurve(animationClip, binding, objectReferenceCurve); | |
float fChunk = 1f / animationClips.Count; | |
float fProgress = (iCurrentClip * fChunk) + fChunk * ((float) iCurrentPath / (float) pathsKeys.Count); | |
EditorUtility.DisplayProgressBar( | |
"Animation Hierarchy Progress", | |
"How far along the animation editing has progressed.", | |
fProgress); | |
} | |
} | |
} | |
AssetDatabase.StopAssetEditing(); | |
EditorUtility.ClearProgressBar(); | |
FillModel(); | |
this.Repaint(); | |
} | |
GameObject FindObjectInRoot(string path) { | |
if (animatorObject == null) { | |
return null; | |
} | |
Transform child = animatorObject.transform.Find(path); | |
if (child != null) { | |
return child.gameObject; | |
} else { | |
return null; | |
} | |
} | |
string ChildPath(GameObject obj, bool sep = false) { | |
if (animatorObject == null) { | |
throw new UnityException("Please assign Referenced Animator (Root) first!"); | |
} | |
if (obj == animatorObject.gameObject) { | |
return ""; | |
} else { | |
if (obj.transform.parent == null) { | |
throw new UnityException("Object must belong to " + animatorObject.ToString() + "!"); | |
} else { | |
return ChildPath(obj.transform.parent.gameObject, true) + obj.name + (sep ? "/" : ""); | |
} | |
} | |
} | |
} | |
#endif |