FAnimationEventInspector.cs 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478
  1. using UnityEngine;
  2. using UnityEditor;
  3. using UnityEditor.Animations;
  4. //using UnityEditorInternal;
  5. using System.Collections.Generic;
  6. using Flux;
  7. using FluxEditor;
  8. namespace FluxEditor
  9. {
  10. [CustomEditor(typeof(FPlayAnimationEvent))]
  11. public class FAnimationEventInspector : FEventInspector
  12. {
  13. private FPlayAnimationEvent _animEvent = null;
  14. private SerializedProperty _animationClip = null;
  15. private SerializedProperty _frameRange = null;
  16. private SerializedProperty _controlsAnimation = null;
  17. private SerializedProperty _startOffset = null;
  18. private SerializedProperty _blendLength = null;
  19. protected override void OnEnable()
  20. {
  21. base.OnEnable();
  22. if( target == null )
  23. {
  24. DestroyImmediate( this );
  25. return;
  26. }
  27. _animEvent = (FPlayAnimationEvent)target;
  28. _animationClip = serializedObject.FindProperty("_animationClip");
  29. _controlsAnimation = serializedObject.FindProperty("_controlsAnimation");
  30. _startOffset = serializedObject.FindProperty("_startOffset");
  31. _blendLength = serializedObject.FindProperty("_blendLength");
  32. _frameRange = serializedObject.FindProperty("_frameRange");
  33. }
  34. public override void OnInspectorGUI ()
  35. {
  36. bool rebuildTrack = false;
  37. base.OnInspectorGUI();
  38. serializedObject.Update();
  39. if( _animationClip.objectReferenceValue == null )
  40. {
  41. EditorGUILayout.HelpBox("There's no Animation Clip", MessageType.Warning);
  42. Rect helpBoxRect = GUILayoutUtility.GetLastRect();
  43. float yCenter = helpBoxRect.center.y;
  44. helpBoxRect.xMax -= 3;
  45. helpBoxRect.xMin = helpBoxRect.xMax - 50;
  46. helpBoxRect.yMin = yCenter - 12.5f;
  47. helpBoxRect.height = 25f;
  48. FAnimationTrack animTrack = (FAnimationTrack)_animEvent.Track;
  49. if( animTrack.AnimatorController == null || animTrack.LayerId == -1 )
  50. GUI.enabled = false;
  51. if( GUI.Button( helpBoxRect, "Create" ) )
  52. {
  53. CreateAnimationClip( _animEvent );
  54. }
  55. GUI.enabled = true;
  56. }
  57. EditorGUI.BeginChangeCheck();
  58. EditorGUILayout.PropertyField( _animationClip );
  59. Rect animationClipRect = GUILayoutUtility.GetLastRect();
  60. if( Event.current.type == EventType.DragUpdated && animationClipRect.Contains( Event.current.mousePosition ) )
  61. {
  62. int numAnimations = NumAnimationsDragAndDrop( _animEvent.Sequence.FrameRate );
  63. if( numAnimations > 0 )
  64. DragAndDrop.visualMode = DragAndDropVisualMode.Link;
  65. }
  66. else if( Event.current.type == EventType.DragPerform && animationClipRect.Contains( Event.current.mousePosition ) )
  67. {
  68. if( NumAnimationsDragAndDrop( _animEvent.Sequence.FrameRate ) > 0 )
  69. {
  70. DragAndDrop.AcceptDrag();
  71. AnimationClip clip = GetAnimationClipDragAndDrop( _animEvent.Sequence.FrameRate );
  72. if( clip != null )
  73. _animationClip.objectReferenceValue = clip;
  74. }
  75. }
  76. if( EditorGUI.EndChangeCheck() )
  77. {
  78. AnimationClip clip = (AnimationClip)_animationClip.objectReferenceValue;
  79. if( clip )
  80. {
  81. if( clip.frameRate != _animEvent.Track.Timeline.Sequence.FrameRate )
  82. {
  83. Debug.LogError(string.Format("Can't add animation, it has a different frame rate from the sequence ({0} vs {1})",
  84. clip.frameRate, _animEvent.Track.Timeline.Sequence.FrameRate ) );
  85. _animationClip.objectReferenceValue = null;
  86. }
  87. else
  88. {
  89. SerializedProperty frameRangeEnd = _frameRange.FindPropertyRelative( "_end" );
  90. FrameRange maxFrameRange = _animEvent.GetMaxFrameRange();
  91. frameRangeEnd.intValue = Mathf.Min( _animEvent.FrameRange.Start + Mathf.RoundToInt( clip.length * clip.frameRate ), maxFrameRange.End );
  92. }
  93. rebuildTrack = true;
  94. }
  95. else
  96. {
  97. CheckDeleteAnimation( _animEvent );
  98. }
  99. }
  100. bool isAnimationEditable = Flux.FUtility.IsAnimationEditable( _animEvent._animationClip );
  101. if( isAnimationEditable )
  102. {
  103. EditorGUILayout.PropertyField( _controlsAnimation );
  104. }
  105. else
  106. {
  107. if( _controlsAnimation.boolValue )
  108. _controlsAnimation.boolValue = false;
  109. }
  110. if( !_controlsAnimation.boolValue )
  111. {
  112. if( _animEvent.IsBlending() )
  113. {
  114. float offset = _startOffset.intValue / _animEvent._animationClip.frameRate;
  115. float blendFinish = offset + (_blendLength.intValue / _animEvent._animationClip.frameRate);
  116. EditorGUILayout.BeginHorizontal();
  117. EditorGUILayout.BeginHorizontal( GUILayout.Width(EditorGUIUtility.labelWidth-5) ); // hack to get around some layout issue with imgui
  118. _showBlendAndOffsetContent = EditorGUILayout.Foldout( _showBlendAndOffsetContent, new GUIContent("Offset+Blend") );
  119. EditorGUILayout.EndHorizontal();
  120. EditorGUI.BeginChangeCheck();
  121. EditorGUILayout.MinMaxSlider( ref offset, ref blendFinish, 0, _animEvent.GetMaxStartOffset() / _animEvent._animationClip.frameRate + _animEvent.LengthTime );
  122. if( EditorGUI.EndChangeCheck() )
  123. {
  124. _startOffset.intValue = Mathf.Clamp( Mathf.RoundToInt( offset * _animEvent.Sequence.FrameRate ), 0, _animEvent.GetMaxStartOffset() );
  125. _blendLength.intValue = Mathf.Clamp( Mathf.RoundToInt( (blendFinish - offset) * _animEvent.Sequence.FrameRate ), 0, _animEvent.Length );
  126. rebuildTrack = true;
  127. }
  128. EditorGUILayout.EndHorizontal();
  129. if( _showBlendAndOffsetContent )
  130. {
  131. EditorGUI.BeginChangeCheck();
  132. ++EditorGUI.indentLevel;
  133. if( !isAnimationEditable )
  134. EditorGUILayout.IntSlider( _startOffset, 0, _animEvent.GetMaxStartOffset() );
  135. // if( _animEvent.IsBlending() )
  136. {
  137. EditorGUILayout.IntSlider( _blendLength, 0, _animEvent.Length );
  138. }
  139. --EditorGUI.indentLevel;
  140. if( EditorGUI.EndChangeCheck() )
  141. rebuildTrack = true;
  142. }
  143. }
  144. else
  145. {
  146. EditorGUI.BeginChangeCheck();
  147. EditorGUILayout.IntSlider( _startOffset, 0, _animEvent.GetMaxStartOffset() );
  148. if( EditorGUI.EndChangeCheck() )
  149. rebuildTrack = true;
  150. }
  151. }
  152. serializedObject.ApplyModifiedProperties();
  153. if( rebuildTrack )
  154. FAnimationTrackInspector.RebuildStateMachine( (FAnimationTrack)_animEvent.Track );
  155. }
  156. public static void CheckDeleteAnimation( FPlayAnimationEvent animEvt )
  157. {
  158. if( animEvt._animationClip != null && Flux.FUtility.IsAnimationEditable( animEvt._animationClip ) /*AssetDatabase.IsSubAsset( animEvt._animationClip )*/
  159. && EditorUtility.DisplayDialog("Delete animation?",
  160. string.Format("The animation clip '{0}' is stored inside an animator controller, do you want to delete it?",animEvt._animationClip.name),
  161. "Delete", "Keep") )
  162. {
  163. Undo.DestroyObjectImmediate( animEvt._animationClip );
  164. AssetDatabase.SaveAssets();
  165. }
  166. }
  167. private static bool _showBlendAndOffsetContent = false;
  168. public static int NumAnimationsDragAndDrop()
  169. {
  170. return NumAnimationsDragAndDrop( -1 );
  171. }
  172. public static int NumAnimationsDragAndDrop( int frameRate )
  173. {
  174. int numAnimations = 0;
  175. if( DragAndDrop.objectReferences.Length == 0 )
  176. return numAnimations;
  177. string pathToAsset = AssetDatabase.GetAssetPath(DragAndDrop.objectReferences[0].GetInstanceID());
  178. if( string.IsNullOrEmpty(pathToAsset) )
  179. return numAnimations;
  180. if( DragAndDrop.objectReferences[0] is AnimationClip && (frameRate <= 0 || Mathf.Approximately( ((AnimationClip)DragAndDrop.objectReferences[0]).frameRate, frameRate)) )
  181. ++numAnimations;
  182. Object[] objs = AssetDatabase.LoadAllAssetRepresentationsAtPath( pathToAsset );
  183. foreach( Object obj in objs )
  184. {
  185. if( obj is AnimationClip && (frameRate <= 0 || Mathf.Approximately(((AnimationClip)obj).frameRate, frameRate)) )
  186. ++numAnimations;
  187. }
  188. return numAnimations;
  189. }
  190. private static AnimationClip _dragAndDropSelectedAnimation = null;
  191. public static AnimationClip GetAnimationClipDragAndDrop( int frameRate )
  192. {
  193. _dragAndDropSelectedAnimation = null;
  194. if( DragAndDrop.objectReferences.Length == 0 )
  195. return null;
  196. string pathToAsset = AssetDatabase.GetAssetPath(DragAndDrop.objectReferences[0].GetInstanceID());
  197. if( string.IsNullOrEmpty(pathToAsset) )
  198. return null;
  199. List<AnimationClip> animationClips = new List<AnimationClip>();
  200. if( DragAndDrop.objectReferences[0] is AnimationClip && (frameRate <= 0 || Mathf.Approximately( ((AnimationClip)DragAndDrop.objectReferences[0]).frameRate, frameRate)) )
  201. animationClips.Add( (AnimationClip)DragAndDrop.objectReferences[0] );
  202. else
  203. {
  204. Object[] objs = AssetDatabase.LoadAllAssetRepresentationsAtPath( pathToAsset );
  205. foreach( Object obj in objs )
  206. {
  207. if( obj is AnimationClip && (frameRate <= 0 || Mathf.Approximately( ((AnimationClip)obj).frameRate, frameRate)) )
  208. {
  209. animationClips.Add( (AnimationClip)obj );
  210. }
  211. }
  212. }
  213. if( animationClips.Count == 0 )
  214. return null;
  215. else if( animationClips.Count == 1 )
  216. return animationClips[0];
  217. GenericMenu menu = new GenericMenu();
  218. for( int i = 0; i != animationClips.Count; ++i )
  219. {
  220. menu.AddItem( new GUIContent(animationClips[i].name), false, SetAnimationClip, animationClips[i] );
  221. }
  222. menu.ShowAsContext();
  223. return _dragAndDropSelectedAnimation;
  224. }
  225. private static void SetAnimationClip( object data )
  226. {
  227. if( data == null )
  228. _dragAndDropSelectedAnimation = null;
  229. else
  230. _dragAndDropSelectedAnimation = (AnimationClip)data;
  231. }
  232. private void SetAnimationClip( AnimationClip animClip )
  233. {
  234. _animationClip.objectReferenceValue = animClip;
  235. _animEvent.ControlsAnimation = AssetDatabase.IsSubAsset( animClip ) && AssetDatabase.LoadMainAssetAtPath(AssetDatabase.GetAssetPath(animClip)) is AnimatorController;
  236. _controlsAnimation.boolValue = _animEvent.ControlsAnimation;
  237. serializedObject.ApplyModifiedProperties();
  238. }
  239. public static void SetAnimationClip( FPlayAnimationEvent animEvent, AnimationClip animClip )
  240. {
  241. FAnimationEventInspector editor = (FAnimationEventInspector)CreateEditor( animEvent, typeof( FAnimationEventInspector ) );
  242. editor.SetAnimationClip( animClip );
  243. DestroyImmediate( editor );
  244. FAnimationTrackInspector.RebuildStateMachine( (FAnimationTrack)animEvent.Track );
  245. }
  246. // animation editing
  247. public static void ScaleAnimationClip( AnimationClip clip, FrameRange range )
  248. {
  249. if( clip == null )
  250. return;
  251. float oldLength = clip.length;
  252. float newLength = range.Length / clip.frameRate;
  253. Undo.RecordObject( clip, "resize Animation" );
  254. EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings( clip );
  255. // when scaling, we need to adjust the tangents otherwise the curves will get completely distorted
  256. float tangentMultiplier = oldLength / newLength;
  257. for( int i = 0; i != curveBindings.Length; ++i )
  258. {
  259. AnimationCurve curve = GetAnimationCurve( clip, ref curveBindings[i] );
  260. if( curve == null )
  261. continue;
  262. if( newLength < oldLength )
  263. {
  264. for( int j = 0; j < curve.keys.Length; ++j )
  265. {
  266. // Keyframe newKeyframe = new Keyframe( Mathf.RoundToInt( (curve.keys[j].time / oldLength) * newLength * clip.frameRate) / clip.frameRate, curve.keys[j].value, curve.keys[j].inTangent*tangentMultiplier, curve.keys[j].outTangent*tangentMultiplier );
  267. Keyframe newKeyframe = new Keyframe( (curve.keys[j].time / oldLength) * newLength, curve.keys[j].value, curve.keys[j].inTangent*tangentMultiplier, curve.keys[j].outTangent*tangentMultiplier );
  268. newKeyframe.tangentMode = curve.keys[j].tangentMode;
  269. curve.MoveKey( j, newKeyframe );
  270. }
  271. }
  272. else
  273. {
  274. for( int j = curve.keys.Length-1; j >= 0; --j )
  275. {
  276. // Keyframe newKeyframe = new Keyframe( Mathf.RoundToInt( (curve.keys[j].time / oldLength) * newLength * clip.frameRate ) /clip.frameRate, curve.keys[j].value, curve.keys[j].inTangent*tangentMultiplier, curve.keys[j].outTangent*tangentMultiplier );
  277. Keyframe newKeyframe = new Keyframe( (curve.keys[j].time / oldLength) * newLength, curve.keys[j].value, curve.keys[j].inTangent*tangentMultiplier, curve.keys[j].outTangent*tangentMultiplier );
  278. newKeyframe.tangentMode = curve.keys[j].tangentMode;
  279. curve.MoveKey( j, newKeyframe );
  280. }
  281. }
  282. AnimationUtility.SetEditorCurve( clip, curveBindings[i], curve );
  283. }
  284. clip.EnsureQuaternionContinuity();
  285. EditorApplication.RepaintAnimationWindow();
  286. EditorUtility.SetDirty( clip );
  287. }
  288. private static AnimationCurve GetAnimationCurve( AnimationClip clip, ref EditorCurveBinding curveBinding )
  289. {
  290. AnimationCurve curve = null;
  291. if( curveBinding.propertyName.StartsWith("m_LocalRotation.") )
  292. {
  293. char axis = curveBinding.propertyName[curveBinding.propertyName.Length-1];
  294. if( axis == 'w' )
  295. return curve; // ignore w
  296. curveBinding.propertyName = "localEulerAngles." + axis;
  297. curve = AnimationUtility.GetEditorCurve( clip, curveBinding );
  298. if( curve == null )
  299. {
  300. curveBinding.propertyName = "localEulerAnglesBaked." + axis;
  301. curve = AnimationUtility.GetEditorCurve( clip, curveBinding );
  302. }
  303. }
  304. else
  305. {
  306. curve = AnimationUtility.GetEditorCurve( clip, curveBinding );
  307. }
  308. return curve;
  309. }
  310. public static AnimationClip CreateAnimationClip( FPlayAnimationEvent animEvent )
  311. {
  312. FAnimationTrack track = (FAnimationTrack)animEvent.Track;
  313. string animName = ((FAnimationTrack)animEvent.Track).LayerName + "_" + animEvent.GetId().ToString();
  314. AnimationClip clip = AnimatorController.AllocateAnimatorClip( animName );
  315. clip.frameRate = animEvent.Sequence.FrameRate;
  316. Transform ownerTransform = animEvent.Owner;
  317. Vector3 pos = ownerTransform.localPosition;
  318. Vector3 rot = ownerTransform.localRotation.eulerAngles; //ownerTransform.localEulerAngles;
  319. Keyframe[] xPosKeys = new Keyframe[]{ new Keyframe(0, pos.x), new Keyframe(animEvent.LengthTime, pos.x) };
  320. Keyframe[] yPosKeys = new Keyframe[]{ new Keyframe(0, pos.y), new Keyframe(animEvent.LengthTime, pos.y) };
  321. Keyframe[] zPosKeys = new Keyframe[]{ new Keyframe(0, pos.z), new Keyframe(animEvent.LengthTime, pos.z) };
  322. Keyframe[] xRotKeys = new Keyframe[]{ new Keyframe(0, rot.x), new Keyframe(animEvent.LengthTime, rot.x) };
  323. Keyframe[] yRotKeys = new Keyframe[]{ new Keyframe(0, rot.y), new Keyframe(animEvent.LengthTime, rot.y) };
  324. Keyframe[] zRotKeys = new Keyframe[]{ new Keyframe(0, rot.z), new Keyframe(animEvent.LengthTime, rot.z) };
  325. // set the tangent mode
  326. int tangentMode = 10; // 10 is unity auto tangent mode
  327. for( int i = 0; i != xPosKeys.Length; ++i )
  328. {
  329. xPosKeys[i].tangentMode = tangentMode;
  330. yPosKeys[i].tangentMode = tangentMode;
  331. zPosKeys[i].tangentMode = tangentMode;
  332. xRotKeys[i].tangentMode = tangentMode;
  333. yRotKeys[i].tangentMode = tangentMode;
  334. zRotKeys[i].tangentMode = tangentMode;
  335. }
  336. AnimationCurve xPos = new AnimationCurve( xPosKeys );
  337. AnimationCurve yPos = new AnimationCurve( yPosKeys );
  338. AnimationCurve zPos = new AnimationCurve( zPosKeys );
  339. AnimationCurve xRot = new AnimationCurve( xRotKeys );
  340. AnimationCurve yRot = new AnimationCurve( yRotKeys );
  341. AnimationCurve zRot = new AnimationCurve( zRotKeys );
  342. AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( string.Empty, typeof(Transform), "m_LocalPosition.x"), xPos );
  343. AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( string.Empty, typeof(Transform), "m_LocalPosition.y"), yPos );
  344. AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( string.Empty, typeof(Transform), "m_LocalPosition.z"), zPos );
  345. // workaround unity bug of spilling errors if you just set localEulerAngles
  346. Quaternion quat = ownerTransform.localRotation;
  347. Keyframe[] xQuatKeys = new Keyframe[] { new Keyframe(0, quat.x), new Keyframe(animEvent.LengthTime, quat.x) };
  348. Keyframe[] yQuatKeys = new Keyframe[] { new Keyframe(0, quat.y), new Keyframe(animEvent.LengthTime, quat.y) };
  349. Keyframe[] zQuatKeys = new Keyframe[] { new Keyframe(0, quat.z), new Keyframe(animEvent.LengthTime, quat.z) };
  350. Keyframe[] wQuatKeys = new Keyframe[] { new Keyframe(0, quat.w), new Keyframe(animEvent.LengthTime, quat.w) };
  351. AnimationCurve xQuat = new AnimationCurve( xQuatKeys );
  352. AnimationCurve yQuat = new AnimationCurve( yQuatKeys );
  353. AnimationCurve zQuat = new AnimationCurve( zQuatKeys );
  354. AnimationCurve wQuat = new AnimationCurve( wQuatKeys );
  355. AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( string.Empty, typeof(Transform), "m_LocalRotation.x"), xQuat );
  356. AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( string.Empty, typeof(Transform), "m_LocalRotation.y"), yQuat );
  357. AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( string.Empty, typeof(Transform), "m_LocalRotation.z"), zQuat );
  358. AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( string.Empty, typeof(Transform), "m_LocalRotation.w"), wQuat );
  359. AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( string.Empty, typeof(Transform), "localEulerAngles.x"), xRot );
  360. AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( string.Empty, typeof(Transform), "localEulerAngles.y"), yRot );
  361. AnimationUtility.SetEditorCurve( clip, EditorCurveBinding.FloatCurve( string.Empty, typeof(Transform), "localEulerAngles.z"), zRot );
  362. clip.EnsureQuaternionContinuity();
  363. AssetDatabase.AddObjectToAsset( clip, track.AnimatorController );
  364. AnimationClipSettings clipSettings = AnimationUtility.GetAnimationClipSettings( clip );
  365. clipSettings.loopTime = false;
  366. AnimationUtility.SetAnimationClipSettings( clip, clipSettings );
  367. AssetDatabase.SaveAssets();
  368. FAnimationEventInspector.SetAnimationClip( animEvent, clip );
  369. return clip;
  370. }
  371. }
  372. }