Bläddra i källkod

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

badlogic 7 år sedan
förälder
incheckning
0e6673c370
21 ändrade filer med 488 tillägg och 120 borttagningar
  1. 6 0
      CHANGELOG.md
  2. 39 37
      spine-cpp/spine-cpp/src/spine/PathConstraint.cpp
  3. 16 8
      spine-csharp/src/Animation.cs
  4. 13 8
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonDataAssetInspector.cs
  5. 2 3
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonRendererInspector.cs
  6. 26 14
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs
  7. 35 1
      spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineInspectorUtility.cs
  8. 2 2
      spine-unity/Assets/Spine/Editor/spine-unity/Modules/SkeletonGraphic/Editor/SkeletonGraphicInspector.cs
  9. 1 1
      spine-unity/Assets/Spine/Runtime/spine-unity/Asset Types/BlendModeMaterialsAsset.cs
  10. 19 27
      spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonAnimation.cs
  11. 3 2
      spine-unity/Assets/Spine/Runtime/spine-unity/Components/SkeletonRenderer.cs
  12. 9 0
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier.meta
  13. 265 0
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/AnimationMatchModifierAsset.cs
  14. 12 0
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/AnimationMatchModifierAsset.cs.meta
  15. 6 0
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/AnimationMatchModifierAsset.txt
  16. 8 0
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/AnimationMatchModifierAsset.txt.meta
  17. BIN
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/Default Match All Animations.asset
  18. 9 0
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AnimationMatchModifier/Default Match All Animations.asset.meta
  19. 1 1
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/AttachmentTools/AttachmentTools.cs
  20. 1 1
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Shaders/Spine-Skeleton-Fill.shader
  21. 15 15
      spine-unity/Assets/Spine/Runtime/spine-unity/Modules/SkeletonGraphic/SkeletonGraphic.cs

+ 6 - 0
CHANGELOG.md

@@ -9,6 +9,7 @@
   * Added additive animation blending. When playing back multiple animations on different tracks, where each animation modifies the same skeleton property, the results of tracks with lower indices are discarded, and only the result from the track with the highest index is used. With animation blending, the results of all tracks are mixed together. This allows effects like mixing multiple facial expressions (angry, happy, sad) with percentage mixes. By default the old behaviour is retained (results from lower tracks are discarded). To enable additive blending across animation tracks, call `TrackEntry#setMixBlend(MixBlend.add)` on each track. To specify the blend percentage, set `TrackEntry#alpha`. See http://esotericsoftware.com/forum/morph-target-track-animation-mix-mode-9459 for a discussion.
   * Support for stretchy IK
   * Support for audio events, see `audioPath`, `volume` and `balance` fields on event (data).
+  * `TrackEntry` has an additional field called `holdPrevious`. It can be used to counter act a limitation of `AnimationState` resulting in "dipping" of parts of the animation. For a full discussion of the problem and the solution we've implemented, see this [forum thread](http://esotericsoftware.com/forum/Probably-Easy-Animation-mixing-with-multiple-tracks-10682?p=48130&hilit=holdprevious#p48130).
 
 ### Starling
 * Added support for vertex effects. See `RaptorExample.as`
@@ -42,6 +43,7 @@
   * Optimized attachment lookup to give a 40x speed-up. See https://github.com/EsotericSoftware/spine-runtimes/commit/cab81276263890b65d07fa2329ace16db1e365ff
   * Support for stretchy IK
   * Support for audio events, see `audioPath`, `volume` and `balance` fields on event (data).
+  * `spTrackEntry` has an additional field called `holdPrevious`. It can be used to counter act a limitation of `AnimationState` resulting in "dipping" of parts of the animation. For a full discussion of the problem and the solution we've implemented, see this [forum thread](http://esotericsoftware.com/forum/Probably-Easy-Animation-mixing-with-multiple-tracks-10682?p=48130&hilit=holdprevious#p48130).
 
 ### Cocos2d-Objc
 * Added vertex effect support to modify vertices of skeletons on the CPU. See `RaptorExample.m`.
