浏览代码

Merge branch 'master' into unity-uwp-fix

pharan 9 年之前
父节点
当前提交
3d6376f0e9

+ 3 - 0
spine-monogame/xamarinstudio-ios/src/spine-monogame-xamarinstudio-ios.csproj

@@ -85,6 +85,9 @@
     <Compile Include="..\..\..\spine-csharp\src\BoneData.cs">
     <Compile Include="..\..\..\spine-csharp\src\BoneData.cs">
       <Link>spine-csharp\BoneData.cs</Link>
       <Link>spine-csharp\BoneData.cs</Link>
     </Compile>
     </Compile>
+    <Compile Include="..\..\..\spine-csharp\src\ExposedList.cs">
+      <Link>spine-csharp\ExposedList.cs</Link>
+    </Compile>
     <Compile Include="..\..\..\spine-csharp\src\Json.cs">
     <Compile Include="..\..\..\spine-csharp\src\Json.cs">
       <Link>spine-csharp\Json.cs</Link>
       <Link>spine-csharp\Json.cs</Link>
     </Compile>
     </Compile>

+ 1 - 0
spine-unity/Assets/Examples/Scripts/BasicPlatformerController.cs

@@ -62,6 +62,7 @@ public class BasicPlatformerController : MonoBehaviour {
 	public AudioSource jumpAudioSource;
 	public AudioSource jumpAudioSource;
 	public AudioSource hardfallAudioSource;
 	public AudioSource hardfallAudioSource;
 	public AudioSource footstepAudioSource;
 	public AudioSource footstepAudioSource;
+	[SpineEvent]
 	public string footstepEventName = "Footstep";
 	public string footstepEventName = "Footstep";
 	CharacterController controller;
 	CharacterController controller;
 	Vector2 velocity = Vector2.zero;
 	Vector2 velocity = Vector2.zero;

+ 3 - 3
spine-unity/Assets/Examples/Scripts/RaggedySpineboy.cs

@@ -18,11 +18,11 @@ public class RaggedySpineboy : MonoBehaviour {
 
 
 	void AddRigidbody () {
 	void AddRigidbody () {
 		var rb = gameObject.AddComponent<Rigidbody2D>();
 		var rb = gameObject.AddComponent<Rigidbody2D>();
-#if UNITY_5_1
+		#if UNITY_5_1 || UNITY_5_2 || UNITY_5_3 || UNITY_5_4 || UNITY_5_5
         rb.freezeRotation = true;
         rb.freezeRotation = true;
-#else
+		#else
 		rb.fixedAngle = true;
 		rb.fixedAngle = true;
-#endif
+		#endif
 		naturalCollider.enabled = true;
 		naturalCollider.enabled = true;
 	}
 	}
 
 

+ 5 - 5
spine-unity/Assets/Examples/Scripts/SpineboyController.cs

@@ -11,11 +11,11 @@ using System.Collections;
 public class SpineboyController : MonoBehaviour {
 public class SpineboyController : MonoBehaviour {
 
 
 	SkeletonAnimation skeletonAnimation;
 	SkeletonAnimation skeletonAnimation;
-	public string idleAnimation = "idle";
-	public string walkAnimation = "walk";
-	public string runAnimation = "run";
-	public string hitAnimation = "hit";
-	public string deathAnimation = "death";
+	[SpineAnimation] public string idleAnimation = "idle";
+	[SpineAnimation] public string walkAnimation = "walk";
+	[SpineAnimation] public string runAnimation = "run";
+	[SpineAnimation] public string hitAnimation = "hit";
+	[SpineAnimation] public string deathAnimation = "death";
 	public float walkVelocity = 1;
 	public float walkVelocity = 1;
 	public float runVelocity = 3;
 	public float runVelocity = 3;
 	public int hp = 10;
 	public int hp = 10;

+ 5 - 6
spine-unity/Assets/spine-unity/Editor/SkeletonAnimationInspector.cs

@@ -36,8 +36,9 @@ using Spine;
 
 
 [CustomEditor(typeof(SkeletonAnimation))]
 [CustomEditor(typeof(SkeletonAnimation))]
 public class SkeletonAnimationInspector : SkeletonRendererInspector {
 public class SkeletonAnimationInspector : SkeletonRendererInspector {
-	protected SerializedProperty animationName, loop, timeScale;
-	protected bool isPrefab;
+	protected SerializedProperty animationName, loop, timeScale, autoReset;
+	protected bool m_isPrefab;
+	protected GUIContent autoResetLabel;
 
 
 	protected override void OnEnable () {
 	protected override void OnEnable () {
 		base.OnEnable();
 		base.OnEnable();
@@ -46,9 +47,7 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector {
 		timeScale = serializedObject.FindProperty("timeScale");
 		timeScale = serializedObject.FindProperty("timeScale");
 
 
 		if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
 		if (PrefabUtility.GetPrefabType(this.target) == PrefabType.Prefab)
-			isPrefab = true;
-
-
+			m_isPrefab = true;
 	}
 	}
 
 
 	protected override void gui () {
 	protected override void gui () {
@@ -100,7 +99,7 @@ public class SkeletonAnimationInspector : SkeletonRendererInspector {
 
 
 		EditorGUILayout.Space();
 		EditorGUILayout.Space();
 
 
-		if (!isPrefab) {
+		if (!m_isPrefab) {
 			if (component.GetComponent<SkeletonUtility>() == null) {
 			if (component.GetComponent<SkeletonUtility>() == null) {
 				if (GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))) {
 				if (GUILayout.Button(new GUIContent("Add Skeleton Utility", SpineEditorUtilities.Icons.skeletonUtility), GUILayout.Height(30))) {
 					component.gameObject.AddComponent<SkeletonUtility>();
 					component.gameObject.AddComponent<SkeletonUtility>();

+ 27 - 27
spine-unity/Assets/spine-unity/Editor/SkeletonDataAssetInspector.cs

@@ -38,7 +38,7 @@ public class SkeletonDataAssetInspector : Editor {
 	private bool needToSerialize;
 	private bool needToSerialize;
 
 
 	List<string> warnings = new List<string>();
 	List<string> warnings = new List<string>();
-
+	
 	void OnEnable () {
 	void OnEnable () {
 
 
 		SpineEditorUtilities.ConfirmInitialization();
 		SpineEditorUtilities.ConfirmInitialization();
@@ -61,7 +61,7 @@ public class SkeletonDataAssetInspector : Editor {
 
 
 			EditorApplication.update += Update;
 			EditorApplication.update += Update;
 		} catch {
 		} catch {
-
+			// TODO: WARNING: empty catch block supresses errors.
 
 
 		}
 		}
 
 
@@ -117,7 +117,7 @@ public class SkeletonDataAssetInspector : Editor {
 			DrawAnimationList();
 			DrawAnimationList();
 			DrawSlotList();
 			DrawSlotList();
 			DrawUnityTools();
 			DrawUnityTools();
-
+			
 		} else {
 		} else {
 
 
 			DrawReimportButton();
 			DrawReimportButton();
@@ -131,8 +131,8 @@ public class SkeletonDataAssetInspector : Editor {
 	}
 	}
 
 
 	void DrawMecanim () {
 	void DrawMecanim () {
-
-		EditorGUILayout.PropertyField(controller, new GUIContent("Controller", SpineEditorUtilities.Icons.controllerIcon));
+		
+		EditorGUILayout.PropertyField(controller, new GUIContent("Controller", SpineEditorUtilities.Icons.controllerIcon));		
 		if (controller.objectReferenceValue == null) {
 		if (controller.objectReferenceValue == null) {
 			GUILayout.BeginHorizontal();
 			GUILayout.BeginHorizontal();
 			GUILayout.Space(32);
 			GUILayout.Space(32);
@@ -142,7 +142,7 @@ public class SkeletonDataAssetInspector : Editor {
 			GUILayout.EndHorizontal();
 			GUILayout.EndHorizontal();
 			EditorGUILayout.LabelField("Alternative to SkeletonAnimation, not required", EditorStyles.miniLabel);
 			EditorGUILayout.LabelField("Alternative to SkeletonAnimation, not required", EditorStyles.miniLabel);
 		}
 		}
-
+		
 	}
 	}
 
 
 	void DrawUnityTools () {
 	void DrawUnityTools () {
@@ -162,7 +162,7 @@ public class SkeletonDataAssetInspector : Editor {
 			EditorGUILayout.HelpBox("WARNING!\n\nBaking is NOT the same as SkeletonAnimator!\nDoes not support the following:\n\tFlipX or Y\n\tInheritScale\n\tColor Keys\n\tDraw Order Keys\n\tIK and Curves are sampled at 60fps and are not realtime.\n\tPlease read SkeletonBaker.cs comments for full details.\n\nThe main use of Baking is to export Spine projects to be used without the Spine Runtime (ie: for sale on the Asset Store, or background objects that are animated only with a wind noise generator)", MessageType.Warning, true);
 			EditorGUILayout.HelpBox("WARNING!\n\nBaking is NOT the same as SkeletonAnimator!\nDoes not support the following:\n\tFlipX or Y\n\tInheritScale\n\tColor Keys\n\tDraw Order Keys\n\tIK and Curves are sampled at 60fps and are not realtime.\n\tPlease read SkeletonBaker.cs comments for full details.\n\nThe main use of Baking is to export Spine projects to be used without the Spine Runtime (ie: for sale on the Asset Store, or background objects that are animated only with a wind noise generator)", MessageType.Warning, true);
 			EditorGUI.indentLevel++;
 			EditorGUI.indentLevel++;
 			bakeAnimations = EditorGUILayout.Toggle("Bake Animations", bakeAnimations);
 			bakeAnimations = EditorGUILayout.Toggle("Bake Animations", bakeAnimations);
-			EditorGUI.BeginDisabledGroup(bakeAnimations == false);
+			EditorGUI.BeginDisabledGroup(!bakeAnimations);
 			{
 			{
 				EditorGUI.indentLevel++;
 				EditorGUI.indentLevel++;
 				bakeIK = EditorGUILayout.Toggle("Bake IK", bakeIK);
 				bakeIK = EditorGUILayout.Toggle("Bake IK", bakeIK);
@@ -195,7 +195,7 @@ public class SkeletonDataAssetInspector : Editor {
 					try {
 					try {
 						GUILayout.BeginVertical();
 						GUILayout.BeginVertical();
 						if (GUILayout.Button(new GUIContent("Bake " + skinName, SpineEditorUtilities.Icons.unityIcon), GUILayout.Height(32), GUILayout.Width(250)))
 						if (GUILayout.Button(new GUIContent("Bake " + skinName, SpineEditorUtilities.Icons.unityIcon), GUILayout.Height(32), GUILayout.Width(250)))
-							SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, new ExposedList<Skin>(new Skin[] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions);
+							SkeletonBaker.BakeToPrefab(m_skeletonDataAsset, new ExposedList<Skin>(new [] { bakeSkin }), "", bakeAnimations, bakeIK, bakeEventOptions);
 
 
 						GUILayout.BeginHorizontal();
 						GUILayout.BeginHorizontal();
 						GUILayout.Label(new GUIContent("Skins", SpineEditorUtilities.Icons.skinsRoot), GUILayout.Width(50));
 						GUILayout.Label(new GUIContent("Skins", SpineEditorUtilities.Icons.skinsRoot), GUILayout.Width(50));
@@ -257,7 +257,7 @@ public class SkeletonDataAssetInspector : Editor {
 		EditorGUILayout.PropertyField(defaultMix);
 		EditorGUILayout.PropertyField(defaultMix);
 
 
 		// Animation names
 		// Animation names
-		String[] animations = new String[m_skeletonData.Animations.Count];
+		var animations = new string[m_skeletonData.Animations.Count];
 		for (int i = 0; i < animations.Length; i++)
 		for (int i = 0; i < animations.Length; i++)
 			animations[i] = m_skeletonData.Animations.Items[i].Name;
 			animations[i] = m_skeletonData.Animations.Items[i].Name;
 
 
@@ -292,7 +292,7 @@ public class SkeletonDataAssetInspector : Editor {
 			serializedObject.ApplyModifiedProperties();
 			serializedObject.ApplyModifiedProperties();
 			needToSerialize = true;
 			needToSerialize = true;
 		}
 		}
-
+			
 	}
 	}
 	void DrawAnimationList () {
 	void DrawAnimationList () {
 		showAnimationList = EditorGUILayout.Foldout(showAnimationList, new GUIContent("Animations", SpineEditorUtilities.Icons.animationRoot));
 		showAnimationList = EditorGUILayout.Foldout(showAnimationList, new GUIContent("Animations", SpineEditorUtilities.Icons.animationRoot));
@@ -445,7 +445,7 @@ public class SkeletonDataAssetInspector : Editor {
 				warnings.Add("Skeleton data file is not a valid JSON or binary file.");
 				warnings.Add("Skeleton data file is not a valid JSON or binary file.");
 			} else {
 			} else {
 				bool detectedNullAtlasEntry = false;
 				bool detectedNullAtlasEntry = false;
-				List<Atlas> atlasList = new List<Atlas>();
+				var atlasList = new List<Atlas>();
 				for (int i = 0; i < atlasAssets.arraySize; i++) {
 				for (int i = 0; i < atlasAssets.arraySize; i++) {
 					if (atlasAssets.GetArrayElementAtIndex(i).objectReferenceValue == null) {
 					if (atlasAssets.GetArrayElementAtIndex(i).objectReferenceValue == null) {
 						detectedNullAtlasEntry = true;
 						detectedNullAtlasEntry = true;
@@ -676,7 +676,7 @@ public class SkeletonDataAssetInspector : Editor {
 
 
 
 
 
 
-			if (drawHandles) {
+			if (drawHandles) {			
 				Handles.SetCamera(m_previewUtility.m_Camera);
 				Handles.SetCamera(m_previewUtility.m_Camera);
 				Handles.color = m_originColor;
 				Handles.color = m_originColor;
 
 
@@ -689,9 +689,10 @@ public class SkeletonDataAssetInspector : Editor {
 			if (drawHandles) {
 			if (drawHandles) {
 				Handles.SetCamera(m_previewUtility.m_Camera);
 				Handles.SetCamera(m_previewUtility.m_Camera);
 				foreach (var slot in m_skeletonAnimation.skeleton.Slots) {
 				foreach (var slot in m_skeletonAnimation.skeleton.Slots) {
-					if (slot.Attachment is BoundingBoxAttachment) {
+					var boundingBoxAttachment = slot.Attachment as BoundingBoxAttachment;
 
 
-						DrawBoundingBox(slot.Bone, (BoundingBoxAttachment)slot.Attachment);
+					if (boundingBoxAttachment != null) {
+						DrawBoundingBox (slot.Bone, boundingBoxAttachment);
 					}
 					}
 				}
 				}
 			}
 			}
@@ -702,8 +703,10 @@ public class SkeletonDataAssetInspector : Editor {
 
 
 	}
 	}
 
 
-	void DrawBoundingBox (Bone bone, BoundingBoxAttachment box) {
-		float[] worldVerts = new float[box.Vertices.Length];
+	static void DrawBoundingBox (Bone bone, BoundingBoxAttachment box) {
+		if (box.Vertices.Length <= 0) return; // Handle cases where user creates a BoundingBoxAttachment but doesn't actually define it.
+
+		var worldVerts = new float[box.Vertices.Length];
 		box.ComputeWorldVertices(bone, worldVerts);
 		box.ComputeWorldVertices(bone, worldVerts);
 
 
 		Handles.color = Color.green;
 		Handles.color = Color.green;
@@ -717,15 +720,12 @@ public class SkeletonDataAssetInspector : Editor {
 			if (i > 0) {
 			if (i > 0) {
 				Handles.DrawLine(lastVert, vert);
 				Handles.DrawLine(lastVert, vert);
 			}
 			}
-
-
+				
 			lastVert = vert;
 			lastVert = vert;
 		}
 		}
 
 
 		Handles.DrawLine(lastVert, firstVert);
 		Handles.DrawLine(lastVert, firstVert);
-
-
-
+		
 	}
 	}
 
 
 	void Update () {
 	void Update () {
@@ -824,7 +824,7 @@ public class SkeletonDataAssetInspector : Editor {
 
 
 				float fr = m_animEventFrames[i];
 				float fr = m_animEventFrames[i];
 
 
-				Rect evRect = new Rect(barRect);
+				var evRect = new Rect(barRect);
 				evRect.x = Mathf.Clamp(((fr / t.Animation.Duration) * width) - (SpineEditorUtilities.Icons._event.width / 2), barRect.x, float.MaxValue);
 				evRect.x = Mathf.Clamp(((fr / t.Animation.Duration) * width) - (SpineEditorUtilities.Icons._event.width / 2), barRect.x, float.MaxValue);
 				evRect.width = SpineEditorUtilities.Icons._event.width;
 				evRect.width = SpineEditorUtilities.Icons._event.width;
 				evRect.height = SpineEditorUtilities.Icons._event.height;
 				evRect.height = SpineEditorUtilities.Icons._event.height;
@@ -882,7 +882,7 @@ public class SkeletonDataAssetInspector : Editor {
 				EditorGUIUtility.SetWantsMouseJumping(1);
 				EditorGUIUtility.SetWantsMouseJumping(1);
 			}
 			}
 			return scrollPosition;
 			return scrollPosition;
-
+			
 		case EventType.MouseUp:
 		case EventType.MouseUp:
 			if (GUIUtility.hotControl == controlID)
 			if (GUIUtility.hotControl == controlID)
 			{
 			{
@@ -890,10 +890,10 @@ public class SkeletonDataAssetInspector : Editor {
 			}
 			}
 			EditorGUIUtility.SetWantsMouseJumping(0);
 			EditorGUIUtility.SetWantsMouseJumping(0);
 			return scrollPosition;
 			return scrollPosition;
-
+			
 		case EventType.MouseMove:
 		case EventType.MouseMove:
 			return scrollPosition;
 			return scrollPosition;
-
+			
 		case EventType.MouseDrag:
 		case EventType.MouseDrag:
 			if (GUIUtility.hotControl == controlID)
 			if (GUIUtility.hotControl == controlID)
 			{
 			{
@@ -930,7 +930,7 @@ public class SkeletonDataAssetInspector : Editor {
 	//TODO:  Fix first-import error
 	//TODO:  Fix first-import error
 	//TODO:  Update preview without thumbnail
 	//TODO:  Update preview without thumbnail
 	public override Texture2D RenderStaticPreview (string assetPath, UnityEngine.Object[] subAssets, int width, int height) {
 	public override Texture2D RenderStaticPreview (string assetPath, UnityEngine.Object[] subAssets, int width, int height) {
-		Texture2D tex = new Texture2D(width, height, TextureFormat.ARGB32, false);
+		var tex = new Texture2D(width, height, TextureFormat.ARGB32, false);
 
 
 		this.InitPreview();
 		this.InitPreview();
 
 
@@ -956,4 +956,4 @@ public class SkeletonDataAssetInspector : Editor {
 		tex = this.m_previewUtility.EndStaticPreview();
 		tex = this.m_previewUtility.EndStaticPreview();
 		return tex;
 		return tex;
 	}
 	}
-}
+}

+ 142 - 379
spine-unity/Assets/spine-unity/Editor/SpineAttributeDrawers.cs

@@ -1,5 +1,4 @@
 
 
-
 /*****************************************************************************
 /*****************************************************************************
  * Spine Attribute Drawers created by Mitch Thompson
  * Spine Attribute Drawers created by Mitch Thompson
  * Full irrevocable rights and permissions granted to Esoteric Software
  * Full irrevocable rights and permissions granted to Esoteric Software
@@ -8,9 +7,6 @@ using UnityEngine;
 using UnityEditor;
 using UnityEditor;
 using System.Collections;
 using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
-using System.IO;
-using System.Text;
-using System.Linq;
 using System.Reflection;
 using System.Reflection;
 using Spine;
 using Spine;
 
 
@@ -25,21 +21,18 @@ public struct SpineDrawerValuePair {
 	}
 	}
 }
 }
 
 
-[CustomPropertyDrawer(typeof(SpineSlot))]
-public class SpineSlotDrawer : PropertyDrawer {
-	SkeletonDataAsset skeletonDataAsset;
-
+public abstract class SpineTreeItemDrawerBase<T> : PropertyDrawer where T:SpineAttributeBase {
+	protected SkeletonDataAsset skeletonDataAsset;
+	protected T TargetAttribute { get { return (T)attribute; } }
 
 
-	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
+	public override void OnGUI (Rect position, SerializedProperty property, GUIContent label) {
 		if (property.propertyType != SerializedPropertyType.String) {
 		if (property.propertyType != SerializedPropertyType.String) {
 			EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
 			EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
 			return;
 			return;
 		}
 		}
-
-		SpineSlot attrib = (SpineSlot)attribute;
-
-		var dataProperty = property.serializedObject.FindProperty(attrib.dataField);
-
+		
+		var dataProperty = property.serializedObject.FindProperty(TargetAttribute.dataField);
+		
 		if (dataProperty != null) {
 		if (dataProperty != null) {
 			if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
 			if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
 				skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue;
 				skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue;
@@ -51,7 +44,7 @@ public class SpineSlotDrawer : PropertyDrawer {
 				EditorGUI.LabelField(position, "ERROR:", "Invalid reference type");
 				EditorGUI.LabelField(position, "ERROR:", "Invalid reference type");
 				return;
 				return;
 			}
 			}
-
+			
 		} else if (property.serializedObject.targetObject is Component) {
 		} else if (property.serializedObject.targetObject is Component) {
 			var component = (Component)property.serializedObject.targetObject;
 			var component = (Component)property.serializedObject.targetObject;
 			if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
 			if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
@@ -59,43 +52,60 @@ public class SpineSlotDrawer : PropertyDrawer {
 				skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
 				skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
 			}
 			}
 		}
 		}
-
+		
 		if (skeletonDataAsset == null) {
 		if (skeletonDataAsset == null) {
 			EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset");
 			EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset");
 			return;
 			return;
 		}
 		}
-
+		
 		position = EditorGUI.PrefixLabel(position, label);
 		position = EditorGUI.PrefixLabel(position, label);
-
+		
 		if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
 		if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
 			Selector(property);
 			Selector(property);
 		}
 		}
-
+		
 	}
 	}
 
 
-	void Selector(SerializedProperty property) {
-		SpineSlot attrib = (SpineSlot)attribute;
+	protected virtual void Selector (SerializedProperty property) {
 		SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
 		SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
 		if (data == null)
 		if (data == null)
 			return;
 			return;
-
+		
 		GenericMenu menu = new GenericMenu();
 		GenericMenu menu = new GenericMenu();
+		PopulateMenu (menu, property, this.TargetAttribute, data);
+		menu.ShowAsContext();
+	}
 
 
-		menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
-		menu.AddSeparator("");
+	protected abstract void PopulateMenu (GenericMenu menu, SerializedProperty property, T targetAttribute, SkeletonData data);
+
+	protected virtual void HandleSelect (object val) {
+		var pair = (SpineDrawerValuePair)val;
+		pair.property.stringValue = pair.str;
+		pair.property.serializedObject.ApplyModifiedProperties();
+	}
 
 
+	public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
+		return 18;
+	}
+
+}
+
+[CustomPropertyDrawer(typeof(SpineSlot))]
+public class SpineSlotDrawer : SpineTreeItemDrawerBase<SpineSlot> {
+
+	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSlot targetAttribute, SkeletonData data) {
 		for (int i = 0; i < data.Slots.Count; i++) {
 		for (int i = 0; i < data.Slots.Count; i++) {
 			string name = data.Slots.Items[i].Name;
 			string name = data.Slots.Items[i].Name;
-			if (name.StartsWith(attrib.startsWith)) {
-				if (attrib.containsBoundingBoxes) {
-
+			if (name.StartsWith(targetAttribute.startsWith)) {
+				if (targetAttribute.containsBoundingBoxes) {
+					
 					int slotIndex = i;
 					int slotIndex = i;
-
+					
 					List<Attachment> attachments = new List<Attachment>();
 					List<Attachment> attachments = new List<Attachment>();
 					foreach (var skin in data.Skins) {
 					foreach (var skin in data.Skins) {
 						skin.FindAttachmentsForSlot(slotIndex, attachments);
 						skin.FindAttachmentsForSlot(slotIndex, attachments);
 					}
 					}
-
+					
 					bool hasBoundingBox = false;
 					bool hasBoundingBox = false;
 					foreach (var attachment in attachments) {
 					foreach (var attachment in attachments) {
 						if (attachment is BoundingBoxAttachment) {
 						if (attachment is BoundingBoxAttachment) {
@@ -104,315 +114,80 @@ public class SpineSlotDrawer : PropertyDrawer {
 							break;
 							break;
 						}
 						}
 					}
 					}
-
+					
 					if (!hasBoundingBox)
 					if (!hasBoundingBox)
 						menu.AddDisabledItem(new GUIContent(name));
 						menu.AddDisabledItem(new GUIContent(name));
 					
 					
-
+					
 				} else {
 				} else {
 					menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 					menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 				}
 				}
 				
 				
 			}
 			}
-				
+			
 		}
 		}
-
-		menu.ShowAsContext();
 	}
 	}
 
 
-	void HandleSelect(object val) {
-		var pair = (SpineDrawerValuePair)val;
-		pair.property.stringValue = pair.str;
-		pair.property.serializedObject.ApplyModifiedProperties();
-	}
-
-	public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
-		return 18;
-	}
 }
 }
 
 
 [CustomPropertyDrawer(typeof(SpineSkin))]
 [CustomPropertyDrawer(typeof(SpineSkin))]
-public class SpineSkinDrawer : PropertyDrawer {
-	SkeletonDataAsset skeletonDataAsset;
-
-	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
-		if (property.propertyType != SerializedPropertyType.String) {
-			EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
-			return;
-		}
-
-		SpineSkin attrib = (SpineSkin)attribute;
-
-		var dataProperty = property.serializedObject.FindProperty(attrib.dataField);
-
-		if (dataProperty != null) {
-			if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
-				skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue;
-			} else if (dataProperty.objectReferenceValue is SkeletonRenderer) {
-				var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue;
-				if (renderer != null)
-					skeletonDataAsset = renderer.skeletonDataAsset;
-			} else {
-				EditorGUI.LabelField(position, "ERROR:", "Invalid reference type");
-				return;
-			}
-
-		} else if (property.serializedObject.targetObject is Component) {
-			var component = (Component)property.serializedObject.targetObject;
-			if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
-				var skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
-				skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
-			}
-		}
-
-		if (skeletonDataAsset == null) {
-			EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset");
-			return;
-		}
-
-		position = EditorGUI.PrefixLabel(position, label);
-
-		if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
-			Selector(property);
-		}
-
-	}
-
-	void Selector(SerializedProperty property) {
-		SpineSkin attrib = (SpineSkin)attribute;
-		SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
-		if (data == null)
-			return;
-
-		GenericMenu menu = new GenericMenu();
+public class SpineSkinDrawer : SpineTreeItemDrawerBase<SpineSkin> {
 
 
+	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineSkin targetAttribute, SkeletonData data) {
 		menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
 		menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
 		menu.AddSeparator("");
 		menu.AddSeparator("");
-
+		
 		for (int i = 0; i < data.Skins.Count; i++) {
 		for (int i = 0; i < data.Skins.Count; i++) {
 			string name = data.Skins.Items[i].Name;
 			string name = data.Skins.Items[i].Name;
-			if (name.StartsWith(attrib.startsWith))
+			if (name.StartsWith(targetAttribute.startsWith))
 				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 		}
 		}
-
-		menu.ShowAsContext();
 	}
 	}
 
 
-	void HandleSelect(object val) {
-		var pair = (SpineDrawerValuePair)val;
-		pair.property.stringValue = pair.str;
-		pair.property.serializedObject.ApplyModifiedProperties();
-	}
-
-	public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
-		return 18;
-	}
-}
-
-[CustomPropertyDrawer(typeof(SpineAtlasRegion))]
-public class SpineAtlasRegionDrawer : PropertyDrawer {
-	Component component;
-	SerializedProperty atlasProp;
-
-	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
-		if (property.propertyType != SerializedPropertyType.String) {
-			EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
-			return;
-		}
-
-		component = (Component)property.serializedObject.targetObject;
-
-		if (component != null)
-			atlasProp = property.serializedObject.FindProperty("atlasAsset");
-		else
-			atlasProp = null;
-
-
-		if (atlasProp == null) {
-			EditorGUI.LabelField(position, "ERROR:", "Must have AtlasAsset variable!");
-			return;
-		} else if (atlasProp.objectReferenceValue == null) {
-			EditorGUI.LabelField(position, "ERROR:", "Atlas variable must not be null!");
-			return;
-		} else if (atlasProp.objectReferenceValue.GetType() != typeof(AtlasAsset)) {
-			EditorGUI.LabelField(position, "ERROR:", "Atlas variable must be of type AtlasAsset!");
-		}
-
-		position = EditorGUI.PrefixLabel(position, label);
-
-		if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
-			Selector(property);
-		}
-
-	}
-
-	void Selector(SerializedProperty property) {
-		GenericMenu menu = new GenericMenu();
-		AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue;
-		Atlas atlas = atlasAsset.GetAtlas();
-		FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
-		List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
-
-		for (int i = 0; i < regions.Count; i++) {
-			string name = regions[i].name;
-			menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
-		}
-
-
-		menu.ShowAsContext();
-	}
-
-	void HandleSelect(object val) {
-		var pair = (SpineDrawerValuePair)val;
-		pair.property.stringValue = pair.str;
-		pair.property.serializedObject.ApplyModifiedProperties();
-	}
-
-	public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
-		return 18;
-	}
 }
 }
 
 
 [CustomPropertyDrawer(typeof(SpineAnimation))]
 [CustomPropertyDrawer(typeof(SpineAnimation))]
-public class SpineAnimationDrawer : PropertyDrawer {
-	SkeletonDataAsset skeletonDataAsset;
-
-
-	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
-
-
-		if (property.propertyType != SerializedPropertyType.String) {
-			EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
-			return;
-		}
-
-		SpineAnimation attrib = (SpineAnimation)attribute;
-
-		var dataProperty = property.serializedObject.FindProperty(attrib.dataField);
-
-		if (dataProperty != null) {
-			if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
-				skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue;
-			} else if (dataProperty.objectReferenceValue is SkeletonRenderer) {
-				var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue;
-				if (renderer != null)
-					skeletonDataAsset = renderer.skeletonDataAsset;
-			} else {
-				EditorGUI.LabelField(position, "ERROR:", "Invalid reference type");
-				return;
-			}
-		} else if (property.serializedObject.targetObject is Component) {
-			var component = (Component)property.serializedObject.targetObject;
-			if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
-				var skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
-				skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
-			}
-		}
-
-		if (skeletonDataAsset == null) {
-			EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset");
-			return;
-		}
-
-		position = EditorGUI.PrefixLabel(position, label);
-
-		if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
-			Selector(property);
-		}
-
-	}
-
-	void Selector(SerializedProperty property) {
-
-		SpineAnimation attrib = (SpineAnimation)attribute;
-
-		GenericMenu menu = new GenericMenu();
-
+public class SpineAnimationDrawer : SpineTreeItemDrawerBase<SpineAnimation> {
+	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAnimation targetAttribute, SkeletonData data) {
 		var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations;
 		var animations = skeletonDataAsset.GetAnimationStateData().SkeletonData.Animations;
 		for (int i = 0; i < animations.Count; i++) {
 		for (int i = 0; i < animations.Count; i++) {
 			string name = animations.Items[i].Name;
 			string name = animations.Items[i].Name;
-			if (name.StartsWith(attrib.startsWith))
+			if (name.StartsWith(targetAttribute.startsWith))
 				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 		}
 		}
-
-		menu.ShowAsContext();
-	}
-
-	void HandleSelect(object val) {
-		var pair = (SpineDrawerValuePair)val;
-		pair.property.stringValue = pair.str;
-		pair.property.serializedObject.ApplyModifiedProperties();
 	}
 	}
 
 
-	public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
-		return 18;
-	}
 }
 }
 
 
-[CustomPropertyDrawer(typeof(SpineAttachment))]
-public class SpineAttachmentDrawer : PropertyDrawer {
-
-	SkeletonDataAsset skeletonDataAsset;
-	SkeletonRenderer skeletonRenderer;
-
-	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
-
-		if (property.propertyType != SerializedPropertyType.String) {
-			EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
-			return;
+[CustomPropertyDrawer(typeof(SpineEvent))]
+public class SpineEventNameDrawer : SpineTreeItemDrawerBase<SpineEvent> {
+	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineEvent targetAttribute, SkeletonData data) {
+		var events = skeletonDataAsset.GetSkeletonData(false).Events;
+		for (int i = 0; i < events.Count; i++) {
+			string name = events.Items[i].Name;
+			if (name.StartsWith(targetAttribute.startsWith))
+				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 		}
 		}
+	}
 
 
-		SpineAttachment attrib = (SpineAttachment)attribute;
-
-		var dataProperty = property.serializedObject.FindProperty(attrib.dataField);
+}
 
 
-		if (dataProperty != null) {
-			if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
-				skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue;
-			} else if (dataProperty.objectReferenceValue is SkeletonRenderer) {
-				var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue;
-				if (renderer != null)
-					skeletonDataAsset = renderer.skeletonDataAsset;
-				else {
-					EditorGUI.LabelField(position, "ERROR:", "No SkeletonRenderer");
-				}
-			} else {
-				EditorGUI.LabelField(position, "ERROR:", "Invalid reference type");
-				return;
-			}
+[CustomPropertyDrawer(typeof(SpineAttachment))]
+public class SpineAttachmentDrawer : SpineTreeItemDrawerBase<SpineAttachment> {
+	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineAttachment targetAttribute, SkeletonData data) {
+		List<Skin> validSkins = new List<Skin>();
+		SkeletonRenderer skeletonRenderer = null;
 
 
-		} else if (property.serializedObject.targetObject is Component) {
-			var component = (Component)property.serializedObject.targetObject;
+		var component = property.serializedObject.targetObject as Component;
+		if (component != null) {
 			if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
 			if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
 				skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
 				skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
+				//if (skeletonDataAsset != skeletonRenderer.skeletonDataAsset) Debug.LogWarning("DataField SkeletonDataAsset and SkeletonRenderer/SkeletonAnimation's SkeletonDataAsset do not match. Remove the explicit dataField parameter of your [SpineAttachment] field.");
 				skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
 				skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
 			}
 			}
 		}
 		}
 
 
-		if (skeletonDataAsset == null && skeletonRenderer == null) {
-			EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset or SkeletonRenderer");
-			return;
-		}
-
-		position = EditorGUI.PrefixLabel(position, label);
-
-		if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
-			Selector(property);
-		}
-
-	}
-
-	void Selector(SerializedProperty property) {
-		SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
-
-		if (data == null)
-			return;
-
-		SpineAttachment attrib = (SpineAttachment)attribute;
-
-		List<Skin> validSkins = new List<Skin>();
-
-		if (skeletonRenderer != null && attrib.currentSkinOnly) {
+		if (skeletonRenderer != null && targetAttribute.currentSkinOnly) {
 			if (skeletonRenderer.skeleton.Skin != null) {
 			if (skeletonRenderer.skeleton.Skin != null) {
 				validSkins.Add(skeletonRenderer.skeleton.Skin);
 				validSkins.Add(skeletonRenderer.skeleton.Skin);
 			} else {
 			} else {
@@ -424,62 +199,63 @@ public class SpineAttachmentDrawer : PropertyDrawer {
 					validSkins.Add(skin);
 					validSkins.Add(skin);
 			}
 			}
 		}
 		}
-
-		GenericMenu menu = new GenericMenu();
+		
 		List<string> attachmentNames = new List<string>();
 		List<string> attachmentNames = new List<string>();
 		List<string> placeholderNames = new List<string>();
 		List<string> placeholderNames = new List<string>();
-
+		
 		string prefix = "";
 		string prefix = "";
-
-		if (skeletonRenderer != null && attrib.currentSkinOnly)
+		
+		if (skeletonRenderer != null && targetAttribute.currentSkinOnly)
 			menu.AddDisabledItem(new GUIContent(skeletonRenderer.gameObject.name + " (SkeletonRenderer)"));
 			menu.AddDisabledItem(new GUIContent(skeletonRenderer.gameObject.name + " (SkeletonRenderer)"));
 		else
 		else
 			menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
 			menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
+		
 		menu.AddSeparator("");
 		menu.AddSeparator("");
-
+		
 		menu.AddItem(new GUIContent("Null"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property));
 		menu.AddItem(new GUIContent("Null"), property.stringValue == "", HandleSelect, new SpineDrawerValuePair("", property));
-		menu.AddSeparator("");
 
 
+		menu.AddSeparator("");
+		
 		Skin defaultSkin = data.Skins.Items[0];
 		Skin defaultSkin = data.Skins.Items[0];
-
-		SerializedProperty slotProperty = property.serializedObject.FindProperty(attrib.slotField);
+		
+		SerializedProperty slotProperty = property.serializedObject.FindProperty(targetAttribute.slotField);
 		string slotMatch = "";
 		string slotMatch = "";
 		if (slotProperty != null) {
 		if (slotProperty != null) {
 			if (slotProperty.propertyType == SerializedPropertyType.String) {
 			if (slotProperty.propertyType == SerializedPropertyType.String) {
 				slotMatch = slotProperty.stringValue.ToLower();
 				slotMatch = slotProperty.stringValue.ToLower();
 			}
 			}
 		}
 		}
-
+		
 		foreach (Skin skin in validSkins) {
 		foreach (Skin skin in validSkins) {
 			string skinPrefix = skin.Name + "/";
 			string skinPrefix = skin.Name + "/";
-
+			
 			if (validSkins.Count > 1)
 			if (validSkins.Count > 1)
 				prefix = skinPrefix;
 				prefix = skinPrefix;
-
+			
 			for (int i = 0; i < data.Slots.Count; i++) {
 			for (int i = 0; i < data.Slots.Count; i++) {
 				if (slotMatch.Length > 0 && data.Slots.Items[i].Name.ToLower().Contains(slotMatch) == false)
 				if (slotMatch.Length > 0 && data.Slots.Items[i].Name.ToLower().Contains(slotMatch) == false)
 					continue;
 					continue;
-
+				
 				attachmentNames.Clear();
 				attachmentNames.Clear();
 				placeholderNames.Clear();
 				placeholderNames.Clear();
-
+				
 				skin.FindNamesForSlot(i, attachmentNames);
 				skin.FindNamesForSlot(i, attachmentNames);
 				if (skin != defaultSkin) {
 				if (skin != defaultSkin) {
 					defaultSkin.FindNamesForSlot(i, attachmentNames);
 					defaultSkin.FindNamesForSlot(i, attachmentNames);
 					skin.FindNamesForSlot(i, placeholderNames);
 					skin.FindNamesForSlot(i, placeholderNames);
 				}
 				}
-					
-
+				
+				
 				for (int a = 0; a < attachmentNames.Count; a++) {
 				for (int a = 0; a < attachmentNames.Count; a++) {
 					
 					
 					string attachmentPath = attachmentNames[a];
 					string attachmentPath = attachmentNames[a];
 					string menuPath = prefix + data.Slots.Items[i].Name + "/" + attachmentPath;
 					string menuPath = prefix + data.Slots.Items[i].Name + "/" + attachmentPath;
 					string name = attachmentNames[a];
 					string name = attachmentNames[a];
-
-					if (attrib.returnAttachmentPath)
+					
+					if (targetAttribute.returnAttachmentPath)
 						name = skin.Name + "/" + data.Slots.Items[i].Name + "/" + attachmentPath;
 						name = skin.Name + "/" + data.Slots.Items[i].Name + "/" + attachmentPath;
-
-					if (attrib.placeholdersOnly && placeholderNames.Contains(attachmentPath) == false) {
+					
+					if (targetAttribute.placeholdersOnly && placeholderNames.Contains(attachmentPath) == false) {
 						menu.AddDisabledItem(new GUIContent(menuPath));
 						menu.AddDisabledItem(new GUIContent(menuPath));
 					} else {
 					} else {
 						menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 						menu.AddItem(new GUIContent(menuPath), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
@@ -489,96 +265,83 @@ public class SpineAttachmentDrawer : PropertyDrawer {
 				}
 				}
 			}
 			}
 		}
 		}
+	}
 
 
+}
 
 
-		menu.ShowAsContext();
-	}
+[CustomPropertyDrawer(typeof(SpineBone))]
+public class SpineBoneDrawer : SpineTreeItemDrawerBase<SpineBone> {
 
 
-	void HandleSelect(object val) {
-		var pair = (SpineDrawerValuePair)val;
-		pair.property.stringValue = pair.str;
-		pair.property.serializedObject.ApplyModifiedProperties();
+	protected override void PopulateMenu (GenericMenu menu, SerializedProperty property, SpineBone targetAttribute, SkeletonData data) {
+		menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
+		menu.AddSeparator("");
+		
+		for (int i = 0; i < data.Bones.Count; i++) {
+			string name = data.Bones.Items[i].Name;
+			if (name.StartsWith(targetAttribute.startsWith))
+				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+		}
 	}
 	}
 
 
-	public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
-		return 18;
-	}
 }
 }
 
 
-[CustomPropertyDrawer(typeof(SpineBone))]
-public class SpineBoneDrawer : PropertyDrawer {
-	SkeletonDataAsset skeletonDataAsset;
-
+[CustomPropertyDrawer(typeof(SpineAtlasRegion))]
+public class SpineAtlasRegionDrawer : PropertyDrawer {
+	Component component;
+	SerializedProperty atlasProp;
+	
 	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
 	public override void OnGUI(Rect position, SerializedProperty property, GUIContent label) {
 		if (property.propertyType != SerializedPropertyType.String) {
 		if (property.propertyType != SerializedPropertyType.String) {
 			EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
 			EditorGUI.LabelField(position, "ERROR:", "May only apply to type string");
 			return;
 			return;
 		}
 		}
-
-		SpineBone attrib = (SpineBone)attribute;
-
-		var dataProperty = property.serializedObject.FindProperty(attrib.dataField);
-
-		if (dataProperty != null) {
-			if (dataProperty.objectReferenceValue is SkeletonDataAsset) {
-				skeletonDataAsset = (SkeletonDataAsset)dataProperty.objectReferenceValue;
-			} else if (dataProperty.objectReferenceValue is SkeletonRenderer) {
-				var renderer = (SkeletonRenderer)dataProperty.objectReferenceValue;
-				if (renderer != null)
-					skeletonDataAsset = renderer.skeletonDataAsset;
-			} else {
-				EditorGUI.LabelField(position, "ERROR:", "Invalid reference type");
-				return;
-			}
-
-		} else if (property.serializedObject.targetObject is Component) {
-			var component = (Component)property.serializedObject.targetObject;
-			if (component.GetComponentInChildren<SkeletonRenderer>() != null) {
-				var skeletonRenderer = component.GetComponentInChildren<SkeletonRenderer>();
-				skeletonDataAsset = skeletonRenderer.skeletonDataAsset;
-			}
-		}
-
-		if (skeletonDataAsset == null) {
-			EditorGUI.LabelField(position, "ERROR:", "Must have reference to a SkeletonDataAsset");
+		
+		component = (Component)property.serializedObject.targetObject;
+		
+		if (component != null)
+			atlasProp = property.serializedObject.FindProperty("atlasAsset");
+		else
+			atlasProp = null;
+		
+		
+		if (atlasProp == null) {
+			EditorGUI.LabelField(position, "ERROR:", "Must have AtlasAsset variable!");
+			return;
+		} else if (atlasProp.objectReferenceValue == null) {
+			EditorGUI.LabelField(position, "ERROR:", "Atlas variable must not be null!");
 			return;
 			return;
+		} else if (atlasProp.objectReferenceValue.GetType() != typeof(AtlasAsset)) {
+			EditorGUI.LabelField(position, "ERROR:", "Atlas variable must be of type AtlasAsset!");
 		}
 		}
-
+		
 		position = EditorGUI.PrefixLabel(position, label);
 		position = EditorGUI.PrefixLabel(position, label);
-
+		
 		if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
 		if (GUI.Button(position, property.stringValue, EditorStyles.popup)) {
 			Selector(property);
 			Selector(property);
 		}
 		}
-
+		
 	}
 	}
-
-	void Selector(SerializedProperty property) {
-		SpineBone attrib = (SpineBone)attribute;
-		SkeletonData data = skeletonDataAsset.GetSkeletonData(true);
-		if (data == null)
-			return;
-
+	
+	void Selector (SerializedProperty property) {
 		GenericMenu menu = new GenericMenu();
 		GenericMenu menu = new GenericMenu();
-
-		menu.AddDisabledItem(new GUIContent(skeletonDataAsset.name));
-		menu.AddSeparator("");
-
-		for (int i = 0; i < data.Bones.Count; i++) {
-			string name = data.Bones.Items[i].Name;
-			if (name.StartsWith(attrib.startsWith))
-				menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
+		AtlasAsset atlasAsset = (AtlasAsset)atlasProp.objectReferenceValue;
+		Atlas atlas = atlasAsset.GetAtlas();
+		FieldInfo field = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.NonPublic);
+		List<AtlasRegion> regions = (List<AtlasRegion>)field.GetValue(atlas);
+		
+		for (int i = 0; i < regions.Count; i++) {
+			string name = regions[i].name;
+			menu.AddItem(new GUIContent(name), name == property.stringValue, HandleSelect, new SpineDrawerValuePair(name, property));
 		}
 		}
-
+		
+		
 		menu.ShowAsContext();
 		menu.ShowAsContext();
 	}
 	}
-
-	void HandleSelect(object val) {
+	
+	static void HandleSelect (object val) {
 		var pair = (SpineDrawerValuePair)val;
 		var pair = (SpineDrawerValuePair)val;
 		pair.property.stringValue = pair.str;
 		pair.property.stringValue = pair.str;
 		pair.property.serializedObject.ApplyModifiedProperties();
 		pair.property.serializedObject.ApplyModifiedProperties();
 	}
 	}
-
-	public override float GetPropertyHeight(SerializedProperty property, GUIContent label) {
-		return 18;
-	}
-}
+	
+}

+ 13 - 7
spine-unity/Assets/spine-unity/Ghost/SkeletonGhost.cs

@@ -11,7 +11,7 @@ using System.Collections.Generic;
 public class SkeletonGhost : MonoBehaviour {
 public class SkeletonGhost : MonoBehaviour {
 	public bool ghostingEnabled = true;
 	public bool ghostingEnabled = true;
 	public float spawnRate = 0.05f;
 	public float spawnRate = 0.05f;
-	public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00);
+	public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00); // default for additive.
 	[Tooltip("Remember to set color alpha to 0 if Additive is true")]
 	[Tooltip("Remember to set color alpha to 0 if Additive is true")]
 	public bool additive = true;
 	public bool additive = true;
 	public int maximumGhosts = 10;
 	public int maximumGhosts = 10;
@@ -21,6 +21,10 @@ public class SkeletonGhost : MonoBehaviour {
 	[Range(0, 1)]
 	[Range(0, 1)]
 	public float textureFade = 1;
 	public float textureFade = 1;
 
 
+	[Header("Sorting")]
+	public bool sortWithDistanceOnly;
+	public float zOffset = 0f;
+
 	float nextSpawnTime;
 	float nextSpawnTime;
 	SkeletonGhostRenderer[] pool;
 	SkeletonGhostRenderer[] pool;
 	int poolIndex = 0;
 	int poolIndex = 0;
@@ -100,14 +104,16 @@ public class SkeletonGhost : MonoBehaviour {
 				materials[i] = ghostMat;
 				materials[i] = ghostMat;
 			}
 			}
 
 
-			pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingOrder - 1);
-			go.transform.parent = transform;
+			var goTransform = go.transform;
+			goTransform.parent = transform;
+
+			pool[poolIndex].Initialize(meshFilter.sharedMesh, materials, color, additive, fadeSpeed, meshRenderer.sortingLayerID, (sortWithDistanceOnly) ? meshRenderer.sortingOrder : meshRenderer.sortingOrder - 1);
 
 
-			go.transform.localPosition = Vector3.zero;
-			go.transform.localRotation = Quaternion.identity;
-			go.transform.localScale = Vector3.one;
+			goTransform.localPosition = new Vector3(0f, 0f, zOffset);
+			goTransform.localRotation = Quaternion.identity;
+			goTransform.localScale = Vector3.one;
 
 
-			go.transform.parent = null;
+			goTransform.parent = null;
 
 
 			poolIndex++;
 			poolIndex++;
 
 

+ 2 - 1
spine-unity/Assets/spine-unity/Ghost/SkeletonGhostRenderer.cs

@@ -20,13 +20,14 @@ public class SkeletonGhostRenderer : MonoBehaviour {
 		meshFilter = gameObject.AddComponent<MeshFilter>();
 		meshFilter = gameObject.AddComponent<MeshFilter>();
 	}
 	}
 
 
-	public void Initialize (Mesh mesh, Material[] materials, Color32 color, bool additive, float speed, int sortingOrder) {
+	public void Initialize (Mesh mesh, Material[] materials, Color32 color, bool additive, float speed, int sortingLayerID, int sortingOrder) {
 		StopAllCoroutines();
 		StopAllCoroutines();
 
 
 		gameObject.SetActive(true);
 		gameObject.SetActive(true);
 
 
 
 
 		meshRenderer.sharedMaterials = materials;
 		meshRenderer.sharedMaterials = materials;
+		meshRenderer.sortingLayerID = sortingLayerID;
 		meshRenderer.sortingOrder = sortingOrder;
 		meshRenderer.sortingOrder = sortingOrder;
 
 
 		meshFilter.sharedMesh = (Mesh)Instantiate(mesh);
 		meshFilter.sharedMesh = (Mesh)Instantiate(mesh);

+ 67 - 10
spine-unity/Assets/spine-unity/SkeletonAnimation.cs

@@ -30,7 +30,6 @@
  *****************************************************************************/
  *****************************************************************************/
 
 
 using System;
 using System;
-using System.IO;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine;
 using Spine;
 using Spine;
@@ -38,11 +37,11 @@ using Spine;
 [ExecuteInEditMode]
 [ExecuteInEditMode]
 [AddComponentMenu("Spine/SkeletonAnimation")]
 [AddComponentMenu("Spine/SkeletonAnimation")]
 public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
 public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
-	public float timeScale = 1;
-	public bool loop;
-	public Spine.AnimationState state;
-
 
 
+	/// <summary>
+	/// This is the Spine.AnimationState object of this SkeletonAnimation. You can control animations through it. 
+	/// Note that this object, like .skeleton, is not guaranteed to exist in Awake. Do all accesses and caching to it in Start</summary>
+	public Spine.AnimationState state;
 
 
 	public event UpdateBonesDelegate UpdateLocal {
 	public event UpdateBonesDelegate UpdateLocal {
 		add { _UpdateLocal += value; }
 		add { _UpdateLocal += value; }
@@ -63,18 +62,21 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
 	protected event UpdateBonesDelegate _UpdateWorld;
 	protected event UpdateBonesDelegate _UpdateWorld;
 	protected event UpdateBonesDelegate _UpdateComplete;
 	protected event UpdateBonesDelegate _UpdateComplete;
 
 
+	// TODO: Make this a safe getter. Lazy-initialize and avoid double-initialization.
 	public Skeleton Skeleton {
 	public Skeleton Skeleton {
-		get {
-			return this.skeleton;
-		}
+		get { return this.skeleton; }
 	}
 	}
 
 
 	[SerializeField]
 	[SerializeField]
-	private String
-		_animationName;
+	private String _animationName;
 
 
 	public String AnimationName {
 	public String AnimationName {
 		get {
 		get {
+			if (!valid) {
+				Debug.LogWarning("You tried access AnimationName but the SkeletonAnimation was not valid. Try checking your Skeleton Data for errors.");
+				return null;
+			}
+			
 			TrackEntry entry = state.GetCurrent(0);
 			TrackEntry entry = state.GetCurrent(0);
 			return entry == null ? null : entry.Animation.Name;
 			return entry == null ? null : entry.Animation.Name;
 		}
 		}
@@ -82,6 +84,12 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
 			if (_animationName == value)
 			if (_animationName == value)
 				return;
 				return;
 			_animationName = value;
 			_animationName = value;
+			
+			if (!valid) {
+				Debug.LogWarning("You tried to change AnimationName but the SkeletonAnimation was not valid. Try checking your Skeleton Data for errors.");
+				return;
+			}
+			
 			if (value == null || value.Length == 0)
 			if (value == null || value.Length == 0)
 				state.ClearTrack(0);
 				state.ClearTrack(0);
 			else
 			else
@@ -89,12 +97,61 @@ public class SkeletonAnimation : SkeletonRenderer, ISkeletonAnimation {
 		}
 		}
 	}
 	}
 
 
+	/// <summary>Whether or not an animation should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.</summary>
+	#if UNITY_5
+	[Tooltip("Whether or not an animation should loop. This only applies to the initial animation specified in the inspector, or any subsequent Animations played through .AnimationName. Animations set through state.SetAnimation are unaffected.")]
+	#endif
+	public bool loop;
+
+	/// <summary>
+	/// The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.
+	/// AnimationState and TrackEntry also have their own timeScale. These are combined multiplicatively.</summary>
+	#if UNITY_5
+	[Tooltip("The rate at which animations progress over time. 1 means 100%. 0.5 means 50%.")]
+	#endif
+	public float timeScale = 1;
+
+	#region AutoReset
+	/**
+	[Tooltip("Setting this to true makes the SkeletonAnimation behave similar to Spine editor. New animations will not inherit the pose from a previous animation. If you need to intermittently and programmatically pose your skeleton, leave this false.")]
+	[SerializeField]
+	protected bool autoReset = false;
+
+	/// <summary>
+	/// Setting this to true makes the SkeletonAnimation behave similar to Spine editor. 
+	/// New animations will not inherit the pose from a previous animation. 
+	/// If you need to intermittently and programmatically pose your skeleton, leave this false.</summary>
+	public bool AutoReset {
+		get { return this.autoReset; }
+		set {
+			if (!autoReset && value) {
+				state.Start -= HandleNewAnimationAutoreset;	// make sure there isn't a double-subscription.
+				state.Start += HandleNewAnimationAutoreset;
+			}
+			autoReset = value;
+		}
+	}
+
+	protected virtual void HandleNewAnimationAutoreset (Spine.AnimationState state, int trackIndex) {
+		if (!autoReset) return;
+		if (skeleton != null) skeleton.SetToSetupPose();
+	}
+	*/
+	#endregion
+
 	public override void Reset () {
 	public override void Reset () {
 		base.Reset();
 		base.Reset();
 		if (!valid)
 		if (!valid)
 			return;
 			return;
 
 
 		state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
 		state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
+
+		/*
+		if (autoReset) {
+			state.Start += HandleNewAnimationAutoreset;
+		}
+		*/
+
 		if (_animationName != null && _animationName.Length > 0) {
 		if (_animationName != null && _animationName.Length > 0) {
 			state.SetAnimation(0, _animationName, loop);
 			state.SetAnimation(0, _animationName, loop);
 			Update(0);
 			Update(0);

+ 5 - 0
spine-unity/Assets/spine-unity/SkeletonDataAsset.cs

@@ -50,6 +50,11 @@ public class SkeletonDataAsset : ScriptableObject {
 	private SkeletonData skeletonData;
 	private SkeletonData skeletonData;
 	private AnimationStateData stateData;
 	private AnimationStateData stateData;
 
 
+	void OnEnable () {
+		if (atlasAssets == null)
+			atlasAssets = new AtlasAsset[0];
+	}
+
 	public void Reset () {
 	public void Reset () {
 		skeletonData = null;
 		skeletonData = null;
 		stateData = null;
 		stateData = null;

+ 191 - 149
spine-unity/Assets/spine-unity/SkeletonRenderer.cs

@@ -39,39 +39,46 @@ using Spine;
 public class SkeletonRenderer : MonoBehaviour {
 public class SkeletonRenderer : MonoBehaviour {
 
 
 	public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer);
 	public delegate void SkeletonRendererDelegate (SkeletonRenderer skeletonRenderer);
-
 	public SkeletonRendererDelegate OnReset;
 	public SkeletonRendererDelegate OnReset;
-	[System.NonSerialized]
-	public bool valid;
-	[System.NonSerialized]
-	public Skeleton skeleton;
+
 	public SkeletonDataAsset skeletonDataAsset;
 	public SkeletonDataAsset skeletonDataAsset;
 	public String initialSkinName;
 	public String initialSkinName;
+
+	#region Advanced
 	public bool calculateNormals, calculateTangents;
 	public bool calculateNormals, calculateTangents;
 	public float zSpacing;
 	public float zSpacing;
 	public bool renderMeshes = true, immutableTriangles;
 	public bool renderMeshes = true, immutableTriangles;
 	public bool frontFacing;
 	public bool frontFacing;
 	public bool logErrors = false;
 	public bool logErrors = false;
 
 
-	[SpineSlot]
-	public string[] submeshSeparators = new string[0];
+	// Submesh Separation
+	[SpineSlot] public string[] submeshSeparators = new string[0];
+	[HideInInspector] public List<Slot> submeshSeparatorSlots = new List<Slot>();
+	#endregion
 
 
-	[HideInInspector]
-	public List<Slot> submeshSeparatorSlots = new List<Slot>();
+	[System.NonSerialized] public bool valid;
+	[System.NonSerialized] public Skeleton skeleton;
 
 
 	private MeshRenderer meshRenderer;
 	private MeshRenderer meshRenderer;
 	private MeshFilter meshFilter;
 	private MeshFilter meshFilter;
+
 	private Mesh mesh1, mesh2;
 	private Mesh mesh1, mesh2;
 	private bool useMesh1;
 	private bool useMesh1;
+
 	private float[] tempVertices = new float[8];
 	private float[] tempVertices = new float[8];
 	private Vector3[] vertices;
 	private Vector3[] vertices;
 	private Color32[] colors;
 	private Color32[] colors;
 	private Vector2[] uvs;
 	private Vector2[] uvs;
 	private Material[] sharedMaterials = new Material[0];
 	private Material[] sharedMaterials = new Material[0];
+
+	private MeshState meshState = new MeshState();
 	private readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>();
 	private readonly ExposedList<Material> submeshMaterials = new ExposedList<Material>();
 	private readonly ExposedList<Submesh> submeshes = new ExposedList<Submesh>();
 	private readonly ExposedList<Submesh> submeshes = new ExposedList<Submesh>();
 	private SkeletonUtilitySubmeshRenderer[] submeshRenderers;
 	private SkeletonUtilitySubmeshRenderer[] submeshRenderers;
-	private MeshState meshState = new MeshState();
+
+	public virtual void Awake () {
+		Reset();
+	}
 
 
 	public virtual void Reset () {
 	public virtual void Reset () {
 		if (meshFilter != null)
 		if (meshFilter != null)
@@ -144,10 +151,6 @@ public class SkeletonRenderer : MonoBehaviour {
 		submeshRenderers = GetComponentsInChildren<SkeletonUtilitySubmeshRenderer>();
 		submeshRenderers = GetComponentsInChildren<SkeletonUtilitySubmeshRenderer>();
 	}
 	}
 
 
-	public virtual void Awake () {
-		Reset();
-	}
-
 	public virtual void OnDestroy () {
 	public virtual void OnDestroy () {
 		if (mesh1 != null) {
 		if (mesh1 != null) {
 			if (Application.isPlaying)
 			if (Application.isPlaying)
@@ -167,7 +170,7 @@ public class SkeletonRenderer : MonoBehaviour {
 		mesh2 = null;
 		mesh2 = null;
 	}
 	}
 
 
-	private Mesh newMesh () {
+	private static Mesh newMesh () {
 		Mesh mesh = new Mesh();
 		Mesh mesh = new Mesh();
 		mesh.name = "Skeleton Mesh";
 		mesh.name = "Skeleton Mesh";
 		mesh.hideFlags = HideFlags.HideAndDontSave;
 		mesh.hideFlags = HideFlags.HideAndDontSave;
@@ -185,35 +188,59 @@ public class SkeletonRenderer : MonoBehaviour {
 
 
 		// Count vertices and submesh triangles.
 		// Count vertices and submesh triangles.
 		int vertexCount = 0;
 		int vertexCount = 0;
+
 		int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0;
 		int submeshTriangleCount = 0, submeshFirstVertex = 0, submeshStartSlotIndex = 0;
 		Material lastMaterial = null;
 		Material lastMaterial = null;
 		ExposedList<Slot> drawOrder = skeleton.drawOrder;
 		ExposedList<Slot> drawOrder = skeleton.drawOrder;
+		var drawOrderItems = drawOrder.Items;
 		int drawOrderCount = drawOrder.Count;
 		int drawOrderCount = drawOrder.Count;
 		int submeshSeparatorSlotsCount = submeshSeparatorSlots.Count;
 		int submeshSeparatorSlotsCount = submeshSeparatorSlots.Count;
 		bool renderMeshes = this.renderMeshes;
 		bool renderMeshes = this.renderMeshes;
 
 
 		// Clear last state of attachments and submeshes
 		// Clear last state of attachments and submeshes
-		MeshState.SingleMeshState stateTemp = meshState.stateTemp;
-		stateTemp.attachments.Clear(true);
-		stateTemp.UpdateDrawOrderCount(drawOrderCount);
+		MeshState.SingleMeshState workingState = meshState.buffer;
+		var workingAttachments = workingState.attachments;
+		workingAttachments.Clear(true);
+		workingState.UpdateAttachmentCount(drawOrderCount);
+		var workingAttachmentsItems = workingAttachments.Items;					// Make sure to not add to or remove from ExposedList inside the loop below
+
+		var workingFlips = workingState.attachmentsFlipState;
+		var workingFlipsItems = workingState.attachmentsFlipState.Items;		// Make sure to not add to or remove from ExposedList inside the loop below
+
+		var workingSubmeshArguments = workingState.addSubmeshArguments;	// Items array should not be cached. There is dynamic writing to this object.
+		workingSubmeshArguments.Clear(false);
+
+		MeshState.SingleMeshState storedState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2;
+		var storedAttachments = storedState.attachments;
+		var storedAttachmentsItems = storedAttachments.Items;		// Make sure to not add to or remove from ExposedList inside the loop below
+
+		var storedFlips = storedState.attachmentsFlipState;
+		var storedFlipsItems = storedFlips.Items;					// Make sure to not add to or remove from ExposedList inside the loop below
+
+		bool mustUpdateMeshStructure = storedState.requiresUpdate ||	// Force update if the mesh was cleared. (prevents flickering due to incorrect state)
+			drawOrder.Count != storedAttachments.Count ||				// Number of slots changed (when does this happen?)
+			immutableTriangles != storedState.immutableTriangles;		// Immutable Triangles flag changed.
 
 
-		stateTemp.addSubmeshArguments.Clear(false);
 		for (int i = 0; i < drawOrderCount; i++) {
 		for (int i = 0; i < drawOrderCount; i++) {
-			Slot slot = drawOrder.Items[i];
+			Slot slot = drawOrderItems[i];
 			Bone bone = slot.bone;
 			Bone bone = slot.bone;
 			Attachment attachment = slot.attachment;
 			Attachment attachment = slot.attachment;
 
 
-			object rendererObject;
+			object rendererObject; // An AtlasRegion in plain Spine-Unity. Spine-TK2D hooks into TK2D's system. eventual source of Material object.
 			int attachmentVertexCount, attachmentTriangleCount;
 			int attachmentVertexCount, attachmentTriangleCount;
-			bool worldScaleXIsPositive = bone.worldScaleX >= 0f;
-			bool worldScaleYIsPositive = bone.worldScaleY >= 0f;
-			bool worldScaleIsSameSigns = (worldScaleXIsPositive && worldScaleYIsPositive) ||
-										 (!worldScaleXIsPositive && !worldScaleYIsPositive);
-			bool flip = frontFacing && ((bone.worldFlipX != bone.worldFlipY) == worldScaleIsSameSigns);
-			stateTemp.attachmentsFlipState.Items[i] = flip;
-
-			stateTemp.attachments.Items[i] = attachment;
-			RegionAttachment regionAttachment = attachment as RegionAttachment;
+
+			// Handle flipping for normals (for lighting).
+			bool worldScaleIsSameSigns = ((bone.worldScaleY >= 0f) == (bone.worldScaleX >= 0f));
+			bool flip = frontFacing && ((bone.worldFlipX != bone.worldFlipY) == worldScaleIsSameSigns); // TODO: bone flipX and flipY will be removed in Spine 3.0
+
+			workingFlipsItems[i] = flip;
+			workingAttachmentsItems[i] = attachment;
+
+			mustUpdateMeshStructure = mustUpdateMeshStructure ||	// Always prefer short circuited or. || and not |=.
+				(attachment != storedAttachmentsItems[i]) || 		// Attachment order changed. // This relies on the drawOrder.Count != storedAttachments.Count check above as a bounds check.
+				(flip != storedFlipsItems[i]);						// Flip states changed.
+
+			var regionAttachment = attachment as RegionAttachment;
 			if (regionAttachment != null) {
 			if (regionAttachment != null) {
 				rendererObject = regionAttachment.RendererObject;
 				rendererObject = regionAttachment.RendererObject;
 				attachmentVertexCount = 4;
 				attachmentVertexCount = 4;
@@ -221,13 +248,13 @@ public class SkeletonRenderer : MonoBehaviour {
 			} else {
 			} else {
 				if (!renderMeshes)
 				if (!renderMeshes)
 					continue;
 					continue;
-				MeshAttachment meshAttachment = attachment as MeshAttachment;
+				var meshAttachment = attachment as MeshAttachment;
 				if (meshAttachment != null) {
 				if (meshAttachment != null) {
 					rendererObject = meshAttachment.RendererObject;
 					rendererObject = meshAttachment.RendererObject;
 					attachmentVertexCount = meshAttachment.vertices.Length >> 1;
 					attachmentVertexCount = meshAttachment.vertices.Length >> 1;
 					attachmentTriangleCount = meshAttachment.triangles.Length;
 					attachmentTriangleCount = meshAttachment.triangles.Length;
 				} else {
 				} else {
-					SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment;
+					var skinnedMeshAttachment = attachment as SkinnedMeshAttachment;
 					if (skinnedMeshAttachment != null) {
 					if (skinnedMeshAttachment != null) {
 						rendererObject = skinnedMeshAttachment.RendererObject;
 						rendererObject = skinnedMeshAttachment.RendererObject;
 						attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1;
 						attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1;
@@ -237,17 +264,27 @@ public class SkeletonRenderer : MonoBehaviour {
 				}
 				}
 			}
 			}
 
 
-			// Populate submesh when material changes.
-#if !SPINE_TK2D
+			#if !SPINE_TK2D
 			Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
 			Material material = (Material)((AtlasRegion)rendererObject).page.rendererObject;
-#else
+			#else
 			Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject;
 			Material material = (rendererObject.GetType() == typeof(Material)) ? (Material)rendererObject : (Material)((AtlasRegion)rendererObject).page.rendererObject;
-#endif
+			#endif
+
+			// Populate submesh when material changes. (or when forced to separate by a submeshSeparator)
 			if ((lastMaterial != null && lastMaterial.GetInstanceID() != material.GetInstanceID()) ||
 			if ((lastMaterial != null && lastMaterial.GetInstanceID() != material.GetInstanceID()) ||
 				(submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot))) {
 				(submeshSeparatorSlotsCount > 0 && submeshSeparatorSlots.Contains(slot))) {
-				stateTemp.addSubmeshArguments.Add(
-					new MeshState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, i, submeshTriangleCount, submeshFirstVertex, false)
-					);
+
+				workingSubmeshArguments.Add(
+					new MeshState.AddSubmeshArguments {
+						material = lastMaterial,
+						startSlot = submeshStartSlotIndex,
+						endSlot = i,
+						triangleCount = submeshTriangleCount,
+						firstVertex = submeshFirstVertex,
+						isLastSubmesh = false
+					}
+				);
+
 				submeshTriangleCount = 0;
 				submeshTriangleCount = 0;
 				submeshFirstVertex = vertexCount;
 				submeshFirstVertex = vertexCount;
 				submeshStartSlotIndex = i;
 				submeshStartSlotIndex = i;
@@ -257,24 +294,43 @@ public class SkeletonRenderer : MonoBehaviour {
 			submeshTriangleCount += attachmentTriangleCount;
 			submeshTriangleCount += attachmentTriangleCount;
 			vertexCount += attachmentVertexCount;
 			vertexCount += attachmentVertexCount;
 		}
 		}
-		stateTemp.addSubmeshArguments.Add(
-			new MeshState.AddSubmeshArguments(lastMaterial, submeshStartSlotIndex, drawOrderCount, submeshTriangleCount, submeshFirstVertex, true)
-			);
 
 
-		bool mustUpdateMeshStructure = CheckIfMustUpdateMeshStructure(stateTemp.attachments, stateTemp.attachmentsFlipState, stateTemp.addSubmeshArguments);
+
+		workingSubmeshArguments.Add(
+			new MeshState.AddSubmeshArguments {
+				material = lastMaterial,
+				startSlot = submeshStartSlotIndex,
+				endSlot = drawOrderCount,
+				triangleCount = submeshTriangleCount,
+				firstVertex = submeshFirstVertex,
+				isLastSubmesh = true
+			}
+		);
+
+		mustUpdateMeshStructure = mustUpdateMeshStructure ||
+			this.sharedMaterials.Length != workingSubmeshArguments.Count ||		// Material array changed in size
+			CheckIfMustUpdateMeshStructure(workingSubmeshArguments);			// Submesh Argument Array changed.
+
+		// CheckIfMustUpdateMaterialArray (workingMaterials, sharedMaterials)
+		if (!mustUpdateMeshStructure) {
+			// Narrow phase material array check.
+			var workingMaterials = workingSubmeshArguments.Items;
+			for (int i = 0, n = sharedMaterials.Length; i < n; i++) {
+				if (this.sharedMaterials[i] != workingMaterials[i].material) {	// Bounds check is implied above.
+					mustUpdateMeshStructure = true;
+					break;
+				}
+			}
+		}
+
+		// NOT ELSE
+
 		if (mustUpdateMeshStructure) {
 		if (mustUpdateMeshStructure) {
-			submeshMaterials.Clear();
-			for (int i = 0, n = stateTemp.addSubmeshArguments.Count; i < n; i++) {
-				MeshState.AddSubmeshArguments arguments = stateTemp.addSubmeshArguments.Items[i];
-				AddSubmesh(
-					arguments.material,
-					arguments.startSlot,
-					arguments.endSlot,
-					arguments.triangleCount,
-					arguments.firstVertex,
-					arguments.lastSubmesh,
-					stateTemp.attachmentsFlipState
-				);
+			this.submeshMaterials.Clear();
+
+			var workingSubmeshArgumentsItems = workingSubmeshArguments.Items;
+			for (int i = 0, n = workingSubmeshArguments.Count; i < n; i++) {
+				AddSubmesh(workingSubmeshArgumentsItems[i], workingFlips);
 			}
 			}
 
 
 			// Set materials.
 			// Set materials.
@@ -286,6 +342,7 @@ public class SkeletonRenderer : MonoBehaviour {
 			meshRenderer.sharedMaterials = sharedMaterials;
 			meshRenderer.sharedMaterials = sharedMaterials;
 		}
 		}
 
 
+
 		// Ensure mesh data is the right size.
 		// Ensure mesh data is the right size.
 		Vector3[] vertices = this.vertices;
 		Vector3[] vertices = this.vertices;
 		bool newTriangles = vertexCount > vertices.Length;
 		bool newTriangles = vertexCount > vertices.Length;
@@ -294,8 +351,12 @@ public class SkeletonRenderer : MonoBehaviour {
 			this.vertices = vertices = new Vector3[vertexCount];
 			this.vertices = vertices = new Vector3[vertexCount];
 			this.colors = new Color32[vertexCount];
 			this.colors = new Color32[vertexCount];
 			this.uvs = new Vector2[vertexCount];
 			this.uvs = new Vector2[vertexCount];
+
 			mesh1.Clear();
 			mesh1.Clear();
 			mesh2.Clear();
 			mesh2.Clear();
+			meshState.stateMesh1.requiresUpdate = true;
+			meshState.stateMesh2.requiresUpdate = true;
+
 		} else {
 		} else {
 			// Too many vertices, zero the extra.
 			// Too many vertices, zero the extra.
 			Vector3 zero = Vector3.zero;
 			Vector3 zero = Vector3.zero;
@@ -332,7 +393,7 @@ public class SkeletonRenderer : MonoBehaviour {
 			}
 			}
 			int i = 0;
 			int i = 0;
 			do {
 			do {
-				Slot slot = drawOrder.Items[i];
+				Slot slot = drawOrderItems[i];
 				Attachment attachment = slot.attachment;
 				Attachment attachment = slot.attachment;
 				RegionAttachment regionAttachment = attachment as RegionAttachment;
 				RegionAttachment regionAttachment = attachment as RegionAttachment;
 				if (regionAttachment != null) {
 				if (regionAttachment != null) {
@@ -502,6 +563,9 @@ public class SkeletonRenderer : MonoBehaviour {
 			mesh.subMeshCount = submeshCount;
 			mesh.subMeshCount = submeshCount;
 			for (int i = 0; i < submeshCount; ++i)
 			for (int i = 0; i < submeshCount; ++i)
 				mesh.SetTriangles(submeshes.Items[i].triangles, i);
 				mesh.SetTriangles(submeshes.Items[i].triangles, i);
+
+			// Done updating mesh.
+			storedState.requiresUpdate = false;
 		}
 		}
 
 
 		Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin;
 		Vector3 meshBoundsExtents = meshBoundsMax - meshBoundsMin;
@@ -526,24 +590,25 @@ public class SkeletonRenderer : MonoBehaviour {
 				mesh2.tangents = tangents;
 				mesh2.tangents = tangents;
 			}
 			}
 		}
 		}
-
+			
 		// Update previous state
 		// Update previous state
-		MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2;
-		currentMeshState.immutableTriangles = immutableTriangles;
+		storedState.immutableTriangles = immutableTriangles;
+
+		storedAttachments.Clear(true);
+		storedAttachments.GrowIfNeeded(workingAttachments.Capacity);
+		storedAttachments.Count = workingAttachments.Count;
+		workingAttachments.CopyTo(storedAttachments.Items);
 
 
-		currentMeshState.attachments.Clear(true);
-		currentMeshState.attachments.GrowIfNeeded(stateTemp.attachments.Capacity);
-		currentMeshState.attachments.Count = stateTemp.attachments.Count;
-		stateTemp.attachments.CopyTo(currentMeshState.attachments.Items);
+		storedFlips.GrowIfNeeded(workingFlips.Capacity);
+		storedFlips.Count = workingFlips.Count;
+		workingFlips.CopyTo(storedFlips.Items);
 
 
-		currentMeshState.attachmentsFlipState.GrowIfNeeded(stateTemp.attachmentsFlipState.Capacity);
-		currentMeshState.attachmentsFlipState.Count = stateTemp.attachmentsFlipState.Count;
-		stateTemp.attachmentsFlipState.CopyTo(currentMeshState.attachmentsFlipState.Items);
+		storedState.addSubmeshArguments.GrowIfNeeded(workingSubmeshArguments.Capacity);
+		storedState.addSubmeshArguments.Count = workingSubmeshArguments.Count;
+		workingSubmeshArguments.CopyTo(storedState.addSubmeshArguments.Items);
 
 
-		currentMeshState.addSubmeshArguments.GrowIfNeeded(stateTemp.addSubmeshArguments.Capacity);
-		currentMeshState.addSubmeshArguments.Count = stateTemp.addSubmeshArguments.Count;
-		stateTemp.addSubmeshArguments.CopyTo(currentMeshState.addSubmeshArguments.Items);
 
 
+		// Submesh Renderers
 		if (submeshRenderers.Length > 0) {
 		if (submeshRenderers.Length > 0) {
 			for (int i = 0; i < submeshRenderers.Length; i++) {
 			for (int i = 0; i < submeshRenderers.Length; i++) {
 				SkeletonUtilitySubmeshRenderer submeshRenderer = submeshRenderers[i];
 				SkeletonUtilitySubmeshRenderer submeshRenderer = submeshRenderers[i];
@@ -558,87 +623,65 @@ public class SkeletonRenderer : MonoBehaviour {
 		useMesh1 = !useMesh1;
 		useMesh1 = !useMesh1;
 	}
 	}
 
 
-	private bool CheckIfMustUpdateMeshStructure (ExposedList<Attachment> attachmentsTemp, ExposedList<bool> attachmentsFlipStateTemp, ExposedList<MeshState.AddSubmeshArguments> addSubmeshArgumentsTemp) {
-#if UNITY_EDITOR
+	private bool CheckIfMustUpdateMeshStructure (ExposedList<MeshState.AddSubmeshArguments> workingAddSubmeshArguments) {
+		#if UNITY_EDITOR
 		if (!Application.isPlaying)
 		if (!Application.isPlaying)
 			return true;
 			return true;
-#endif
+		#endif
 
 
 		// Check if any mesh settings were changed
 		// Check if any mesh settings were changed
-		bool mustUpdateMeshStructure =
-			immutableTriangles != (useMesh1 ? meshState.stateMesh1.immutableTriangles : meshState.stateMesh2.immutableTriangles);
-
-		if (mustUpdateMeshStructure)
-			return true;
-
-		// Check if any attachments were enabled/disabled
-		// or submesh structures has changed
 		MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2;
 		MeshState.SingleMeshState currentMeshState = useMesh1 ? meshState.stateMesh1 : meshState.stateMesh2;
-		ExposedList<Attachment> attachmentsCurrentMesh = currentMeshState.attachments;
-		ExposedList<MeshState.AddSubmeshArguments> addSubmeshArgumentsCurrentMesh = currentMeshState.addSubmeshArguments;
-		ExposedList<bool> attachmentsFlipStateCurrentMesh = currentMeshState.attachmentsFlipState;
-
-		// Check attachments
-		int attachmentCount = attachmentsTemp.Count;
-		if (attachmentsCurrentMesh.Count != attachmentCount)
-			return true;
 
 
-		for (int i = 0; i < attachmentCount; i++) {
-			if (attachmentsCurrentMesh.Items[i] != attachmentsTemp.Items[i])
-				return true;
-		}
-
-		// Check flip state
-		for (int i = 0; i < attachmentCount; i++) {
-			if (attachmentsFlipStateCurrentMesh.Items[i] != attachmentsFlipStateTemp.Items[i])
-				return true;
-		}
-
-		// Check submeshes
-		int submeshCount = addSubmeshArgumentsTemp.Count;
+		// Check if submesh structures has changed
+		ExposedList<MeshState.AddSubmeshArguments> addSubmeshArgumentsCurrentMesh = currentMeshState.addSubmeshArguments;
+		int submeshCount = workingAddSubmeshArguments.Count;
 		if (addSubmeshArgumentsCurrentMesh.Count != submeshCount)
 		if (addSubmeshArgumentsCurrentMesh.Count != submeshCount)
 			return true;
 			return true;
 
 
 		for (int i = 0; i < submeshCount; i++) {
 		for (int i = 0; i < submeshCount; i++) {
-			if (!addSubmeshArgumentsCurrentMesh.Items[i].Equals(ref addSubmeshArgumentsTemp.Items[i]))
+			if (!addSubmeshArgumentsCurrentMesh.Items[i].Equals(ref workingAddSubmeshArguments.Items[i]))
 				return true;
 				return true;
 		}
 		}
 
 
 		return false;
 		return false;
 	}
 	}
 
 
-	/** Stores vertices and triangles for a single material. */
-	private void AddSubmesh (Material material, int startSlot, int endSlot, int triangleCount, int firstVertex, bool lastSubmesh, ExposedList<bool> flipStates) {
+	private void AddSubmesh (MeshState.AddSubmeshArguments submeshArguments, ExposedList<bool> flipStates) { //submeshArguments is a struct, so it's ok.
 		int submeshIndex = submeshMaterials.Count;
 		int submeshIndex = submeshMaterials.Count;
-		submeshMaterials.Add(material);
+		submeshMaterials.Add(submeshArguments.material);
 
 
 		if (submeshes.Count <= submeshIndex)
 		if (submeshes.Count <= submeshIndex)
 			submeshes.Add(new Submesh());
 			submeshes.Add(new Submesh());
 		else if (immutableTriangles)
 		else if (immutableTriangles)
 			return;
 			return;
 
 
-		Submesh submesh = submeshes.Items[submeshIndex];
+		Submesh currentSubmesh = submeshes.Items[submeshIndex];
+		int[] triangles = currentSubmesh.triangles;
+
+		int triangleCount = submeshArguments.triangleCount;
+		int firstVertex = submeshArguments.firstVertex;
 
 
-		int[] triangles = submesh.triangles;
 		int trianglesCapacity = triangles.Length;
 		int trianglesCapacity = triangles.Length;
-		if (lastSubmesh && trianglesCapacity > triangleCount) {
+		if (submeshArguments.isLastSubmesh && trianglesCapacity > triangleCount) {
 			// Last submesh may have more triangles than required, so zero triangles to the end.
 			// Last submesh may have more triangles than required, so zero triangles to the end.
-			for (int i = triangleCount; i < trianglesCapacity; i++)
+			for (int i = triangleCount; i < trianglesCapacity; i++) {
 				triangles[i] = 0;
 				triangles[i] = 0;
-			submesh.triangleCount = triangleCount;
+			}
+			currentSubmesh.triangleCount = triangleCount;
+
 		} else if (trianglesCapacity != triangleCount) {
 		} else if (trianglesCapacity != triangleCount) {
 			// Reallocate triangles when not the exact size needed.
 			// Reallocate triangles when not the exact size needed.
-			submesh.triangles = triangles = new int[triangleCount];
-			submesh.triangleCount = 0;
+			currentSubmesh.triangles = triangles = new int[triangleCount];
+			currentSubmesh.triangleCount = 0;
 		}
 		}
 
 
-		if (!renderMeshes && !frontFacing) {
+		if (!this.renderMeshes && !this.frontFacing) {
 			// Use stored triangles if possible.
 			// Use stored triangles if possible.
-			if (submesh.firstVertex != firstVertex || submesh.triangleCount < triangleCount) {
-				submesh.triangleCount = triangleCount;
-				submesh.firstVertex = firstVertex;
-				int drawOrderIndex = 0;
-				for (int i = 0; i < triangleCount; i += 6, firstVertex += 4, drawOrderIndex++) {
+			if (currentSubmesh.firstVertex != firstVertex || currentSubmesh.triangleCount < triangleCount) { //|| currentSubmesh.triangleCount == 0
+				currentSubmesh.triangleCount = triangleCount;
+				currentSubmesh.firstVertex = firstVertex;
+
+				for (int i = 0; i < triangleCount; i += 6, firstVertex += 4) {
 					triangles[i] = firstVertex;
 					triangles[i] = firstVertex;
 					triangles[i + 1] = firstVertex + 2;
 					triangles[i + 1] = firstVertex + 2;
 					triangles[i + 2] = firstVertex + 1;
 					triangles[i + 2] = firstVertex + 1;
@@ -646,18 +689,23 @@ public class SkeletonRenderer : MonoBehaviour {
 					triangles[i + 4] = firstVertex + 3;
 					triangles[i + 4] = firstVertex + 3;
 					triangles[i + 5] = firstVertex + 1;
 					triangles[i + 5] = firstVertex + 1;
 				}
 				}
+
 			}
 			}
 			return;
 			return;
 		}
 		}
 
 
-		// Store triangles.
-		ExposedList<Slot> drawOrder = skeleton.DrawOrder;
-		for (int i = startSlot, triangleIndex = 0; i < endSlot; i++) {
-			Slot slot = drawOrder.Items[i];
-			Attachment attachment = slot.attachment;
+		// Iterate through all slots and store their triangles. 
 
 
-			bool flip = flipStates.Items[i];
+		var drawOrderItems = skeleton.DrawOrder.Items;	// Make sure to not modify ExposedList inside the loop below
+		var flipStatesItems = flipStates.Items;			// Make sure to not modify ExposedList inside the loop below
 
 
+		int triangleIndex = 0; // Modified by loop
+		for (int i = submeshArguments.startSlot, n = submeshArguments.endSlot; i < n; i++) {			
+			Attachment attachment = drawOrderItems[i].attachment;
+
+			bool flip = flipStatesItems[i];
+
+			// Add RegionAttachment triangles
 			if (attachment is RegionAttachment) {
 			if (attachment is RegionAttachment) {
 				if (!flip) {
 				if (!flip) {
 					triangles[triangleIndex] = firstVertex;
 					triangles[triangleIndex] = firstVertex;
@@ -679,16 +727,18 @@ public class SkeletonRenderer : MonoBehaviour {
 				firstVertex += 4;
 				firstVertex += 4;
 				continue;
 				continue;
 			}
 			}
+
+			// Add (Skinned)MeshAttachment triangles
 			int[] attachmentTriangles;
 			int[] attachmentTriangles;
 			int attachmentVertexCount;
 			int attachmentVertexCount;
-			MeshAttachment meshAttachment = attachment as MeshAttachment;
+			var meshAttachment = attachment as MeshAttachment;
 			if (meshAttachment != null) {
 			if (meshAttachment != null) {
-				attachmentVertexCount = meshAttachment.vertices.Length >> 1;
+				attachmentVertexCount = meshAttachment.vertices.Length >> 1; //  length/2
 				attachmentTriangles = meshAttachment.triangles;
 				attachmentTriangles = meshAttachment.triangles;
 			} else {
 			} else {
-				SkinnedMeshAttachment skinnedMeshAttachment = attachment as SkinnedMeshAttachment;
+				var skinnedMeshAttachment = attachment as SkinnedMeshAttachment;
 				if (skinnedMeshAttachment != null) {
 				if (skinnedMeshAttachment != null) {
-					attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1;
+					attachmentVertexCount = skinnedMeshAttachment.uvs.Length >> 1; // length/2
 					attachmentTriangles = skinnedMeshAttachment.triangles;
 					attachmentTriangles = skinnedMeshAttachment.triangles;
 				} else
 				} else
 					continue;
 					continue;
@@ -710,7 +760,7 @@ public class SkeletonRenderer : MonoBehaviour {
 		}
 		}
 	}
 	}
 
 
-#if UNITY_EDITOR
+	#if UNITY_EDITOR
 	void OnDrawGizmos () {
 	void OnDrawGizmos () {
 		// Make selection easier by drawing a clear gizmo over the skeleton.
 		// Make selection easier by drawing a clear gizmo over the skeleton.
 		meshFilter = GetComponent<MeshFilter>();
 		meshFilter = GetComponent<MeshFilter>();
@@ -724,26 +774,27 @@ public class SkeletonRenderer : MonoBehaviour {
 		Gizmos.matrix = transform.localToWorldMatrix;
 		Gizmos.matrix = transform.localToWorldMatrix;
 		Gizmos.DrawCube(meshBounds.center, meshBounds.size);
 		Gizmos.DrawCube(meshBounds.center, meshBounds.size);
 	}
 	}
-#endif
+	#endif
 
 
 	private class MeshState {
 	private class MeshState {
 		public int vertexCount;
 		public int vertexCount;
-		public readonly SingleMeshState stateTemp = new SingleMeshState();
+		public readonly SingleMeshState buffer = new SingleMeshState();
 		public readonly SingleMeshState stateMesh1 = new SingleMeshState();
 		public readonly SingleMeshState stateMesh1 = new SingleMeshState();
 		public readonly SingleMeshState stateMesh2 = new SingleMeshState();
 		public readonly SingleMeshState stateMesh2 = new SingleMeshState();
 
 
 		public class SingleMeshState {
 		public class SingleMeshState {
 			public bool immutableTriangles;
 			public bool immutableTriangles;
+			public bool requiresUpdate;
 			public readonly ExposedList<Attachment> attachments = new ExposedList<Attachment>();
 			public readonly ExposedList<Attachment> attachments = new ExposedList<Attachment>();
 			public readonly ExposedList<bool> attachmentsFlipState = new ExposedList<bool>();
 			public readonly ExposedList<bool> attachmentsFlipState = new ExposedList<bool>();
 			public readonly ExposedList<AddSubmeshArguments> addSubmeshArguments = new ExposedList<AddSubmeshArguments>();
 			public readonly ExposedList<AddSubmeshArguments> addSubmeshArguments = new ExposedList<AddSubmeshArguments>();
 
 
-			public void UpdateDrawOrderCount(int drawOrderCount) {
-				attachmentsFlipState.GrowIfNeeded(drawOrderCount);
-				attachmentsFlipState.Count = drawOrderCount;
+			public void UpdateAttachmentCount (int attachmentCount) {
+				attachmentsFlipState.GrowIfNeeded(attachmentCount);
+				attachmentsFlipState.Count = attachmentCount;
 
 
-				attachments.GrowIfNeeded(drawOrderCount);
-				attachments.Count = drawOrderCount;
+				attachments.GrowIfNeeded(attachmentCount);
+				attachments.Count = attachmentCount;
 			}
 			}
 		}
 		}
 
 
@@ -753,22 +804,13 @@ public class SkeletonRenderer : MonoBehaviour {
 			public int endSlot;
 			public int endSlot;
 			public int triangleCount;
 			public int triangleCount;
 			public int firstVertex;
 			public int firstVertex;
-			public bool lastSubmesh;
-
-			public AddSubmeshArguments (Material material, int startSlot, int endSlot, int triangleCount, int firstVertex, bool lastSubmesh) {
-				this.material = material;
-				this.startSlot = startSlot;
-				this.endSlot = endSlot;
-				this.triangleCount = triangleCount;
-				this.firstVertex = firstVertex;
-				this.lastSubmesh = lastSubmesh;
-			}
+			public bool isLastSubmesh;
 
 
 			public bool Equals (ref AddSubmeshArguments other) {
 			public bool Equals (ref AddSubmeshArguments other) {
 				return
 				return
-					!ReferenceEquals(material, null) &&
-					!ReferenceEquals(other.material, null) &&
-					material.GetInstanceID() == other.material.GetInstanceID() &&
+					//!ReferenceEquals(material, null) &&
+					//!ReferenceEquals(other.material, null) &&
+					//material.GetInstanceID() == other.material.GetInstanceID() &&
 					startSlot == other.startSlot &&
 					startSlot == other.startSlot &&
 					endSlot == other.endSlot &&
 					endSlot == other.endSlot &&
 					triangleCount == other.triangleCount &&
 					triangleCount == other.triangleCount &&

+ 39 - 35
spine-unity/Assets/spine-unity/SpineAttributes.cs

@@ -7,9 +7,12 @@
 using UnityEngine;
 using UnityEngine;
 using System.Collections;
 using System.Collections;
 
 
-public class SpineSlot : PropertyAttribute {
-	public string startsWith = "";
+public abstract class SpineAttributeBase : PropertyAttribute {
 	public string dataField = "";
 	public string dataField = "";
+	public string startsWith = "";
+}
+
+public class SpineSlot : SpineAttributeBase {
 	public bool containsBoundingBoxes = false;
 	public bool containsBoundingBoxes = false;
 
 
 	/// <summary>
 	/// <summary>
@@ -21,17 +24,29 @@ public class SpineSlot : PropertyAttribute {
 	/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
 	/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
 	/// </param>
 	/// </param>
 	/// <param name="containsBoundingBoxes">Disables popup results that don't contain bounding box attachments when true.</param>
 	/// <param name="containsBoundingBoxes">Disables popup results that don't contain bounding box attachments when true.</param>
-	public SpineSlot (string startsWith = "", string dataField = "", bool containsBoundingBoxes = false) {
+	public SpineSlot(string startsWith = "", string dataField = "", bool containsBoundingBoxes = false) {
 		this.startsWith = startsWith;
 		this.startsWith = startsWith;
 		this.dataField = dataField;
 		this.dataField = dataField;
 		this.containsBoundingBoxes = containsBoundingBoxes;
 		this.containsBoundingBoxes = containsBoundingBoxes;
 	}
 	}
 }
 }
 
 
-public class SpineSkin : PropertyAttribute {
-	public string startsWith = "";
-	public string dataField = "";
+public class SpineEvent : SpineAttributeBase {
+	/// <summary>
+	/// Smart popup menu for Spine Events (Spine.EventData)
+	/// </summary>
+	/// <param name="startsWith">Filters popup results to elements that begin with supplied string.</param>
+	/// <param name="dataField">If specified, a locally scoped field with the name supplied by in dataField will be used to fill the popup results.
+	/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives).
+	/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
+	/// </param>
+	public SpineEvent(string startsWith = "", string dataField = "") {
+		this.startsWith = startsWith;
+		this.dataField = dataField;
+	}
+}
 
 
+public class SpineSkin : SpineAttributeBase {
 	/// <summary>
 	/// <summary>
 	/// Smart popup menu for Spine Skins
 	/// Smart popup menu for Spine Skins
 	/// </summary>
 	/// </summary>
@@ -40,15 +55,12 @@ public class SpineSkin : PropertyAttribute {
 	/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
 	/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
 	/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
 	/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
 	/// </param>
 	/// </param>
-	public SpineSkin (string startsWith = "", string dataField = "") {
+	public SpineSkin(string startsWith = "", string dataField = "") {
 		this.startsWith = startsWith;
 		this.startsWith = startsWith;
 		this.dataField = dataField;
 		this.dataField = dataField;
 	}
 	}
 }
 }
-public class SpineAnimation : PropertyAttribute {
-	public string startsWith = "";
-	public string dataField = "";
-
+public class SpineAnimation : SpineAttributeBase {
 	/// <summary>
 	/// <summary>
 	/// Smart popup menu for Spine Animations
 	/// Smart popup menu for Spine Animations
 	/// </summary>
 	/// </summary>
@@ -57,24 +69,18 @@ public class SpineAnimation : PropertyAttribute {
 	/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
 	/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
 	/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
 	/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
 	/// </param>
 	/// </param>
-	public SpineAnimation (string startsWith = "", string dataField = "") {
+	public SpineAnimation(string startsWith = "", string dataField = "") {
 		this.startsWith = startsWith;
 		this.startsWith = startsWith;
 		this.dataField = dataField;
 		this.dataField = dataField;
 	}
 	}
 }
 }
 
 
-public class SpineAttachment : PropertyAttribute {
+public class SpineAttachment : SpineAttributeBase {
 	public bool returnAttachmentPath = false;
 	public bool returnAttachmentPath = false;
 	public bool currentSkinOnly = false;
 	public bool currentSkinOnly = false;
 	public bool placeholdersOnly = false;
 	public bool placeholdersOnly = false;
-	public string dataField = "";
 	public string slotField = "";
 	public string slotField = "";
 
 
-
-	public SpineAttachment () {
-
-	}
-
 	/// <summary>
 	/// <summary>
 	/// Smart popup menu for Spine Attachments
 	/// Smart popup menu for Spine Attachments
 	/// </summary>
 	/// </summary>
@@ -86,19 +92,19 @@ public class SpineAttachment : PropertyAttribute {
 	/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
 	/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
 	/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
 	/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
 	/// </param>
 	/// </param>
-	public SpineAttachment (bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "") {
+	public SpineAttachment(bool currentSkinOnly = true, bool returnAttachmentPath = false, bool placeholdersOnly = false, string slotField = "", string dataField = "") {
 		this.currentSkinOnly = currentSkinOnly;
 		this.currentSkinOnly = currentSkinOnly;
 		this.returnAttachmentPath = returnAttachmentPath;
 		this.returnAttachmentPath = returnAttachmentPath;
 		this.placeholdersOnly = placeholdersOnly;
 		this.placeholdersOnly = placeholdersOnly;
 		this.slotField = slotField;
 		this.slotField = slotField;
-		this.dataField = dataField;
+		this.dataField = dataField;		
 	}
 	}
 
 
-	public static Hierarchy GetHierarchy (string fullPath) {
+	public static Hierarchy GetHierarchy(string fullPath) {
 		return new Hierarchy(fullPath);
 		return new Hierarchy(fullPath);
 	}
 	}
 
 
-	public static Spine.Attachment GetAttachment (string attachmentPath, Spine.SkeletonData skeletonData) {
+	public static Spine.Attachment GetAttachment(string attachmentPath, Spine.SkeletonData skeletonData) {
 		var hierarchy = SpineAttachment.GetHierarchy(attachmentPath);
 		var hierarchy = SpineAttachment.GetHierarchy(attachmentPath);
 		if (hierarchy.name == "")
 		if (hierarchy.name == "")
 			return null;
 			return null;
@@ -106,7 +112,7 @@ public class SpineAttachment : PropertyAttribute {
 		return skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name);
 		return skeletonData.FindSkin(hierarchy.skin).GetAttachment(skeletonData.FindSlotIndex(hierarchy.slot), hierarchy.name);
 	}
 	}
 
 
-	public static Spine.Attachment GetAttachment (string attachmentPath, SkeletonDataAsset skeletonDataAsset) {
+	public static Spine.Attachment GetAttachment(string attachmentPath, SkeletonDataAsset skeletonDataAsset) {
 		return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true));
 		return GetAttachment(attachmentPath, skeletonDataAsset.GetSkeletonData(true));
 	}
 	}
 
 
@@ -115,14 +121,15 @@ public class SpineAttachment : PropertyAttribute {
 		public string slot;
 		public string slot;
 		public string name;
 		public string name;
 
 
-		public Hierarchy (string fullPath) {
-			string[] chunks = fullPath.Split(new char[] { '/' }, System.StringSplitOptions.RemoveEmptyEntries);
+		public Hierarchy(string fullPath) {
+			string[] chunks = fullPath.Split(new char[]{'/'}, System.StringSplitOptions.RemoveEmptyEntries);
 			if (chunks.Length == 0) {
 			if (chunks.Length == 0) {
 				skin = "";
 				skin = "";
 				slot = "";
 				slot = "";
 				name = "";
 				name = "";
 				return;
 				return;
-			} else if (chunks.Length < 2) {
+			}
+			else if (chunks.Length < 2) {
 				throw new System.Exception("Cannot generate Attachment Hierarchy from string! Not enough components! [" + fullPath + "]");
 				throw new System.Exception("Cannot generate Attachment Hierarchy from string! Not enough components! [" + fullPath + "]");
 			}
 			}
 			skin = chunks[0];
 			skin = chunks[0];
@@ -135,10 +142,7 @@ public class SpineAttachment : PropertyAttribute {
 	}
 	}
 }
 }
 
 
-public class SpineBone : PropertyAttribute {
-	public string startsWith = "";
-	public string dataField = "";
-
+public class SpineBone : SpineAttributeBase {
 	/// <summary>
 	/// <summary>
 	/// Smart popup menu for Spine Bones
 	/// Smart popup menu for Spine Bones
 	/// </summary>
 	/// </summary>
@@ -147,19 +151,19 @@ public class SpineBone : PropertyAttribute {
 	/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
 	/// Valid types are SkeletonDataAsset and SkeletonRenderer (and derivatives)
 	/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
 	/// If left empty and the script the attribute is applied to is derived from Component, GetComponent<SkeletonRenderer>() will be called as a fallback.
 	/// </param>
 	/// </param>
-	public SpineBone (string startsWith = "", string dataField = "") {
+	public SpineBone(string startsWith = "", string dataField = "") {
 		this.startsWith = startsWith;
 		this.startsWith = startsWith;
 		this.dataField = dataField;
 		this.dataField = dataField;
 	}
 	}
 
 
-	public static Spine.Bone GetBone (string boneName, SkeletonRenderer renderer) {
+	public static Spine.Bone GetBone(string boneName, SkeletonRenderer renderer) {
 		if (renderer.skeleton == null)
 		if (renderer.skeleton == null)
 			return null;
 			return null;
 
 
 		return renderer.skeleton.FindBone(boneName);
 		return renderer.skeleton.FindBone(boneName);
 	}
 	}
 
 
-	public static Spine.BoneData GetBoneData (string boneName, SkeletonDataAsset skeletonDataAsset) {
+	public static Spine.BoneData GetBoneData(string boneName, SkeletonDataAsset skeletonDataAsset) {
 		var data = skeletonDataAsset.GetSkeletonData(true);
 		var data = skeletonDataAsset.GetSkeletonData(true);
 
 
 		return data.FindBone(boneName);
 		return data.FindBone(boneName);
@@ -169,4 +173,4 @@ public class SpineBone : PropertyAttribute {
 public class SpineAtlasRegion : PropertyAttribute {
 public class SpineAtlasRegion : PropertyAttribute {
 	//TODO:  Standardize with Skeleton attributes
 	//TODO:  Standardize with Skeleton attributes
 	//NOTE:  For now, relies on locally scoped field named "atlasAsset" for source.
 	//NOTE:  For now, relies on locally scoped field named "atlasAsset" for source.
-}
+}

+ 87 - 0
spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineAnimationComplete.cs

@@ -0,0 +1,87 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.3
+ * 
+ * Copyright (c) 2013-2015, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to use, install, execute and perform the Spine
+ * Runtimes Software (the "Software") and derivative works solely for personal
+ * or internal use. Without the written permission of Esoteric Software (see
+ * Section 2 of the Spine Software License Agreement), you may not (a) modify,
+ * translate, adapt or otherwise create derivative works, improvements of the
+ * Software or develop new applications using the Software or (b) remove,
+ * delete, alter or obscure any trademarks or any copyright, trademark, patent
+ * or other intellectual property or proprietary rights notices on or in the
+ * Software, including any copy thereof. Redistributions in binary or source
+ * form must include this license and terms.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+//using UnityEngine;
+using System.Collections;
+using Spine;
+
+namespace Spine {
+	/// <summary>
+	/// Use this as a condition-blocking yield instruction for Unity Coroutines. 
+	/// The routine will pause until the AnimationState.TrackEntry fires its Complete event.</summary>
+	public class WaitForSpineAnimationComplete : IEnumerator {
+		
+		bool m_WasFired = false;
+
+		public WaitForSpineAnimationComplete (Spine.TrackEntry trackEntry) {
+			SafeSubscribe(trackEntry);
+		}
+
+		void HandleComplete (AnimationState state, int trackIndex, int loopCount) {
+			m_WasFired = true;
+		}
+
+		void SafeSubscribe (Spine.TrackEntry trackEntry) {
+			if (trackEntry == null) {
+				// Break immediately if trackEntry is null.
+				m_WasFired = true;
+			} else {
+				// Function normally.
+				trackEntry.Complete += HandleComplete;
+			}
+		}
+
+		#region Reuse
+		/// <summary>
+		/// One optimization high-frequency YieldInstruction returns is to cache instances to minimize pressure. 
+		/// Use NowWaitFor to reuse the same instance of WaitForSpineAnimationComplete.</summary>
+		public WaitForSpineAnimationComplete NowWaitFor (Spine.TrackEntry trackEntry) {
+			SafeSubscribe(trackEntry);
+			return this;
+		}
+		#endregion
+
+		#region IEnumerator
+		bool IEnumerator.MoveNext () {
+			if (m_WasFired) {
+				((IEnumerator)this).Reset();	// auto-reset for YieldInstruction reuse
+				return false;
+			}
+
+			return true;
+		}
+		void IEnumerator.Reset () { m_WasFired = false; }
+		object IEnumerator.Current { get { return null; } }
+		#endregion
+
+	}
+
+}

+ 12 - 0
spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineAnimationComplete.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: a807dd9fb79db3545b6c2859a2bbfc0b
+timeCreated: 1449704018
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 154 - 0
spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs

@@ -0,0 +1,154 @@
+/******************************************************************************
+ * Spine Runtimes Software License
+ * Version 2.3
+ * 
+ * Copyright (c) 2013-2015, Esoteric Software
+ * All rights reserved.
+ * 
+ * You are granted a perpetual, non-exclusive, non-sublicensable and
+ * non-transferable license to use, install, execute and perform the Spine
+ * Runtimes Software (the "Software") and derivative works solely for personal
+ * or internal use. Without the written permission of Esoteric Software (see
+ * Section 2 of the Spine Software License Agreement), you may not (a) modify,
+ * translate, adapt or otherwise create derivative works, improvements of the
+ * Software or develop new applications using the Software or (b) remove,
+ * delete, alter or obscure any trademarks or any copyright, trademark, patent
+ * or other intellectual property or proprietary rights notices on or in the
+ * Software, including any copy thereof. Redistributions in binary or source
+ * form must include this license and terms.
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ESOTERIC SOFTWARE "AS IS" AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL ESOTERIC SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *****************************************************************************/
+
+//using UnityEngine;
+using System.Collections;
+using Spine;
+
+namespace Spine {
+	/// <summary>
+	/// Use this as a condition-blocking yield instruction for Unity Coroutines. 
+	/// The routine will pause until the AnimationState fires an event matching the given event name or EventData reference.</summary>
+	public class WaitForSpineEvent : IEnumerator {
+
+		Spine.EventData m_TargetEvent;
+		string m_EventName;
+		Spine.AnimationState m_AnimationState;
+
+		bool m_WasFired = false;
+		bool m_unsubscribeAfterFiring = false;
+
+		#region Constructors
+		void Subscribe (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribe) {
+			if (state == null || eventDataReference == null) {
+				m_WasFired = true;
+			} else {
+				m_AnimationState = state;
+				m_TargetEvent = eventDataReference;
+				state.Event += HandleAnimationStateEvent;
+
+				m_unsubscribeAfterFiring = unsubscribe;
+			}
+		}
+
+		void SubscribeByName (Spine.AnimationState state, string eventName, bool unsubscribe) {
+			if (state == null || string.IsNullOrEmpty(eventName)) {
+				m_WasFired = true;
+			} else {
+				m_AnimationState = state;
+				m_EventName = eventName;
+				state.Event += HandleAnimationStateEventByName;
+
+				m_unsubscribeAfterFiring = unsubscribe;
+			}
+		}
+
+		public WaitForSpineEvent (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) {
+			Subscribe(state, eventDataReference, unsubscribeAfterFiring);
+		}
+
+		public WaitForSpineEvent (SkeletonAnimation skeletonAnimation, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) {			
+			// If skeletonAnimation is invalid, its state will be null. Subscribe handles null states just fine.
+			Subscribe(skeletonAnimation.state, eventDataReference, unsubscribeAfterFiring);
+		}
+			
+		public WaitForSpineEvent (Spine.AnimationState state, string eventName, bool unsubscribeAfterFiring = true) {
+			SubscribeByName(state, eventName, unsubscribeAfterFiring);
+		}
+
+		public WaitForSpineEvent (SkeletonAnimation skeletonAnimation, string eventName, bool unsubscribeAfterFiring = true) {
+			// If skeletonAnimation is invalid, its state will be null. Subscribe handles null states just fine.
+			SubscribeByName(skeletonAnimation.state, eventName, unsubscribeAfterFiring);
+		}
+		#endregion
+
+		#region Event Handlers
+		void HandleAnimationStateEventByName (AnimationState state, int trackIndex, Spine.Event e) {
+			if (state != m_AnimationState) return;
+
+			m_WasFired |= (e.Data.Name == m_EventName);			// Check event name string match.
+			if (m_WasFired && m_unsubscribeAfterFiring)
+				state.Event -= HandleAnimationStateEventByName;	// Unsubscribe after correct event fires.
+		}
+
+		void HandleAnimationStateEvent (AnimationState state, int trackIndex, Spine.Event e) {
+			if (state != m_AnimationState) return;
+
+			m_WasFired |= (e.Data == m_TargetEvent);			// Check event data reference match.
+			if (m_WasFired && m_unsubscribeAfterFiring)
+				state.Event -= HandleAnimationStateEvent; 		// Usubscribe after correct event fires.
+		}
+		#endregion
+
+		#region Reuse
+		/// <summary>
+		/// By default, WaitForSpineEvent will unsubscribe from the event immediately after it fires a correct matching event. 
+		/// If you want to reuse this WaitForSpineEvent instance on the same event, you can set this to false.</summary>
+		public bool WillUnsubscribeAfterFiring { get { return m_unsubscribeAfterFiring; } set { m_unsubscribeAfterFiring = value; } }
+
+		public WaitForSpineEvent NowWaitFor (Spine.AnimationState state, Spine.EventData eventDataReference, bool unsubscribeAfterFiring = true) {
+			((IEnumerator)this).Reset();
+			Clear(state);
+			Subscribe(state, eventDataReference, unsubscribeAfterFiring);
+
+			return this;
+		}
+
+		public WaitForSpineEvent NowWaitFor (Spine.AnimationState state, string eventName, bool unsubscribeAfterFiring = true) {
+			((IEnumerator)this).Reset();
+			Clear(state);
+			SubscribeByName(state, eventName, unsubscribeAfterFiring);
+
+			return this;
+		}
+
+		void Clear (Spine.AnimationState state) {
+			state.Event -= HandleAnimationStateEvent;
+			state.Event -= HandleAnimationStateEventByName;
+		}
+		#endregion
+
+		#region IEnumerator
+		bool IEnumerator.MoveNext () {
+			if (m_WasFired) {
+				((IEnumerator)this).Reset();	// auto-reset for YieldInstruction reuse
+				return false;
+			}
+
+			return true;
+		}
+		void IEnumerator.Reset () { m_WasFired = false; }
+		object IEnumerator.Current { get { return null; } }
+		#endregion
+
+
+	}
+}

+ 12 - 0
spine-unity/Assets/spine-unity/YieldInstructions/WaitForSpineEvent.cs.meta

@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: fc166d883db083e469872998172f2d38
+timeCreated: 1449701857
+licenseType: Free
+MonoImporter:
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 33 - 22
spine-unity/README.md

@@ -1,8 +1,10 @@
 # spine-unity
 # spine-unity
 
 
-The spine-unity runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [Unity](http://unity3d.com/). spine-unity is based on [spine-csharp](https://github.com/EsotericSoftware/spine-runtimes/tree/master/spine-csharp).
+The **Spine-Unity** runtime provides functionality to load, manipulate and render [Spine](http://esotericsoftware.com) skeletal animation data using [Unity](http://unity3d.com/). spine-unity is based on [spine-csharp](https://github.com/EsotericSoftware/spine-runtimes/tree/master/spine-csharp).
 
 
-While spine-unity can render directly with Unity, without the need for any other plugins, it also works with [2D Toolkit](http://www.unikronsoftware.com/2dtoolkit/) and can render skeletons from a TK2D texture atlas.
+For more documentation, see [Spine-Unity Documentation](https://github.com/pharan/spine-unity-docs/blob/master/README.md).
+
+While spine-unity can render directly with Unity, without the need for any other plugins, it also works with [2D Toolkit](http://www.unikronsoftware.com/2dtoolkit/) and can render skeletons using a TK2D texture atlas.
 
 
 ## Licensing
 ## Licensing
 
 
@@ -12,12 +14,16 @@ The Spine Runtimes are developed with the intent to be used with data exported f
 
 
 ## Documentation
 ## Documentation
 
 
-A Spine skeleton is a GameObject and can be used throughout Unity like any other GameObject. It depends only on Unity's `MeshRenderer`, so it is close to the metal. `SkeletonUtility` allows other GameObjects to interact with the Spine skeleton, to control bones in the skeleton, be controlled by the skeleton, attach colliders, etc.
+A Spine skeleton GameObject (a GameObject with a SkeletonAnimation component on it) can be used throughout Unity like any other GameObject. It renders through `MeshRenderer`, so it is close to the metal.
+
+`SkeletonUtility` allows other GameObjects to interact with the Spine skeleton, to control bones in the skeleton, be controlled by the skeleton, attach colliders, etc.
 
 
-Spine skeletons can be "baked" into native Unity animations for use with Mecanim. Baking is an advanced feature with a number of limitations, so in most cases baking should not be used.
+For advanced uses and specific optimization cases, Spine skeletons can be "baked" into native Unity animation assets. Since Unity's animation feature-set does not overlap with Spine's perfectly, baked assets have many limitations and removed features. For most uses, baking is not necessary.
 
 
 The [Spine Unity Features Tutorial](http://esotericsoftware.com/forum/Unity-Feature-Tutorials-4839) forum thread has many videos on how to use spine-unity.
 The [Spine Unity Features Tutorial](http://esotericsoftware.com/forum/Unity-Feature-Tutorials-4839) forum thread has many videos on how to use spine-unity.
 
 
+For more documentation, see [Spine-Unity Documentation](https://github.com/pharan/spine-unity-docs/blob/master/README.md).
+
 ## Quick installation
 ## Quick installation
 
 
 Download and run this Unity package:
 Download and run this Unity package:
@@ -31,31 +37,36 @@ In the `Assets/Examples/Scenes` folder you will find many example scenes that de
 You can also choose to setup and run from the Git files:
 You can also choose to setup and run from the Git files:
 
 
 1. Download the Spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/master.zip).
 1. Download the Spine Runtimes source using [git](https://help.github.com/articles/set-up-git) or by downloading it [as a zip](https://github.com/EsotericSoftware/spine-runtimes/archive/master.zip).
-1. Copy the contents of `spine-csharp/src` to `Assets/spine-csharp` in your Unity project directory.
-1. Copy the `spine-unity/Assets/spine-unity` to `Assets/spine-unity` in your Unity project directory.
+2. Spine-Unity requires both `spine-csharp` and `spine-unity`.
+	- Copy the contents of `spine-csharp/src` to `Assets/spine-csharp` in your Unity project directory.
+	- Copy the contents of `spine-unity/Assets/` to `Assets/` in your Unity project directory. Including `Gizmos` and `spine-unity` and `Examples` if you want them.
 
 
-## Importing skeleton data
 
 
-There are a few options for importing Spine skeletons into your Unity project:
+> - `Gizmos` is a [special folder](http://docs.unity3d.com/Manual/SpecialFolders.html) in Unity. It needs to be at the root of your assets folder to function correctly. (ie. `Assets/Gizmos`
+- `spine-csharp` and `spine-unity` can be placed in any subfolder you want.
+
+----------
 
 
-### Drag and drop import
+## Importing skeleton data
 
 
-1. Drag and drop a folder containing the JSON, atlas and PNG files exported from Spine directly into Unity.
-1. Drag the prefab that was created into your scene.
+1. Add your `.json`, `.atlas.txt` and `.png` into your Unity project.
+	- You can do this through Unity's Project View: Drag and drop a folder containing the `.json`, `.atlas.txt` and `.png` files exported from Spine directly into the Unity Project view.
+	- ... or you can opt to do this through Windows File Explorer or OSX Finder. Move or copy your `.json`, `.atlas.txt` and `.png` files into your Unity project's `Assets` folder, ideally in its own subfolder.
+2. Spine-Unity will automatically detect the `.json` and `.atlas.txt` and attempt to generate the necessary Spine-Unity assets.
+3. To start using your Spine assets, right-click on the SkeletonDataAsset (the asset with the orange Spine logo on it) and choose `Spine > Instantiate(SkeletonAnimation)`. This will add a GameObject with a `SkeletonAnimation` component on it.
+	-  If you are more familiar with Mecanim, you may choose `Spine > Instantiate(Mecanim)` instead.
+4. For more info on how to control the animation, see the [Spine-Unity Animation Control documentation](https://github.com/pharan/spine-unity-docs/blob/master/Animation.md).
 
 
-The [drag and drop video](http://www.youtube.com/watch?v=-Gk_zJsY1Ms) shows how this works.
 
 
-### Automated import
 
 
-1. Place the JSON, atlas and PNG files exported from Spine into your Unity project directory.
-1. Right click the JSON file and click `Spine`, `Ingest` to create the Unity assets.
-1. Right click the SkeletonData asset that was created and click `Spine`, `Spawn`.
+> The original [manual setup video](https://www.youtube.com/watch?v=-V84OIvZdQc) to shows which assets belong where and what Spine-Unity's automatic import actually does for you under the hood. In case you have a specialized asset setup, this video will be useful for understanding how assets fit together.
 
 
-The [readme PDF](https://raw.githubusercontent.com/EsotericSoftware/spine-runtimes/master/spine-unity/README.pdf) illustrates these steps.
+> More resources:
+[Drag and drop video](http://www.youtube.com/watch?v=-Gk_zJsY1Ms)
+[readme PDF](https://raw.githubusercontent.com/EsotericSoftware/spine-runtimes/master/spine-unity/README.pdf)
 
 
-### Manual import
+----------
 
 
-1. Follow the [setup video](https://www.youtube.com/watch?v=-V84OIvZdQc) to manually set up the assets for a skeleton in Unity. This video may prove useful to understand how the pieces fit together but the other import methods are much easier.
 
 
 ## Examples
 ## Examples
 
 
@@ -71,7 +82,7 @@ To run the examples:
 
 
 ## Notes
 ## Notes
 
 
-- Atlas images should use premultiplied alpha when using the shaders that come with spine-unity.
 - This slightly outdated [spine-unity tutorial video](http://www.youtube.com/watch?v=x1umSQulghA) may still be useful.
 - This slightly outdated [spine-unity tutorial video](http://www.youtube.com/watch?v=x1umSQulghA) may still be useful.
-- Unity scales large images down by default if they exceed 1024x1024, which causes atlas coordinates to be incorrect. To fix this, override the import settings in the Inspector for any large atlas image you have so Unity does not scale it down.
-- Unity 4.3+'s 2D project defaults cause atlas images added to the project to be imported with the Texture Type "Sprite", which may cause artifacts when using Spine's Skeleton shader. To avoid these artifacts, make sure the Texture Type is set to "Texture".
+- Atlas images should use **Premultiplied Alpha** when using the shaders that come with spine-unity (`Spine/Skeleton` or `Spine/SkeletonLit`).
+- **TEXTURE SIZES.** Unity scales large images down by default if they exceed 1024x1024. This can cause atlas coordinates to be incorrect. To fix this, make sure to set import settings in the Inspector for any large atlas image you have so Unity does not scale it down.
+- **TEXTURE ARTIFACTS FROM COMPRESSION.** Unity's 2D project defaults import new images added to the project with the Texture Type "Sprite". This can cause artifacts when using the `Spine/Skeleton` shader. To avoid these artifacts, make sure the Texture Type is set to "Texture". Spine-Unity's automatic import will attempt to apply these settings but in the process of updating your textures, these settings may be reverted.

+ 3 - 2
spine-xna/src/SkeletonMeshRenderer.cs

@@ -90,10 +90,11 @@ namespace Spine {
 
 
 		public void Draw (Skeleton skeleton) {
 		public void Draw (Skeleton skeleton) {
 			float[] vertices = this.vertices;
 			float[] vertices = this.vertices;
-			List<Slot> drawOrder = skeleton.DrawOrder;
+			var drawOrder = skeleton.DrawOrder;
+			var drawOrderItems = skeleton.DrawOrder.Items;
 			float skeletonR = skeleton.R, skeletonG = skeleton.G, skeletonB = skeleton.B, skeletonA = skeleton.A;
 			float skeletonR = skeleton.R, skeletonG = skeleton.G, skeletonB = skeleton.B, skeletonA = skeleton.A;
 			for (int i = 0, n = drawOrder.Count; i < n; i++) {
 			for (int i = 0, n = drawOrder.Count; i < n; i++) {
-				Slot slot = drawOrder[i];
+				Slot slot = drawOrderItems[i];
 				Attachment attachment = slot.Attachment;
 				Attachment attachment = slot.Attachment;
 				if (attachment is RegionAttachment) {
 				if (attachment is RegionAttachment) {
 					RegionAttachment regionAttachment = (RegionAttachment)attachment;
 					RegionAttachment regionAttachment = (RegionAttachment)attachment;

+ 3 - 2
spine-xna/src/SkeletonRegionRenderer.cs

@@ -83,10 +83,11 @@ namespace Spine {
 		}
 		}
 
 
 		public void Draw (Skeleton skeleton) {
 		public void Draw (Skeleton skeleton) {
-			List<Slot> drawOrder = skeleton.DrawOrder;
+			var drawOrder = skeleton.DrawOrder;
+			var drawOrderItems = skeleton.DrawOrder.Items;
 			float skeletonR = skeleton.R, skeletonG = skeleton.G, skeletonB = skeleton.B, skeletonA = skeleton.A;
 			float skeletonR = skeleton.R, skeletonG = skeleton.G, skeletonB = skeleton.B, skeletonA = skeleton.A;
 			for (int i = 0, n = drawOrder.Count; i < n; i++) {
 			for (int i = 0, n = drawOrder.Count; i < n; i++) {
-				Slot slot = drawOrder[i];
+				Slot slot = drawOrderItems[i];
 				RegionAttachment regionAttachment = slot.Attachment as RegionAttachment;
 				RegionAttachment regionAttachment = slot.Attachment as RegionAttachment;
 				if (regionAttachment != null) {
 				if (regionAttachment != null) {
 					BlendState blend = slot.Data.BlendMode == BlendMode.additive ? BlendState.Additive : defaultBlendState;
 					BlendState blend = slot.Data.BlendMode == BlendMode.additive ? BlendState.Additive : defaultBlendState;