mixingToArray, IntSet propertyIDs) {
- if (to != null) mixingToArray.add(to);
- TrackEntry lastEntry = mixingFrom != null ? mixingFrom.setTimelineData(this, mixingToArray, propertyIDs) : this;
- if (to != null) mixingToArray.pop();
-
- Object[] mixingTo = mixingToArray.items;
- int mixingToLast = mixingToArray.size - 1;
- Object[] timelines = animation.timelines.items;
- int timelinesCount = animation.timelines.size;
- int[] timelineData = this.timelineData.setSize(timelinesCount);
- timelineDipMix.clear();
- Object[] timelineDipMix = this.timelineDipMix.setSize(timelinesCount);
- outer:
- for (int i = 0; i < timelinesCount; i++) {
- int id = ((Timeline)timelines[i]).getPropertyId();
- if (!propertyIDs.add(id))
- timelineData[i] = SUBSEQUENT;
- else if (to == null || !to.hasTimeline(id))
- timelineData[i] = FIRST;
- else {
- for (int ii = mixingToLast; ii >= 0; ii--) {
- TrackEntry entry = (TrackEntry)mixingTo[ii];
- if (!entry.hasTimeline(id)) {
- if (entry.mixDuration > 0) {
- timelineData[i] = DIP_MIX;
- timelineDipMix[i] = entry;
- continue outer;
- }
- break;
- }
- }
- timelineData[i] = DIP;
- }
- }
- return lastEntry;
- }
-
- private boolean hasTimeline (int id) {
- Object[] timelines = animation.timelines.items;
- for (int i = 0, n = animation.timelines.size; i < n; i++)
- if (((Timeline)timelines[i]).getPropertyId() == id) return true;
- return false;
- }
-
/** The index of the track where this track entry is either current or queued.
*
* See {@link AnimationState#getCurrent(int)}. */
@@ -1054,6 +1067,25 @@ public class AnimationState {
return mixingFrom;
}
+ public void setHoldPrevious (boolean holdPrevious) {
+ this.holdPrevious = holdPrevious;
+ }
+
+ /** If true, when mixing from the previous animation to this animation, the previous animation is applied as normal instead
+ * of being mixed out.
+ *
+ * When mixing between animations that key the same property, if a lower track also keys that property then the value will
+ * briefly dip toward the lower track value during the mix. This happens because the first animation mixes from 100% to 0%
+ * while the second animation mixes from 0% to 100%. Setting holdPrevious to true applies the first animation
+ * at 100% during the mix so the lower track value is overwritten. Such dipping does not occur on the lowest track which
+ * keys the property, only when a higher track also keys the property.
+ *
+ * Snapping will occur if holdPrevious is true and this animation does not key all the same properties as the
+ * previous animation. */
+ public boolean getHoldPrevious () {
+ return holdPrevious;
+ }
+
/** Resets the rotation directions for mixing this entry's rotate timelines. This can be useful to avoid bones rotating the
* long way around when using {@link #alpha} and starting animations on other tracks.
*
@@ -1165,7 +1197,7 @@ public class AnimationState {
start, interrupt, end, dispose, complete, event
}
- /** The interface which can be implemented to receive TrackEntry events.
+ /** The interface to implement for receiving TrackEntry events.
*
* See TrackEntry {@link TrackEntry#setListener(AnimationStateListener)} and AnimationState
* {@link AnimationState#addListener(AnimationStateListener)}. */
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java
index 77623cbad..4fbcebab4 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraint.java
@@ -43,7 +43,7 @@ public class IkConstraint implements Constraint {
final Array bones;
Bone target;
int bendDirection;
- boolean stretch;
+ boolean compress, stretch;
float mix = 1;
public IkConstraint (IkConstraintData data, Skeleton skeleton) {
@@ -52,6 +52,7 @@ public class IkConstraint implements Constraint {
this.data = data;
mix = data.mix;
bendDirection = data.bendDirection;
+ compress = data.compress;
stretch = data.stretch;
bones = new Array(data.bones.size);
@@ -71,6 +72,7 @@ public class IkConstraint implements Constraint {
target = skeleton.bones.get(constraint.target.data.index);
mix = constraint.mix;
bendDirection = constraint.bendDirection;
+ compress = constraint.compress;
stretch = constraint.stretch;
}
@@ -84,7 +86,7 @@ public class IkConstraint implements Constraint {
Array bones = this.bones;
switch (bones.size) {
case 1:
- apply(bones.first(), target.worldX, target.worldY, stretch, mix);
+ apply(bones.first(), target.worldX, target.worldY, compress, stretch, data.uniform, mix);
break;
case 2:
apply(bones.first(), bones.get(1), target.worldX, target.worldY, bendDirection, stretch, mix);
@@ -128,8 +130,17 @@ 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. */
+ /** When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it. */
+ public boolean getCompress () {
+ return compress;
+ }
+
+ public void setCompress (boolean compress) {
+ this.compress = compress;
+ }
+
+ /** When true, if the target is out of range, the parent bone is scaled to reach it. If more than one bone is being constrained
+ * and the parent bone has local nonuniform scale, stretch is not applied. */
public boolean getStretch () {
return stretch;
}
@@ -148,7 +159,8 @@ 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, boolean stretch, float alpha) {
+ static public void apply (Bone bone, float targetX, float targetY, boolean compress, boolean stretch, boolean uniform,
+ float alpha) {
if (!bone.appliedValid) bone.updateAppliedTransform();
Bone p = bone.parent;
float id = 1 / (p.a * p.d - p.b * p.c);
@@ -158,14 +170,18 @@ public class IkConstraint implements Constraint {
if (bone.ascaleX < 0) rotationIK += 180;
if (rotationIK > 180)
rotationIK -= 360;
- else if (rotationIK < -180) rotationIK += 360;
- float sx = bone.ascaleX;
- if (stretch) {
+ else if (rotationIK < -180) //
+ rotationIK += 360;
+ float sx = bone.ascaleX, sy = bone.ascaleY;
+ if (compress || 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;
+ if ((compress && dd < b) || (stretch && dd > b) && b > 0.0001f) {
+ float s = (dd / b - 1) * alpha + 1;
+ sx *= s;
+ if (uniform) sy *= s;
+ }
}
- bone.updateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, bone.ascaleY, bone.ashearX,
- bone.ashearY);
+ bone.updateWorldTransform(bone.ax, bone.ay, bone.arotation + rotationIK * alpha, sx, sy, bone.ashearX, bone.ashearY);
}
/** Applies 2 bone IK. The target is specified in the world coordinate system.
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java
index 81bceb23b..0ef63a675 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/IkConstraintData.java
@@ -41,7 +41,7 @@ public class IkConstraintData {
final Array bones = new Array();
BoneData target;
int bendDirection = 1;
- boolean stretch;
+ boolean compress, stretch, uniform;
float mix = 1;
public IkConstraintData (String name) {
@@ -78,25 +78,6 @@ public class IkConstraintData {
this.target = target;
}
- /** Controls the bend direction of the IK bones, either 1 or -1. */
- public int getBendDirection () {
- return bendDirection;
- }
-
- public void setBendDirection (int bendDirection) {
- 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;
@@ -106,6 +87,44 @@ public class IkConstraintData {
this.mix = mix;
}
+ /** Controls the bend direction of the IK bones, either 1 or -1. */
+ public int getBendDirection () {
+ return bendDirection;
+ }
+
+ public void setBendDirection (int bendDirection) {
+ this.bendDirection = bendDirection;
+ }
+
+ /** When true and only a single bone is being constrained, if the target is too close, the bone is scaled to reach it. */
+ public boolean getCompress () {
+ return compress;
+ }
+
+ public void setCompress (boolean compress) {
+ this.compress = compress;
+ }
+
+ /** When true, if the target is out of range, the parent bone is scaled to reach it. If more than one bone is being constrained
+ * and the parent bone has local nonuniform scale, stretch is not applied. */
+ public boolean getStretch () {
+ return stretch;
+ }
+
+ public void setStretch (boolean stretch) {
+ this.stretch = stretch;
+ }
+
+ /** When true, only a single bone is being constrained, and {@link #getCompress()} or {@link #getStretch()} is used, the bone
+ * is scaled on both the X and Y axes. */
+ public boolean getUniform () {
+ return uniform;
+ }
+
+ public void setUniform (boolean uniform) {
+ this.uniform = uniform;
+ }
+
public String toString () {
return name;
}
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
index 1e3168037..498dd54d2 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/Skeleton.java
@@ -394,9 +394,10 @@ public class Skeleton {
Array ikConstraints = this.ikConstraints;
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;
+ constraint.bendDirection = constraint.data.bendDirection;
+ constraint.compress = constraint.data.compress;
+ constraint.stretch = constraint.data.stretch;
}
Array transformConstraints = this.transformConstraints;
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java
index 1d5e0959b..3f88d8651 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonBinary.java
@@ -232,7 +232,9 @@ public class SkeletonBinary {
data.target = skeletonData.bones.get(input.readInt(true));
data.mix = input.readFloat();
data.bendDirection = input.readByte();
+ data.compress = input.readBoolean();
data.stretch = input.readBoolean();
+ data.uniform = input.readBoolean();
skeletonData.ikConstraints.add(data);
}
@@ -307,7 +309,7 @@ public class SkeletonBinary {
data.intValue = input.readInt(false);
data.floatValue = input.readFloat();
data.stringValue = input.readString();
- data.audioPath = input.readString();
+ data.audioPath = input.readString();
skeletonData.events.add(data);
}
@@ -661,7 +663,8 @@ 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(), input.readBoolean());
+ timeline.setFrame(frameIndex, input.readFloat(), input.readFloat(), input.readByte(), input.readBoolean(),
+ input.readBoolean());
if (frameIndex < frameCount - 1) readCurve(input, frameIndex, timeline);
}
timelines.add(timeline);
diff --git a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java
index 3120595b6..9ec1d11ab 100644
--- a/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java
+++ b/spine-libgdx/spine-libgdx/src/com/esotericsoftware/spine/SkeletonJson.java
@@ -185,9 +185,11 @@ public class SkeletonJson {
data.target = skeletonData.findBone(targetName);
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);
+ data.bendDirection = constraintMap.getBoolean("bendPositive", true) ? 1 : -1;
+ data.compress = constraintMap.getBoolean("compress", false);
+ data.stretch = constraintMap.getBoolean("stretch", false);
+ data.uniform = constraintMap.getBoolean("uniform", false);
skeletonData.ikConstraints.add(data);
}
@@ -569,7 +571,8 @@ 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("stretch", false));
+ valueMap.getBoolean("bendPositive", true) ? 1 : -1, valueMap.getBoolean("compress", false),
+ valueMap.getBoolean("stretch", false));
readCurve(valueMap, timeline, frameIndex);
frameIndex++;
}
diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonDebugWindow.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonDebugWindow.cs
index 7bf8406e6..73396814c 100644
--- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonDebugWindow.cs
+++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SkeletonDebugWindow.cs
@@ -375,6 +375,7 @@ namespace Spine.Unity.Editor {
EditorGUI.BeginChangeCheck();
c.Mix = EditorGUILayout.Slider("Mix", c.Mix, MixMin, MixMax);
c.BendDirection = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Bend Clockwise", tooltip: "IkConstraint.BendDirection == 1 if clockwise; -1 if counterclockwise."), c.BendDirection > 0) ? 1 : -1;
+ c.Stretch = EditorGUILayout.Toggle(SpineInspectorUtility.TempContent("Stretch", tooltip: "Stretch the parent bone when the target is out of range. Not applied when parent bone has nonuniform scale."), c.Stretch);
if (EditorGUI.EndChangeCheck()) requireRepaint = true;
EditorGUILayout.Space();
diff --git a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs
index d7ab79121..8ecc74075 100644
--- a/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs
+++ b/spine-unity/Assets/Spine/Editor/spine-unity/Editor/SpineEditorUtilities.cs
@@ -905,7 +905,7 @@ namespace Spine.Unity.Editor {
}
if (!match)
- Debug.LogWarningFormat("Skeleton '{0}' (exported with Spine {1}) may be incompatible with your runtime version: spine-unity v{2}", asset.name, rawVersion, primaryRuntimeVersionDebugString);
+ Debug.LogWarningFormat("Skeleton '{0}' (exported with Spine {1}) may be incompatible with your runtime version: spine-csharp v{2}", asset.name, rawVersion, primaryRuntimeVersionDebugString);
}
}
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Ghost/SkeletonGhost.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Ghost/SkeletonGhost.cs
index ed791b533..8eb7508c6 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Ghost/SkeletonGhost.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Ghost/SkeletonGhost.cs
@@ -45,10 +45,9 @@ namespace Spine.Unity.Modules {
public bool ghostingEnabled = true;
[Tooltip("The time between invididual ghost pieces being spawned.")]
[UnityEngine.Serialization.FormerlySerializedAs("spawnRate")]
- public float spawnInterval = 0.05f;
+ public float spawnInterval = 1f/30f;
[Tooltip("Maximum number of ghosts that can exist at a time. If the fade speed is not fast enough, the oldest ghost will immediately disappear to enforce the maximum number.")]
public int maximumGhosts = 10;
- [Tooltip("Fadespeed 1 means it will take 1 second for a piece to fade out. 2 means it will take 1/2 second. 10 means it will take 1/10 of a second.")]
public float fadeSpeed = 10;
[Header("Rendering")]
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Ghost/SkeletonGhostRenderer.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Ghost/SkeletonGhostRenderer.cs
index e9f86c881..37e4213f4 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Ghost/SkeletonGhostRenderer.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/Ghost/SkeletonGhostRenderer.cs
@@ -35,17 +35,23 @@ using System.Collections;
namespace Spine.Unity.Modules {
public class SkeletonGhostRenderer : MonoBehaviour {
+ static readonly Color32 TransparentBlack = new Color32(0, 0, 0, 0);
+ const string colorPropertyName = "_Color";
- public float fadeSpeed = 10;
-
- Color32[] colors;
- Color32 black = new Color32(0, 0, 0, 0);
+ float fadeSpeed = 10;
+ Color32 startColor;
MeshFilter meshFilter;
MeshRenderer meshRenderer;
+ MaterialPropertyBlock mpb;
+ int colorId;
+
void Awake () {
meshRenderer = gameObject.AddComponent();
meshFilter = gameObject.AddComponent();
+
+ colorId = Shader.PropertyToID(colorPropertyName);
+ mpb = new MaterialPropertyBlock();
}
public void Initialize (Mesh mesh, Material[] materials, Color32 color, bool additive, float speed, int sortingLayerID, int sortingOrder) {
@@ -56,12 +62,9 @@ namespace Spine.Unity.Modules {
meshRenderer.sortingLayerID = sortingLayerID;
meshRenderer.sortingOrder = sortingOrder;
meshFilter.sharedMesh = Instantiate(mesh);
- colors = meshFilter.sharedMesh.colors32;
-
- if ((color.a + color.r + color.g + color.b) > 0) {
- for (int i = 0; i < colors.Length; i++)
- colors[i] = color;
- }
+ startColor = color;
+ mpb.SetColor(colorId, color);
+ meshRenderer.SetPropertyBlock(mpb);
fadeSpeed = speed;
@@ -72,19 +75,17 @@ namespace Spine.Unity.Modules {
}
IEnumerator Fade () {
- Color32 c;
- for (int t = 0; t < 500; t++) {
- bool breakout = true;
- for (int i = 0; i < colors.Length; i++) {
- c = colors[i];
- if (c.a > 0)
- breakout = false;
+ Color32 c = startColor;
+ Color32 black = SkeletonGhostRenderer.TransparentBlack;
- colors[i] = Color32.Lerp(c, black, Time.deltaTime * fadeSpeed);
- }
- meshFilter.sharedMesh.colors32 = colors;
+ float t = 1f;
+ for (float hardTimeLimit = 5f; hardTimeLimit > 0; hardTimeLimit -= Time.deltaTime) {
+ c = Color32.Lerp(black, startColor, t);
+ mpb.SetColor(colorId, c);
+ meshRenderer.SetPropertyBlock(mpb);
- if (breakout)
+ t = Mathf.Lerp(t, 0, Time.deltaTime * fadeSpeed);
+ if (t <= 0)
break;
yield return null;
@@ -95,25 +96,20 @@ namespace Spine.Unity.Modules {
}
IEnumerator FadeAdditive () {
- Color32 c;
- Color32 black = this.black;
+ Color32 c = startColor;
+ Color32 black = SkeletonGhostRenderer.TransparentBlack;
- for (int t = 0; t < 500; t++) {
+ float t = 1f;
+
+ for (float hardTimeLimit = 5f; hardTimeLimit > 0; hardTimeLimit -= Time.deltaTime) {
+ c = Color32.Lerp(black, startColor, t);
+ mpb.SetColor(colorId, c);
+ meshRenderer.SetPropertyBlock(mpb);
- bool breakout = true;
- for (int i = 0; i < colors.Length; i++) {
- c = colors[i];
- black.a = c.a;
- if (c.r > 0 || c.g > 0 || c.b > 0)
- breakout = false;
-
- colors[i] = Color32.Lerp(c, black, Time.deltaTime * fadeSpeed);
- }
-
- meshFilter.sharedMesh.colors32 = colors;
-
- if (breakout)
+ t = Mathf.Lerp(t, 0, Time.deltaTime * fadeSpeed);
+ if (t <= 0)
break;
+
yield return null;
}
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/SkeletonGraphic/SkeletonGraphicMirror.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/SkeletonGraphic/SkeletonGraphicMirror.cs
index 8fa89962b..dae92d141 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/SkeletonGraphic/SkeletonGraphicMirror.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Modules/SkeletonGraphic/SkeletonGraphicMirror.cs
@@ -1,4 +1,4 @@
-/******************************************************************************
+/******************************************************************************
* Spine Runtimes Software License v2.5
*
* Copyright (c) 2013-2016, Esoteric Software
@@ -49,7 +49,8 @@ namespace Spine.Unity.Modules {
}
void Start () {
- if (mirrorOnStart) StartMirroring();
+ if (mirrorOnStart)
+ StartMirroring();
}
void LateUpdate () {
@@ -57,13 +58,16 @@ namespace Spine.Unity.Modules {
}
void OnDisable () {
- if (restoreOnDisable) RestoreIndependentSkeleton();
+ if (restoreOnDisable)
+ RestoreIndependentSkeleton();
}
/// Freeze the SkeletonGraphic on this GameObject, and use the source as the Skeleton to be rendered by the SkeletonGraphic.
public void StartMirroring () {
- if (source == null) return;
- if (skeletonGraphic == null) return;
+ if (source == null)
+ return;
+ if (skeletonGraphic == null)
+ return;
skeletonGraphic.startingAnimation = string.Empty;
diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/SkeletonExtensions.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/SkeletonExtensions.cs
index 63c1a52b7..864593ae2 100644
--- a/spine-unity/Assets/Spine/Runtime/spine-unity/SkeletonExtensions.cs
+++ b/spine-unity/Assets/Spine/Runtime/spine-unity/SkeletonExtensions.cs
@@ -503,6 +503,7 @@ namespace Spine {
ikc = skeleton.ikConstraints.Items[i];
ikc.mix = ikc.data.mix;
ikc.bendDirection = ikc.data.bendDirection;
+ ikc.stretch = ikc.data.stretch;
break;
// TransformConstraint