Hello,
I made a graphical view for the AnimationState of spine objects. It allows you to view what the Animation State of an object looks like in real time, in a graphical format. i.e. what animations are playing, on what tracks. This can be useful for debugging what is happening with an animation stuff.
Although it is basic at the moment, I plan on continually developing it, including adding a recorder so you can view past animations, not just the ones currently playing or queued on the spine object.
Its simple to install just add the attached file to your unity project, preferably inside a folder called editor.
One installed, in unity go to Window>Spine AnimationState Viewer (this will open the window)
Then run your game,
And select your SpineObject in the hierarchy.
Track Queueing
(and looping)
Loading Image
Multiple Tracks
Loading Image
Loading Image
Animation History
Loading Image
See what interrupted your animations and such:
Loading Image
Items to the right of the track boundary is the live animation state. Items to the left of the boundary is what was previously played on your spine object!
If you have any Requests/Additions/Alterations Just post in this thread and I will update it with the latest code.
I plan on putting it on GitHub, when I get around to it.
Enjoy
11 Dec 2016, 18:21
Latest Code:
using UnityEngine;
using UnityEditor;
using Spine;
using System;
using System.Collections.Generic;
class SpineAnimationStateViewer : EditorWindow
{
#if UNITY_EDITOR
Transform SelectedTrans;
Rect m_AnimationRect = new Rect();
Spine.AnimationState m_AnimationState;
GUIStyle m_InfoBoxStyle;
List<EntryData> m_History = new List<EntryData>();
struct EntryData
{
public string AnimName;
public float StartTime, EndTime, Duration;
public int Track;
public bool Looping;
public EntryData(TrackEntry entry, float endTime, float startTime)
{
EndTime = endTime;
StartTime = startTime;
Track = entry.TrackIndex;
Duration = EndTime - StartTime;
AnimName = entry.Animation.Name;
Looping = entry.Loop;
}
}
void OnEnable()
{
m_History = new List<EntryData>();
}
[MenuItem("Window/Spine AnimationState Viewer")]
public static void ShowWindow()
{
GetWindow(typeof(SpineAnimationStateViewer)).Close();
EditorWindow.GetWindow(typeof(SpineAnimationStateViewer));
}
public void OnInspectorUpdate()
{
Repaint();
SelectedTrans = Selection.activeTransform;
if (SelectedTrans)
{
Spine.Unity.SkeletonAnimation skeletonAnimation = SelectedTrans.GetComponentInChildren<Spine.Unity.SkeletonAnimation>();
if (skeletonAnimation)
{
bool adddele = false;
if (m_AnimationState != null && m_AnimationState != skeletonAnimation.AnimationState)
{
// m_AnimationState.Complete -= AnimationComplete;
m_AnimationState.Interrupt -= AnimationInterupt;
//m_AnimationState.End -= AnimationStopped;
adddele = true;
}
if (m_AnimationState == null)
adddele = true;
m_AnimationState = skeletonAnimation.AnimationState;
if (adddele && m_AnimationState != null)
{
// m_AnimationState.Complete += AnimationComplete;
m_AnimationState.Interrupt += AnimationInterupt;
ResetVars();
}
}
}
}
void ResetVars()
{
m_History.Clear();
m_LastPresentPos = 0;
scrollPosition = Vector2.zero;
m_InfoBoxStyle.richText = true;
m_InfoBoxStyle.normal.background = MakeTex(2, 2, new Color(0.75f, 0.75f, 0.75f, 1f));
}
private void AnimationComplete(TrackEntry trackEntry)
{
if (!trackEntry.Loop)
{
m_History.Add(new EntryData(trackEntry, Time.time, Time.time - trackEntry.TrackTime));
m_MoveScrollpos = true;
}
}
private void AnimationInterupt(TrackEntry trackEntry)
{
//if (!trackEntry.IsComplete || trackEntry.Loop)
{
m_History.Add(new EntryData(trackEntry, Time.time, Time.time - trackEntry.TrackTime));
m_MoveScrollpos = true;
}
}
public Vector2 scrollPosition = Vector2.zero;
[System.NonSerialized]
float m_TrackTextSize = 80;
Vector2 ScrollViewArea = new Vector2();
private bool m_MoveScrollpos;
private float m_LastPresentPos;
private bool m_ShowInfoBox = false;
EntryData m_InfoData;
Rect m_InfoRect;
void OnGUI()
{
if (m_InfoBoxStyle == null)
{
m_InfoBoxStyle = new GUIStyle(GUI.skin.box);
}
m_InfoRect.width = 150;
m_InfoRect.height = 150;
m_AnimationRect.height = 30.0f;
m_AnimationRect.x = 0;
m_AnimationRect.y = 0;
m_AnimationRect.width = 0;
if (Vector2.Distance(m_InfoRect.position, UnityEngine.Event.current.mousePosition) > 10)
{
m_ShowInfoBox = false;
}
scrollPosition = GUI.BeginScrollView(new Rect(0, 0, position.width, position.height), scrollPosition, new Rect(Vector2.zero, ScrollViewArea), true, true);
ScrollViewArea.y = position.height;
ScrollViewArea.x = position.width;
if (Application.isPlaying && m_AnimationState != null)
{
for (int i = 0; i < m_History.Count; i++)
{
m_AnimationRect.x = m_TrackTextSize;
m_AnimationRect.x += m_History[i].StartTime * 100;
m_AnimationRect.y = m_AnimationRect.height * m_History[i].Track;
m_AnimationRect.width = (m_History[i].EndTime - m_History[i].StartTime) * 100;
GUI.Box(m_AnimationRect, m_History[i].AnimName);
if (UnityEngine.Event.current.type == EventType.MouseDown)
{
Vector2 mousepos = UnityEngine.Event.current.mousePosition;
Rect realrect = m_AnimationRect;
if (realrect.Contains(mousepos))
{
m_ShowInfoBox = true;
m_InfoData = m_History[i];
}
}
}
float presentPos = m_AnimationRect.x + m_AnimationRect.width;
m_AnimationRect.y = 0;
Spine.ExposedList<TrackEntry> tracks = m_AnimationState.Tracks;
for (int i = 0, length = tracks.Count; i < length; i++)
{
m_AnimationRect.width = m_TrackTextSize;
m_AnimationRect.x = presentPos;
GUI.Label(m_AnimationRect, "Track " + i);
m_AnimationRect.x += m_TrackTextSize;
TrackEntry entry = tracks.Items[i];
while (entry != null)
{
m_AnimationRect.x += entry.Delay * 100;
m_AnimationRect.width = (entry.AnimationEnd + 0.033f) * 100;
GUI.Box(m_AnimationRect, entry.Animation.Name);
if(m_AnimationRect.xMax> ScrollViewArea.x - position.height)
ScrollViewArea.x += m_AnimationRect.xMax;
if (UnityEngine.Event.current.type == EventType.MouseDown)
{
Vector2 mousepos = UnityEngine.Event.current.mousePosition;
Rect realrect = m_AnimationRect;
if (realrect.Contains(mousepos))
{
m_ShowInfoBox = true;
m_InfoData = new EntryData(entry, (m_AnimationRect.x / 100) + entry.AnimationEnd, (m_AnimationRect.x / 100));
}
}
entry = entry.Next;
}
m_AnimationRect.y += m_AnimationRect.height;
if (m_AnimationRect.yMax > ScrollViewArea.y - position.height)
ScrollViewArea.y += m_AnimationRect.yMax;
Handles.color = Color.black;
Vector3 p1 = new Vector3(0, m_AnimationRect.y);
Vector3 p2 = new Vector3(ScrollViewArea.x, m_AnimationRect.y);
Handles.DrawLine(p1, p2);
Spine.TrackEntry currentanim = m_AnimationState.GetCurrent(i);
if (currentanim != null)
{
float xpos = (currentanim.Loop ? currentanim.AnimationTime : currentanim.TrackTime);
xpos += m_TrackTextSize / 100;
xpos += presentPos / 100;
Handles.color = Color.red;
Vector3 p3 = new Vector3(xpos * 100, m_AnimationRect.y);
Vector3 p4 = new Vector3(xpos * 100, m_AnimationRect.y - m_AnimationRect.height);
Handles.DrawLine(p3, p4);
}
}
m_AnimationRect.y = m_AnimationRect.height * 3;
Handles.color = Color.black;
Vector3 p5 = new Vector3(m_TrackTextSize + presentPos, 0);
Vector3 p6 = new Vector3(m_TrackTextSize + presentPos, ScrollViewArea.y);
Handles.DrawLine(p5, p6);
Vector3 p7 = new Vector3(presentPos, 0);
Vector3 p8 = new Vector3(presentPos, ScrollViewArea.y);
Handles.DrawLine(p7, p8);
if (m_MoveScrollpos)
{
float change = presentPos - m_LastPresentPos;
m_LastPresentPos = presentPos;
scrollPosition.x += change;
m_MoveScrollpos = false;
}
}
GUI.EndScrollView();
if (m_ShowInfoBox)
{
//m_InfoRect.position = UnityEngine.Event.current.mousePosition;
//GUI.Box(m_InfoRect, m_InfoData.AnimName);
////GUI.Box(m_InfoRect, m_InfoData.AnimName);
m_InfoRect.position = UnityEngine.Event.current.mousePosition;
GUI.Box(m_InfoRect, MakeText(m_InfoData), m_InfoBoxStyle);
}
}
string MakeText(EntryData data)
{
return "<b>Name:</b> " + data.AnimName + "\n<b>Track:</b> " + data.Track + "\n<b>Loop:</b>" + data.Looping + "\n<b>StartTime:</b> " + data.StartTime + "\n<b>EndTime:</b> " + data.EndTime;
}
private Texture2D MakeTex(int width, int height, Color col)
{
Color[] pix = new Color[width * height];
for (int i = 0; i < pix.Length; ++i)
{
pix[i] = col;
}
Texture2D result = new Texture2D(width, height);
result.SetPixels(pix);
result.Apply();
return result;
}
#endif
}