Khoros

Hi there, I just wanted to mention that I expanded on the rootmotion implementation to allow me to track dynamic rootmotion such as where a character plays an animation by blending from A to B with a transform constraint. This allows me to play rootmotion jump animations with dynamic end positions. I added the following to the timeline extensions for this.
/// <summary>Evaluates the resulting value of a TransformConstraintTimeline at a given time.
/// SkeletonData can be accessed from Skeleton.Data or from SkeletonDataAsset.GetSkeletonData.
/// If no SkeletonData is given, values are computed relative to setup pose instead of local-absolute.</summary>
public static Vector2 Evaluate(this TransformConstraintTimeline timeline, float time, TransformConstraint transformConstraint)
{
var frames = timeline.frames;

if (time < frames[0]) return Vector2.zero;

if (transformConstraint == null) return Vector2.zero;

int i = Timeline.Search(frames, time, TransformConstraintTimeline.ENTRIES);
int curveType = (int)timeline.curves[i / TransformConstraintTimeline.ENTRIES];

float translateMixX, translateMixY;
switch (curveType)
{
case CurveTimeline.LINEAR:

float before = frames[i];

translateMixX = frames[i + TransformConstraintTimeline.X];
translateMixY = frames[i + TransformConstraintTimeline.Y];

float t = (time - before) / (frames[i + TransformConstraintTimeline.ENTRIES] - before);

translateMixX += (frames[i + TransformConstraintTimeline.ENTRIES + TransformConstraintTimeline.X] - translateMixX) * t;
translateMixY += (frames[i + TransformConstraintTimeline.ENTRIES + TransformConstraintTimeline.Y] - translateMixY) * t;

break;
case CurveTimeline.STEPPED:
translateMixX = frames[i + TransformConstraintTimeline.X];
translateMixY = frames[i + TransformConstraintTimeline.Y];
break;
default:
translateMixX = timeline.GetBezierValue(time, i, TransformConstraintTimeline.X, curveType - CurveTimeline.BEZIER);
translateMixY = timeline.GetBezierValue(time, i, TransformConstraintTimeline.Y, curveType + CurveTimeline.BEZIER_SIZE - CurveTimeline.BEZIER);
break;
}

return new Vector2(transformConstraint.target.x * translateMixX, transformConstraint.target.y * translateMixY);
}

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;
}
It surprised me that the standard rootmotion implementation was unable to handle this.
User avatar
Khoros
  • Posts: 4

RemDust

Hi Khoros,
Thanks for posting this. :clap:

I've been playing around with RootMotion in spine-unity and I'm always excited to know more about it.

Would you mind developing a bit your setup and uses if you have a moment ? ;)
Also, I see you're new here so, welcome :)
RemDust
  • Posts: 223

Harald

@Khoros Sorry for the inconvenience, and thanks very much for sharing your code! Admittedly the SkeletonRootMotion component implementations are rather new and have recently been expanded to cover use cases such as delta compensation. Thanks for reporting this missing support for transform constraints, this indeed slipped through.

I have created an issue ticket here to officially support transform constraint timelines (we will have an in-depth look at it next week): https://github.com/EsotericSoftware/spine-runtimes/issues/1944.
Khoros wrote:This allows me to play rootmotion jump animations with dynamic end positions.
This is indeed a good solution. There is also support for delta compensation at the SkeletonRootMotion components, as described in the comment box here, also discussed on this forum thread:
skeletonRootMotion.AdjustRootMotionToDistance(targetPosition - transform.position, trackIndex);
However, a solution using transform constraints will of course be more powerful and allows more control.

---

Sorry that it took longer than we hoped for, nevertheless we have now pushed a commit that introduces automatic support for TransformConstraints at SkeletonRootMotion components to the 4.1-beta branch. The SkeletonRootMotion components now check for any TransformConstraints that affect the Root Motion Bone and apply root motion accordingly.

We chose not to include it on the 4.0 branch to not break existing behaviour. If you need it for 4.0, you should be able to copy the three modified files in the commit without any modification over the respective files in your spine-unity 4.0 installation:
  • spine-csharp/src/Animation.cs
  • spine-unity/Assets/Spine/Runtime/spine-unity/Components/RootMotion/SkeletonRootMotionBase.cs
  • spine-unity/Assets/Spine/Runtime/spine-unity/Utility/TimelineExtensions.cs

Supported Scenario
The currently intended and supported scenario is to have a bone hierarchy similar to the following one:
- root
-- rootmotion
--- <hip, ect., your complete skeleton here>
-- jump-target
You then have a TransformConstraint that constrains the rootmotion bone (assigned under Bones of the constraint in Spine) by a factor of 0-100% to the jump-target bone (assigned under Target of the constraint) location.
Your jump-constraint-rootmotion animation would then set the Mix - Translate value to 0% at the start of the jump and over time reach 100% when the jump ends.

Please let us know if this fits your needs and mirrors your scenario. If you had a difference use-case in mind, please don't hesitate to let us know, we will then check if it can be supported as well.

The updated spine-unity 4.1-beta unitypackage is available for download here as usual:
Spine Unity Download
User avatar
Harald

Harri
  • Posts: 4101


Return to Unity