Przeglądaj źródła

Merge branch '3.7-beta' into 3.7-beta-cpp

badlogic 7 lat temu
rodzic
commit
2b9f215c9e

+ 4 - 1
spine-unity/Assets/Spine/Editor/spine-unity/Editor/AnimationReferenceAssetEditor.cs

@@ -56,6 +56,7 @@ namespace Spine.Unity.Editor {
 		bool changeNextFrame = false;
 		bool changeNextFrame = false;
 		SerializedProperty animationNameProperty;
 		SerializedProperty animationNameProperty;
 		SkeletonDataAsset lastSkeletonDataAsset;
 		SkeletonDataAsset lastSkeletonDataAsset;
+		SkeletonData lastSkeletonData;
 
 
 		void OnEnable () { HandleOnEnablePreview(); }
 		void OnEnable () { HandleOnEnablePreview(); }
 		void OnDestroy () { HandleOnDestroyPreview(); }
 		void OnDestroy () { HandleOnDestroyPreview(); }
@@ -76,7 +77,7 @@ namespace Spine.Unity.Editor {
 			if (changeNextFrame) {
 			if (changeNextFrame) {
 				changeNextFrame = false;
 				changeNextFrame = false;
 				
 				
-				if (ThisSkeletonDataAsset != lastSkeletonDataAsset) {
+				if (ThisSkeletonDataAsset != lastSkeletonDataAsset || ThisSkeletonDataAsset.GetSkeletonData(true) != lastSkeletonData) {
 					preview.Clear();
 					preview.Clear();
 					preview.Initialize(Repaint, ThisSkeletonDataAsset, LastSkinName);
 					preview.Initialize(Repaint, ThisSkeletonDataAsset, LastSkinName);
 
 
@@ -91,7 +92,9 @@ namespace Spine.Unity.Editor {
 				if (!string.IsNullOrEmpty(animationNameProperty.stringValue))
 				if (!string.IsNullOrEmpty(animationNameProperty.stringValue))
 					preview.PlayPauseAnimation(animationNameProperty.stringValue, true);
 					preview.PlayPauseAnimation(animationNameProperty.stringValue, true);
 			}
 			}
+
 			lastSkeletonDataAsset = ThisSkeletonDataAsset;
 			lastSkeletonDataAsset = ThisSkeletonDataAsset;
+			lastSkeletonData = ThisSkeletonDataAsset.GetSkeletonData(true);
 
 
 			//EditorGUILayout.HelpBox(AnimationReferenceAssetEditor.InspectorHelpText, MessageType.Info, true);
 			//EditorGUILayout.HelpBox(AnimationReferenceAssetEditor.InspectorHelpText, MessageType.Info, true);
 			EditorGUILayout.Space();
 			EditorGUILayout.Space();

+ 104 - 80
spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonDataAssetInspector.cs

@@ -355,27 +355,35 @@ namespace Spine.Unity.Editor {
 				using (new SpineInspectorUtility.IndentScope())
 				using (new SpineInspectorUtility.IndentScope())
 					SpineInspectorUtility.PropertyFieldWideLabel(defaultMix, DefaultMixLabel, 160);
 					SpineInspectorUtility.PropertyFieldWideLabel(defaultMix, DefaultMixLabel, 160);
 
 
-				// Do not use EditorGUIUtility.indentLevel. It will add spaces on every field.
-				for (int i = 0; i < fromAnimation.arraySize; i++) {
-					SerializedProperty from = fromAnimation.GetArrayElementAtIndex(i);
-					SerializedProperty to = toAnimation.GetArrayElementAtIndex(i);
-					SerializedProperty durationProp = duration.GetArrayElementAtIndex(i);
-					using (new EditorGUILayout.HorizontalScope()) {
-						GUILayout.Space(16f);
-						EditorGUILayout.PropertyField(from, GUIContent.none);
-						EditorGUILayout.PropertyField(to, GUIContent.none);
-						durationProp.floatValue = EditorGUILayout.FloatField(durationProp.floatValue, GUILayout.MinWidth(25f), GUILayout.MaxWidth(60f));
-						if (GUILayout.Button("Delete", EditorStyles.miniButton)) {
-							duration.DeleteArrayElementAtIndex(i);
-							toAnimation.DeleteArrayElementAtIndex(i);
-							fromAnimation.DeleteArrayElementAtIndex(i);
+				
+				if (fromAnimation.arraySize > 0) {
+					using (new SpineInspectorUtility.IndentScope()) {
+						EditorGUILayout.LabelField("Custom Mix Durations", EditorStyles.boldLabel);
+					}
+
+					for (int i = 0; i < fromAnimation.arraySize; i++) {
+						SerializedProperty from = fromAnimation.GetArrayElementAtIndex(i);
+						SerializedProperty to = toAnimation.GetArrayElementAtIndex(i);
+						SerializedProperty durationProp = duration.GetArrayElementAtIndex(i);
+						using (new EditorGUILayout.HorizontalScope()) {
+							GUILayout.Space(16f); // Space instead of EditorGUIUtility.indentLevel. indentLevel will add the space on every field.
+							EditorGUILayout.PropertyField(from, GUIContent.none);
+							//EditorGUILayout.LabelField(">", EditorStyles.miniLabel, GUILayout.Width(9f));
+							EditorGUILayout.PropertyField(to, GUIContent.none);
+							//GUILayout.Space(5f);
+							durationProp.floatValue = EditorGUILayout.FloatField(durationProp.floatValue, GUILayout.MinWidth(25f), GUILayout.MaxWidth(60f));
+							if (GUILayout.Button("Delete", EditorStyles.miniButton)) {
+								duration.DeleteArrayElementAtIndex(i);
+								toAnimation.DeleteArrayElementAtIndex(i);
+								fromAnimation.DeleteArrayElementAtIndex(i);
+							}
 						}
 						}
 					}
 					}
-				}
+				}				
 
 
 				using (new EditorGUILayout.HorizontalScope()) {
 				using (new EditorGUILayout.HorizontalScope()) {
 					EditorGUILayout.Space();
 					EditorGUILayout.Space();
-					if (GUILayout.Button("Add Mix")) {
+					if (GUILayout.Button("Add Custom Mix")) {
 						duration.arraySize++;
 						duration.arraySize++;
 						toAnimation.arraySize++;
 						toAnimation.arraySize++;
 						fromAnimation.arraySize++;
 						fromAnimation.arraySize++;
@@ -438,59 +446,75 @@ namespace Spine.Unity.Editor {
 			if (!showSlotList) return;
 			if (!showSlotList) return;
 			if (!preview.IsValid) return;
 			if (!preview.IsValid) return;
 
 
-			EditorGUI.indentLevel++;
-			showAttachments = EditorGUILayout.ToggleLeft("Show Attachments", showAttachments);
-			var slotAttachments = new List<Attachment>();
-			var slotAttachmentNames = new List<string>();
-			var defaultSkinAttachmentNames = new List<string>();
-			var defaultSkin = targetSkeletonData.Skins.Items[0];
+			var defaultSkin = targetSkeletonData.DefaultSkin;
 			Skin skin = preview.Skeleton.Skin ?? defaultSkin;
 			Skin skin = preview.Skeleton.Skin ?? defaultSkin;
-			var slotsItems = preview.Skeleton.Slots.Items;
-
-			for (int i = preview.Skeleton.Slots.Count - 1; i >= 0; i--) {
-				Slot slot = slotsItems[i];
-				EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot));
-				if (showAttachments) {
-					
-					EditorGUI.indentLevel++;
-					slotAttachments.Clear();
-					slotAttachmentNames.Clear();
-					defaultSkinAttachmentNames.Clear();
-
-					skin.FindNamesForSlot(i, slotAttachmentNames);
-					skin.FindAttachmentsForSlot(i, slotAttachments);
-
-					if (skin != defaultSkin) {
-						defaultSkin.FindNamesForSlot(i, defaultSkinAttachmentNames);
-						defaultSkin.FindNamesForSlot(i, slotAttachmentNames);
-						defaultSkin.FindAttachmentsForSlot(i, slotAttachments);
-					} else {
-						defaultSkin.FindNamesForSlot(i, defaultSkinAttachmentNames);
+
+			using (new SpineInspectorUtility.IndentScope()) {
+
+				using (new EditorGUILayout.HorizontalScope()) {
+					showAttachments = EditorGUILayout.ToggleLeft("Show Attachments", showAttachments, GUILayout.MaxWidth(150f));
+					if (showAttachments) {
+						if (skin != null && skin != defaultSkin)
+							EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(skin.Name, Icons.skin));
 					}
 					}
+				}
 
 
-					for (int a = 0; a < slotAttachments.Count; a++) {
-						Attachment attachment = slotAttachments[a];
-						string attachmentName = slotAttachmentNames[a];
-						Texture2D icon = Icons.GetAttachmentIcon(attachment);
-						bool initialState = slot.Attachment == attachment;
-						bool toggled = EditorGUILayout.ToggleLeft(SpineInspectorUtility.TempContent(attachmentName, icon), slot.Attachment == attachment);
-
-						if (!defaultSkinAttachmentNames.Contains(attachmentName)) {
-							Rect skinPlaceHolderIconRect = GUILayoutUtility.GetLastRect();
-							skinPlaceHolderIconRect.width = Icons.skinPlaceholder.width;
-							skinPlaceHolderIconRect.height = Icons.skinPlaceholder.height;
-							GUI.DrawTexture(skinPlaceHolderIconRect, Icons.skinPlaceholder);
-						}
+				var slotAttachments = new List<Attachment>();
+				var slotAttachmentNames = new List<string>();
+				var defaultSkinAttachmentNames = new List<string>();
+				var slotsItems = preview.Skeleton.Slots.Items;
+				for (int i = preview.Skeleton.Slots.Count - 1; i >= 0; i--) {
+					Slot slot = slotsItems[i];
+					EditorGUILayout.LabelField(SpineInspectorUtility.TempContent(slot.Data.Name, Icons.slot));
+					if (showAttachments) {
+						slotAttachments.Clear();
+						slotAttachmentNames.Clear();
+						defaultSkinAttachmentNames.Clear();
+
+						using (new SpineInspectorUtility.IndentScope()) {
+							{
+								skin.FindNamesForSlot(i, slotAttachmentNames);
+								skin.FindAttachmentsForSlot(i, slotAttachments);
+
+								if (skin != defaultSkin) {
+									defaultSkin.FindNamesForSlot(i, defaultSkinAttachmentNames);
+									defaultSkin.FindNamesForSlot(i, slotAttachmentNames);
+									defaultSkin.FindAttachmentsForSlot(i, slotAttachments);
+								} else {
+									defaultSkin.FindNamesForSlot(i, defaultSkinAttachmentNames);
+								}
+							}
 
 
-						if (toggled != initialState) {
-							slot.Attachment = toggled ? attachment : null;
-							preview.RefreshOnNextUpdate();
+							for (int a = 0; a < slotAttachments.Count; a++) {
+								Attachment attachment = slotAttachments[a];
+								string attachmentName = slotAttachmentNames[a];
+								bool attachmentIsFromSkin = !defaultSkinAttachmentNames.Contains(attachmentName);
+
+								Texture2D attachmentTypeIcon = Icons.GetAttachmentIcon(attachment);
+								bool initialState = slot.Attachment == attachment;
+
+								Texture2D iconToUse = attachmentIsFromSkin ? Icons.skinPlaceholder : attachmentTypeIcon;
+								bool toggled = EditorGUILayout.ToggleLeft(SpineInspectorUtility.TempContent(attachmentName, iconToUse), slot.Attachment == attachment, GUILayout.MinWidth(150f));
+								
+								if (attachmentIsFromSkin) {
+									Rect extraIconRect = GUILayoutUtility.GetLastRect();
+									extraIconRect.x += extraIconRect.width - (attachmentTypeIcon.width * 2f);
+									extraIconRect.width = attachmentTypeIcon.width;
+									extraIconRect.height = attachmentTypeIcon.height;
+									GUI.DrawTexture(extraIconRect, attachmentTypeIcon);
+								}
+
+								if (toggled != initialState) {
+									slot.Attachment = toggled ? attachment : null;
+									preview.RefreshOnNextUpdate();
+								}
+							}
 						}
 						}
+
 					}
 					}
-					EditorGUI.indentLevel--;
 				}
 				}
 			}
 			}
-			EditorGUI.indentLevel--;
+			
 		}
 		}
 
 
 		void DrawUnityTools () {
 		void DrawUnityTools () {
@@ -498,29 +522,29 @@ namespace Spine.Unity.Editor {
 			using (new SpineInspectorUtility.BoxScope()) {
 			using (new SpineInspectorUtility.BoxScope()) {
 				isMecanimExpanded = EditorGUILayout.Foldout(isMecanimExpanded, SpineInspectorUtility.TempContent("SkeletonAnimator", SpineInspectorUtility.UnityIcon<SceneAsset>()));
 				isMecanimExpanded = EditorGUILayout.Foldout(isMecanimExpanded, SpineInspectorUtility.TempContent("SkeletonAnimator", SpineInspectorUtility.UnityIcon<SceneAsset>()));
 				if (isMecanimExpanded) {
 				if (isMecanimExpanded) {
-					EditorGUI.indentLevel++;
-					EditorGUILayout.PropertyField(controller, SpineInspectorUtility.TempContent("Controller", SpineInspectorUtility.UnityIcon<Animator>()));		
-					if (controller.objectReferenceValue == null) {
-
-						// Generate Mecanim Controller Button
-						using (new GUILayout.HorizontalScope()) {
-							GUILayout.Space(EditorGUIUtility.labelWidth);
-							if (GUILayout.Button(SpineInspectorUtility.TempContent("Generate Mecanim Controller"), GUILayout.Height(20)))
-								SkeletonBaker.GenerateMecanimAnimationClips(targetSkeletonDataAsset);						
-						}
-						EditorGUILayout.HelpBox("SkeletonAnimator is the Mecanim alternative to SkeletonAnimation.\nIt is not required.", MessageType.Info);
+					using (new SpineInspectorUtility.IndentScope()) {
+						EditorGUILayout.PropertyField(controller, SpineInspectorUtility.TempContent("Controller", SpineInspectorUtility.UnityIcon<Animator>()));
+						if (controller.objectReferenceValue == null) {
+
+							// Generate Mecanim Controller Button
+							using (new GUILayout.HorizontalScope()) {
+								GUILayout.Space(EditorGUIUtility.labelWidth);
+								if (GUILayout.Button(SpineInspectorUtility.TempContent("Generate Mecanim Controller"), GUILayout.Height(20)))
+									SkeletonBaker.GenerateMecanimAnimationClips(targetSkeletonDataAsset);
+							}
+							EditorGUILayout.HelpBox("SkeletonAnimator is the Mecanim alternative to SkeletonAnimation.\nIt is not required.", MessageType.Info);
 
 
-					} else {
+						} else {
 
 
-						// Update AnimationClips button.
-						using (new GUILayout.HorizontalScope()) {
-							GUILayout.Space(EditorGUIUtility.labelWidth);
-							if (GUILayout.Button(SpineInspectorUtility.TempContent("Force Update AnimationClips"), GUILayout.Height(20)))
-								SkeletonBaker.GenerateMecanimAnimationClips(targetSkeletonDataAsset);				
-						}
+							// Update AnimationClips button.
+							using (new GUILayout.HorizontalScope()) {
+								GUILayout.Space(EditorGUIUtility.labelWidth);
+								if (GUILayout.Button(SpineInspectorUtility.TempContent("Force Update AnimationClips"), GUILayout.Height(20)))
+									SkeletonBaker.GenerateMecanimAnimationClips(targetSkeletonDataAsset);
+							}
 
 
+						}
 					}
 					}
-					EditorGUI.indentLevel--;
 				}
 				}
 			}
 			}
 			#endif
 			#endif

+ 4 - 1
spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonDebugWindow.cs

@@ -446,6 +446,9 @@ namespace Spine.Unity.Editor {
 				}
 				}
 
 
 				showDrawOrderTree.target = EditorGUILayout.Foldout(showDrawOrderTree.target, SpineInspectorUtility.TempContent("Draw Order and Separators", Icons.slotRoot), BoldFoldoutStyle);
 				showDrawOrderTree.target = EditorGUILayout.Foldout(showDrawOrderTree.target, SpineInspectorUtility.TempContent("Draw Order and Separators", Icons.slotRoot), BoldFoldoutStyle);
+
+				//var separatorSlotNamesField = 
+				//SpineInspectorUtility.ge
 				if (showDrawOrderTree.faded > 0) {
 				if (showDrawOrderTree.faded > 0) {
 					using (new SpineInspectorUtility.IndentScope()) {
 					using (new SpineInspectorUtility.IndentScope()) {
 						using (new EditorGUILayout.FadeGroupScope(showDrawOrderTree.faded)) {
 						using (new EditorGUILayout.FadeGroupScope(showDrawOrderTree.faded)) {
@@ -459,7 +462,7 @@ namespace Spine.Unity.Editor {
 								}
 								}
 							} else {
 							} else {
 								foreach (var slot in skeleton.DrawOrder) {
 								foreach (var slot in skeleton.DrawOrder) {
-									var slotNames = skeletonRenderer.separatorSlotNames;
+									var slotNames = SkeletonRendererInspector.GetSeparatorSlotMember(skeletonRenderer);
 									for (int i = 0, n = slotNames.Length; i < n; i++) {
 									for (int i = 0, n = slotNames.Length; i < n; i++) {
 										if (string.Equals(slotNames[i], slot.Data.Name, System.StringComparison.Ordinal)) {
 										if (string.Equals(slotNames[i], slot.Data.Name, System.StringComparison.Ordinal)) {
 											EditorGUILayout.LabelField(SeparatorString);
 											EditorGUILayout.LabelField(SeparatorString);

+ 8 - 18
spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonRendererInspector.cs

@@ -33,6 +33,7 @@
 using UnityEditor;
 using UnityEditor;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using UnityEngine;
 using UnityEngine;
+using System.Reflection;
 
 
 namespace Spine.Unity.Editor {
 namespace Spine.Unity.Editor {
 	using Event = UnityEngine.Event;
 	using Event = UnityEngine.Event;
@@ -43,6 +44,8 @@ namespace Spine.Unity.Editor {
 	public class SkeletonRendererInspector : UnityEditor.Editor {
 	public class SkeletonRendererInspector : UnityEditor.Editor {
 		public static bool advancedFoldout;
 		public static bool advancedFoldout;
 
 
+		const string SeparatorSlotNamesFieldName = "separatorSlotNames";
+
 		protected SerializedProperty skeletonDataAsset, initialSkinName;
 		protected SerializedProperty skeletonDataAsset, initialSkinName;
 		protected SerializedProperty initialFlipX, initialFlipY;
 		protected SerializedProperty initialFlipX, initialFlipY;
 		protected SerializedProperty singleSubmesh, separatorSlotNames, clearStateOnDisable, immutableTriangles;
 		protected SerializedProperty singleSubmesh, separatorSlotNames, clearStateOnDisable, immutableTriangles;
@@ -112,24 +115,6 @@ namespace Spine.Unity.Editor {
 			sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(rso);
 			sortingProperties = new SpineInspectorUtility.SerializedSortingProperties(rso);
 		}
 		}
 
 
-		public static void ReapplySeparatorSlotNames (SkeletonRenderer skeletonRenderer) {
-			if (!skeletonRenderer.valid) return;
-
-			var separatorSlots = skeletonRenderer.separatorSlots;
-			var separatorSlotNames = skeletonRenderer.separatorSlotNames;
-			var skeleton = skeletonRenderer.skeleton;
-
-			separatorSlots.Clear();
-			for (int i = 0, n = separatorSlotNames.Length; i < n; i++) {
-				var slot = skeleton.FindSlot(separatorSlotNames[i]);
-				if (slot != null) {
-					separatorSlots.Add(slot);
-				} else {
-					Debug.LogWarning(separatorSlotNames[i] + " is not a slot in " + skeletonRenderer.skeletonDataAsset.skeletonJSON.name);				
-				}
-			}
-		}
-
 		GUIContent[] skins;
 		GUIContent[] skins;
 		ExposedList<Skin> loadedSkinList;
 		ExposedList<Skin> loadedSkinList;
 
 
@@ -333,6 +318,11 @@ namespace Spine.Unity.Editor {
 			}
 			}
 		}
 		}
 
 
+		public static string[] GetSeparatorSlotMember (SkeletonRenderer skeletonRenderer) {
+			var field = SpineInspectorUtility.GetNonPublicField(typeof(SkeletonRenderer), SeparatorSlotNamesFieldName);
+			return field.GetValue(skeletonRenderer) as string[];
+		}
+
 		public static void SeparatorsField (SerializedProperty separatorSlotNames) {
 		public static void SeparatorsField (SerializedProperty separatorSlotNames) {
 			bool multi = separatorSlotNames.serializedObject.isEditingMultipleObjects;
 			bool multi = separatorSlotNames.serializedObject.isEditingMultipleObjects;
 			bool hasTerminalSlot = false;
 			bool hasTerminalSlot = false;

+ 1 - 1
spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineAtlasAssetInspector.cs

@@ -63,7 +63,7 @@ namespace Spine.Unity.Editor {
 		}
 		}
 
 
 		static List<AtlasRegion> GetRegions (Atlas atlas) {
 		static List<AtlasRegion> GetRegions (Atlas atlas) {
-			FieldInfo regionsField = typeof(Atlas).GetField("regions", BindingFlags.Instance | BindingFlags.NonPublic);
+			FieldInfo regionsField = SpineInspectorUtility.GetNonPublicField(typeof(Atlas), "regions");
 			return (List<AtlasRegion>)regionsField.GetValue(atlas);
 			return (List<AtlasRegion>)regionsField.GetValue(atlas);
 		}
 		}
 
 

+ 4 - 0
spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineInspectorUtility.cs

@@ -83,6 +83,10 @@ namespace Spine.Unity.Editor {
 			return EditorGUIUtility.ObjectContent(null, type).image as Texture2D;
 			return EditorGUIUtility.ObjectContent(null, type).image as Texture2D;
 		}
 		}
 
 
+		public static FieldInfo GetNonPublicField (System.Type type, string fieldName) {
+			return type.GetField(fieldName, BindingFlags.NonPublic | BindingFlags.Instance);
+		}
+
 		#region SerializedProperty Helpers
 		#region SerializedProperty Helpers
 		public static SerializedProperty FindBaseOrSiblingProperty (this SerializedProperty property, string propertyName) {
 		public static SerializedProperty FindBaseOrSiblingProperty (this SerializedProperty property, string propertyName) {
 			if (string.IsNullOrEmpty(propertyName)) return null;
 			if (string.IsNullOrEmpty(propertyName)) return null;

+ 1 - 1
spine-unity/Assets/Spine/Editor/spine-unity/Modules/SkeletonRenderSeparator/Editor/SkeletonRenderSeparatorInspector.cs

@@ -211,7 +211,7 @@ namespace Spine.Unity.Modules {
 			serializedObject.ApplyModifiedProperties();
 			serializedObject.ApplyModifiedProperties();
 
 
 			if (slotsReapplyRequired && UnityEngine.Event.current.type == EventType.Repaint) {
 			if (slotsReapplyRequired && UnityEngine.Event.current.type == EventType.Repaint) {
-				SkeletonRendererInspector.ReapplySeparatorSlotNames(component.SkeletonRenderer);
+				component.SkeletonRenderer.ReapplySeparatorSlotNames();
 				component.SkeletonRenderer.LateUpdate();
 				component.SkeletonRenderer.LateUpdate();
 				SceneView.RepaintAll();
 				SceneView.RepaintAll();
 				slotsReapplyRequired = false;
 				slotsReapplyRequired = false;

+ 22 - 2
spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs

@@ -53,7 +53,7 @@ namespace Spine.Unity {
 
 
 		#region Advanced
 		#region Advanced
 		// Submesh Separation
 		// Submesh Separation
-		[UnityEngine.Serialization.FormerlySerializedAs("submeshSeparators")] [SpineSlot] public string[] separatorSlotNames = new string[0];
+		[UnityEngine.Serialization.FormerlySerializedAs("submeshSeparators")][SerializeField][SpineSlot] protected string[] separatorSlotNames = new string[0];
 		[System.NonSerialized] public readonly List<Slot> separatorSlots = new List<Slot>();
 		[System.NonSerialized] public readonly List<Slot> separatorSlots = new List<Slot>();
 
 
 		[Range(-0.1f, 0f)] public float zSpacing;
 		[Range(-0.1f, 0f)] public float zSpacing;
@@ -61,7 +61,7 @@ namespace Spine.Unity {
 		public bool useClipping = true;
 		public bool useClipping = true;
 		public bool immutableTriangles = false;
 		public bool immutableTriangles = false;
 		public bool pmaVertexColors = true;
 		public bool pmaVertexColors = true;
-		/// <summary>Clears the state when this component or its GameObject is disabled. This prevents previous state from being retained when it is enabled again. When pooling your skeleton, setting this to true can be helpful.</summary>
+		/// <summary>Clears the state of the render and skeleton when this component or its GameObject is disabled. This prevents previous state from being retained when it is enabled again. When pooling your skeleton, setting this to true can be helpful.</summary>
 		public bool clearStateOnDisable = false;
 		public bool clearStateOnDisable = false;
 		public bool tintBlack = false;
 		public bool tintBlack = false;
 		public bool singleSubmesh = false;
 		public bool singleSubmesh = false;
@@ -323,5 +323,25 @@ namespace Spine.Unity {
 			meshFilter.sharedMesh = currentMesh;
 			meshFilter.sharedMesh = currentMesh;
 			currentSmartMesh.instructionUsed.Set(currentInstructions);
 			currentSmartMesh.instructionUsed.Set(currentInstructions);
 		}
 		}
+
+		public void ReapplySeparatorSlotNames () {
+			if (!valid)
+				return;
+
+			separatorSlots.Clear();
+			for (int i = 0, n = separatorSlotNames.Length; i < n; i++) {
+				var slot = skeleton.FindSlot(separatorSlotNames[i]);
+				if (slot != null) {
+					separatorSlots.Add(slot);
+				}
+				#if UNITY_EDITOR
+				else
+				{
+					Debug.LogWarning(separatorSlotNames[i] + " is not a slot in " + skeletonDataAsset.skeletonJSON.name);
+				}
+				#endif
+			}
+		}
+
 	}
 	}
 }
 }

+ 48 - 29
spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Ghost/SkeletonGhost.cs

@@ -41,14 +41,21 @@ namespace Spine.Unity.Modules {
 		const HideFlags GhostHideFlags = HideFlags.HideInHierarchy;
 		const HideFlags GhostHideFlags = HideFlags.HideInHierarchy;
 		const string GhostingShaderName = "Spine/Special/SkeletonGhost";
 		const string GhostingShaderName = "Spine/Special/SkeletonGhost";
 
 
+		[Header("Animation")]
 		public bool ghostingEnabled = true;
 		public bool ghostingEnabled = true;
-		public float spawnRate = 0.05f;
-		public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00); // default for additive.
-		[Tooltip("Remember to set color alpha to 0 if Additive is true")]
-		public bool additive = true;
+		[Tooltip("The time between invididual ghost pieces being spawned.")]
+		[UnityEngine.Serialization.FormerlySerializedAs("spawnRate")]
+		public float spawnInterval = 0.05f;
+		[Tooltip("Maximum number of ghosts that can exist at a time. If the fade speed is not fast enough, the oldest ghost will immediately disappear to enforce the maximum number.")]
 		public int maximumGhosts = 10;
 		public int maximumGhosts = 10;
+		[Tooltip("Fadespeed 1 means it will take 1 second for a piece to fade out. 2 means it will take 1/2 second. 10 means it will take 1/10 of a second.")]
 		public float fadeSpeed = 10;
 		public float fadeSpeed = 10;
+
+		[Header("Rendering")]
 		public Shader ghostShader;
 		public Shader ghostShader;
+		public Color32 color = new Color32(0xFF, 0xFF, 0xFF, 0x00); // default for additive.
+		[Tooltip("Remember to set color alpha to 0 if Additive is true")]
+		public bool additive = true;
 		[Tooltip("0 is Color and Alpha, 1 is Alpha only.")]
 		[Tooltip("0 is Color and Alpha, 1 is Alpha only.")]
 		[Range(0, 1)]
 		[Range(0, 1)]
 		public float textureFade = 1;
 		public float textureFade = 1;
@@ -67,23 +74,30 @@ namespace Spine.Unity.Modules {
 		readonly Dictionary<Material, Material> materialTable = new Dictionary<Material, Material>();
 		readonly Dictionary<Material, Material> materialTable = new Dictionary<Material, Material>();
 
 
 		void Start () {
 		void Start () {
-			if (ghostShader == null)
-				ghostShader = Shader.Find(GhostingShaderName);
-
-			skeletonRenderer = GetComponent<SkeletonRenderer>();
-			meshFilter = GetComponent<MeshFilter>();
-			meshRenderer = GetComponent<MeshRenderer>();
-			nextSpawnTime = Time.time + spawnRate;
-			pool = new SkeletonGhostRenderer[maximumGhosts];
-			for (int i = 0; i < maximumGhosts; i++) {
-				GameObject go = new GameObject(gameObject.name + " Ghost", typeof(SkeletonGhostRenderer));
-				pool[i] = go.GetComponent<SkeletonGhostRenderer>();
-				go.SetActive(false);
-				go.hideFlags = GhostHideFlags;
-			}
+			Initialize(false);
+		}
+
+		public void Initialize (bool overwrite) {
+			if (pool == null || overwrite) {
+				if (ghostShader == null)
+					ghostShader = Shader.Find(GhostingShaderName);
+
+				skeletonRenderer = GetComponent<SkeletonRenderer>();
+				meshFilter = GetComponent<MeshFilter>();
+				meshRenderer = GetComponent<MeshRenderer>();
+				nextSpawnTime = Time.time + spawnInterval;
+				pool = new SkeletonGhostRenderer[maximumGhosts];
+				for (int i = 0; i < maximumGhosts; i++) {
+					GameObject go = new GameObject(gameObject.name + " Ghost", typeof(SkeletonGhostRenderer));
+					pool[i] = go.GetComponent<SkeletonGhostRenderer>();
+					go.SetActive(false);
+					go.hideFlags = GhostHideFlags;
+				}
 
 
-			var skeletonAnimation = skeletonRenderer as Spine.Unity.IAnimationStateComponent;
-			if (skeletonAnimation != null) skeletonAnimation.AnimationState.Event += OnEvent;
+				var skeletonAnimation = skeletonRenderer as Spine.Unity.IAnimationStateComponent;
+				if (skeletonAnimation != null)
+					skeletonAnimation.AnimationState.Event += OnEvent;
+			}
 		}
 		}
 
 
 		//SkeletonAnimation
 		//SkeletonAnimation
@@ -96,7 +110,7 @@ namespace Spine.Unity.Modules {
 			if (e.Data.Name.Equals("Ghosting", System.StringComparison.Ordinal)) {
 			if (e.Data.Name.Equals("Ghosting", System.StringComparison.Ordinal)) {
 				ghostingEnabled = e.Int > 0;
 				ghostingEnabled = e.Int > 0;
 				if (e.Float > 0)
 				if (e.Float > 0)
-					spawnRate = e.Float;
+					spawnInterval = e.Float;
 				
 				
 				if (!string.IsNullOrEmpty(e.stringValue))
 				if (!string.IsNullOrEmpty(e.stringValue))
 					this.color = HexToColor(e.String);
 					this.color = HexToColor(e.String);
@@ -121,11 +135,14 @@ namespace Spine.Unity.Modules {
 					var originalMat = materials[i];
 					var originalMat = materials[i];
 					Material ghostMat;
 					Material ghostMat;
 					if (!materialTable.ContainsKey(originalMat)) {
 					if (!materialTable.ContainsKey(originalMat)) {
-						ghostMat = new Material(originalMat);
-						ghostMat.shader = ghostShader;
-						ghostMat.color = Color.white;
+						ghostMat = new Material(originalMat) {
+							shader = ghostShader,
+							color = Color.white
+						};
+
 						if (ghostMat.HasProperty("_TextureFade"))
 						if (ghostMat.HasProperty("_TextureFade"))
 							ghostMat.SetFloat("_TextureFade", textureFade);
 							ghostMat.SetFloat("_TextureFade", textureFade);
+
 						materialTable.Add(originalMat, ghostMat);
 						materialTable.Add(originalMat, ghostMat);
 					} else {
 					} else {
 						ghostMat = materialTable[originalMat];
 						ghostMat = materialTable[originalMat];
@@ -150,7 +167,7 @@ namespace Spine.Unity.Modules {
 				if (poolIndex == pool.Length)
 				if (poolIndex == pool.Length)
 					poolIndex = 0;
 					poolIndex = 0;
 
 
-				nextSpawnTime = Time.time + spawnRate;
+				nextSpawnTime = Time.time + spawnInterval;
 			}
 			}
 		}
 		}
 
 
@@ -166,16 +183,18 @@ namespace Spine.Unity.Modules {
 
 
 		//based on UnifyWiki  http://wiki.unity3d.com/index.php?title=HexConverter
 		//based on UnifyWiki  http://wiki.unity3d.com/index.php?title=HexConverter
 		static Color32 HexToColor (string hex) {
 		static Color32 HexToColor (string hex) {
+			const System.Globalization.NumberStyles HexNumber = System.Globalization.NumberStyles.HexNumber;
+
 			if (hex.Length < 6)
 			if (hex.Length < 6)
 				return Color.magenta;
 				return Color.magenta;
 
 
 			hex = hex.Replace("#", "");
 			hex = hex.Replace("#", "");
-			byte r = byte.Parse(hex.Substring(0, 2), System.Globalization.NumberStyles.HexNumber);
-			byte g = byte.Parse(hex.Substring(2, 2), System.Globalization.NumberStyles.HexNumber);
-			byte b = byte.Parse(hex.Substring(4, 2), System.Globalization.NumberStyles.HexNumber);
+			byte r = byte.Parse(hex.Substring(0, 2), HexNumber);
+			byte g = byte.Parse(hex.Substring(2, 2), HexNumber);
+			byte b = byte.Parse(hex.Substring(4, 2), HexNumber);
 			byte a = 0xFF;
 			byte a = 0xFF;
 			if (hex.Length == 8)
 			if (hex.Length == 8)
-				a = byte.Parse(hex.Substring(6, 2), System.Globalization.NumberStyles.HexNumber);
+				a = byte.Parse(hex.Substring(6, 2), HexNumber);
 
 
 			return new Color32(r, g, b, a);
 			return new Color32(r, g, b, a);
 		}
 		}

+ 11 - 1
spine-unity/Assets/Spine/Runtime/spine-unity/SkeletonExtensions.cs

@@ -543,6 +543,16 @@ namespace Spine {
 			System.Array.Copy(slotsItems, drawOrder.Items, n);
 			System.Array.Copy(slotsItems, drawOrder.Items, n);
 		}
 		}
 
 
+		/// <summary>Resets all the slots on the skeleton to their Setup Pose attachments but does not reset slot colors.</summary>
+		public static void SetSlotAttachmentsToSetupPose (this Skeleton skeleton) {
+			var slotsItems = skeleton.slots.Items;
+			for (int i = 0; i < skeleton.slots.Count; i++) {
+				Slot slot = slotsItems[i];
+				string attachmentName = slot.data.attachmentName;
+				slot.Attachment = string.IsNullOrEmpty(attachmentName) ? null : skeleton.GetAttachment(i, attachmentName);
+			}
+		}
+
 		/// <summary>Resets the color of a slot to Setup Pose value.</summary>
 		/// <summary>Resets the color of a slot to Setup Pose value.</summary>
 		public static void SetColorToSetupPose (this Slot slot) {
 		public static void SetColorToSetupPose (this Slot slot) {
 			slot.r = slot.data.r;
 			slot.r = slot.data.r;
@@ -563,7 +573,7 @@ namespace Spine {
 		/// <summary>Resets the attachment of slot at a given slotIndex to setup pose. This is faster than Slot.SetAttachmentToSetupPose.</summary>
 		/// <summary>Resets the attachment of slot at a given slotIndex to setup pose. This is faster than Slot.SetAttachmentToSetupPose.</summary>
 		public static void SetSlotAttachmentToSetupPose (this Skeleton skeleton, int slotIndex) {
 		public static void SetSlotAttachmentToSetupPose (this Skeleton skeleton, int slotIndex) {
 			var slot = skeleton.slots.Items[slotIndex];
 			var slot = skeleton.slots.Items[slotIndex];
-			var attachmentName = slot.data.attachmentName;
+			string attachmentName = slot.data.attachmentName;
 			if (string.IsNullOrEmpty(attachmentName)) {
 			if (string.IsNullOrEmpty(attachmentName)) {
 				slot.Attachment = null;
 				slot.Attachment = null;
 			} else {
 			} else {