@@ -100,6 +102,7 @@
   * Added additive animation blending. When playing back multiple animations on different tracks, where each animation modifies the same skeleton property, the results of tracks with lower indices are discarded, and only the result from the track with the highest index is used. With animation blending, the results of all tracks are mixed together. This allows effects like mixing multiple facial expressions (angry, happy, sad) with percentage mixes. By default the old behaviour is retained (results from lower tracks are discarded). To enable additive blending across animation tracks, call `TrackEntry#MixBlend = MixBlend.add` on each track. To specify the blend percentage, set `TrackEntry#Alpha`. See http://esotericsoftware.com/forum/morph-target-track-animation-mix-mode-9459 for a discussion.
   * Support for stretchy IK
   * Support for audio events, see `audioPath`, `volume` and `balance` fields on event (data).
+  * `TrackEntry` has an additional field called `holdPrevious`. It can be used to counter act a limitation of `AnimationState` resulting in "dipping" of parts of the animation. For a full discussion of the problem and the solution we've implemented, see this [forum thread](http://esotericsoftware.com/forum/Probably-Easy-Animation-mixing-with-multiple-tracks-10682?p=48130&hilit=holdprevious#p48130).
 
 ### Unity
 * **Runtime and Editor, and Assembly Definition** Files and folders have been reorganized into "Runtime" and "Editor". Each of these have an `.asmdef` file that defines these separately as their own assembly in Unity. For projects not using assembly definition, you may delete the `.asmdef` files. These assembly definitions will be ignored by older versions of Unity that don't support it.
@@ -141,6 +144,7 @@
   * Added additive animation blending. When playing back multiple animations on different tracks, where each animation modifies the same skeleton property, the results of tracks with lower indices are discarded, and only the result from the track with the highest index is used. With animation blending, the results of all tracks are mixed together. This allows effects like mixing multiple facial expressions (angry, happy, sad) with percentage mixes. By default the old behaviour is retained (results from lower tracks are discarded). To enable additive blending across animation tracks, call `TrackEntry#setMixBlend(MixBlend.add)` on each track. To specify the blend percentage, set `TrackEntry#alpha`. See http://esotericsoftware.com/forum/morph-target-track-animation-mix-mode-9459 for a discussion.
   * Support for stretchy IK
   * Support for audio events, see `audioPath`, `volume` and `balance` fields on event (data).
+  * `TrackEntry` has an additional field called `holdPrevious`. It can be used to counter act a limitation of `AnimationState` resulting in "dipping" of parts of the animation. For a full discussion of the problem and the solution we've implemented, see this [forum thread](http://esotericsoftware.com/forum/Probably-Easy-Animation-mixing-with-multiple-tracks-10682?p=48130&hilit=holdprevious#p48130).
 
 ### libGDX
 * Added `VertexEffect` interface, instances of which can be set on `SkeletonRenderer`. Allows to modify vertices before submitting them to GPU. See `SwirlEffect`, `JitterEffect` and `VertexEffectTest`.
@@ -159,6 +163,7 @@
   * Added additive animation blending. When playing back multiple animations on different tracks, where each animation modifies the same skeleton property, the results of tracks with lower indices are discarded, and only the result from the track with the highest index is used. With animation blending, the results of all tracks are mixed together. This allows effects like mixing multiple facial expressions (angry, happy, sad) with percentage mixes. By default the old behaviour is retained (results from lower tracks are discarded). To enable additive blending across animation tracks, call `TrackEntry:setMixBlend(MixBlend.add)` on each track. To specify the blend percentage, set `TrackEntry.alpha`. See http://esotericsoftware.com/forum/morph-target-track-animation-mix-mode-9459 for a discussion.
   * Support for stretchy IK
   * Support for audio events, see `audioPath`, `volume` and `balance` fields on event (data).
+  * `TrackEntry` has an additional field called `holdPrevious`. It can be used to counter act a limitation of `AnimationState` resulting in "dipping" of parts of the animation. For a full discussion of the problem and the solution we've implemented, see this [forum thread](http://esotericsoftware.com/forum/Probably-Easy-Animation-mixing-with-multiple-tracks-10682?p=48130&hilit=holdprevious#p48130).
 
 ### Love2D
 * Added support for vertex effects. Set an implementation like "JitterEffect" on `Skeleton.vertexEffect`. See `main.lua` for an example.
