Skip to content

Instantly share code, notes, and snippets.

@avrahamy
Last active January 9, 2022 08:19
Show Gist options
  • Save avrahamy/9ecb177eebb29033d4bda53df392730a to your computer and use it in GitHub Desktop.
Save avrahamy/9ecb177eebb29033d4bda53df392730a to your computer and use it in GitHub Desktop.
A tool to scrub Spine2D animations in Unity. Select a Game Object in the scene (or in Prefab Mode) with a SkeletonAnimation, select the animation to preview and use the slider to pick the time of the frame you want to see.
using UnityEditor;
using UnityEngine;
using Spine.Unity;
namespace Avrahamy.Animation {
public class SkeletonAnimationPreviewWindow : EditorWindow {
private const string WINDOW_TITLE = "Skeleton Animation Preview";
private string animationName;
private float time;
private SkeletonAnimation _skeleton;
private Spine.Animation _animation;
private bool animationChanged;
private GameObject Target {
get {
if (Selection.gameObjects.Length == 0) return null;
return Selection.gameObjects[0];
}
}
private SkeletonAnimation Skeleton {
get {
if (_skeleton == null && Target != null) {
_skeleton = Target.GetComponentInParent<SkeletonAnimation>();
if (_skeleton == null) {
_skeleton = Target.GetComponentInChildren<SkeletonAnimation>();
if (_skeleton == null) {
_skeleton = Target.transform.root.GetComponentInChildren<SkeletonAnimation>();
}
}
}
return _skeleton;
}
}
private Spine.Animation Animation {
get {
if (!string.IsNullOrEmpty(animationName)
&& (_animation == null || _animation.Name != animationName)) {
_animation = Skeleton.skeletonDataAsset.GetSkeletonData(true).FindAnimation(animationName);
animationChanged = true;
}
return _animation;
}
}
[MenuItem("Window/GG/Skeleton Preview")]
public static void ShowWindow() {
// Opens the window, otherwise focuses it if it’s already open.
var window = GetWindow<SkeletonAnimationPreviewWindow>(WINDOW_TITLE);
// Sets a minimum size to the window.
window.minSize = new Vector2(340f, 250f);
}
private void OnEnable() {
Selection.selectionChanged += OnSelectionChanged;
}
private void OnDisable() {
Selection.selectionChanged -= OnSelectionChanged;
if (_skeleton != null) {
ResetSkeleton(_skeleton);
_skeleton = null;
_animation = null;
animationName = null;
time = 0f;
}
}
private void OnSelectionChanged() {
var previousSkeleton = _skeleton;
_skeleton = Skeleton ?? previousSkeleton;
if (_skeleton != previousSkeleton) {
animationChanged = true;
}
Repaint();
}
private void ResetSkeleton(SkeletonAnimation skeleton) {
var defaultAnimation = new SerializedObject(skeleton).FindProperty("_animationName").stringValue;
if (string.IsNullOrEmpty(defaultAnimation)) {
Debug.LogError("Skeleton has no default animation set. Can't reset it");
return;
}
SetAnimation(defaultAnimation, 0f);
}
private void OnGUI() {
_skeleton = EditorGUILayout.ObjectField(
new GUIContent("Skeleton"),
Skeleton,
typeof(SkeletonAnimation),
true) as SkeletonAnimation;
if (Skeleton == null) return;
if (GUILayout.Button("Reset", GUILayout.Width(60f))) {
ResetSkeleton(_skeleton);
_skeleton = null;
_animation = null;
animationName = null;
time = 0f;
return;
}
using (new EditorGUILayout.HorizontalScope()) {
EditorGUILayout.LabelField("Animation", GUILayout.Width(EditorGUIUtility.labelWidth));
if (EditorGUILayout.DropdownButton(new GUIContent(animationName), FocusType.Keyboard, EditorStyles.popup)) {
var menu = new GenericMenu();
var animations = Skeleton.skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations;
for (int i = 0; i < animations.Count; i++) {
var itemName = animations.Items[i].Name;
menu.AddItem(new GUIContent(itemName), itemName == animationName, (currentItemName) => {
animationName = currentItemName as string;
animationChanged = true;
}, itemName);
}
menu.ShowAsContext();
}
}
if (Animation == null) return;
var timeChanged = false;
using (var check = new EditorGUI.ChangeCheckScope()) {
time = EditorGUILayout.Slider("Time", time, 0f, Animation.Duration);
if (check.changed || time > Animation.Duration) {
time = Mathf.Min(time, Animation.Duration);
timeChanged = true;
}
}
if (Animation != null && animationChanged) {
SetAnimation(animationName, time);
animationChanged = false;
} else if (timeChanged) {
Skeleton.AnimationState.GetCurrent(0).TrackTime = time;
Skeleton.LateUpdate();
}
}
private void SetAnimation(string name, float time) {
Skeleton.AnimationState.SetAnimation(
0,
name,
true)
.TrackTime = time;
Skeleton.LateUpdate();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment