Browse Source

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

badlogic 7 years ago
parent
commit
5898057c96
100 changed files with 620 additions and 205 deletions
  1. 15 41
      spine-csharp/src/Bone.cs
  2. 9 3
      spine-csharp/src/Skeleton.cs
  3. 1 1
      spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TestHarness.java
  4. 32 11
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java
  5. 16 39
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java
  6. 33 12
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java
  7. 11 0
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java
  8. 30 38
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
  9. 2 1
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java
  10. 2 1
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java
  11. 3 3
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java
  12. 1 1
      spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonActorPool.java
  13. 90 38
      spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java
  14. 2 0
      spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpinePlugin.cpp
  15. 8 6
      spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp
  16. 13 0
      spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp
  17. 2 0
      spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpinePlugin.h
  18. 9 0
      spine-unity/Assets/Spine Examples/Other Examples/Animation Tester.meta
  19. BIN
      spine-unity/Assets/Spine Examples/Other Examples/Animation Tester/Animation Tester.unity
  20. 8 0
      spine-unity/Assets/Spine Examples/Other Examples/Animation Tester/Animation Tester.unity.meta
  21. 117 0
      spine-unity/Assets/Spine Examples/Other Examples/Animation Tester/SpineAnimationTesterTool.cs
  22. 12 0
      spine-unity/Assets/Spine Examples/Other Examples/Animation Tester/SpineAnimationTesterTool.cs.meta
  23. BIN
      spine-unity/Assets/Spine Examples/Other Examples/Mix and Match Equip.unity
  24. 9 0
      spine-unity/Assets/Spine Examples/Other Examples/StateMachine SkeletonAnimation.meta
  25. BIN
      spine-unity/Assets/Spine Examples/Other Examples/StateMachine SkeletonAnimation/AnimationState with Mecanim.unity
  26. 8 0
      spine-unity/Assets/Spine Examples/Other Examples/StateMachine SkeletonAnimation/AnimationState with Mecanim.unity.meta
  27. BIN
      spine-unity/Assets/Spine Examples/Other Examples/StateMachine SkeletonAnimation/Hero.controller
  28. 9 0
      spine-unity/Assets/Spine Examples/Other Examples/StateMachine SkeletonAnimation/Hero.controller.meta
  29. 62 0
      spine-unity/Assets/Spine Examples/Scripts/AnimationStateMecanimState.cs
  30. 12 0
      spine-unity/Assets/Spine Examples/Scripts/AnimationStateMecanimState.cs.meta
  31. 82 0
      spine-unity/Assets/Spine Examples/Scripts/AnimationStateWithMecanimExample.cs
  32. 12 0
      spine-unity/Assets/Spine Examples/Scripts/AnimationStateWithMecanimExample.cs.meta
  33. 2 2
      spine-unity/Assets/Spine Examples/Scripts/FootSoldierExample.cs
  34. 1 1
      spine-unity/Assets/Spine Examples/Scripts/Getting Started Scripts/BasicPlatformerController.cs
  35. 2 2
      spine-unity/Assets/Spine Examples/Scripts/Getting Started Scripts/SpineBeginnerTwo.cs
  36. 2 2
      spine-unity/Assets/Spine Examples/Scripts/Getting Started Scripts/SpineboyBeginnerView.cs
  37. 1 1
      spine-unity/Assets/Spine Examples/Scripts/Getting Started Scripts/SpineboyTargetController.cs
  38. 2 2
      spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonAnimationMulti/SkeletonAnimationMulti.cs
  39. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons.meta
  40. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon.meta
  41. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon.atlas.txt
  42. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon.atlas.txt.meta
  43. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon.json
  44. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon.json.meta
  45. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon.png
  46. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon.png.meta
  47. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon2.png
  48. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon2.png.meta
  49. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_Atlas.asset
  50. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_Atlas.asset.meta
  51. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_SkeletonData.asset
  52. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_SkeletonData.asset.meta
  53. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_dragon.mat
  54. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_dragon.mat.meta
  55. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_dragon2.mat
  56. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_dragon2.mat.meta
  57. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/license.txt
  58. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/license.txt.meta
  59. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes.meta
  60. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes.atlas.txt
  61. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes.atlas.txt.meta
  62. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes.json
  63. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes.json.meta
  64. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes.png
  65. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes.png.meta
  66. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes_Atlas.asset
  67. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes_Atlas.asset.meta
  68. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes_Material.mat
  69. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes_Material.mat.meta
  70. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes_SkeletonData.asset
  71. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes_SkeletonData.asset.meta
  72. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier.meta
  73. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment.meta
  74. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment.atlas.txt
  75. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment.atlas.txt.meta
  76. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment.png
  77. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment.png.meta
  78. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment_Atlas.asset
  79. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment_Atlas.asset.meta
  80. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment_Material.mat
  81. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment_Material.mat.meta
  82. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White.atlas.txt
  83. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White.atlas.txt.meta
  84. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White.png
  85. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White.png.meta
  86. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White_Atlas.asset
  87. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White_Atlas.asset.meta
  88. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White_Material.mat
  89. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White_Material.mat.meta
  90. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FootSoldier.json
  91. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FootSoldier.json.meta
  92. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FootSoldier_SkeletonData.asset
  93. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FootSoldier_SkeletonData.asset.meta
  94. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/license.txt
  95. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/license.txt.meta
  96. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Gauge.meta
  97. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Gauge/Gauge.atlas.txt
  98. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Gauge/Gauge.atlas.txt.meta
  99. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Gauge/Gauge.json
  100. 0 0
      spine-unity/Assets/Spine Examples/Spine Skeletons/Gauge/Gauge.json.meta

+ 15 - 41
spine-csharp/src/Bone.cs