@@ -176,6 +181,7 @@
   * Added work-around for iOS WebKit JIT bug, see https://github.com/EsotericSoftware/spine-runtimes/commit/c28bbebf804980f55cdd773fed9ff145e0e7e76c
   * Support for stretchy IK
   * Support for audio events, see `audioPath`, `volume` and `balance` fields on event (data).
+  * `TrackEntry` has an additional field called `holdPrevious`. It can be used to counter act a limitation of `AnimationState` resulting in "dipping" of parts of the animation. For a full discussion of the problem and the solution we've implemented, see this [forum thread](http://esotericsoftware.com/forum/Probably-Easy-Animation-mixing-with-multiple-tracks-10682?p=48130&hilit=holdprevious#p48130).
 
 ### WebGL backend
 * Added `VertexEffect` interface, instances of which can be set on `SkeletonRenderer`. Allows to modify vertices before submitting them to GPU. See `SwirlEffect`, `JitterEffect`, and the example which allows to set effects.

+ 39 - 37
spine-cpp/spine-cpp/src/spine/PathConstraint.cpp

@@ -271,6 +271,8 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
 	Slot &target = *_target;
 	float position = _position;
 	_positions.setSize(spacesCount * 3 + 2, 0);
+	Vector<float> &out = _positions;
+	Vector<float> &world = _world;
 	bool closed = path.isClosed();
 	int verticesLength = path.getWorldVerticesLength();
 	int curveCount = verticesLength / 6;
@@ -291,7 +293,7 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
 			}
 		}
 
-		_world.setSize(8, 0);
+		world.setSize(8, 0);
 		for (int i = 0, o = 0, curve = 0; i < spacesCount; i++, o += 3) {
 			float space = _spaces[i];
 			position += space;
@@ -307,19 +309,19 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
 			} else if (p < 0) {
 				if (prevCurve != BEFORE) {
 					prevCurve = BEFORE;
-					path.computeWorldVertices(target, 2, 4, _world, 0);
+					path.computeWorldVertices(target, 2, 4, world, 0);
 				}
 
-				addBeforePosition(p, _world, 0, _positions, o);
+				addBeforePosition(p, world, 0, out, o);
 
 				continue;
 			} else if (p > pathLength) {
 				if (prevCurve != AFTER) {
 					prevCurve = AFTER;
-					path.computeWorldVertices(target, verticesLength - 6, 4, _world, 0);
+					path.computeWorldVertices(target, verticesLength - 6, 4, world, 0);
 				}
 
-				addAfterPosition(p - pathLength, _world, 0, _positions, o);
+				addAfterPosition(p - pathLength, world, 0, out, o);
 
 				continue;
 			}
@@ -343,46 +345,46 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
 			if (curve != prevCurve) {
 				prevCurve = curve;
 				if (closed && curve == curveCount) {
-					path.computeWorldVertices(target, verticesLength - 4, 4, _world, 0);
-					path.computeWorldVertices(target, 0, 4, _world, 4);
+					path.computeWorldVertices(target, verticesLength - 4, 4, world, 0);
+					path.computeWorldVertices(target, 0, 4, world, 4);
 				} else {
-					path.computeWorldVertices(target, curve * 6 + 2, 8, _world, 0);
+					path.computeWorldVertices(target, curve * 6 + 2, 8, world, 0);
 				}
 			}
 
-			addCurvePosition(p, _world[0], _world[1], _world[2], _world[3], _world[4], _world[5], _world[6], _world[7],
-							 _positions, o, tangents || (i > 0 && space < EPSILON));
+			addCurvePosition(p, world[0], world[1], world[2], world[3], world[4], world[5], world[6], world[7],
+							 out, o, tangents || (i > 0 && space < EPSILON));
 		}
