Explorar el Código

[unity] Added `Scene Preview` to `SkeletonMecanim` Inspector for Mecanim AnimationClip preview. Closes #1253.

Harald Csaszar hace 8 meses
padre
commit
0371e4d936

+ 1 - 0
CHANGELOG.md

@@ -166,6 +166,7 @@
   - Skeleton Mecanim: Added new `Mix Mode` `Match`. When selected, Spine animation weights are calculated to best match the provided Mecanim clip weights. This mix mode is recommended on any layer using blend tree nodes.
   - URP Shaders: Added `ZWrite` variant of outline shader `Universal Render Pipeline/Spine/Outline/Skeleton-OutlineOnly ZWrite`. Suitable for e.g. depth of field (DoF) effect where writing to the depth buffer is required. Note that for DoF effect, `Render Queue` needs to be set to `Alpha Test`.
   - SkeletonGraphic: Exposed `SetScaledPivotOffset` as public method outside of the editor to support programatically moving mesh offsets at runtime based on mesh bounds.
+  - SkeletonMecanim: Added `Scene Preview` option to preview an Animation Clip for e.g. easier event placement. When enabled, the Animation Clip selected in the Animation window is previewed in the Scene and Game views. Lock the `SkeletonMecanim` Inspector window, open the Animation window and select the Animation Clip. Then in the Animation window scrub through the timeline to see the current animation frame previewed.
 
 - **Breaking changes**
 

+ 46 - 1
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Components/SkeletonMecanimInspector.cs

@@ -37,6 +37,7 @@ namespace Spine.Unity.Editor {
 	[CanEditMultipleObjects]
 	public class SkeletonMecanimInspector : SkeletonRendererInspector {
 		public static bool mecanimSettingsFoldout;
+		public static bool enableScenePreview;
 
 		protected SerializedProperty autoReset;
 		protected SerializedProperty useCustomMixMode;
@@ -53,7 +54,6 @@ namespace Spine.Unity.Editor {
 		}
 
 		protected override void DrawInspectorGUI (bool multi) {
-
 			AddRootMotionComponentIfEnabled();
 
 			base.DrawInspectorGUI(multi);
@@ -75,6 +75,18 @@ namespace Spine.Unity.Editor {
 					}
 				}
 			}
+
+			EditorGUI.BeginChangeCheck();
+			enableScenePreview = EditorGUILayout.Toggle(new GUIContent("Scene Preview",
+				"Preview the Animation Clip selected in the Animation window. Lock this SkeletonMecanim Inspector " +
+				"window, open the Animation window and select the Animation Clip. Then in the Animation window " +
+				"scrub through the timeline."),
+				enableScenePreview, GUILayout.MaxWidth(150f));
+			bool wasScenePreviewChanged = EditorGUI.EndChangeCheck();
+			if (enableScenePreview)
+				HandleAnimationPreview();
+			else if (wasScenePreviewChanged) // just disabled, back to setup pose
+				PreviewAnimationInScene(null, 0.0f);
 		}
 
 		protected void AddRootMotionComponentIfEnabled () {
@@ -89,6 +101,39 @@ namespace Spine.Unity.Editor {
 			}
 		}
 
+		protected void HandleAnimationPreview () {
+			UnityEngine.Object animationWindow = AnimationWindowPreview.GetOpenAnimationWindow();
+
+			AnimationClip selectedClip = null;
+			if (animationWindow != null) {
+				selectedClip = AnimationWindowPreview.GetAnimationClip(animationWindow);
+			}
+
+			if (selectedClip != null) {
+				float time = AnimationWindowPreview.GetAnimationTime(animationWindow);
+				PreviewAnimationInScene(selectedClip, time);
+			}
+			else // back to setup pose
+				PreviewAnimationInScene(null, 0.0f);
+		}
+
+		protected void PreviewAnimationInScene (AnimationClip clip, float time) {
+			foreach (UnityEngine.Object c in targets) {
+				SkeletonRenderer skeletonRenderer = c as SkeletonRenderer;
+				if (skeletonRenderer == null) continue;
+				Skeleton skeleton = skeletonRenderer.Skeleton;
+				SkeletonData skeletonData = skeleton.Data;
+
+				skeleton.SetToSetupPose();
+				if (clip != null) {
+					Spine.Animation animation = skeletonData.FindAnimation(clip.name);
+					animation.Apply(skeleton, 0, time, false, null, 1.0f, MixBlend.First, MixDirection.In);
+				}
+				skeletonRenderer.LateUpdate();
+			}
+			SceneView.RepaintAll();
+		}
+
 		protected void DrawLayerSettings () {
 			string[] layerNames = GetLayerNames();
 			float widthLayerColumn = 140;

+ 50 - 0
spine-unity/Assets/Spine/Editor/spine-unity/Editor/Utility/SpineEditorUtilities.cs

@@ -578,4 +578,54 @@ namespace Spine.Unity.Editor {
 			return paths;
 		}
 	}
+
+	public class AnimationWindowPreview {
+		static System.Type animationWindowType;
+		public static System.Type AnimationWindowType {
+			get {
+				if (animationWindowType == null)
+					animationWindowType = System.Type.GetType("UnityEditor.AnimationWindow,UnityEditor");
+				return animationWindowType;
+			}
+		}
+
+		public static UnityEngine.Object GetOpenAnimationWindow () {
+			UnityEngine.Object[] openAnimationWindows = Resources.FindObjectsOfTypeAll(AnimationWindowType);
+			return openAnimationWindows.Length == 0 ? null : openAnimationWindows[0];
+		}
+
+		public static AnimationClip GetAnimationClip (UnityEngine.Object animationWindow) {
+			if (animationWindow == null)
+				return null;
+
+			const BindingFlags bindingFlagsInstance = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
+			FieldInfo animEditorField = AnimationWindowType.GetField("m_AnimEditor", bindingFlagsInstance);
+
+			PropertyInfo selectionProperty = animEditorField.FieldType.GetProperty("selection", bindingFlagsInstance);
+			object animEditor = animEditorField.GetValue(animationWindow);
+			if (animEditor == null) return null;
+			object selection = selectionProperty.GetValue(animEditor);
+			if (selection == null) return null;
+
+			PropertyInfo animationClipProperty = selection.GetType().GetProperty("animationClip");
+			return animationClipProperty.GetValue(selection) as AnimationClip;
+		}
+
+		public static float GetAnimationTime (UnityEngine.Object animationWindow) {
+			if (animationWindow == null)
+				return 0.0f;
+
+			const BindingFlags bindingFlagsInstance = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
+			FieldInfo animEditorField = AnimationWindowType.GetField("m_AnimEditor", bindingFlagsInstance);
+			object animEditor = animEditorField.GetValue(animationWindow);
+
+			System.Type animEditorFieldType  = animEditorField.FieldType;
+			PropertyInfo stateProperty = animEditorFieldType.GetProperty("state", bindingFlagsInstance);
+			System.Type animWindowStateType = stateProperty.PropertyType;
+			PropertyInfo timeProperty = animWindowStateType.GetProperty("currentTime", bindingFlagsInstance);
+
+			object state = stateProperty.GetValue(animEditor);
+			return (float)timeProperty.GetValue(state);
+		}
+	}
 }