@@ -53,10 +53,6 @@ namespace Spine {
 		internal float a, b, worldX;
 		internal float c, d, worldY;
 
-//		internal float worldSignX, worldSignY;
-//		public float WorldSignX { get { return worldSignX; } }
-//		public float WorldSignY { get { return worldSignY; } }
-
 		internal bool sorted;
 
 		public BoneData Data { get { return data; } }
@@ -152,27 +148,13 @@ namespace Spine {
 
 			Bone parent = this.parent;
 			if (parent == null) { // Root bone.
-				float rotationY = rotation + 90 + shearY;
-				float la = MathUtils.CosDeg(rotation + shearX) * scaleX;
-				float lb = MathUtils.CosDeg(rotationY) * scaleY;
-				float lc = MathUtils.SinDeg(rotation + shearX) * scaleX;
-				float ld = MathUtils.SinDeg(rotationY) * scaleY;
-				if (skeleton.flipX) {
-					x = -x;
-					la = -la;
-					lb = -lb;
-				}
-				if (skeleton.flipY != yDown) {
-					y = -y;
-					lc = -lc;
-					ld = -ld;
-				}
-				a = la;
-				b = lb;
-				c = lc;
-				d = ld;
-				worldX = x + skeleton.x;
-				worldY = y + skeleton.y;
+				float rotationY = rotation + 90 + shearY, sx = skeleton.scaleX, sy = skeleton.scaleY;
+				a = MathUtils.CosDeg(rotation + shearX) * scaleX * sx;
+				b = MathUtils.CosDeg(rotationY) * scaleY * sy;
+				c = MathUtils.SinDeg(rotation + shearX) * scaleX * sx;
+				d = MathUtils.SinDeg(rotationY) * scaleY * sy;
+				worldX = x * sx + skeleton.x;
+				worldY = y * sy + skeleton.y;
 				return;
 			}
 
@@ -228,8 +210,8 @@ namespace Spine {
 			case TransformMode.NoScale:
 			case TransformMode.NoScaleOrReflection: {
 					float cos = MathUtils.CosDeg(rotation), sin = MathUtils.SinDeg(rotation);
-					float za = pa * cos + pb * sin;
-					float zc = pc * cos + pd * sin;
+					float za = (pa * cos + pb * sin) / skeleton.scaleX;
+					float zc = (pc * cos + pd * sin) / skeleton.scaleY;
 					float s = (float)Math.Sqrt(za * za + zc * zc);
 					if (s > 0.00001f) s = 1 / s;
 					za *= s;
@@ -242,26 +224,18 @@ namespace Spine {
 					float lb = MathUtils.CosDeg(90 + shearY) * scaleY;
 					float lc = MathUtils.SinDeg(shearX) * scaleX;
 					float ld = MathUtils.SinDeg(90 + shearY) * scaleY;
-					if (data.transformMode != TransformMode.NoScaleOrReflection? pa * pd - pb* pc< 0 : skeleton.flipX != skeleton.flipY) {
-						zb = -zb;
-						zd = -zd;
-					}
 					a = za * la + zb * lc;
 					b = za * lb + zb * ld;
 					c = zc * la + zd * lc;
-					d = zc * lb + zd * ld;					
-					return;
+					d = zc * lb + zd * ld;
+					break;
 				}
 			}
 
-			if (skeleton.flipX) {
-				a = -a;
-				b = -b;
-			}
-			if (skeleton.flipY != Bone.yDown) {
-				c = -c;
-				d = -d;
-			}
+			a *= skeleton.scaleX;
+			b *= skeleton.scaleX;
+			c *= skeleton.scaleY;
+			d *= skeleton.scaleY;
 		}
 
 		public void SetToSetupPose () {

+ 9 - 3
spine-csharp/src/Skeleton.cs

@@ -45,7 +45,7 @@ namespace Spine {
 		internal Skin skin;
 		internal float r = 1, g = 1, b = 1, a = 1;
 		internal float time;
-		internal bool flipX, flipY;
+		internal float scaleX, scaleY;
 		internal float x, y;
 
 		public SkeletonData Data { get { return data; } }
@@ -64,8 +64,14 @@ namespace Spine {
 		public float Time { get { return time; } set { time = value; } }
 		public float X { get { return x; } set { x = value; } }
 		public float Y { get { return y; } set { y = value; } }
-		public bool FlipX { get { return flipX; } set { flipX = value; } }
-		public bool FlipY { get { return flipY; } set { flipY = value; } }
+		public float ScaleX { get { return scaleX; } set { scaleX = value; } }
+		public float ScaleY { get { return scaleY; } set { scaleY = value; } }
+
+		[Obsolete("Use ScaleX instead. FlipX is when ScaleX is negative.")]
+		public bool FlipX { get { return scaleX < 0; } set { scaleX = value ? -1f : 1f; } }
+
+		[Obsolete("Use ScaleY instead. FlipY is when ScaleY is negative.")]
+		public bool FlipY { get { return scaleY < 0; } set { scaleY = value ? -1f : 1f; } }
 
 		public Bone RootBone {
 			get { return bones.Count == 0 ? null : bones.Items[0]; }

+ 1 - 1
spine-libgdx/spine-libgdx-tests/src/com/esotericsoftware/spine/TestHarness.java

@@ -50,7 +50,7 @@ public class TestHarness extends ApplicationAdapter {
 		
 		skeleton = new Skeleton(skeletonData);		
 		skeleton.setPosition(320, 590);
-		skeleton.flipY = true;
+		skeleton.setScaleY(-1);
 
 		AnimationStateData stateData = new AnimationStateData(skeletonData);		
 		state = new AnimationState(stateData);		

+ 32 - 11
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Animation.java

@@ -37,6 +37,7 @@ import com.badlogic.gdx.graphics.Color;
 import com.badlogic.gdx.math.MathUtils;
 import com.badlogic.gdx.utils.Array;
 import com.badlogic.gdx.utils.FloatArray;
+
 import com.esotericsoftware.spine.attachments.Attachment;
 import com.esotericsoftware.spine.attachments.VertexAttachment;
 
@@ -1294,11 +1295,12 @@ public class Animation {
 		}
 	}
 
-	/** Changes an IK constraint's {@link IkConstraint#getMix()} and {@link IkConstraint#getBendDirection()}. */
+	/** Changes an IK constraint's {@link IkConstraint#getMix()}, {@link IkConstraint#getBendDirection()}, and
+	 * {@link IkConstraint#getStretch()}. */
 	static public class IkConstraintTimeline extends CurveTimeline {
-		static public final int ENTRIES = 3;
-		static private final int PREV_TIME = -3, PREV_MIX = -2, PREV_BEND_DIRECTION = -1;
-		static private final int MIX = 1, BEND_DIRECTION = 2;
+		static public final int ENTRIES = 4;
+		static private final int PREV_TIME = -4, PREV_MIX = -3, PREV_BEND_DIRECTION = -2, PREV_STRETCH = -1;
+		static private final int MIX = 1, BEND_DIRECTION = 2, STRETCH = 3;
 
 		int ikConstraintIndex;
 		private final float[] frames; // time, mix, bendDirection, ...
@@ -1328,11 +1330,12 @@ public class Animation {
 		}
 
 		/** Sets the time in seconds, mix, and bend direction for the specified key frame. */
-		public void setFrame (int frameIndex, float time, float mix, int bendDirection) {
+		public void setFrame (int frameIndex, float time, float mix, int bendDirection, boolean stretch) {
 			frameIndex *= ENTRIES;
 			frames[frameIndex] = time;
 			frames[frameIndex + MIX] = mix;
 			frames[frameIndex + BEND_DIRECTION] = bendDirection;
+			frames[frameIndex + STRETCH] = stretch ? 1 : 0;
 		}
 
 		public void apply (Skeleton skeleton, float lastTime, float time, Array<Event> events, float alpha, MixBlend blend,
@@ -1345,10 +1348,12 @@ public class Animation {
 				case setup:
 					constraint.mix = constraint.data.mix;
 					constraint.bendDirection = constraint.data.bendDirection;
+					constraint.stretch = constraint.data.stretch;
 					return;
 				case first:
 					constraint.mix += (constraint.data.mix - constraint.mix) * alpha;
 					constraint.bendDirection = constraint.data.bendDirection;
+					constraint.stretch = constraint.data.stretch;
 				}
 				return;
 			}
@@ -1356,11 +1361,19 @@ public class Animation {
 			if (time >= frames[frames.length - ENTRIES]) { // Time is after last frame.
 				if (blend == setup) {
 					constraint.mix = constraint.data.mix + (frames[frames.length + PREV_MIX] - constraint.data.mix) * alpha;
-					constraint.bendDirection = direction == out ? constraint.data.bendDirection
-						: (int)frames[frames.length + PREV_BEND_DIRECTION];
+					if (direction == out) {
+						constraint.bendDirection = constraint.data.bendDirection;
+						constraint.stretch = constraint.data.stretch;
+					} else {
+						constraint.bendDirection = (int)frames[frames.length + PREV_BEND_DIRECTION];
+						constraint.stretch = frames[frames.length + PREV_STRETCH] != 0;
+					}
 				} else {
 					constraint.mix += (frames[frames.length + PREV_MIX] - constraint.mix) * alpha;
-					if (direction == in) constraint.bendDirection = (int)frames[frames.length + PREV_BEND_DIRECTION];
+					if (direction == in) {
+						constraint.bendDirection = (int)frames[frames.length + PREV_BEND_DIRECTION];
+						constraint.stretch = frames[frames.length + PREV_STRETCH] != 0;
+					}
 				}
 				return;
 			}
@@ -1373,11 +1386,19 @@ public class Animation {
 
 			if (blend == setup) {
 				constraint.mix = constraint.data.mix + (mix + (frames[frame + MIX] - mix) * percent - constraint.data.mix) * alpha;
-				constraint.bendDirection = direction == out ? constraint.data.bendDirection
-					: (int)frames[frame + PREV_BEND_DIRECTION];
+				if (direction == out) {
+					constraint.bendDirection = constraint.data.bendDirection;
+					constraint.stretch = constraint.data.stretch;
+				} else {
+					constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
+					constraint.stretch = frames[frame + PREV_STRETCH] != 0;
+				}
 			} else {
 				constraint.mix += (mix + (frames[frame + MIX] - mix) * percent - constraint.mix) * alpha;
-				if (direction == in) constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
+				if (direction == in) {
+					constraint.bendDirection = (int)frames[frame + PREV_BEND_DIRECTION];
+					constraint.stretch = frames[frame + PREV_STRETCH] != 0;
+				}
 			}
 		}
 	}

+ 16 - 39
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Bone.java

@@ -30,13 +30,12 @@
 
 package com.esotericsoftware.spine;
 
-import static com.esotericsoftware.spine.utils.SpineUtils.*;
 import static com.badlogic.gdx.math.Matrix3.*;
+import static com.esotericsoftware.spine.utils.SpineUtils.*;
 
 import com.badlogic.gdx.math.Matrix3;
 import com.badlogic.gdx.math.Vector2;
 import com.badlogic.gdx.utils.Array;
-import com.esotericsoftware.spine.BoneData.TransformMode;
 
 /** Stores a bone's current pose.
  * <p>
@@ -111,28 +110,14 @@ public class Bone implements Updatable {
 
 		Bone parent = this.parent;
 		if (parent == null) { // Root bone.
-			float rotationY = rotation + 90 + shearY;
-			float la = cosDeg(rotation + shearX) * scaleX;
-			float lb = cosDeg(rotationY) * scaleY;
-			float lc = sinDeg(rotation + shearX) * scaleX;
-			float ld = sinDeg(rotationY) * scaleY;
 			Skeleton skeleton = this.skeleton;
-			if (skeleton.flipX) {
-				x = -x;
-				la = -la;
-				lb = -lb;
-			}
-			if (skeleton.flipY) {
-				y = -y;
-				lc = -lc;
-				ld = -ld;
-			}
-			a = la;
-			b = lb;
-			c = lc;
-			d = ld;
-			worldX = x + skeleton.x;
-			worldY = y + skeleton.y;
+			float rotationY = rotation + 90 + shearY, sx = skeleton.scaleX, sy = skeleton.scaleY;
+			a = cosDeg(rotation + shearX) * scaleX * sx;
+			b = cosDeg(rotationY) * scaleY * sy;
+			c = sinDeg(rotation + shearX) * scaleX * sx;
+			d = sinDeg(rotationY) * scaleY * sy;
+			worldX = x * sx + skeleton.x;
+			worldY = y * sy + skeleton.y;
 			return;
 		}
 
@@ -188,8 +173,8 @@ public class Bone implements Updatable {
 		case noScale:
 		case noScaleOrReflection: {
 			float cos = cosDeg(rotation), sin = sinDeg(rotation);
-			float za = pa * cos + pb * sin;
-			float zc = pc * cos + pd * sin;
+			float za = (pa * cos + pb * sin) / skeleton.scaleX;
+			float zc = (pc * cos + pd * sin) / skeleton.scaleY;
 			float s = (float)Math.sqrt(za * za + zc * zc);
 			if (s > 0.00001f) s = 1 / s;
 			za *= s;
@@ -201,26 +186,18 @@ public class Bone implements Updatable {
 			float la = cosDeg(shearX) * scaleX;
 			float lb = cosDeg(90 + shearY) * scaleY;
 			float lc = sinDeg(shearX) * scaleX;
-			float ld = sinDeg(90 + shearY) * scaleY;			
-			if (data.transformMode != TransformMode.noScaleOrReflection ? pa * pd - pb * pc < 0 : skeleton.flipX != skeleton.flipY) {
-			    zb = -zb;
-			    zd = -zd;
-			}			
+			float ld = sinDeg(90 + shearY) * scaleY;
 			a = za * la + zb * lc;
 			b = za * lb + zb * ld;
 			c = zc * la + zd * lc;
 			d = zc * lb + zd * ld;
-			return;
-		}
-		}
-		if (skeleton.flipX) {
-			a = -a;
-			b = -b;
+			break;
 		}
-		if (skeleton.flipY) {
-			c = -c;
-			d = -d;
 		}
+		a *= skeleton.scaleX;
+		b *= skeleton.scaleX;
+		c *= skeleton.scaleY;
+		d *= skeleton.scaleY;
 	}
 
 	/** Sets this bone's local transform to the setup pose. */

+ 33 - 12
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java

@@ -42,8 +42,9 @@ public class IkConstraint implements Constraint {
 	final IkConstraintData data;
 	final Array<Bone> bones;
 	Bone target;
-	float mix = 1;
 	int bendDirection;
+	boolean stretch;
+	float mix = 1;
 
 	public IkConstraint (IkConstraintData data, Skeleton skeleton) {
 		if (data == null) throw new IllegalArgumentException("data cannot be null.");
@@ -51,6 +52,7 @@ public class IkConstraint implements Constraint {
 		this.data = data;
 		mix = data.mix;
 		bendDirection = data.bendDirection;
+		stretch = data.stretch;
 
 		bones = new Array(data.bones.size);
 		for (BoneData boneData : data.bones)
@@ -69,6 +71,7 @@ public class IkConstraint implements Constraint {
 		target = skeleton.bones.get(constraint.target.data.index);
 		mix = constraint.mix;
 		bendDirection = constraint.bendDirection;
+		stretch = constraint.stretch;
 	}
 
 	/** Applies the constraint to the constrained bones. */
@@ -81,10 +84,10 @@ public class IkConstraint implements Constraint {
 		Array<Bone> bones = this.bones;
 		switch (bones.size) {
 		case 1:
-			apply(bones.first(), target.worldX, target.worldY, mix);
+			apply(bones.first(), target.worldX, target.worldY, stretch, mix);
 			break;
 		case 2:
-			apply(bones.first(), bones.get(1), target.worldX, target.worldY, bendDirection, mix);
+			apply(bones.first(), bones.get(1), target.worldX, target.worldY, bendDirection, stretch, mix);
 			break;
 		}
 	}
@@ -125,6 +128,16 @@ public class IkConstraint implements Constraint {
 		this.bendDirection = bendDirection;
 	}
 
+	/** When true, if the target is out of range, the parent bone is scaled on the X axis to reach it. If the parent bone has local
+	 * nonuniform scale, stretching is not applied. */
+	public boolean getStretch () {
+		return stretch;
+	}
+
+	public void setStretch (boolean stretch) {
+		this.stretch = stretch;
+	}
+
 	/** The IK constraint's setup pose data. */
 	public IkConstraintData getData () {
 		return data;
@@ -135,7 +148,7 @@ public class IkConstraint implements Constraint {
 	}
 
 	/** Applies 1 bone IK. The target is specified in the world coordinate system. */
-	static public void apply (Bone bone, float targetX, float targetY, float alpha) {
+	static public void apply (Bone bone, float targetX, float targetY, boolean stretch, float alpha) {
 		if (!bone.appliedValid) bone.updateAppliedTransform();
 		Bone p = bone.parent;
 		float id = 1 / (p.a * p.d - p.b * p.c);
@@ -146,20 +159,25 @@ public class IkConstraint implements Constraint {
 		if (rotationIK > 180)
 			rotationIK -= 360;
 		else if (rotationIK < -180) rotationIK += 360;
-		bone.updateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, bone.ascaleX, bone.ascaleY, bone.ashearX,
+		float sx = bone.ascaleX;
+		if (stretch) {
+			float b = bone.data.length * sx, dd = (float)Math.sqrt(tx * tx + ty * ty);
+			if (dd > b && b > 0.0001f) sx *= (dd / b - 1) * alpha + 1;
+		}
+		bone.updateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, bone.ascaleY, bone.ashearX,
 			bone.ashearY);
 	}
 
 	/** Applies 2 bone IK. The target is specified in the world coordinate system.
 	 * @param child A direct descendant of the parent bone. */
-	static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, float alpha) {
+	static public void apply (Bone parent, Bone child, float targetX, float targetY, int bendDir, boolean stretch, float alpha) {
 		if (alpha == 0) {
 			child.updateWorldTransform();
 			return;
 		}
 		if (!parent.appliedValid) parent.updateAppliedTransform();
 		if (!child.appliedValid) child.updateAppliedTransform();
-		float px = parent.ax, py = parent.ay, psx = parent.ascaleX, psy = parent.ascaleY, csx = child.ascaleX;
+		float px = parent.ax, py = parent.ay, psx = parent.ascaleX, sx = psx, psy = parent.ascaleY, csx = child.ascaleX;
 		int os1, os2, s2;
 		if (psx < 0) {
 			psx = -psx;
@@ -195,7 +213,7 @@ public class IkConstraint implements Constraint {
 		c = pp.c;
 		d = pp.d;
 		float id = 1 / (a * d - b * c), x = targetX - pp.worldX, y = targetY - pp.worldY;
-		float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py;
+		float tx = (x * d - y * b) * id - px, ty = (y * a - x * c) * id - py, dd = tx * tx + ty * ty;
 		x = cwx - pp.worldX;
 		y = cwy - pp.worldY;
 		float dx = (x * d - y * b) * id - px, dy = (y * a - x * c) * id - py;
@@ -203,10 +221,13 @@ public class IkConstraint implements Constraint {
 		outer:
 		if (u) {
 			l2 *= psx;
-			float cos = (tx * tx + ty * ty - l1 * l1 - l2 * l2) / (2 * l1 * l2);
+			float cos = (dd - l1 * l1 - l2 * l2) / (2 * l1 * l2);
 			if (cos < -1)
 				cos = -1;
-			else if (cos > 1) cos = 1;
+			else if (cos > 1) {
+				cos = 1;
+				if (stretch && l1 + l2 > 0.0001f) sx *= ((float)Math.sqrt(dd) / (l1 + l2) - 1) * alpha + 1;
+			}
 			a2 = (float)Math.acos(cos) * bendDir;
 			a = l1 + l2 * cos;
 			b = l2 * sin(a2);
@@ -214,7 +235,7 @@ public class IkConstraint implements Constraint {
 		} else {
 			a = psx * l2;
 			b = psy * l2;
-			float aa = a * a, bb = b * b, dd = tx * tx + ty * ty, ta = atan2(ty, tx);
+			float aa = a * a, bb = b * b, ta = atan2(ty, tx);
 			c = bb * l1 * l1 + aa * dd - aa * bb;
 			float c1 = -2 * bb * l1, c2 = bb - aa;
 			d = c1 * c1 - 4 * c2 * c;
@@ -266,7 +287,7 @@ public class IkConstraint implements Constraint {
 		if (a1 > 180)
 			a1 -= 360;
 		else if (a1 < -180) a1 += 360;
-		parent.updateWorldTransform(px, py, rotation + a1 * alpha, parent.ascaleX, parent.ascaleY, 0, 0);
+		parent.updateWorldTransform(px, py, rotation + a1 * alpha, sx, parent.ascaleY, 0, 0);
 		rotation = child.arotation;
 		a2 = ((a2 + os) * radDeg - child.ashearX) * s2 + os2 - rotation;
 		if (a2 > 180)

+ 11 - 0
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java

@@ -41,6 +41,7 @@ public class IkConstraintData {
 	final Array<BoneData> bones = new Array();
 	BoneData target;
 	int bendDirection = 1;
+	boolean stretch;
 	float mix = 1;
 
 	public IkConstraintData (String name) {
@@ -86,6 +87,16 @@ public class IkConstraintData {
 		this.bendDirection = bendDirection;
 	}
 
+	/** When true, if the target is out of range, the parent bone is scaled on the X axis to reach it. If the parent bone has local
+	 * nonuniform scale, stretching is not applied. */
+	public boolean getStretch () {
+		return stretch;
+	}
+
+	public void setStretch (boolean stretch) {
+		this.stretch = stretch;
+	}
+
 	/** A percentage (0-1) that controls the mix between the constrained and unconstrained rotations. */
 	public float getMix () {
 		return mix;

+ 30 - 38
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java

@@ -30,14 +30,14 @@
 
 package com.esotericsoftware.spine;
 
-import static com.esotericsoftware.spine.utils.SpineUtils.cosDeg;
-import static com.esotericsoftware.spine.utils.SpineUtils.sinDeg;
+import static com.esotericsoftware.spine.utils.SpineUtils.*;
 
 import com.badlogic.gdx.graphics.Color;
 import com.badlogic.gdx.math.Vector2;
 import com.badlogic.gdx.utils.Array;
 import com.badlogic.gdx.utils.FloatArray;
 import com.badlogic.gdx.utils.ObjectMap.Entry;
+
 import com.esotericsoftware.spine.Skin.Key;
 import com.esotericsoftware.spine.attachments.Attachment;
 import com.esotericsoftware.spine.attachments.MeshAttachment;
@@ -61,7 +61,7 @@ public class Skeleton {
 	Skin skin;
 	final Color color;
 	float time;
-	boolean flipX, flipY;
+	float scaleX = 1, scaleY = 1;
 	float x, y;
 
 	public Skeleton (SkeletonData data) {
@@ -150,8 +150,8 @@ public class Skeleton {
 		skin = skeleton.skin;
 		color = new Color(skeleton.color);
 		time = skeleton.time;
-		flipX = skeleton.flipX;
-		flipY = skeleton.flipY;
+		scaleX = skeleton.scaleX;
+		scaleY = skeleton.scaleY;
 
 		updateCache();
 	}
@@ -331,9 +331,9 @@ public class Skeleton {
 		for (int i = 0, n = updateCache.size; i < n; i++)
 			updateCache.get(i).update();
 	}
-	
-	/** Updates the world transform for each bone and applies all constraints. The 
-	 *  root bone will be temporarily parented to the specified bone.
+
+	/** Updates the world transform for each bone and applies all constraints. The root bone will be temporarily parented to the
+	 * specified bone.
 	 * <p>
 	 * See <a href="http://esotericsoftware.com/spine-runtime-skeletons#World-transforms">World transforms</a> in the Spine
 	 * Runtimes Guide. */
@@ -353,7 +353,7 @@ public class Skeleton {
 			bone.ashearY = bone.shearY;
 			bone.appliedValid = true;
 		}
-		
+
 		// Apply the parent bone transform to the root bone. The root bone
 		// always inherits scale, rotation and reflection.
 		Bone rootBone = getRootBone();
@@ -366,20 +366,11 @@ public class Skeleton {
 		float lb = cosDeg(rotationY) * rootBone.scaleY;
 		float lc = sinDeg(rootBone.rotation + rootBone.shearX) * rootBone.scaleX;
 		float ld = sinDeg(rotationY) * rootBone.scaleY;
-		rootBone.a = pa * la + pb * lc;
-		rootBone.b = pa * lb + pb * ld;
-		rootBone.c = pc * la + pd * lc;
-		rootBone.d = pc * lb + pd * ld;
-		
-		if (flipY) {
-			rootBone.a = -rootBone.a;
-			rootBone.b = -rootBone.b;
-		}
-		if (flipX) {
-			rootBone.c = -rootBone.c;
-			rootBone.d = -rootBone.d;
-		}
-		
+		rootBone.a = (pa * la + pb * lc) * scaleX;
+		rootBone.b = (pa * lb + pb * ld) * scaleX;
+		rootBone.c = (pc * la + pd * lc) * scaleY;
+		rootBone.d = (pc * lb + pd * ld) * scaleY;
+
 		// Update everything except root bone.
 		Array<Updatable> updateCache = this.updateCache;
 		for (int i = 0, n = updateCache.size; i < n; i++) {
@@ -404,6 +395,7 @@ public class Skeleton {
 		for (int i = 0, n = ikConstraints.size; i < n; i++) {
 			IkConstraint constraint = ikConstraints.get(i);
 			constraint.bendDirection = constraint.data.bendDirection;
+			constraint.stretch = constraint.data.stretch;
 			constraint.mix = constraint.data.mix;
 		}
 
@@ -685,29 +677,29 @@ public class Skeleton {
 		this.color.set(color);
 	}
 
-	/** If true, the entire skeleton is flipped over the Y axis. This affects all bones, even if the bone's transform mode
-	 * disallows scale inheritance. */
-	public boolean getFlipX () {
-		return flipX;
+	/** Scales the entire skeleton on the X axis. This affects all bones, even if the bone's transform mode disallows scale
+	 * inheritance. */
+	public float getScaleX () {
+		return scaleX;
 	}
 
-	public void setFlipX (boolean flipX) {
-		this.flipX = flipX;
+	public void setScaleX (float scaleX) {
+		this.scaleX = scaleX;
 	}
 
-	/** If true, the entire skeleton is flipped over the X axis. This affects all bones, even if the bone's transform mode
-	 * disallows scale inheritance. */
-	public boolean getFlipY () {
-		return flipY;
+	/** Scales the entire skeleton on the Y axis. This affects all bones, even if the bone's transform mode disallows scale
+	 * inheritance. */
+	public float getScaleY () {
+		return scaleY;
 	}
 
-	public void setFlipY (boolean flipY) {
-		this.flipY = flipY;
+	public void setScaleY (float scaleY) {
+		this.scaleY = scaleY;
 	}
 
-	public void setFlip (boolean flipX, boolean flipY) {
-		this.flipX = flipX;
-		this.flipY = flipY;
+	public void setScale (float scaleX, float scaleY) {
+		this.scaleX = scaleX;
+		this.scaleY = scaleY;
 	}
 
 	/** Sets the skeleton X position, which is added to the root bone worldX position. */

+ 2 - 1
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java

@@ -232,6 +232,7 @@ public class SkeletonBinary {
 				data.target = skeletonData.bones.get(input.readInt(true));
 				data.mix = input.readFloat();
 				data.bendDirection = input.readByte();
+				data.stretch = input.readBoolean();
 				skeletonData.ikConstraints.add(data);
 			}
 
@@ -660,7 +661,7 @@ public class SkeletonBinary {
 				IkConstraintTimeline timeline = new IkConstraintTimeline(frameCount);
 				timeline.ikConstraintIndex = index;
 				for (int frameIndex = 0; frameIndex < frameCount; frameIndex++) {
-					timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readByte());
+					timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readByte(), input.readBoolean());
 					if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
 				}
 				timelines.add(timeline);

+ 2 - 1
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java

@@ -186,6 +186,7 @@ public class SkeletonJson {
 			if (data.target == null) throw new SerializationException("IK target bone not found: " + targetName);
 
 			data.bendDirection = constraintMap.getBoolean("bendPositive", true) ? 1 : -1;
+			data.stretch = constraintMap.getBoolean("stretch", false);
 			data.mix = constraintMap.getFloat("mix", 1);
 
 			skeletonData.ikConstraints.add(data);
@@ -568,7 +569,7 @@ public class SkeletonJson {
 			int frameIndex = 0;
 			for (JsonValue valueMap = constraintMap.child; valueMap != null; valueMap = valueMap.next) {
 				timeline.setFrame(frameIndex, valueMap.getFloat("time"), valueMap.getFloat("mix", 1),
-					valueMap.getBoolean("bendPositive", true) ? 1 : -1);
+					valueMap.getBoolean("bendPositive", true) ? 1 : -1, valueMap.getBoolean("stretch", false));
 				readCurve(valueMap, timeline, frameIndex);
 				frameIndex++;
 			}

+ 3 - 3
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonRenderer.java

@@ -67,7 +67,7 @@ public class SkeletonRenderer {
 	 * skeleton is rendered without two color tinting and any mesh attachments will throw an exception.
 	 * <p>
 	 * This method may change the batch's {@link Batch#setBlendFunctionSeparate(int, int, int, int) blending function}. The
-	 * previous blend function is not restore, since that could result in unnecessary flushes, depending on what is rendered
+	 * previous blend function is not restored, since that could result in unnecessary flushes, depending on what is rendered
 	 * next. */
 	public void draw (Batch batch, Skeleton skeleton) {
 		if (batch instanceof PolygonSpriteBatch) {
@@ -144,7 +144,7 @@ public class SkeletonRenderer {
 	/** Renders the specified skeleton, including meshes, but without two color tinting.
 	 * <p>
 	 * This method may change the batch's {@link Batch#setBlendFunctionSeparate(int, int, int, int) blending function}. The
-	 * previous blend function is not restore, since that could result in unnecessary flushes, depending on what is rendered
+	 * previous blend function is not restored, since that could result in unnecessary flushes, depending on what is rendered
 	 * next. */
 	@SuppressWarnings("null")
 	public void draw (PolygonSpriteBatch batch, Skeleton skeleton) {
@@ -266,7 +266,7 @@ public class SkeletonRenderer {
 	/** Renders the specified skeleton, including meshes and two color tinting.
 	 * <p>
 	 * This method may change the batch's {@link Batch#setBlendFunctionSeparate(int, int, int, int) blending function}. The
-	 * previous blend function is not restore, since that could result in unnecessary flushes, depending on what is rendered
+	 * previous blend function is not restored, since that could result in unnecessary flushes, depending on what is rendered
 	 * next. */
 	@SuppressWarnings("null")
 	public void draw (TwoColorPolygonBatch batch, Skeleton skeleton) {

+ 1 - 1
spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/utils/SkeletonActorPool.java

@@ -70,7 +70,7 @@ public class SkeletonActorPool extends Pool<SkeletonActor> {
 
 			protected void reset (Skeleton skeleton) {
 				skeleton.setColor(Color.WHITE);
-				skeleton.setFlip(false, false);
+				skeleton.setScale(1, 1);
 				skeleton.setSkin((Skin)null);
 				skeleton.setSkin(SkeletonActorPool.this.skeletonData.getDefaultSkin());
 				skeleton.setToSetupPose();

+ 90 - 38
spine-libgdx/spine-skeletonviewer/src/com/esotericsoftware/spine/SkeletonViewer.java

@@ -192,11 +192,11 @@ public class SkeletonViewer extends ApplicationAdapter {
 			String extension = skeletonFile.extension();
 			if (extension.equalsIgnoreCase("json") || extension.equalsIgnoreCase("txt")) {
 				SkeletonJson json = new SkeletonJson(atlas);
-				json.setScale(ui.scaleSlider.getValue());
+				json.setScale(ui.loadScaleSlider.getValue());
 				skeletonData = json.readSkeletonData(skeletonFile);
 			} else {
 				SkeletonBinary binary = new SkeletonBinary(atlas);
-				binary.setScale(ui.scaleSlider.getValue());
+				binary.setScale(ui.loadScaleSlider.getValue());
 				skeletonData = binary.readSkeletonData(skeletonFile);
 				if (skeletonData.getBones().size == 0) throw new Exception("No bones in skeleton data.");
 			}
@@ -311,7 +311,10 @@ public class SkeletonViewer extends ApplicationAdapter {
 			renderer.setPremultipliedAlpha(ui.premultipliedCheckbox.isChecked());
 			batch.setPremultipliedAlpha(ui.premultipliedCheckbox.isChecked());
 
-			skeleton.setFlip(ui.flipXCheckbox.isChecked(), ui.flipYCheckbox.isChecked());
+			float scaleX = ui.xScaleSlider.getValue(), scaleY = ui.yScaleSlider.getValue();
+			if (skeleton.scaleX == 0) skeleton.scaleX = 0.01f;
+			if (skeleton.scaleY == 0) skeleton.scaleY = 0.01f;
+			skeleton.setScale(scaleX, scaleY);
 
 			delta = Math.min(delta, 0.032f) * ui.speedSlider.getValue();
 			skeleton.update(delta);
@@ -412,16 +415,21 @@ public class SkeletonViewer extends ApplicationAdapter {
 		TextButton openButton = new TextButton("Open", skin);
 		TextButton minimizeButton = new TextButton("-", skin);
 
-		Slider scaleSlider = new Slider(0.1f, 3, 0.01f, false, skin);
-		Label scaleLabel = new Label("1.0", skin);
-		TextButton scaleResetButton = new TextButton("Reset", skin);
+		Slider loadScaleSlider = new Slider(0.1f, 3, 0.01f, false, skin);
+		Label loadScaleLabel = new Label("100%", skin);
+		TextButton loadScaleResetButton = new TextButton("Reset", skin);
 
 		Slider zoomSlider = new Slider(0.01f, 10, 0.01f, false, skin);
-		Label zoomLabel = new Label("1.0", skin);
+		Label zoomLabel = new Label("100%", skin);
 		TextButton zoomResetButton = new TextButton("Reset", skin);
 
-		CheckBox flipXCheckbox = new CheckBox("X", skin);
-		CheckBox flipYCheckbox = new CheckBox("Y", skin);
+		Slider xScaleSlider = new Slider(-2, 2, 0.01f, false, skin);
+		Label xScaleLabel = new Label("100%", skin);
+		TextButton xScaleResetButton = new TextButton("Reset", skin);
+
+		Slider yScaleSlider = new Slider(-2, 2, 0.01f, false, skin);
+		Label yScaleLabel = new Label("100%", skin);
+		TextButton yScaleResetButton = new TextButton("Reset", skin);
 
 		CheckBox debugBonesCheckbox = new CheckBox("Bones", skin);
 		CheckBox debugRegionsCheckbox = new CheckBox("Regions", skin);
@@ -448,17 +456,17 @@ public class SkeletonViewer extends ApplicationAdapter {
 		CheckBox addCheckbox = new CheckBox("Add", skin);
 
 		Slider alphaSlider = new Slider(0, 1, 0.01f, false, skin);
-		Label alphaLabel = new Label("1.0", skin);
+		Label alphaLabel = new Label("100%", skin);
 
 		List<String> animationList = new List(skin);
 		ScrollPane animationScroll = new ScrollPane(animationList, skin, "bg");
 
 		Slider speedSlider = new Slider(0, 3, 0.01f, false, skin);
-		Label speedLabel = new Label("1.0", skin);
+		Label speedLabel = new Label("1.0x", skin);
 		TextButton speedResetButton = new TextButton("Reset", skin);
 
 		Slider mixSlider = new Slider(0, 4, 0.01f, false, skin);
-		Label mixLabel = new Label("0.3", skin);
+		Label mixLabel = new Label("0.3s", skin);
 
 		Label statusLabel = new Label("", skin);
 		WidgetGroup toasts = new WidgetGroup();
@@ -483,21 +491,27 @@ public class SkeletonViewer extends ApplicationAdapter {
 
 			loopCheckbox.setChecked(true);
 
-			scaleSlider.setValue(1);
-			scaleSlider.setSnapToValues(new float[] {0.5f, 1, 1.5f, 2, 2.5f, 3, 3.5f}, 0.01f);
+			loadScaleSlider.setValue(1);
+			loadScaleSlider.setSnapToValues(new float[] {0.5f, 1, 1.5f, 2, 2.5f}, 0.09f);
 
 			zoomSlider.setValue(1);
-			zoomSlider.setSnapToValues(new float[] {0.5f, 1, 1.5f, 2, 2.5f, 3, 3.5f}, 0.01f);
+			zoomSlider.setSnapToValues(new float[] {1, 2}, 0.30f);
+
+			xScaleSlider.setValue(1);
+			xScaleSlider.setSnapToValues(new float[] {-1.5f, -1, -0.5f, 0.5f, 1, 1.5f}, 0.12f);
+
+			yScaleSlider.setValue(1);
+			yScaleSlider.setSnapToValues(new float[] {-1.5f, -1, -0.5f, 0.5f, 1, 1.5f}, 0.12f);
 
 			mixSlider.setValue(0.3f);
-			mixSlider.setSnapToValues(new float[] {1, 1.5f, 2, 2.5f, 3, 3.5f}, 0.1f);
+			mixSlider.setSnapToValues(new float[] {1, 1.5f, 2, 2.5f, 3, 3.5f}, 0.12f);
 
 			speedSlider.setValue(1);
-			speedSlider.setSnapToValues(new float[] {0.5f, 0.75f, 1, 1.25f, 1.5f, 2, 2.5f}, 0.01f);
+			speedSlider.setSnapToValues(new float[] {0.5f, 0.75f, 1, 1.25f, 1.5f, 2, 2.5f}, 0.09f);
 
 			alphaSlider.setValue(1);
 			alphaSlider.setDisabled(true);
-			
+
 			addCheckbox.setDisabled(true);
 
 			window.setMovable(false);
@@ -519,12 +533,12 @@ public class SkeletonViewer extends ApplicationAdapter {
 			root.defaults().space(6);
 			root.columnDefaults(0).top().right().padTop(3);
 			root.columnDefaults(1).left();
-			root.add("Scale:");
+			root.add("Load scale:");
 			{
 				Table table = table();
-				table.add(scaleLabel).width(29);
-				table.add(scaleSlider).growX();
-				table.add(scaleResetButton);
+				table.add(loadScaleLabel).width(29);
+				table.add(loadScaleSlider).growX();
+				table.add(loadScaleResetButton);
 				root.add(table).fill().row();
 			}
 			root.add("Zoom:");
@@ -535,8 +549,22 @@ public class SkeletonViewer extends ApplicationAdapter {
 				table.add(zoomResetButton);
 				root.add(table).fill().row();
 			}
-			root.add("Flip:");
-			root.add(table(flipXCheckbox, flipYCheckbox)).row();
+			root.add("Scale X:");
+			{
+				Table table = table();
+				table.add(xScaleLabel).width(29);
+				table.add(xScaleSlider).growX();
+				table.add(xScaleResetButton).row();
+				root.add(table).fill().row();
+			}
+			root.add("Scale Y:");
+			{
+				Table table = table();
+				table.add(yScaleLabel).width(29);
+				table.add(yScaleSlider).growX();
+				table.add(yScaleResetButton);
+				root.add(table).fill().row();
+			}
 			root.add("Debug:");
 			root.add(table(debugBonesCheckbox, debugRegionsCheckbox, debugBoundingBoxesCheckbox)).row();
 			root.add();
@@ -677,25 +705,25 @@ public class SkeletonViewer extends ApplicationAdapter {
 				}
 			});
 
-			scaleSlider.addListener(new ChangeListener() {
+			loadScaleSlider.addListener(new ChangeListener() {
 				public void changed (ChangeEvent event, Actor actor) {
-					scaleLabel.setText(Float.toString((int)(scaleSlider.getValue() * 100) / 100f));
-					if (!scaleSlider.isDragging()) loadSkeleton(skeletonFile);
+					loadScaleLabel.setText(Integer.toString((int)(loadScaleSlider.getValue() * 100)) + "%");
+					if (!loadScaleSlider.isDragging()) loadSkeleton(skeletonFile);
 				}
 			});
-			scaleResetButton.addListener(new ChangeListener() {
+			loadScaleResetButton.addListener(new ChangeListener() {
 				public void changed (ChangeEvent event, Actor actor) {
 					resetCameraPosition();
-					if (scaleSlider.getValue() == 1)
+					if (loadScaleSlider.getValue() == 1)
 						loadSkeleton(skeletonFile);
 					else
-						scaleSlider.setValue(1);
+						loadScaleSlider.setValue(1);
 				}
 			});
 
 			zoomSlider.addListener(new ChangeListener() {
 				public void changed (ChangeEvent event, Actor actor) {
-					zoomLabel.setText(Float.toString((int)(zoomSlider.getValue() * 100) / 100f));
+					zoomLabel.setText(Integer.toString((int)(zoomSlider.getValue() * 100)) + "%");
 					float newZoom = 1 / zoomSlider.getValue();
 					camera.position.x -= window.getWidth() / 2 * (newZoom - camera.zoom);
 					camera.zoom = newZoom;
@@ -710,9 +738,33 @@ public class SkeletonViewer extends ApplicationAdapter {
 				}
 			});
 
+			xScaleSlider.addListener(new ChangeListener() {
+				public void changed (ChangeEvent event, Actor actor) {
+					if (xScaleSlider.getValue() == 0) xScaleSlider.setValue(0.01f);
+					xScaleLabel.setText(Integer.toString((int)(xScaleSlider.getValue() * 100)) + "%");
+				}
+			});
+			xScaleResetButton.addListener(new ChangeListener() {
+				public void changed (ChangeEvent event, Actor actor) {
+					xScaleSlider.setValue(1);
+				}
+			});
+
+			yScaleSlider.addListener(new ChangeListener() {
+				public void changed (ChangeEvent event, Actor actor) {
+					if (yScaleSlider.getValue() == 0) yScaleSlider.setValue(0.01f);
+					yScaleLabel.setText(Integer.toString((int)(yScaleSlider.getValue() * 100)) + "%");
+				}
+			});
+			yScaleResetButton.addListener(new ChangeListener() {
+				public void changed (ChangeEvent event, Actor actor) {
+					yScaleSlider.setValue(1);
+				}
+			});
+
 			speedSlider.addListener(new ChangeListener() {
 				public void changed (ChangeEvent event, Actor actor) {
-					speedLabel.setText(Float.toString((int)(speedSlider.getValue() * 100) / 100f));
+					speedLabel.setText(Float.toString((int)(speedSlider.getValue() * 100) / 100f) + "x");
 				}
 			});
 			speedResetButton.addListener(new ChangeListener() {
@@ -723,7 +775,7 @@ public class SkeletonViewer extends ApplicationAdapter {
 
 			alphaSlider.addListener(new ChangeListener() {
 				public void changed (ChangeEvent event, Actor actor) {
-					alphaLabel.setText(Float.toString((int)(alphaSlider.getValue() * 100) / 100f));
+					alphaLabel.setText(Integer.toString((int)(alphaSlider.getValue() * 100)) + "%");
 					int track = trackButtons.getCheckedIndex();
 					if (track > 0) {
 						TrackEntry current = state.getCurrent(track);
@@ -737,7 +789,7 @@ public class SkeletonViewer extends ApplicationAdapter {
 
 			mixSlider.addListener(new ChangeListener() {
 				public void changed (ChangeEvent event, Actor actor) {
-					mixLabel.setText(Float.toString((int)(mixSlider.getValue() * 100) / 100f));
+					mixLabel.setText(Float.toString((int)(mixSlider.getValue() * 100) / 100f) + "s");
 					if (state != null) state.getData().setDefaultMix(mixSlider.getValue());
 				}
 			});
@@ -878,8 +930,8 @@ public class SkeletonViewer extends ApplicationAdapter {
 			speedSlider.addListener(savePrefsListener);
 			speedResetButton.addListener(savePrefsListener);
 			mixSlider.addListener(savePrefsListener);
-			scaleSlider.addListener(savePrefsListener);
-			scaleResetButton.addListener(savePrefsListener);
+			loadScaleSlider.addListener(savePrefsListener);
+			loadScaleResetButton.addListener(savePrefsListener);
 			zoomSlider.addListener(savePrefsListener);
 			zoomResetButton.addListener(savePrefsListener);
 			animationList.addListener(savePrefsListener);
@@ -942,7 +994,7 @@ public class SkeletonViewer extends ApplicationAdapter {
 			prefs.putBoolean("add", addCheckbox.isChecked());
 			prefs.putFloat("speed", speedSlider.getValue());
 			prefs.putFloat("mix", mixSlider.getValue());
-			prefs.putFloat("scale", scaleSlider.getValue());
+			prefs.putFloat("scale", loadScaleSlider.getValue());
 			prefs.putFloat("zoom", zoomSlider.getValue());
 			prefs.putFloat("x", camera.position.x);
 			prefs.putFloat("y", camera.position.y);
@@ -978,7 +1030,7 @@ public class SkeletonViewer extends ApplicationAdapter {
 				camera.position.x = prefs.getFloat("x", 0);
 				camera.position.y = prefs.getFloat("y", 0);
 
-				scaleSlider.setValue(prefs.getFloat("scale", 1));
+				loadScaleSlider.setValue(prefs.getFloat("scale", 1));
 				animationList.setSelected(prefs.getString("animationName", null));
 				skinList.setSelected(prefs.getString("skinName", null));
 			} catch (Exception ex) {

+ 2 - 0
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpinePlugin.cpp

@@ -30,6 +30,8 @@
 
 #include "SpinePluginPrivatePCH.h"
 
+DEFINE_LOG_CATEGORY(SpineLog);
+
 class FSpinePlugin : public SpinePlugin {
 	virtual void StartupModule() override;
 	virtual void ShutdownModule() override;

+ 8 - 6
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonAnimationComponent.cpp

@@ -107,12 +107,14 @@ void USpineSkeletonAnimationComponent::CheckState () {
 		
 		if (Atlas && SkeletonData) {
 			spSkeletonData* data = SkeletonData->GetSkeletonData(Atlas->GetAtlas(false), false);
-			skeleton = spSkeleton_create(data);
-			spAnimationStateData* stateData = SkeletonData->GetAnimationStateData(Atlas->GetAtlas(false));
-			state = spAnimationState_create(stateData);
-			state->rendererObject = (void*)this;
-			state->listener = callback;
-			trackEntries.Empty();
+			if (data) {
+				skeleton = spSkeleton_create(data);
+				spAnimationStateData* stateData = SkeletonData->GetAnimationStateData(Atlas->GetAtlas(false));
+				state = spAnimationState_create(stateData);
+				state->rendererObject = (void*)this;
+				state->listener = callback;
+				trackEntries.Empty();
+			}
 		}
 		
 		lastAtlas = Atlas;

+ 13 - 0
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Private/SpineSkeletonDataAsset.cpp

@@ -33,6 +33,7 @@
 #include <string.h>
 #include <string>
 #include <stdlib.h>
+#include "Runtime/Core/Public/Misc/MessageDialog.h"
 
 #define LOCTEXT_NAMESPACE "Spine"
 
@@ -103,10 +104,22 @@ spSkeletonData* USpineSkeletonDataAsset::GetSkeletonData (spAtlas* Atlas, bool F
 		if (skeletonDataFileName.GetPlainNameString().Contains(TEXT(".json"))) {
 			spSkeletonJson* json = spSkeletonJson_create(Atlas);
 			this->skeletonData = spSkeletonJson_readSkeletonData(json, (const char*)rawData.GetData());
+			if (!skeletonData) {
+#if WITH_EDITORONLY_DATA
+				FMessageDialog::Debugf(FText::FromString(UTF8_TO_TCHAR(json->error)));
+#endif
+				UE_LOG(SpineLog, Error, TEXT("Couldn't load skeleton data and atlas: %s"), UTF8_TO_TCHAR(json->error));
+			}
 			spSkeletonJson_dispose(json);
 		} else {
 			spSkeletonBinary* binary = spSkeletonBinary_create(Atlas);
 			this->skeletonData = spSkeletonBinary_readSkeletonData(binary, (const unsigned char*)rawData.GetData(), (int)rawData.Num());
+			if (!skeletonData) {
+#if WITH_EDITORONLY_DATA
+				FMessageDialog::Debugf(FText::FromString(UTF8_TO_TCHAR(binary->error)));
+#endif
+				UE_LOG(SpineLog, Error, TEXT("Couldn't load skeleton data and atlas: %s"), UTF8_TO_TCHAR(binary->error));
+			}
 			spSkeletonBinary_dispose(binary);
 		}
 		if (animationStateData) {

+ 2 - 0
spine-ue4/Plugins/SpinePlugin/Source/SpinePlugin/Public/SpinePlugin.h

@@ -32,6 +32,8 @@
 
 #include "ModuleManager.h"
 
+DECLARE_LOG_CATEGORY_EXTERN(SpineLog, Log, All);
+
 class SPINEPLUGIN_API SpinePlugin : public IModuleInterface {
 
 public:

+ 9 - 0
spine-unity/Assets/Spine Examples/Other Examples/Animation Tester.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 173cd2c662ebd674f994bff2385cfbf6
+folderAsset: yes
+timeCreated: 1529972040
+licenseType: Free
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

BIN
spine-unity/Assets/Spine Examples/Other Examples/Animation Tester/Animation Tester.unity


+ 8 - 0
spine-unity/Assets/Spine Examples/Other Examples/Animation Tester/Animation Tester.unity.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b804088948820194cbda76af39c08174
+timeCreated: 1529972058
+licenseType: Free
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 117 - 0
spine-unity/Assets/Spine Examples/Other Examples/Animation Tester/SpineAnimationTesterTool.cs

@@ -0,0 +1,117 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+using Spine;
+using Spine.Unity;
+
+using System.Text;
+
+namespace Spine.Unity.Examples {
+	public class SpineAnimationTesterTool : MonoBehaviour, IHasSkeletonDataAsset, IHasSkeletonComponent {
+
+		public SkeletonAnimation skeletonAnimation;
+		public SkeletonDataAsset SkeletonDataAsset { get { return skeletonAnimation.SkeletonDataAsset; } }
+		public ISkeletonComponent SkeletonComponent { get { return skeletonAnimation; } }
+
+		public bool useOverrideMixDuration;
+		public float overrideMixDuration = 0.2f;
+
+		[System.Serializable]
+		public struct AnimationControl {
+			[SpineAnimation]
+			public string animationName;
+			public bool loop;
+			public KeyCode key;
+
+			[Space]			
+			public bool useCustomMixDuration;
+			public float mixDuration;
+			//public bool useChainToControl;
+			//public int chainToControl;
+		}
+		[System.Serializable]
+		public class ControlledTrack {
+			public List<AnimationControl> controls = new List<AnimationControl>();
+		}
+
+		[Space]
+		public List<ControlledTrack> trackControls = new List<ControlledTrack>();
+
+		[Header("UI")]
+		public UnityEngine.UI.Text boundAnimationsText;
+		public UnityEngine.UI.Text skeletonNameText;
+
+		void OnValidate () {
+			// Fill in the SkeletonData asset name
+			if (skeletonNameText != null) {
+				if (skeletonAnimation != null && skeletonAnimation.skeletonDataAsset != null) {
+					skeletonNameText.text = SkeletonDataAsset.name.Replace("_SkeletonData", "");
+				}
+			}
+
+			// Fill in the control list.
+			if (boundAnimationsText != null) {
+				var boundAnimationsStringBuilder = new StringBuilder();
+				boundAnimationsStringBuilder.AppendLine("Animation Controls:");
+
+				for (int trackIndex = 0; trackIndex < trackControls.Count; trackIndex++) {
+
+					if (trackIndex > 0)
+						boundAnimationsStringBuilder.AppendLine();
+
+					boundAnimationsStringBuilder.AppendFormat("---- Track {0} ---- \n", trackIndex);
+					foreach (var ba in trackControls[trackIndex].controls) {
+						string animationName = ba.animationName;
+						if (string.IsNullOrEmpty(animationName))
+							animationName = "SetEmptyAnimation";
+
+						boundAnimationsStringBuilder.AppendFormat("[{0}]  {1}\n", ba.key.ToString(), animationName);
+					}
+
+				}	
+
+				boundAnimationsText.text = boundAnimationsStringBuilder.ToString();
+
+			}
+				
+		}
+
+		void Start () {
+			if (useOverrideMixDuration) {
+				skeletonAnimation.AnimationState.Data.DefaultMix = overrideMixDuration;
+			}
+		}
+
+		void Update () {
+			var animationState = skeletonAnimation.AnimationState;
+
+			// For each track
+			for (int trackIndex = 0; trackIndex < trackControls.Count; trackIndex++) {
+
+				// For each control in the track
+				foreach (var control in trackControls[trackIndex].controls) {
+
+					// Check each control, and play the appropriate animation.
+					if (Input.GetKeyDown(control.key)) {
+						if (!string.IsNullOrEmpty(control.animationName)) {
+							var trackEntry = animationState.SetAnimation(trackIndex, control.animationName, control.loop);
+							if (control.useCustomMixDuration)
+								trackEntry.MixDuration = control.mixDuration;
+
+						} else {
+							float mix = control.useCustomMixDuration ? control.mixDuration : animationState.Data.DefaultMix;
+							animationState.SetEmptyAnimation(trackIndex, mix);
+						}
+
+						// Don't parse more than one animation per track.
+						break; 
+					}
+				}
+			}
+
+		}
+
+	}
+}
+

+ 12 - 0
spine-unity/Assets/Spine Examples/Other Examples/Animation Tester/SpineAnimationTesterTool.cs.meta

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

BIN
spine-unity/Assets/Spine Examples/Other Examples/Mix and Match Equip.unity


+ 9 - 0
spine-unity/Assets/Spine Examples/Other Examples/StateMachine SkeletonAnimation.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 4766fcfd6167d2e46aad772ce3bc898c
+folderAsset: yes
+timeCreated: 1531292725
+licenseType: Free
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

BIN
spine-unity/Assets/Spine Examples/Other Examples/StateMachine SkeletonAnimation/AnimationState with Mecanim.unity


+ 8 - 0
spine-unity/Assets/Spine Examples/Other Examples/StateMachine SkeletonAnimation/AnimationState with Mecanim.unity.meta

@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: afd3c9ec31200bc49b169c22f00b010b
+timeCreated: 1531300871
+licenseType: Free
+DefaultImporter:
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

BIN
spine-unity/Assets/Spine Examples/Other Examples/StateMachine SkeletonAnimation/Hero.controller


+ 9 - 0
spine-unity/Assets/Spine Examples/Other Examples/StateMachine SkeletonAnimation/Hero.controller.meta

@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: cec34498f2eb26b488452ec274c54439
+timeCreated: 1531292741
+licenseType: Free
+NativeFormatImporter:
+  mainObjectFileID: 9100000
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 

+ 62 - 0
spine-unity/Assets/Spine Examples/Scripts/AnimationStateMecanimState.cs

@@ -0,0 +1,62 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+using Spine;
+using Spine.Unity;
+
+public class AnimationStateMecanimState : StateMachineBehaviour {
+
+	#region Inspector
+	public AnimationReferenceAsset animation;
+
+	[System.Serializable]
+	public struct AnimationTransition {
+		public AnimationReferenceAsset from;
+		public AnimationReferenceAsset transition;
+	}
+
+	[UnityEngine.Serialization.FormerlySerializedAs("transitions")]
+	public List<AnimationTransition> fromTransitions = new List<AnimationTransition>();
+	#endregion
+
+	Spine.AnimationState state;
+
+	public void Initialize (Animator animator) {
+		if (state == null) {
+			var animationStateComponent = (animator.GetComponent(typeof(IAnimationStateComponent))) as IAnimationStateComponent;
+			state = animationStateComponent.AnimationState;
+		}
+	}
+
+	override public void OnStateEnter (Animator animator, AnimatorStateInfo stateInfo, int layerIndex) {
+		if (state == null) {
+			Initialize(animator);
+		}
+		
+		float timeScale = stateInfo.speed;
+		var current = state.GetCurrent(layerIndex);
+
+		bool transitionPlayed = false;
+		if (current != null && fromTransitions.Count > 0) {
+			foreach (var t in fromTransitions) {
+				if (t.from.Animation == current.Animation) {
+					var transitionEntry = state.SetAnimation(layerIndex, t.transition.Animation, false);
+					transitionEntry.TimeScale = timeScale;
+					transitionPlayed = true;
+					break;
+				}
+			}
+		}
+
+		TrackEntry trackEntry;
+		if (transitionPlayed) {
+			trackEntry = state.AddAnimation(layerIndex, animation.Animation, stateInfo.loop, 0);
+		} else {
+			trackEntry = state.SetAnimation(layerIndex, animation.Animation, stateInfo.loop);
+		}
+		trackEntry.TimeScale = timeScale;
+
+	}
+
+}

+ 12 - 0
spine-unity/Assets/Spine Examples/Scripts/AnimationStateMecanimState.cs.meta

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

+ 82 - 0
spine-unity/Assets/Spine Examples/Scripts/AnimationStateWithMecanimExample.cs

@@ -0,0 +1,82 @@
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+using Spine;
+using Spine.Unity;
+
+namespace Spine.Unity.Examples {
+	public class AnimationStateWithMecanimExample : MonoBehaviour {
+
+		SkeletonAnimation skeletonAnimation;
+		Animator logicAnimator;
+		
+		[Header("Controls")]
+		public KeyCode walkButton = KeyCode.LeftShift;
+		public KeyCode jumpButton = KeyCode.Space;
+
+		[Header("Animator Properties")]
+		public string horizontalSpeedProperty = "Speed";
+		public string verticalSpeedProperty = "VerticalSpeed";
+		public string groundedProperty = "Grounded";
+
+		[Header("Fake Physics")]
+		public float jumpDuration = 1.5f;
+		public Vector2 speed;
+		public bool isGrounded;
+
+		void Awake () {
+			skeletonAnimation = GetComponent<SkeletonAnimation>();
+			logicAnimator = GetComponent<Animator>();
+
+			isGrounded = true;
+		}
+
+		void Update () {
+			float x = Input.GetAxisRaw("Horizontal");			
+			if (Input.GetKey(walkButton)) {
+				x *= 0.4f;
+			}
+
+			speed.x = x;
+
+			// Flip skeleton.
+			if (x != 0) {
+				skeletonAnimation.Skeleton.ScaleX = x > 0 ? 1f : -1f;
+			}
+
+			if (Input.GetKeyDown(jumpButton)) {
+				if (isGrounded)
+					StartCoroutine(FakeJump());
+			}
+				
+			logicAnimator.SetFloat(horizontalSpeedProperty, Mathf.Abs(speed.x));
+			logicAnimator.SetFloat(verticalSpeedProperty, speed.y);
+			logicAnimator.SetBool(groundedProperty, isGrounded);
+			
+		}
+
+		IEnumerator FakeJump () {
+			// Rise
+			isGrounded = false;
+			speed.y = 10f;
+			float durationLeft = jumpDuration * 0.5f;
+			while (durationLeft > 0) {
+				durationLeft -= Time.deltaTime;
+				if (!Input.GetKey(jumpButton)) break;
+				yield return null;
+			}
+
+			// Fall
+			speed.y = -10f;
+			float fallDuration = (jumpDuration * 0.5f) - durationLeft;
+			yield return new WaitForSeconds(fallDuration);
+
+			// Land
+			speed.y = 0f;
+			isGrounded = true;
+			yield return null;
+		}
+	}
+
+}

+ 12 - 0
spine-unity/Assets/Spine Examples/Scripts/AnimationStateWithMecanimExample.cs.meta

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

+ 2 - 2
spine-unity/Assets/Spine Examples/Scripts/FootSoldierExample.cs

@@ -80,11 +80,11 @@ namespace Spine.Unity.Examples {
 			} else {
 				if (Input.GetKey(rightKey)) {
 					skeletonAnimation.AnimationName = moveAnimation;
-					skeletonAnimation.Skeleton.FlipX = false;
+					skeletonAnimation.Skeleton.ScaleX = 1;
 					transform.Translate(moveSpeed * Time.deltaTime, 0, 0);
 				} else if(Input.GetKey(leftKey)) {
 					skeletonAnimation.AnimationName = moveAnimation;
-					skeletonAnimation.Skeleton.FlipX = true;
+					skeletonAnimation.Skeleton.ScaleX = -1;
 					transform.Translate(-moveSpeed * Time.deltaTime, 0, 0);
 				} else {
 					skeletonAnimation.AnimationName = idleAnimation;

+ 1 - 1
spine-unity/Assets/Spine Examples/Scripts/Getting Started Scripts/BasicPlatformerController.cs

@@ -183,7 +183,7 @@ namespace Spine.Unity.Examples {
 
 			// Face intended direction.
 			if (input.x != 0)
-				skeletonAnimation.Skeleton.FlipX = input.x < 0;
+				skeletonAnimation.Skeleton.ScaleX = Mathf.Sign(input.x);
 
 
 			// Effects

+ 2 - 2
spine-unity/Assets/Spine Examples/Scripts/Getting Started Scripts/SpineBeginnerTwo.cs

@@ -93,11 +93,11 @@ namespace Spine.Unity.Examples {
 				spineAnimationState.AddAnimation(0, idleAnimationName, true, 0);
 				yield return new WaitForSeconds(1f);
 
-				skeleton.FlipX = true;		// skeleton allows you to flip the skeleton.
+				skeleton.ScaleX = -1;		// skeleton allows you to flip the skeleton.
 				spineAnimationState.SetAnimation(0, idleTurnAnimationName, false);
 				spineAnimationState.AddAnimation(0, idleAnimationName, true, 0);
 				yield return new WaitForSeconds(0.5f);
-				skeleton.FlipX = false;
+				skeleton.ScaleX = 1;
 				spineAnimationState.SetAnimation(0, idleTurnAnimationName, false);
 				spineAnimationState.AddAnimation(0, idleAnimationName, true, 0);
 				yield return new WaitForSeconds(0.5f);

+ 2 - 2
spine-unity/Assets/Spine Examples/Scripts/Getting Started Scripts/SpineboyBeginnerView.cs

@@ -69,7 +69,7 @@ namespace Spine.Unity.Examples {
 			if (skeletonAnimation == null) return;
 			if (model == null) return;
 
-			if (skeletonAnimation.skeleton.FlipX != model.facingLeft) {	// Detect changes in model.facingLeft
+			if ((skeletonAnimation.skeleton.ScaleX < 0) != model.facingLeft) {	// Detect changes in model.facingLeft
 				Turn(model.facingLeft);
 			}
 
@@ -134,7 +134,7 @@ namespace Spine.Unity.Examples {
 		}
 
 		public void Turn (bool facingLeft) {
-			skeletonAnimation.Skeleton.FlipX = facingLeft;
+			skeletonAnimation.Skeleton.ScaleX = facingLeft ? -1f : 1f;
 			// Maybe play a transient turning animation too, then call ChangeStableAnimation.
 		}
 		#endregion

+ 1 - 1
spine-unity/Assets/Spine Examples/Scripts/Getting Started Scripts/SpineboyTargetController.cs

@@ -23,7 +23,7 @@ namespace Spine.Unity.Examples {
 			var mousePosition = Input.mousePosition;
 			var worldMousePosition = camera.ScreenToWorldPoint(mousePosition);
 			var skeletonSpacePoint = skeletonAnimation.transform.InverseTransformPoint(worldMousePosition);
-			if (skeletonAnimation.Skeleton.FlipX) skeletonSpacePoint.x *= -1;
+			//if (skeletonAnimation.Skeleton.FlipX) skeletonSpacePoint.x *= -1;
 			bone.SetPosition(skeletonSpacePoint);
 		}
 	}

+ 2 - 2
spine-unity/Assets/Spine Examples/Scripts/Sample Components/SkeletonAnimationMulti/SkeletonAnimationMulti.cs

@@ -69,8 +69,8 @@ namespace Spine.Unity {
 				sa.initialFlipX = this.initialFlipX;
 				sa.initialFlipY = this.initialFlipY;
 				var skeleton = sa.skeleton;
-				skeleton.FlipX = this.initialFlipX;
-				skeleton.FlipY = this.initialFlipY;
+				skeleton.ScaleX = this.initialFlipX ? 1 : -1;
+				skeleton.ScaleY = this.initialFlipY ? 1 : -1;
 
 				sa.Initialize(false);
 				skeletonAnimations.Add(sa);

+ 0 - 0
spine-unity/Assets/Spine Examples/Spine.meta → spine-unity/Assets/Spine Examples/Spine Skeletons.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon.atlas.txt → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon.atlas.txt


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon.atlas.txt.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon.atlas.txt.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon.json → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon.json


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon.json.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon.json.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon.png → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon.png


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon.png.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon.png.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon2.png → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon2.png


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon2.png.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon2.png.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon_Atlas.asset → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_Atlas.asset


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon_Atlas.asset.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_Atlas.asset.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon_SkeletonData.asset → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_SkeletonData.asset


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon_SkeletonData.asset.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_SkeletonData.asset.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon_dragon.mat → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_dragon.mat


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon_dragon.mat.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_dragon.mat.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon_dragon2.mat → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_dragon2.mat


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/dragon_dragon2.mat.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/dragon_dragon2.mat.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/license.txt → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/license.txt


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Dragon/license.txt.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Dragon/license.txt.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes/eyes.atlas.txt → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes.atlas.txt


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes/eyes.atlas.txt.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes.atlas.txt.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes/eyes.json → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes.json


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes/eyes.json.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes.json.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes/eyes.png → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes.png


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes/eyes.png.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes.png.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes/eyes_Atlas.asset → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes_Atlas.asset


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes/eyes_Atlas.asset.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes_Atlas.asset.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes/eyes_Material.mat → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes_Material.mat


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes/eyes_Material.mat.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes_Material.mat.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes/eyes_SkeletonData.asset → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes_SkeletonData.asset


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Eyes/eyes_SkeletonData.asset.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Eyes/eyes_SkeletonData.asset.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/Equipment.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/Equipment/Equipment.atlas.txt → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment.atlas.txt


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/Equipment/Equipment.atlas.txt.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment.atlas.txt.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/Equipment/Equipment.png → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment.png


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/Equipment/Equipment.png.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment.png.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/Equipment/Equipment_Atlas.asset → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment_Atlas.asset


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/Equipment/Equipment_Atlas.asset.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment_Atlas.asset.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/Equipment/Equipment_Material.mat → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment_Material.mat


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/Equipment/Equipment_Material.mat.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/Equipment/Equipment_Material.mat.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/FS_White.atlas.txt → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White.atlas.txt


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/FS_White.atlas.txt.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White.atlas.txt.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/FS_White.png → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White.png


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/FS_White.png.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White.png.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/FS_White_Atlas.asset → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White_Atlas.asset


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/FS_White_Atlas.asset.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White_Atlas.asset.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/FS_White_Material.mat → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White_Material.mat


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/FS_White_Material.mat.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FS_White_Material.mat.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/FootSoldier.json → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FootSoldier.json


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/FootSoldier.json.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FootSoldier.json.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/FootSoldier_SkeletonData.asset → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FootSoldier_SkeletonData.asset


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/FootSoldier_SkeletonData.asset.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/FootSoldier_SkeletonData.asset.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/license.txt → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/license.txt


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/FootSoldier/license.txt.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/FootSoldier/license.txt.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Gauge.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Gauge.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Gauge/Gauge.atlas.txt → spine-unity/Assets/Spine Examples/Spine Skeletons/Gauge/Gauge.atlas.txt


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Gauge/Gauge.atlas.txt.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Gauge/Gauge.atlas.txt.meta


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Gauge/Gauge.json → spine-unity/Assets/Spine Examples/Spine Skeletons/Gauge/Gauge.json


+ 0 - 0
spine-unity/Assets/Spine Examples/Spine/Gauge/Gauge.json.meta → spine-unity/Assets/Spine Examples/Spine Skeletons/Gauge/Gauge.json.meta


Some files were not shown because too many files changed in this diff