Kaynağa Gözat

Merge branch '3.7-beta' of https://github.com/esotericsoftware/spine-runtimes into 3.7-beta

badlogic 7 yıl önce
ebeveyn
işleme
854ec90657
19 değiştirilmiş dosya ile 443 ekleme ve 83 silme
  1. 16 8
      spine-csharp/src/Animation.cs
  2. 13 8
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonDataAssetInspector.cs
  3. 2 3
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonRendererInspector.cs
  4. 26 14
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs
  5. 35 1
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineInspectorUtility.cs
  6. 2 2
      spine-unity/Assets/Spine/Editor/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs
  7. 1 1
      spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/BlendModeMaterialsAsset.cs
  8. 19 27
      spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs
  9. 3 2
      spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs
  10. 9 0
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier.meta
  11. 265 0
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/AnimationMatchModifierAsset.cs
  12. 12 0
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/AnimationMatchModifierAsset.cs.meta
  13. 6 0
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/AnimationMatchModifierAsset.txt
  14. 8 0
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/AnimationMatchModifierAsset.txt.meta
  15. BIN
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/Default Match All Animations.asset
  16. 9 0
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/Default Match All Animations.asset.meta
  17. 1 1
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AttachmentTools/AttachmentTools.cs
  18. 1 1
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Shaders/Spine-Skeleton-Fill.shader
  19. 15 15
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs

+ 16 - 8
spine-csharp/src/Animation.cs

@@ -169,6 +169,14 @@ namespace Spine {
 		TwoColor
 	}
 
+	public interface IBoneTimeline {
+		int BoneIndex { get; }
+	}
+
+	public interface ISlotTimeline {
+		int SlotIndex { get; }
+	}
+
 	/// <summary>Base class for frames that use an interpolation bezier curve.</summary>
 	abstract public class CurveTimeline : Timeline {
 		protected const float LINEAR = 0, STEPPED = 1, BEZIER = 2;
@@ -251,7 +259,7 @@ namespace Spine {
 		}
 	}
 
