mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 17:56:04 +08:00
[unity] SkeletonRootMotion components now respect TransformConstraint timelines. Closes #1944.
This commit is contained in:
parent
da6016cf07
commit
90dd4bc8af
@ -2348,6 +2348,30 @@ namespace Spine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
float rotate, x, y, scaleX, scaleY, shearY;
|
float rotate, x, y, scaleX, scaleY, shearY;
|
||||||
|
GetCurveValue(out rotate, out x, out y, out scaleX, out scaleY, out shearY, time);
|
||||||
|
|
||||||
|
if (blend == MixBlend.Setup) {
|
||||||
|
TransformConstraintData data = constraint.data;
|
||||||
|
constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
|
||||||
|
constraint.mixX = data.mixX + (x - data.mixX) * alpha;
|
||||||
|
constraint.mixY = data.mixY + (y - data.mixY) * alpha;
|
||||||
|
constraint.mixScaleX = data.mixScaleX + (scaleX - data.mixScaleX) * alpha;
|
||||||
|
constraint.mixScaleY = data.mixScaleY + (scaleY - data.mixScaleY) * alpha;
|
||||||
|
constraint.mixShearY = data.mixShearY + (shearY - data.mixShearY) * alpha;
|
||||||
|
} else {
|
||||||
|
constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
|
||||||
|
constraint.mixX += (x - constraint.mixX) * alpha;
|
||||||
|
constraint.mixY += (y - constraint.mixY) * alpha;
|
||||||
|
constraint.mixScaleX += (scaleX - constraint.mixScaleX) * alpha;
|
||||||
|
constraint.mixScaleY += (scaleY - constraint.mixScaleY) * alpha;
|
||||||
|
constraint.mixShearY += (shearY - constraint.mixShearY) * alpha;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void GetCurveValue (out float rotate, out float x, out float y,
|
||||||
|
out float scaleX, out float scaleY, out float shearY, float time) {
|
||||||
|
|
||||||
|
float[] frames = this.frames;
|
||||||
int i = Search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES];
|
int i = Search(frames, time, ENTRIES), curveType = (int)curves[i / ENTRIES];
|
||||||
switch (curveType) {
|
switch (curveType) {
|
||||||
case LINEAR:
|
case LINEAR:
|
||||||
@ -2383,23 +2407,6 @@ namespace Spine {
|
|||||||
shearY = GetBezierValue(time, i, SHEARY, curveType + BEZIER_SIZE * 5 - BEZIER);
|
shearY = GetBezierValue(time, i, SHEARY, curveType + BEZIER_SIZE * 5 - BEZIER);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (blend == MixBlend.Setup) {
|
|
||||||
TransformConstraintData data = constraint.data;
|
|
||||||
constraint.mixRotate = data.mixRotate + (rotate - data.mixRotate) * alpha;
|
|
||||||
constraint.mixX = data.mixX + (x - data.mixX) * alpha;
|
|
||||||
constraint.mixY = data.mixY + (y - data.mixY) * alpha;
|
|
||||||
constraint.mixScaleX = data.mixScaleX + (scaleX - data.mixScaleX) * alpha;
|
|
||||||
constraint.mixScaleY = data.mixScaleY + (scaleY - data.mixScaleY) * alpha;
|
|
||||||
constraint.mixShearY = data.mixShearY + (shearY - data.mixShearY) * alpha;
|
|
||||||
} else {
|
|
||||||
constraint.mixRotate += (rotate - constraint.mixRotate) * alpha;
|
|
||||||
constraint.mixX += (x - constraint.mixX) * alpha;
|
|
||||||
constraint.mixY += (y - constraint.mixY) * alpha;
|
|
||||||
constraint.mixScaleX += (scaleX - constraint.mixScaleX) * alpha;
|
|
||||||
constraint.mixScaleY += (scaleY - constraint.mixScaleY) * alpha;
|
|
||||||
constraint.mixShearY += (shearY - constraint.mixShearY) * alpha;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -66,6 +66,8 @@ namespace Spine.Unity {
|
|||||||
protected ISkeletonComponent skeletonComponent;
|
protected ISkeletonComponent skeletonComponent;
|
||||||
protected Bone rootMotionBone;
|
protected Bone rootMotionBone;
|
||||||
protected int rootMotionBoneIndex;
|
protected int rootMotionBoneIndex;
|
||||||
|
protected List<int> transformConstraintIndices = new List<int>();
|
||||||
|
protected List<Vector2> transformConstraintLastPos = new List<Vector2>();
|
||||||
protected List<Bone> topLevelBones = new List<Bone>();
|
protected List<Bone> topLevelBones = new List<Bone>();
|
||||||
protected Vector2 initialOffset = Vector2.zero;
|
protected Vector2 initialOffset = Vector2.zero;
|
||||||
protected Vector2 tempSkeletonDisplacement;
|
protected Vector2 tempSkeletonDisplacement;
|
||||||
@ -94,7 +96,6 @@ namespace Spine.Unity {
|
|||||||
return; // Root motion is only applied when component is enabled.
|
return; // Root motion is only applied when component is enabled.
|
||||||
|
|
||||||
if (rigidBody2D != null) {
|
if (rigidBody2D != null) {
|
||||||
|
|
||||||
Vector2 gravityAndVelocityMovement = Vector2.zero;
|
Vector2 gravityAndVelocityMovement = Vector2.zero;
|
||||||
if (applyRigidbody2DGravity) {
|
if (applyRigidbody2DGravity) {
|
||||||
float deltaTime = Time.fixedDeltaTime;
|
float deltaTime = Time.fixedDeltaTime;
|
||||||
@ -108,10 +109,12 @@ namespace Spine.Unity {
|
|||||||
rigidBody2D.MovePosition(gravityAndVelocityMovement + new Vector2(transform.position.x, transform.position.y)
|
rigidBody2D.MovePosition(gravityAndVelocityMovement + new Vector2(transform.position.x, transform.position.y)
|
||||||
+ rigidbodyDisplacement);
|
+ rigidbodyDisplacement);
|
||||||
}
|
}
|
||||||
if (rigidBody != null) {
|
else if (rigidBody != null) {
|
||||||
rigidBody.MovePosition(transform.position
|
rigidBody.MovePosition(transform.position
|
||||||
+ new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0));
|
+ new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0));
|
||||||
}
|
}
|
||||||
|
else return;
|
||||||
|
|
||||||
Vector2 parentBoneScale;
|
Vector2 parentBoneScale;
|
||||||
GetScaleAffectingRootMotion(out parentBoneScale);
|
GetScaleAffectingRootMotion(out parentBoneScale);
|
||||||
ClearEffectiveBoneOffsets(parentBoneScale);
|
ClearEffectiveBoneOffsets(parentBoneScale);
|
||||||
@ -155,6 +158,7 @@ namespace Spine.Unity {
|
|||||||
if (bone != null) {
|
if (bone != null) {
|
||||||
this.rootMotionBoneIndex = bone.Data.Index;
|
this.rootMotionBoneIndex = bone.Data.Index;
|
||||||
this.rootMotionBone = bone;
|
this.rootMotionBone = bone;
|
||||||
|
FindTransformConstraintsAffectingBone();
|
||||||
} else {
|
} else {
|
||||||
Debug.Log("Bone named \"" + name + "\" could not be found.");
|
Debug.Log("Bone named \"" + name + "\" could not be found.");
|
||||||
this.rootMotionBoneIndex = 0;
|
this.rootMotionBoneIndex = 0;
|
||||||
@ -196,11 +200,70 @@ namespace Spine.Unity {
|
|||||||
public Vector2 GetAnimationRootMotion (float startTime, float endTime,
|
public Vector2 GetAnimationRootMotion (float startTime, float endTime,
|
||||||
Animation animation) {
|
Animation animation) {
|
||||||
|
|
||||||
var timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
|
if (startTime == endTime)
|
||||||
if (timeline != null) {
|
|
||||||
return GetTimelineMovementDelta(startTime, endTime, timeline, animation);
|
|
||||||
}
|
|
||||||
return Vector2.zero;
|
return Vector2.zero;
|
||||||
|
|
||||||
|
TranslateTimeline translateTimeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
|
||||||
|
|
||||||
|
// Non-looped base
|
||||||
|
Vector2 endPos = Vector2.zero;
|
||||||
|
Vector2 startPos = Vector2.zero;
|
||||||
|
if (translateTimeline != null) {
|
||||||
|
endPos = translateTimeline.Evaluate(endTime);
|
||||||
|
startPos = translateTimeline.Evaluate(startTime);
|
||||||
|
}
|
||||||
|
var transformConstraintsItems = skeletonComponent.Skeleton.TransformConstraints.Items;
|
||||||
|
foreach (int constraintIndex in this.transformConstraintIndices) {
|
||||||
|
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
|
||||||
|
ApplyConstraintToPos(animation, constraint, constraintIndex, endTime, false, ref endPos);
|
||||||
|
ApplyConstraintToPos(animation, constraint, constraintIndex, startTime, true, ref startPos);
|
||||||
|
}
|
||||||
|
Vector2 currentDelta = endPos - startPos;
|
||||||
|
|
||||||
|
// Looped additions
|
||||||
|
if (startTime > endTime) {
|
||||||
|
Vector2 loopPos = Vector2.zero;
|
||||||
|
Vector2 zeroPos = Vector2.zero;
|
||||||
|
if (translateTimeline != null) {
|
||||||
|
loopPos = translateTimeline.Evaluate(animation.Duration);
|
||||||
|
zeroPos = translateTimeline.Evaluate(0);
|
||||||
|
}
|
||||||
|
foreach (int constraintIndex in this.transformConstraintIndices) {
|
||||||
|
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
|
||||||
|
ApplyConstraintToPos(animation, constraint, constraintIndex, animation.Duration, false, ref loopPos);
|
||||||
|
ApplyConstraintToPos(animation, constraint, constraintIndex, 0, false, ref zeroPos);
|
||||||
|
}
|
||||||
|
currentDelta += loopPos - zeroPos;
|
||||||
|
}
|
||||||
|
UpdateLastConstraintPos(transformConstraintsItems);
|
||||||
|
return currentDelta;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApplyConstraintToPos (Animation animation, TransformConstraint constraint,
|
||||||
|
int constraintIndex, float time, bool useLastConstraintPos, ref Vector2 pos) {
|
||||||
|
TransformConstraintTimeline timeline = animation.FindTransformConstraintTimeline(constraintIndex);
|
||||||
|
if (timeline == null)
|
||||||
|
return;
|
||||||
|
Vector2 mixXY = timeline.EvaluateTranslateXYMix(time);
|
||||||
|
Vector2 invMixXY = timeline.EvaluateTranslateXYMix(time);
|
||||||
|
Vector2 constraintPos;
|
||||||
|
if (useLastConstraintPos)
|
||||||
|
constraintPos = transformConstraintLastPos[constraintIndex];
|
||||||
|
else {
|
||||||
|
Bone targetBone = constraint.Target;
|
||||||
|
constraintPos = new Vector2(targetBone.X, targetBone.Y);
|
||||||
|
}
|
||||||
|
pos = new Vector2(
|
||||||
|
pos.x * invMixXY.x + constraintPos.x * mixXY.x,
|
||||||
|
pos.y * invMixXY.y + constraintPos.y * mixXY.y);
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateLastConstraintPos (TransformConstraint[] transformConstraintsItems) {
|
||||||
|
foreach (int constraintIndex in this.transformConstraintIndices) {
|
||||||
|
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
|
||||||
|
Bone targetBone = constraint.Target;
|
||||||
|
transformConstraintLastPos[constraintIndex] = new Vector2(targetBone.X, targetBone.Y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public RootMotionInfo GetAnimationRootMotionInfo (Animation animation, float currentTime) {
|
public RootMotionInfo GetAnimationRootMotionInfo (Animation animation, float currentTime) {
|
||||||
@ -218,18 +281,18 @@ namespace Spine.Unity {
|
|||||||
return rootMotion;
|
return rootMotion;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vector2 GetTimelineMovementDelta (float startTime, float endTime,
|
void FindTransformConstraintsAffectingBone () {
|
||||||
TranslateTimeline timeline, Animation animation) {
|
var constraints = skeletonComponent.Skeleton.TransformConstraints;
|
||||||
|
var constraintsItems = constraints.Items;
|
||||||
Vector2 currentDelta;
|
for (int i = 0, n = constraints.Count; i < n; ++i) {
|
||||||
if (startTime > endTime) // Looped
|
TransformConstraint constraint = constraintsItems[i];
|
||||||
currentDelta = (timeline.Evaluate(animation.Duration) - timeline.Evaluate(startTime))
|
if (constraint.Bones.Contains(rootMotionBone)) {
|
||||||
+ (timeline.Evaluate(endTime) - timeline.Evaluate(0));
|
transformConstraintIndices.Add(i);
|
||||||
else if (startTime != endTime) // Non-looped
|
Bone targetBone = constraint.Target;
|
||||||
currentDelta = timeline.Evaluate(endTime) - timeline.Evaluate(startTime);
|
Vector2 constraintPos = new Vector2(targetBone.X, targetBone.Y);
|
||||||
else
|
transformConstraintLastPos.Add(constraintPos);
|
||||||
currentDelta = Vector2.zero;
|
}
|
||||||
return currentDelta;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GatherTopLevelBones () {
|
void GatherTopLevelBones () {
|
||||||
@ -267,6 +330,19 @@ namespace Spine.Unity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ApplyTransformConstraints () {
|
||||||
|
rootMotionBone.AX = rootMotionBone.X;
|
||||||
|
rootMotionBone.AY = rootMotionBone.Y;
|
||||||
|
var transformConstraintsItems = skeletonComponent.Skeleton.TransformConstraints.Items;
|
||||||
|
foreach (int constraintIndex in this.transformConstraintIndices) {
|
||||||
|
TransformConstraint constraint = transformConstraintsItems[constraintIndex];
|
||||||
|
// apply the constraint and sets Bone.ax and Bone.ay values.
|
||||||
|
/// Update is based on Bone.x and Bone.y, so skeleton.UpdateWorldTransform()
|
||||||
|
/// can be called afterwards without having a different starting point.
|
||||||
|
constraint.Update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Vector2 GetScaleAffectingRootMotion () {
|
Vector2 GetScaleAffectingRootMotion () {
|
||||||
Vector2 parentBoneScale;
|
Vector2 parentBoneScale;
|
||||||
return GetScaleAffectingRootMotion(out parentBoneScale);
|
return GetScaleAffectingRootMotion(out parentBoneScale);
|
||||||
@ -309,6 +385,9 @@ namespace Spine.Unity {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, Vector2 parentBoneScale) {
|
void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, Vector2 parentBoneScale) {
|
||||||
|
|
||||||
|
ApplyTransformConstraints();
|
||||||
|
|
||||||
// Move top level bones in opposite direction of the root motion bone
|
// Move top level bones in opposite direction of the root motion bone
|
||||||
var skeleton = skeletonComponent.Skeleton;
|
var skeleton = skeletonComponent.Skeleton;
|
||||||
foreach (var topLevelBone in topLevelBones) {
|
foreach (var topLevelBone in topLevelBones) {
|
||||||
@ -316,8 +395,13 @@ namespace Spine.Unity {
|
|||||||
if (transformPositionX) topLevelBone.X = displacementSkeletonSpace.x / skeleton.ScaleX;
|
if (transformPositionX) topLevelBone.X = displacementSkeletonSpace.x / skeleton.ScaleX;
|
||||||
if (transformPositionY) topLevelBone.Y = displacementSkeletonSpace.y / skeleton.ScaleY;
|
if (transformPositionY) topLevelBone.Y = displacementSkeletonSpace.y / skeleton.ScaleY;
|
||||||
} else {
|
} else {
|
||||||
float offsetX = (initialOffset.x - rootMotionBone.X) * parentBoneScale.x;
|
bool useAppliedPosition = transformConstraintIndices.Count > 0;
|
||||||
float offsetY = (initialOffset.y - rootMotionBone.Y) * parentBoneScale.y;
|
float rootMotionBoneX = useAppliedPosition ? rootMotionBone.AX : rootMotionBone.X;
|
||||||
|
float rootMotionBoneY = useAppliedPosition ? rootMotionBone.AY : rootMotionBone.Y;
|
||||||
|
|
||||||
|
float offsetX = (initialOffset.x - rootMotionBoneX) * parentBoneScale.x;
|
||||||
|
float offsetY = (initialOffset.y - rootMotionBoneY) * parentBoneScale.y;
|
||||||
|
|
||||||
if (transformPositionX) topLevelBone.X = (displacementSkeletonSpace.x / skeleton.ScaleX) + offsetX;
|
if (transformPositionX) topLevelBone.X = (displacementSkeletonSpace.x / skeleton.ScaleX) + offsetX;
|
||||||
if (transformPositionY) topLevelBone.Y = (displacementSkeletonSpace.y / skeleton.ScaleY) + offsetY;
|
if (transformPositionY) topLevelBone.Y = (displacementSkeletonSpace.y / skeleton.ScaleY) + offsetY;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -36,7 +36,7 @@ namespace Spine.Unity.AnimationTools {
|
|||||||
|
|
||||||
/// <summary>Evaluates the resulting value of a TranslateTimeline at a given time.
|
/// <summary>Evaluates the resulting value of a TranslateTimeline at a given time.
|
||||||
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
|
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
|
||||||
/// If no SkeletonData is given, values are returned as difference to setup pose
|
/// If no SkeletonData is provided, values are returned as difference to setup pose
|
||||||
/// instead of absolute values.</summary>
|
/// instead of absolute values.</summary>
|
||||||
public static Vector2 Evaluate (this TranslateTimeline timeline, float time, SkeletonData skeletonData = null) {
|
public static Vector2 Evaluate (this TranslateTimeline timeline, float time, SkeletonData skeletonData = null) {
|
||||||
if (time < timeline.Frames[0]) return Vector2.zero;
|
if (time < timeline.Frames[0]) return Vector2.zero;
|
||||||
@ -52,8 +52,18 @@ namespace Spine.Unity.AnimationTools {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Evaluates the resulting X and Y translate mix values of a
|
||||||
|
/// TransformConstraintTimeline at a given time.</summary>
|
||||||
|
public static Vector2 EvaluateTranslateXYMix (this TransformConstraintTimeline timeline, float time) {
|
||||||
|
if (time < timeline.Frames[0]) return Vector2.zero;
|
||||||
|
|
||||||
|
float rotate, mixX, mixY, scaleX, scaleY, shearY;
|
||||||
|
timeline.GetCurveValue(out rotate, out mixX, out mixY, out scaleX, out scaleY, out shearY, time);
|
||||||
|
return new Vector2(mixX, mixY);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>Gets the translate timeline for a given boneIndex.
|
/// <summary>Gets the translate timeline for a given boneIndex.
|
||||||
/// You can get the boneIndex using SkeletonData.FindBoneIndex.
|
/// You can get the boneIndex using SkeletonData.FindBone().Index.
|
||||||
/// The root bone is always boneIndex 0.
|
/// The root bone is always boneIndex 0.
|
||||||
/// This will return null if a TranslateTimeline is not found.</summary>
|
/// This will return null if a TranslateTimeline is not found.</summary>
|
||||||
public static TranslateTimeline FindTranslateTimelineForBone (this Animation a, int boneIndex) {
|
public static TranslateTimeline FindTranslateTimelineForBone (this Animation a, int boneIndex) {
|
||||||
@ -67,5 +77,22 @@ namespace Spine.Unity.AnimationTools {
|
|||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>Gets the transform constraint timeline for a given boneIndex.
|
||||||
|
/// You can get the boneIndex using SkeletonData.FindBone().Index.
|
||||||
|
/// The root bone is always boneIndex 0.
|
||||||
|
/// This will return null if a TranslateTimeline is not found.</summary>
|
||||||
|
public static TransformConstraintTimeline FindTransformConstraintTimeline (this Animation a, int transformConstraintIndex) {
|
||||||
|
foreach (var timeline in a.Timelines) {
|
||||||
|
if (timeline.GetType().IsSubclassOf(typeof(TransformConstraintTimeline)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var transformConstraintTimeline = timeline as TransformConstraintTimeline;
|
||||||
|
if (transformConstraintTimeline != null &&
|
||||||
|
transformConstraintTimeline.TransformConstraintIndex == transformConstraintIndex)
|
||||||
|
return transformConstraintTimeline;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user