mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2025-12-21 09:46:02 +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;
|
||||
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];
|
||||
switch (curveType) {
|
||||
case LINEAR:
|
||||
@ -2383,23 +2407,6 @@ namespace Spine {
|
||||
shearY = GetBezierValue(time, i, SHEARY, curveType + BEZIER_SIZE * 5 - BEZIER);
|
||||
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 Bone rootMotionBone;
|
||||
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 Vector2 initialOffset = Vector2.zero;
|
||||
protected Vector2 tempSkeletonDisplacement;
|
||||
@ -94,7 +96,6 @@ namespace Spine.Unity {
|
||||
return; // Root motion is only applied when component is enabled.
|
||||
|
||||
if (rigidBody2D != null) {
|
||||
|
||||
Vector2 gravityAndVelocityMovement = Vector2.zero;
|
||||
if (applyRigidbody2DGravity) {
|
||||
float deltaTime = Time.fixedDeltaTime;
|
||||
@ -108,10 +109,12 @@ namespace Spine.Unity {
|
||||
rigidBody2D.MovePosition(gravityAndVelocityMovement + new Vector2(transform.position.x, transform.position.y)
|
||||
+ rigidbodyDisplacement);
|
||||
}
|
||||
if (rigidBody != null) {
|
||||
else if (rigidBody != null) {
|
||||
rigidBody.MovePosition(transform.position
|
||||
+ new Vector3(rigidbodyDisplacement.x, rigidbodyDisplacement.y, 0));
|
||||
}
|
||||
else return;
|
||||
|
||||
Vector2 parentBoneScale;
|
||||
GetScaleAffectingRootMotion(out parentBoneScale);
|
||||
ClearEffectiveBoneOffsets(parentBoneScale);
|
||||
@ -155,6 +158,7 @@ namespace Spine.Unity {
|
||||
if (bone != null) {
|
||||
this.rootMotionBoneIndex = bone.Data.Index;
|
||||
this.rootMotionBone = bone;
|
||||
FindTransformConstraintsAffectingBone();
|
||||
} else {
|
||||
Debug.Log("Bone named \"" + name + "\" could not be found.");
|
||||
this.rootMotionBoneIndex = 0;
|
||||
@ -196,11 +200,70 @@ namespace Spine.Unity {
|
||||
public Vector2 GetAnimationRootMotion (float startTime, float endTime,
|
||||
Animation animation) {
|
||||
|
||||
var timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex);
|
||||
if (timeline != null) {
|
||||
return GetTimelineMovementDelta(startTime, endTime, timeline, animation);
|
||||
}
|
||||
if (startTime == endTime)
|
||||
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) {
|
||||
@ -218,18 +281,18 @@ namespace Spine.Unity {
|
||||
return rootMotion;
|
||||
}
|
||||
|
||||
Vector2 GetTimelineMovementDelta (float startTime, float endTime,
|
||||
TranslateTimeline timeline, Animation animation) {
|
||||
|
||||
Vector2 currentDelta;
|
||||
if (startTime > endTime) // Looped
|
||||
currentDelta = (timeline.Evaluate(animation.Duration) - timeline.Evaluate(startTime))
|
||||
+ (timeline.Evaluate(endTime) - timeline.Evaluate(0));
|
||||
else if (startTime != endTime) // Non-looped
|
||||
currentDelta = timeline.Evaluate(endTime) - timeline.Evaluate(startTime);
|
||||
else
|
||||
currentDelta = Vector2.zero;
|
||||
return currentDelta;
|
||||
void FindTransformConstraintsAffectingBone () {
|
||||
var constraints = skeletonComponent.Skeleton.TransformConstraints;
|
||||
var constraintsItems = constraints.Items;
|
||||
for (int i = 0, n = constraints.Count; i < n; ++i) {
|
||||
TransformConstraint constraint = constraintsItems[i];
|
||||
if (constraint.Bones.Contains(rootMotionBone)) {
|
||||
transformConstraintIndices.Add(i);
|
||||
Bone targetBone = constraint.Target;
|
||||
Vector2 constraintPos = new Vector2(targetBone.X, targetBone.Y);
|
||||
transformConstraintLastPos.Add(constraintPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 parentBoneScale;
|
||||
return GetScaleAffectingRootMotion(out parentBoneScale);
|
||||
@ -309,6 +385,9 @@ namespace Spine.Unity {
|
||||
}
|
||||
|
||||
void SetEffectiveBoneOffsetsTo (Vector2 displacementSkeletonSpace, Vector2 parentBoneScale) {
|
||||
|
||||
ApplyTransformConstraints();
|
||||
|
||||
// Move top level bones in opposite direction of the root motion bone
|
||||
var skeleton = skeletonComponent.Skeleton;
|
||||
foreach (var topLevelBone in topLevelBones) {
|
||||
@ -316,8 +395,13 @@ namespace Spine.Unity {
|
||||
if (transformPositionX) topLevelBone.X = displacementSkeletonSpace.x / skeleton.ScaleX;
|
||||
if (transformPositionY) topLevelBone.Y = displacementSkeletonSpace.y / skeleton.ScaleY;
|
||||
} else {
|
||||
float offsetX = (initialOffset.x - rootMotionBone.X) * parentBoneScale.x;
|
||||
float offsetY = (initialOffset.y - rootMotionBone.Y) * parentBoneScale.y;
|
||||
bool useAppliedPosition = transformConstraintIndices.Count > 0;
|
||||
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 (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.
|
||||
/// 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>
|
||||
public static Vector2 Evaluate (this TranslateTimeline timeline, float time, SkeletonData skeletonData = null) {
|
||||
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.
|
||||
/// You can get the boneIndex using SkeletonData.FindBoneIndex.
|
||||
/// 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 TranslateTimeline FindTranslateTimelineForBone (this Animation a, int boneIndex) {
|
||||
@ -67,5 +77,22 @@ namespace Spine.Unity.AnimationTools {
|
||||
}
|
||||
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