-		return _world;
+		return out;
 	}
 
 	// World vertices.
 	if (closed) {
 		verticesLength += 2;
-		_world.setSize(verticesLength, 0);
-		path.computeWorldVertices(target, 2, verticesLength - 4, _world, 0);
-		path.computeWorldVertices(target, 0, 2, _world, verticesLength - 4);
-		_world[verticesLength - 2] = _world[0];
-		_world[verticesLength - 1] = _world[1];
+		world.setSize(verticesLength, 0);
+		path.computeWorldVertices(target, 2, verticesLength - 4, world, 0);
+		path.computeWorldVertices(target, 0, 2, world, verticesLength - 4);
+		world[verticesLength - 2] = world[0];
+		world[verticesLength - 1] = world[1];
 	} else {
 		curveCount--;
 		verticesLength -= 4;
-		_world.setSize(verticesLength, 0);
-		path.computeWorldVertices(target, 2, verticesLength, _world, 0);
+		world.setSize(verticesLength, 0);
+		path.computeWorldVertices(target, 2, verticesLength, world, 0);
 	}
 
 	// Curve lengths.
 	_curves.setSize(curveCount, 0);
 	pathLength = 0;
-	float x1 = _world[0], y1 = _world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
+	float x1 = world[0], y1 = world[1], cx1 = 0, cy1 = 0, cx2 = 0, cy2 = 0, x2 = 0, y2 = 0;
 	float tmpx, tmpy, dddfx, dddfy, ddfx, ddfy, dfx, dfy;
 	for (int i = 0, w = 2; i < curveCount; i++, w += 6) {
-		cx1 = _world[w];
-		cy1 = _world[w + 1];
-		cx2 = _world[w + 2];
-		cy2 = _world[w + 3];
-		x2 = _world[w + 4];
-		y2 = _world[w + 5];
+		cx1 = world[w];
+		cy1 = world[w + 1];
+		cx2 = world[w + 2];
+		cy2 = world[w + 3];
+		x2 = world[w + 4];
+		y2 = world[w + 5];
 		tmpx = (x1 - cx1 * 2 + cx2) * 0.1875f;
 		tmpy = (y1 - cy1 * 2 + cy2) * 0.1875f;
 		dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.09375f;
@@ -434,10 +436,10 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
 			}
 			curve = 0;
 		} else if (p < 0) {
-			addBeforePosition(p, _world, 0, _positions, o);
+			addBeforePosition(p, world, 0, out, o);
 			continue;
 		} else if (p > pathLength) {
-			addAfterPosition(p - pathLength, _world, verticesLength - 4, _positions, o);
+			addAfterPosition(p - pathLength, world, verticesLength - 4, out, o);
 			continue;
 		}
 
@@ -461,14 +463,14 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
 		if (curve != prevCurve) {
 			prevCurve = curve;
 			int ii = curve * 6;
-			x1 = _world[ii];
-			y1 = _world[ii + 1];
-			cx1 = _world[ii + 2];
-			cy1 = _world[ii + 3];
-			cx2 = _world[ii + 4];
-			cy2 = _world[ii + 5];
-			x2 = _world[ii + 6];
-			y2 = _world[ii + 7];
+			x1 = world[ii];
+			y1 = world[ii + 1];
+			cx1 = world[ii + 2];
+			cy1 = world[ii + 3];
+			cx2 = world[ii + 4];
+			cy2 = world[ii + 5];
+			x2 = world[ii + 6];
+			y2 = world[ii + 7];
 			tmpx = (x1 - cx1 * 2 + cx2) * 0.03f;
 			tmpy = (y1 - cy1 * 2 + cy2) * 0.03f;
 			dddfx = ((cx1 - cx2) * 3 - x1 + x2) * 0.006f;
@@ -514,11 +516,11 @@ PathConstraint::computeWorldPositions(PathAttachment &path, int spacesCount, boo
 			}
 			break;
 		}
-		addCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, _positions, o,
+		addCurvePosition(p * 0.1f, x1, y1, cx1, cy1, cx2, cy2, x2, y2, out, o,
 						 tangents || (i > 0 && space < EPSILON));
 	}
 
-	return _positions;
+	return out;
 }
 
 void PathConstraint::addBeforePosition(float p, Vector<float> &temp, int i, Vector<float> &output, int o) {

+ 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 () {