From 7e765c1b5b4b063a364d693002c279a6df01a8d0 Mon Sep 17 00:00:00 2001 From: Harald Csaszar Date: Tue, 7 Dec 2021 19:13:03 +0100 Subject: [PATCH] [unity] Fixed root motion components ignoring split translate timelines. Closes #1997. --- .../RootMotion/SkeletonRootMotionBase.cs | 44 +++++++++++++++++-- .../spine-unity/Utility/TimelineExtensions.cs | 34 ++++++++++++++ 2 files changed, 74 insertions(+), 4 deletions(-) diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs index a88be642d..16107d81a 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs @@ -202,6 +202,8 @@ namespace Spine.Unity { return Vector2.zero; TranslateTimeline translateTimeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex); + TranslateXTimeline xTimeline = animation.FindTimelineForBone(rootMotionBoneIndex); + TranslateYTimeline yTimeline = animation.FindTimelineForBone(rootMotionBoneIndex); // Non-looped base Vector2 endPos = Vector2.zero; @@ -209,6 +211,9 @@ namespace Spine.Unity { if (translateTimeline != null) { endPos = translateTimeline.Evaluate(endTime); startPos = translateTimeline.Evaluate(startTime); + } else if (xTimeline != null || yTimeline != null) { + endPos = TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime); + startPos = TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime); } var transformConstraintsItems = skeletonComponent.Skeleton.TransformConstraints.Items; foreach (int constraintIndex in this.transformConstraintIndices) { @@ -225,6 +230,9 @@ namespace Spine.Unity { if (translateTimeline != null) { loopPos = translateTimeline.Evaluate(animation.Duration); zeroPos = translateTimeline.Evaluate(0); + } else if (xTimeline != null || yTimeline != null) { + loopPos = TimelineExtensions.Evaluate(xTimeline, yTimeline, animation.Duration); + zeroPos = TimelineExtensions.Evaluate(xTimeline, yTimeline, 0); } foreach (int constraintIndex in this.transformConstraintIndices) { TransformConstraint constraint = transformConstraintsItems[constraintIndex]; @@ -266,15 +274,25 @@ namespace Spine.Unity { public RootMotionInfo GetAnimationRootMotionInfo (Animation animation, float currentTime) { RootMotionInfo rootMotion = new RootMotionInfo(); - var timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex); + float duration = animation.Duration; + float mid = duration * 0.5f; + rootMotion.timeIsPastMid = currentTime > mid; + TranslateTimeline timeline = animation.FindTranslateTimelineForBone(rootMotionBoneIndex); if (timeline != null) { - float duration = animation.Duration; - float mid = duration * 0.5f; rootMotion.start = timeline.Evaluate(0); rootMotion.current = timeline.Evaluate(currentTime); rootMotion.mid = timeline.Evaluate(mid); rootMotion.end = timeline.Evaluate(duration); - rootMotion.timeIsPastMid = currentTime > mid; + return rootMotion; + } + TranslateXTimeline xTimeline = animation.FindTimelineForBone(rootMotionBoneIndex); + TranslateYTimeline yTimeline = animation.FindTimelineForBone(rootMotionBoneIndex); + if (xTimeline != null || yTimeline != null) { + rootMotion.start = TimelineExtensions.Evaluate(xTimeline, yTimeline, 0); + rootMotion.current = TimelineExtensions.Evaluate(xTimeline, yTimeline, currentTime); + rootMotion.mid = TimelineExtensions.Evaluate(xTimeline, yTimeline, mid); + rootMotion.end = TimelineExtensions.Evaluate(xTimeline, yTimeline, duration); + return rootMotion; } return rootMotion; } @@ -293,6 +311,24 @@ namespace Spine.Unity { } } + Vector2 GetTimelineMovementDelta (float startTime, float endTime, + TranslateXTimeline xTimeline, TranslateYTimeline yTimeline, Animation animation) { + + Vector2 currentDelta; + if (startTime > endTime) // Looped + currentDelta = + (TimelineExtensions.Evaluate(xTimeline, yTimeline, animation.Duration) + - TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime)) + + (TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime) + - TimelineExtensions.Evaluate(xTimeline, yTimeline, 0)); + else if (startTime != endTime) // Non-looped + currentDelta = TimelineExtensions.Evaluate(xTimeline, yTimeline, endTime) + - TimelineExtensions.Evaluate(xTimeline, yTimeline, startTime); + else + currentDelta = Vector2.zero; + return currentDelta; + } + void GatherTopLevelBones () { topLevelBones.Clear(); var skeleton = skeletonComponent.Skeleton; diff --git a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/TimelineExtensions.cs b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/TimelineExtensions.cs index d58be1108..47e0ccc0c 100644 --- a/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/TimelineExtensions.cs +++ b/spine-unity/Assets/Spine/Runtime/spine-unity/Utility/TimelineExtensions.cs @@ -52,6 +52,27 @@ namespace Spine.Unity.AnimationTools { } } + /// Evaluates the resulting value of a pair of split translate timelines 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 + /// instead of absolute values. + public static Vector2 Evaluate (TranslateXTimeline xTimeline, TranslateYTimeline yTimeline, + float time, SkeletonData skeletonData = null) { + + float x = 0, y = 0; + if (xTimeline != null && time > xTimeline.Frames[0]) x = xTimeline.GetCurveValue(time); + if (yTimeline != null && time > yTimeline.Frames[0]) y = yTimeline.GetCurveValue(time); + + if (skeletonData == null) { + return new Vector2(x, y); + } else { + var bonesItems = skeletonData.Bones.Items; + BoneData boneDataX = bonesItems[xTimeline.BoneIndex]; + BoneData boneDataY = bonesItems[yTimeline.BoneIndex]; + return new Vector2(boneDataX.X + x, boneDataY.Y + y); + } + } + /// Evaluates the resulting X and Y translate mix values of a /// TransformConstraintTimeline at a given time. public static Vector2 EvaluateTranslateXYMix (this TransformConstraintTimeline timeline, float time) { @@ -78,6 +99,19 @@ namespace Spine.Unity.AnimationTools { return null; } + /// Gets the IBoneTimeline timeline of a given type for a given boneIndex. + /// You can get the boneIndex using SkeletonData.FindBoneIndex. + /// The root bone is always boneIndex 0. + /// This will return null if a timeline of the given type is not found. + public static T FindTimelineForBone (this Animation a, int boneIndex) where T : class, IBoneTimeline { + foreach (var timeline in a.Timelines) { + T translateTimeline = timeline as T; + if (translateTimeline != null && translateTimeline.BoneIndex == boneIndex) + return translateTimeline; + } + return null; + } + /// 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.