Skip to content

Commit

Permalink
[unity] Added Scene Preview to SkeletonMecanim Inspector for Meca…
Browse files Browse the repository at this point in the history
…nim AnimationClip preview. Closes #1253.
  • Loading branch information
HaraldCsaszar committed Dec 20, 2024
1 parent ee55855 commit 0371e4d
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 1 deletion.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -53,7 +54,6 @@ protected override void OnEnable () {
}

protected override void DrawInspectorGUI (bool multi) {

AddRootMotionComponentIfEnabled();

base.DrawInspectorGUI(multi);
Expand All @@ -75,6 +75,18 @@ protected override void DrawInspectorGUI (bool multi) {
}
}
}

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 () {
Expand All @@ -89,6 +101,39 @@ protected void AddRootMotionComponentIfEnabled () {
}
}

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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -578,4 +578,54 @@ static string[] OnWillSaveAssets (string[] paths) {
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);
}
}
}

0 comments on commit 0371e4d

Please sign in to comment.