-	public class RotateTimeline : CurveTimeline {
+	public class RotateTimeline : CurveTimeline, IBoneTimeline {
 		public const int ENTRIES = 2;
 		internal const int PREV_TIME = -2, PREV_ROTATION = -1;
 		internal const int ROTATION = 1;
@@ -339,7 +347,7 @@ namespace Spine {
 		}
 	}
 
-	public class TranslateTimeline : CurveTimeline {
+	public class TranslateTimeline : CurveTimeline, IBoneTimeline {
 		public const int ENTRIES = 3;
 		protected const int PREV_TIME = -3, PREV_X = -2, PREV_Y = -1;
 		protected const int X = 1, Y = 2;
@@ -419,7 +427,7 @@ namespace Spine {
 		}
 	}
 
-	public class ScaleTimeline : TranslateTimeline {
+	public class ScaleTimeline : TranslateTimeline, IBoneTimeline {
 		override public int PropertyId {
 			get { return ((int)TimelineType.Scale << 24) + boneIndex; }
 		}
@@ -522,7 +530,7 @@ namespace Spine {
 		}
 	}
 
-	public class ShearTimeline : TranslateTimeline {
+	public class ShearTimeline : TranslateTimeline, IBoneTimeline {
 		override public int PropertyId {
 			get { return ((int)TimelineType.Shear << 24) + boneIndex; }
 		}
@@ -582,7 +590,7 @@ namespace Spine {
 		}
 	}
 
-	public class ColorTimeline : CurveTimeline {
+	public class ColorTimeline : CurveTimeline, ISlotTimeline {
 		public const int ENTRIES = 5;
 		protected const int PREV_TIME = -5, PREV_R = -4, PREV_G = -3, PREV_B = -2, PREV_A = -1;
 		protected const int R = 1, G = 2, B = 3, A = 4;
@@ -683,7 +691,7 @@ namespace Spine {
 		}
 	}
 
-	public class TwoColorTimeline : CurveTimeline {
+	public class TwoColorTimeline : CurveTimeline, ISlotTimeline {
 		public const int ENTRIES = 8;
 		protected const int PREV_TIME = -8, PREV_R = -7, PREV_G = -6, PREV_B = -5, PREV_A = -4;
 		protected const int PREV_R2 = -3, PREV_G2 = -2, PREV_B2 = -1;
@@ -826,7 +834,7 @@ namespace Spine {
 
 	}
 
-	public class AttachmentTimeline : Timeline {
+	public class AttachmentTimeline : Timeline, ISlotTimeline {
 		internal int slotIndex;
 		internal float[] frames;
 		internal string[] attachmentNames;
@@ -880,7 +888,7 @@ namespace Spine {
 		}
 	}
 
-	public class DeformTimeline : CurveTimeline {
+	public class DeformTimeline : CurveTimeline, ISlotTimeline {
 		internal int slotIndex;
 		internal float[] frames;
 		internal float[][] frameVertices;

+ 13 - 8
spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonDataAssetInspector.cs

@@ -30,6 +30,11 @@
 
 #define SPINE_SKELETON_MECANIM
 
+ #if (UNITY_2017_4 || UNITY_2018)
+ #define SPINE_UNITY_2018_PREVIEW_API
+ #endif
+
+
 using System;
 using System.Reflection;
 using System.Collections.Generic;
@@ -51,7 +56,7 @@ namespace Spine.Unity.Editor {
 		internal static bool showAttachments = false;
 
 		SerializedProperty atlasAssets, skeletonJSON, scale, fromAnimation, toAnimation, duration, defaultMix;
-		SerializedProperty blendModeMaterials;
+		SerializedProperty skeletonDataModifiers;
 		#if SPINE_TK2D
 		SerializedProperty spriteCollection;
 		#endif
@@ -101,7 +106,7 @@ namespace Spine.Unity.Editor {
 			duration = serializedObject.FindProperty("duration");
 			defaultMix = serializedObject.FindProperty("defaultMix");
 
-			blendModeMaterials = serializedObject.FindProperty("blendModeMaterials");
+			skeletonDataModifiers = serializedObject.FindProperty("skeletonDataModifiers");
 
 			#if SPINE_SKELETON_MECANIM
 			controller = serializedObject.FindProperty("controller");
@@ -302,6 +307,8 @@ namespace Spine.Unity.Editor {
 			}
 			EditorGUILayout.PropertyField(skeletonJSON, SpineInspectorUtility.TempContent(skeletonJSON.displayName, Icons.spine));
 			EditorGUILayout.PropertyField(scale);
+			EditorGUILayout.Space();
+			EditorGUILayout.PropertyField(skeletonDataModifiers, true);
 		}
 
 		void DrawAtlasAssetsFields () {
@@ -318,8 +325,6 @@ namespace Spine.Unity.Editor {
 
 			if (atlasAssets.arraySize == 0)
 				EditorGUILayout.HelpBox("AtlasAssets array is empty. Skeleton's attachments will load without being mapped to images.", MessageType.Info);
-
-			EditorGUILayout.PropertyField(blendModeMaterials);
 		}
 
 		void HandleAtlasAssetsNulls () {
@@ -681,7 +686,7 @@ namespace Spine.Unity.Editor {
 		GameObject previewGameObject;
 		internal bool requiresRefresh;
 
-		#if !(UNITY_2017_4 || UNITY_2018)
+		#if !SPINE_UNITY_2018_PREVIEW_API
 		float animationLastTime;
 		#endif
 
@@ -777,7 +782,7 @@ namespace Spine.Unity.Editor {
 
 			if (previewRenderUtility == null) {
 				previewRenderUtility = new PreviewRenderUtility(true);
-				#if !(UNITY_2017_4 || UNITY_2018)
+				#if !SPINE_UNITY_2018_PREVIEW_API
 				animationLastTime = CurrentTime;
 				#endif
 
@@ -808,7 +813,7 @@ namespace Spine.Unity.Editor {
 							skeletonAnimation.LateUpdate();
 							previewGameObject.GetComponent<Renderer>().enabled = false;
 
-							#if UNITY_2017_4 || UNITY_2018
+							#if SPINE_UNITY_2018_PREVIEW_API
 							previewRenderUtility.AddSingleGO(previewGameObject);
 							#endif
 						}
@@ -869,7 +874,7 @@ namespace Spine.Unity.Editor {
 
 				
 				if (!EditorApplication.isPlaying) {
-					#if !(UNITY_2017_4 || UNITY_2018)
+					#if !SPINE_UNITY_2018_PREVIEW_API
 					float current = CurrentTime;
 					float deltaTime = (current - animationLastTime);
 					skeletonAnimation.Update(deltaTime);

+ 2 - 3
spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonRendererInspector.cs

@@ -253,8 +253,8 @@ namespace Spine.Unity.Editor {
 					
 					using (new SpineInspectorUtility.IndentScope()) {
 						using (new EditorGUILayout.HorizontalScope()) {
-							initialFlipX.boolValue = EditorGUILayout.ToggleLeft(initialFlipX.displayName, initialFlipX.boolValue, GUILayout.Width(120f));
-							initialFlipY.boolValue = EditorGUILayout.ToggleLeft(initialFlipY.displayName, initialFlipY.boolValue, GUILayout.Width(120f));
+							SpineInspectorUtility.ToggleLeftLayout(initialFlipX);
+							SpineInspectorUtility.ToggleLeftLayout(initialFlipY);							
 							EditorGUILayout.Space();
 						}
 
@@ -370,7 +370,6 @@ namespace Spine.Unity.Editor {
 		}
 
 		override public void OnInspectorGUI () {
-			//serializedObject.Update();
 			bool multi = serializedObject.isEditingMultipleObjects;
 			DrawInspectorGUI(multi);
 			if (serializedObject.ApplyModifiedProperties() || SpineInspectorUtility.UndoRedoPerformed(Event.current)) {

+ 26 - 14
spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs

@@ -206,15 +206,15 @@ namespace Spine.Unity.Editor {
 
 			// Data Refresh Edit Mode.
 			// This prevents deserialized SkeletonData from persisting from play mode to edit mode.
-#if UNITY_2017_2_OR_NEWER
-			EditorApplication.playModeStateChanged -= DataReloadHandler.OnPlaymodeStateChanged;
-			EditorApplication.playModeStateChanged += DataReloadHandler.OnPlaymodeStateChanged;
-			DataReloadHandler.OnPlaymodeStateChanged(PlayModeStateChange.EnteredEditMode);
-#else
-			EditorApplication.playmodeStateChanged -= DataReloadHandler.OnPlaymodeStateChanged;
-			EditorApplication.playmodeStateChanged += DataReloadHandler.OnPlaymodeStateChanged;
-			DataReloadHandler.OnPlaymodeStateChanged();
-#endif
+//#if UNITY_2017_2_OR_NEWER
+//			EditorApplication.playModeStateChanged -= DataReloadHandler.OnPlaymodeStateChanged;
+//			EditorApplication.playModeStateChanged += DataReloadHandler.OnPlaymodeStateChanged;
+//			DataReloadHandler.OnPlaymodeStateChanged(PlayModeStateChange.EnteredEditMode);
+//#else
+//			EditorApplication.playmodeStateChanged -= DataReloadHandler.OnPlaymodeStateChanged;
+//			EditorApplication.playmodeStateChanged += DataReloadHandler.OnPlaymodeStateChanged;
+//			DataReloadHandler.OnPlaymodeStateChanged();
+//#endif
 
 			initialized = true;
 		}
@@ -351,10 +351,15 @@ namespace Spine.Unity.Editor {
 #else
 			internal static void OnPlaymodeStateChanged () {
 #endif
-				ReloadAllActiveSkeletons();
+				ReloadAllActiveSkeletonsEditMode();
 			}
 
-			static void ReloadAllActiveSkeletons () {
+			static void ReloadAllActiveSkeletonsEditMode () {
+				if (EditorApplication.isPaused) return;
+				if (EditorApplication.isPlaying) return;
+				if (EditorApplication.isCompiling) return;
+				if (EditorApplication.isPlayingOrWillChangePlaymode) return;
+
 				var skeletonDataAssetsToReload = new HashSet<SkeletonDataAsset>();
 
 				var activeSkeletonRenderers = GameObject.FindObjectsOfType<SkeletonRenderer>();
@@ -369,11 +374,18 @@ namespace Spine.Unity.Editor {
 					if (skeletonDataAsset != null) skeletonDataAssetsToReload.Add(skeletonDataAsset);
 				}
 
-				foreach (var sda in skeletonDataAssetsToReload)
+				foreach (var sda in skeletonDataAssetsToReload) {
 					sda.Clear();
+					sda.GetSkeletonData(true);
+				}
 
-				foreach (var sr in activeSkeletonRenderers)	sr.Initialize(true);
-				foreach (var sg in activeSkeletonGraphics) sg.Initialize(true);
+				foreach (var sr in activeSkeletonRenderers) {
+					sr.Initialize(true);
+				}
+
+				foreach (var sg in activeSkeletonGraphics) {
+					sg.Initialize(true);
+				}
 			}
 		}
 

+ 35 - 1
spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineInspectorUtility.cs

@@ -71,6 +71,40 @@ namespace Spine.Unity.Editor {
 			PropertyFieldWideLabel(property, label, width);
 		}
 
+		/// <summary>Multi-edit-compatible version of EditorGUILayout.ToggleLeft(SerializedProperty)</summary>
+		public static void ToggleLeftLayout (SerializedProperty property, GUIContent label = null, float width = 120f) {
+			if (label == null) label = SpineInspectorUtility.TempContent(property.displayName, tooltip: property.tooltip);
+
+			if (property.hasMultipleDifferentValues) {
+				bool previousShowMixedValue = EditorGUI.showMixedValue;
+				EditorGUI.showMixedValue = true;
+
+				bool clicked = EditorGUILayout.ToggleLeft(label, property.boolValue, GUILayout.Width(width));
+				if (clicked) property.boolValue = true; // Set all values to true when clicked.
+
+				EditorGUI.showMixedValue = previousShowMixedValue;
+			} else {
+				property.boolValue = EditorGUILayout.ToggleLeft(label, property.boolValue, GUILayout.Width(width));
+			}
+		}
+
+		/// <summary>Multi-edit-compatible version of EditorGUILayout.ToggleLeft(SerializedProperty)</summary>
+		public static void ToggleLeft (Rect rect, SerializedProperty property, GUIContent label = null) {
+			if (label == null) label = SpineInspectorUtility.TempContent(property.displayName, tooltip: property.tooltip);
+
+			if (property.hasMultipleDifferentValues) {
+				bool previousShowMixedValue = EditorGUI.showMixedValue;
+				EditorGUI.showMixedValue = true;
+
+				bool clicked = EditorGUI.ToggleLeft(rect, label, property.boolValue);
+				if (clicked) property.boolValue = true; // Set all values to true when clicked.
+
+				EditorGUI.showMixedValue = previousShowMixedValue;
+			} else {
+				property.boolValue = EditorGUI.ToggleLeft(rect, label, property.boolValue);
+			}
+		}
+
 		public static bool UndoRedoPerformed (UnityEngine.Event current) {
 			return current.type == EventType.ValidateCommand && current.commandName == "UndoRedoPerformed";
 		}
@@ -342,7 +376,7 @@ namespace Spine.Unity.Editor {
 			EditorGUILayout.PropertyField(prop.sortingOrder, OrderInLayerLabel);
 
 			if (applyModifiedProperties && EditorGUI.EndChangeCheck())
-					prop.ApplyModifiedProperties();
+				prop.ApplyModifiedProperties();
 		}
 		#endregion
 	}

+ 2 - 2
spine-unity/Assets/Spine/Editor/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs

@@ -99,9 +99,9 @@ namespace Spine.Unity.Editor {
 				EditorGUI.PrefixLabel(rect, SpineInspectorUtility.TempContent("Initial Flip"));
 				rect.x += EditorGUIUtility.labelWidth;
 				rect.width = 30f;
-				initialFlipX.boolValue = EditorGUI.ToggleLeft(rect, SpineInspectorUtility.TempContent("X", tooltip:"initialFlipX"), initialFlipX.boolValue);
+				SpineInspectorUtility.ToggleLeft(rect, initialFlipX, SpineInspectorUtility.TempContent("X", tooltip: "initialFlipX"));
 				rect.x += 35f;
-				initialFlipY.boolValue = EditorGUI.ToggleLeft(rect, SpineInspectorUtility.TempContent("Y", tooltip:"initialFlipY"), initialFlipY.boolValue);
+				SpineInspectorUtility.ToggleLeft(rect, initialFlipY, SpineInspectorUtility.TempContent("Y", tooltip: "initialFlipY"));
 			}
 
 			EditorGUILayout.Space();

+ 1 - 1
spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/BlendModeMaterialsAsset.cs

@@ -37,7 +37,7 @@ using Spine;
 using Spine.Unity;
 
 namespace Spine.Unity {
-	[CreateAssetMenu(menuName = "Spine/Blend Mode Materials Asset", order = 200)]
+	[CreateAssetMenu(menuName = "Spine/SkeletonData Modifiers/Blend Mode Materials", order = 200)]
 	public class BlendModeMaterialsAsset : SkeletonDataModifierAsset {
 		public Material multiplyMaterialTemplate;
 		public Material screenMaterialTemplate;

+ 19 - 27
spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs

@@ -97,19 +97,13 @@ namespace Spine.Unity {
 				if (string.IsNullOrEmpty(value)) {
 					state.ClearTrack(0);
 				} else {
-					TrySetAnimation(value, loop);
+					var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(value);
+					if (animationObject != null)
+						state.SetAnimation(0, animationObject, loop);
 				}
 			}
 		}
 
-		TrackEntry TrySetAnimation (string animationName, bool animationLoop) {
-			var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(animationName);
-			if (animationObject != null)
-				return state.SetAnimation(0, animationObject, animationLoop);
-
-			return null;
-		}
-
 		/// <summary>Whether or not <see cref="AnimationName"/> 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>
 		public bool loop;
 
@@ -153,27 +147,25 @@ namespace Spine.Unity {
 				return;
 
 			state = new Spine.AnimationState(skeletonDataAsset.GetAnimationStateData());
-
-			#if UNITY_EDITOR
+		
 			if (!string.IsNullOrEmpty(_animationName)) {
-				if (Application.isPlaying) {
-					TrackEntry startingTrack = TrySetAnimation(_animationName, loop);
-					if (startingTrack != null)
-						Update(0);
-				} else {
-					// Assume SkeletonAnimation is valid for skeletonData and skeleton. Checked above.
-					var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName);
-					if (animationObject != null)
-						animationObject.PoseSkeleton(skeleton, 0f);
+				var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(_animationName);
+				if (animationObject != null) {
+					animationObject.PoseSkeleton(skeleton, 0f);
+					skeleton.UpdateWorldTransform();
+
+					#if UNITY_EDITOR
+					if (Application.isPlaying) {
+					#endif
+
+						// Make this block not run in Unity Editor edit mode.
+						state.SetAnimation(0, animationObject, loop);
+
+					#if UNITY_EDITOR
+					}
+					#endif
 				}
 			}
-			#else
-			if (!string.IsNullOrEmpty(_animationName)) {
-				TrackEntry startingTrack = TrySetAnimation(_animationName, loop);
-				if (startingTrack != null)
-					Update(0);
-			}
-			#endif
 		}
 
 		void Update () {

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

@@ -166,7 +166,8 @@ namespace Spine.Unity {
 		/// <summary>
 		/// Clears the previously generated mesh and resets the skeleton's pose.</summary>
 		public virtual void ClearState () {
-			meshFilter.sharedMesh = null;
+			var meshFilter = GetComponent<MeshFilter>();
+			if (meshFilter != null) meshFilter.sharedMesh = null;
 			currentInstructions.Clear();
 			if (skeleton != null) skeleton.SetToSetupPose();
 		}
@@ -193,7 +194,7 @@ namespace Spine.Unity {
 				valid = false;
 			}
 
-			if (!skeletonDataAsset) {
+			if (skeletonDataAsset == null) {
 				if (logErrors) Debug.LogError("Missing SkeletonData asset.", this);
 				return;
 			}

+ 9 - 0
spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: bdfae2bc4b385b84eb4f5f6855d0f991
+folderAsset: yes
+timeCreated: 1537527020
+licenseType: Pro
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 265 - 0
spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/AnimationMatchModifierAsset.cs

@@ -0,0 +1,265 @@
+/******************************************************************************
+ * Spine Runtimes Software License v2.5
+ *
+ * Copyright (c) 2013-2016, 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 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 develop new applications using the Spine Runtimes or otherwise
+ * create derivative works or improvements of the Spine Runtimes 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, BUSINESS INTERRUPTION, OR LOSS OF
+ * USE, DATA, OR PROFITS) 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 System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+using Spine;
+using Spine.Unity;
+
+namespace Spine.Unity.Modules {
+
+	//[CreateAssetMenu(menuName = "Spine/SkeletonData Modifiers/Animation Match", order = 200)]
+	public class AnimationMatchModifierAsset : SkeletonDataModifierAsset {
+
+		public bool matchAllAnimations = true;
+
+		public override void Apply (SkeletonData skeletonData) {
+			if (matchAllAnimations)
+				AnimationTools.MatchAnimationTimelines(skeletonData.animations, skeletonData);
+		}
+
+		public static class AnimationTools {
+
+			#region Filler Timelines
+			/// <summary>
+			/// Matches the animation timelines across the given set of animations.
+			/// This allows unkeyed properties to assume setup pose when animations are naively mixed using Animation.Apply.
+			/// </summary>
+			/// <param name="animations">An enumerable collection animations whose timelines will be matched.</param>
+			/// <param name="skeletonData">The SkeletonData where the animations belong.</param>
+			public static void MatchAnimationTimelines (IEnumerable<Spine.Animation> animations, SkeletonData skeletonData) {
+				if (animations == null) return;
+				if (skeletonData == null) throw new System.ArgumentNullException("skeletonData", "Timelines can't be matched without a SkeletonData source.");
+
+				// Build a reference collection of timelines to match
+				// and a collection of dummy timelines that can be used to fill-in missing items.
+				var timelineDictionary = new Dictionary<int, Spine.Timeline>();
+				foreach (var animation in animations) {
+					foreach (var timeline in animation.timelines) {
+						if (timeline is EventTimeline) continue;
+
+						int propertyID = timeline.PropertyId;
+						if (!timelineDictionary.ContainsKey(propertyID)) {
+							timelineDictionary.Add(propertyID, GetFillerTimeline(timeline, skeletonData));
+						}
+					}
+				}
+				var idsToMatch = new List<int>(timelineDictionary.Keys);
+
+				// For each animation in the list, check for and add missing timelines.
+				var currentAnimationIDs = new HashSet<int>();
+				foreach (var animation in animations) {
+					currentAnimationIDs.Clear();
+					foreach (var timeline in animation.timelines) {
+						if (timeline is EventTimeline) continue;
+						currentAnimationIDs.Add(timeline.PropertyId);
+					}
+
+					var animationTimelines = animation.timelines;
+					foreach (int propertyID in idsToMatch) {
+						if (!currentAnimationIDs.Contains(propertyID))
+							animationTimelines.Add(timelineDictionary[propertyID]);
+					}
+				}
+
+				// These are locals, but sometimes Unity's GC does weird stuff. So let's clean up.
+				timelineDictionary.Clear();
+				timelineDictionary = null;
+				idsToMatch.Clear();
+				idsToMatch = null;
+				currentAnimationIDs.Clear();
+				currentAnimationIDs = null;
+			}
+
+			static Timeline GetFillerTimeline (Timeline timeline, SkeletonData skeletonData) {
+				int propertyID = timeline.PropertyId;
+				int tt = propertyID >> 24;
+				var timelineType = (TimelineType)tt;
+
+				switch (timelineType) {
+					// Bone
+					case TimelineType.Rotate:
+						return GetFillerTimeline((RotateTimeline)timeline, skeletonData);
+					case TimelineType.Translate:
+						return GetFillerTimeline((TranslateTimeline)timeline, skeletonData);
+					case TimelineType.Scale:
+						return GetFillerTimeline((ScaleTimeline)timeline, skeletonData);
+					case TimelineType.Shear:
+						return GetFillerTimeline((ShearTimeline)timeline, skeletonData);
+
+					// Slot
+					case TimelineType.Attachment:
+						return GetFillerTimeline((AttachmentTimeline)timeline, skeletonData);
+					case TimelineType.Color:
+						return GetFillerTimeline((ColorTimeline)timeline, skeletonData);
+					case TimelineType.TwoColor:
+						return GetFillerTimeline((TwoColorTimeline)timeline, skeletonData);
+					case TimelineType.Deform:
+						return GetFillerTimeline((DeformTimeline)timeline, skeletonData);
+
+					// Skeleton
+					case TimelineType.DrawOrder:
+						return GetFillerTimeline((DrawOrderTimeline)timeline, skeletonData);
+
+					// IK Constraint
+					case TimelineType.IkConstraint:
+						return GetFillerTimeline((IkConstraintTimeline)timeline, skeletonData);
+
+					// TransformConstraint
+					case TimelineType.TransformConstraint:
+						return GetFillerTimeline((TransformConstraintTimeline)timeline, skeletonData);
+
+					// Path Constraint
+					case TimelineType.PathConstraintPosition:
+						return GetFillerTimeline((PathConstraintPositionTimeline)timeline, skeletonData);
+					case TimelineType.PathConstraintSpacing:
+						return GetFillerTimeline((PathConstraintSpacingTimeline)timeline, skeletonData);
+					case TimelineType.PathConstraintMix:
+						return GetFillerTimeline((PathConstraintMixTimeline)timeline, skeletonData);
+				}
+
+				return null;
+			}
+
+			static RotateTimeline GetFillerTimeline (RotateTimeline timeline, SkeletonData skeletonData) {
+				var t = new RotateTimeline(1);
+				t.boneIndex = timeline.boneIndex;
+				t.SetFrame(0, 0, 0);
+				return t;
+			}
+
+			static TranslateTimeline GetFillerTimeline (TranslateTimeline timeline, SkeletonData skeletonData) {
+				var t = new TranslateTimeline(1);
+				t.boneIndex = timeline.boneIndex;
+				t.SetFrame(0, 0, 0, 0);
+				return t;
+			}
+
+			static ScaleTimeline GetFillerTimeline (ScaleTimeline timeline, SkeletonData skeletonData) {
+				var t = new ScaleTimeline(1);
+				t.boneIndex = timeline.boneIndex;
+				t.SetFrame(0, 0, 0, 0);
+				return t;
+			}
+
+			static ShearTimeline GetFillerTimeline (ShearTimeline timeline, SkeletonData skeletonData) {
+				var t = new ShearTimeline(1);
+				t.boneIndex = timeline.boneIndex;
+				t.SetFrame(0, 0, 0, 0);
+				return t;
+			}
+
+			static AttachmentTimeline GetFillerTimeline (AttachmentTimeline timeline, SkeletonData skeletonData) {
+				var t = new AttachmentTimeline(1);
+				t.slotIndex = timeline.slotIndex;
+				var slotData = skeletonData.slots.Items[t.slotIndex];
+				t.SetFrame(0, 0, slotData.attachmentName);
+				return t;
+			}
+
+			static ColorTimeline GetFillerTimeline (ColorTimeline timeline, SkeletonData skeletonData) {
+				var t = new ColorTimeline(1);
+				t.slotIndex = timeline.slotIndex;
+				var slotData = skeletonData.slots.Items[t.slotIndex];
+				t.SetFrame(0, 0, slotData.r, slotData.g, slotData.b, slotData.a);
+				return t;
+			}
+
+			static TwoColorTimeline GetFillerTimeline (TwoColorTimeline timeline, SkeletonData skeletonData) {
+				var t = new TwoColorTimeline(1);
+				t.slotIndex = timeline.slotIndex;
+				var slotData = skeletonData.slots.Items[t.slotIndex];
+				t.SetFrame(0, 0, slotData.r, slotData.g, slotData.b, slotData.a, slotData.r2, slotData.g2, slotData.b2);
+				return t;
+			}
+
+			static DeformTimeline GetFillerTimeline (DeformTimeline timeline, SkeletonData skeletonData) {
+				var t = new DeformTimeline(1);
+				t.slotIndex = timeline.slotIndex;
+				t.attachment = timeline.attachment;
+				var slotData = skeletonData.slots.Items[t.slotIndex];
+
+				if (t.attachment.IsWeighted()) {
+					t.SetFrame(0, 0, new float[t.attachment.vertices.Length]);
+				} else {
+					t.SetFrame(0, 0, t.attachment.vertices.Clone() as float[]);
+				}
+
+				return t;
+			}
+
+			static DrawOrderTimeline GetFillerTimeline (DrawOrderTimeline timeline, SkeletonData skeletonData) {
+				var t = new DrawOrderTimeline(1);
+				t.SetFrame(0, 0, null); // null means use setup pose in DrawOrderTimeline.Apply.
+				return t;
+			}
+
+			static IkConstraintTimeline GetFillerTimeline (IkConstraintTimeline timeline, SkeletonData skeletonData) {
+				var t = new IkConstraintTimeline(1);
+				var ikConstraintData = skeletonData.ikConstraints.Items[timeline.ikConstraintIndex];
+				t.SetFrame(0, 0, ikConstraintData.mix, ikConstraintData.bendDirection, ikConstraintData.compress, ikConstraintData.stretch);
+				return t;
+			}
+
+			static TransformConstraintTimeline GetFillerTimeline (TransformConstraintTimeline timeline, SkeletonData skeletonData) {
+				var t = new TransformConstraintTimeline(1);
+				var data = skeletonData.transformConstraints.Items[timeline.transformConstraintIndex];
+				t.SetFrame(0, 0, data.rotateMix, data.translateMix, data.scaleMix, data.shearMix);
+				return t;
+			}
+
+			static PathConstraintPositionTimeline GetFillerTimeline (PathConstraintPositionTimeline timeline, SkeletonData skeletonData) {
+				var t = new PathConstraintPositionTimeline(1);
+				var data = skeletonData.pathConstraints.Items[timeline.pathConstraintIndex];
+				t.SetFrame(0, 0, data.position);
+				return t;
+			}
+
+			static PathConstraintSpacingTimeline GetFillerTimeline (PathConstraintSpacingTimeline timeline, SkeletonData skeletonData) {
+				var t = new PathConstraintSpacingTimeline(1);
+				var data = skeletonData.pathConstraints.Items[timeline.pathConstraintIndex];
+				t.SetFrame(0, 0, data.spacing);
+				return t;
+			}
+
+			static PathConstraintMixTimeline GetFillerTimeline (PathConstraintMixTimeline timeline, SkeletonData skeletonData) {
+				var t = new PathConstraintMixTimeline(1);
+				var data = skeletonData.pathConstraints.Items[timeline.pathConstraintIndex];
+				t.SetFrame(0, 0, data.rotateMix, data.translateMix);
+				return t;
+			}
+			#endregion
+		}
+
+	}
+
+}

+ 12 - 0
spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/AnimationMatchModifierAsset.cs.meta

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

+ 6 - 0
spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/AnimationMatchModifierAsset.txt

@@ -0,0 +1,6 @@
+AnimationMatchModifierAsset
+===========================
+
+This is a SkeletonDataModifierAsset. Add it to a SkeletonDataAsset to apply its effects when its SkeletonData is loaded.
+
+AnimationMatchModifierAsset processes animations so that their timelines match. This allows them to function with naive Animation Apply systems such as SkeletonMecanim without the need for autoreset functionality.

+ 8 - 0
spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/AnimationMatchModifierAsset.txt.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b4436c0c78cc5ee469089ed864f4e1ea
+timeCreated: 1537528259
+licenseType: Pro
+TextScriptImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

BIN
spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/Default Match All Animations.asset


+ 9 - 0
spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/Default Match All Animations.asset.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 8623082208cae724e88314ec951691e1
+timeCreated: 1537527914
+licenseType: Pro
+NativeFormatImporter:
+  mainObjectFileID: 11400000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 1 - 1
spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AttachmentTools/AttachmentTools.cs

@@ -901,7 +901,7 @@ namespace Spine.Unity.Modules.AttachmentTools {
 		}
 
 		public static RegionAttachment GetClone (this RegionAttachment o) {
-			return new RegionAttachment(o.Name + "clone") {
+			return new RegionAttachment(o.Name) {
 				x = o.x,
 				y = o.y,
 				rotation = o.rotation,

+ 1 - 1
spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Shaders/Spine-Skeleton-Fill.shader

@@ -102,4 +102,4 @@ Shader "Spine/Skeleton Fill" {
 		}
 	}
 	FallBack "Diffuse"
-}
+}

+ 15 - 15
spine-unity/Assets/Spine/Runtime/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs

@@ -244,24 +244,24 @@ namespace Spine.Unity {
 			if (!string.IsNullOrEmpty(initialSkinName))
 				skeleton.SetSkin(initialSkinName);
 
-			#if UNITY_EDITOR
 			if (!string.IsNullOrEmpty(startingAnimation)) {
-				if (Application.isPlaying) {
-					state.SetAnimation(0, startingAnimation, startingLoop);
-				} else {
-					// Assume SkeletonAnimation is valid for skeletonData and skeleton. Checked above.
-					var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(startingAnimation);
-					if (animationObject != null)
-						animationObject.PoseSkeleton(skeleton, 0);
+				var animationObject = skeletonDataAsset.GetSkeletonData(false).FindAnimation(startingAnimation);
+				if (animationObject != null) {
+					animationObject.PoseSkeleton(skeleton, 0f);
+					skeleton.UpdateWorldTransform();
+
+					#if UNITY_EDITOR
+					if (Application.isPlaying) {
+					#endif
+
+						// Make this block not run in Unity Editor edit mode.
+						state.SetAnimation(0, animationObject, startingLoop);
+
+					#if UNITY_EDITOR
+					}
+					#endif
 				}
-				Update(0);
-			}
-			#else
-			if (!string.IsNullOrEmpty(startingAnimation)) {
-				state.SetAnimation(0, startingAnimation, startingLoop);
-				Update(0);
 			}
-			#endif
 		}
 
 		public void UpdateMesh () {