• Unity
  • Question about baking rotation in Unity

Related Discussions
...

Hello, we have a kinda small problem.
We are working on a series of small projects - 2D animation. Initially we were using native Unity animation (native Dope Sheet and Curve Editor). But this was not ideal because these editors have a lot of essential features that makes work very slow. So we switched to Spine, and for technical reasons we have to use the animation baking feature. We convert the original Spine animation clips into native Unity .anim files that are then needed by our build. We plan to use the full Spine runtime in the next series of projects as it's meant to.
Until then, the problem that we have is the conversion of the Rotation keyframes in the baking process. They are brought in as a keyframe every frame. Even if we convert all our Bezier curves into Linear or Stepped curves they still come in a keyframe every frame. Why is this a problem? Well, Unity freezes when dealing with too many keyframes. The .anim file format in which they are stored within Unity takes a long time to process and makes Unity unusable. Even if we left the animation clips as they are and simply not touch them at all, the final exports we generate to use in our build are double or triple the size they need to be because of all those redundant keyframes.
The position and scaling, if we convert them to Linear interpolation in Spine, they bake fine without redunant keyframes. We then apply a general Flat easing in Unity and deal with any circumstance of custom easing - which we try to keep to a minimum anyway.
We are currenlty considering creating a custom script which will do the following:
Phase 1: Look at the time stamps of the keyframes inside the JSON export file generated by Spine, find all the keyframes that have a different time stamp inside the Unity .ANIM file generated by the baking process and delete them.
Phase 2: Change the code that represents curve shape in the Unity .ANIM file to match the curve shape representation in JSON Spine file.
Here are snippet comparisons of keyframe data between the 2 files
.ANIM code:
.JSON code:
{
"time": 1.2166662,
"angle": 2.5,
"curve": [ 0.906, -0.01, 0.094, 1.02 ]
}
time: 1.2166662
value: 2.5
inSlope: 0
outSlope: 0
tangentMode: 0
weightedMode: 0
inWeight: 0.33333334
outWeight: 0.33333334
TIME where the keyframe is placed in the timeline and VALUE of that keyframe are the same and remain unchanged.
What is different is how the CURVE SHAPE is represente in code.
CURVE SHAPE in Spine is:
"curve": [value 1, value 2, value 3, value 4]
CURVE SHAPE in Unity is:
inSlope, outSlope, tangentMode, weightedMode, inWeight and outWeight
Does anyone have any suggestion about how best to convert CURVE SHAPE representation in .ANIM to match that in the .JSON?
Any ideas would be highly appreciated.
Maybe someone has already created this script and would like to share?
Many thanks in advance.

4 días más tarde

Hi,

The four Bezier values should be transferrable to Unity's representation following this documentation as guidance:

Documentation page for Bone Timelines, sub-section "Common bone keyframe attributes"
Spine: JSON export format: Bone timelines
"A Bézier curve has 4 elements which define the control points: cx1, cy1, cx2, cy2. The X axis is from 0 to 1 and represents the percent of time between the two keyframes. The Y axis is from 0 to 1 and represents the percent of the difference between the keyframe's values."

Note that the keyframes in Unity are described as: in_tangent_P_i left, out_tangent_P_i right of a control point with

ks[0].inTangent = 5f;    // -5 units on the y axis for 1 unit on the x axis.
ks[0].outTangent = 5f;    // +5 units on the y axis for 1 unit on the x axis.

https://docs.unity3d.com/ScriptReference/Keyframe-inTangent.html
https://docs.unity3d.com/ScriptReference/Keyframe-outTangent.html

In Spine it's [0-1] normalized x/y values of both tangent-control-points before P_i: points_for_out_tangent_P_i-1, points_for_in_tangent_P_i. Each tangent could then be described as: (y/x) * (y_value_diff/x_time_diff).
In short: it's absolute vs relative encoding here, and the out-tangent is stored at frame i in Unity and at frame i+1 in Spine.

One thing to consider, since you want to do this via an external script:
You could also consider modifying/replacing the SkeletonBaker code parts that write the Unity anim Keyframes out.
I would perform the changes myself and extend the Skeleton Baker functionality by a "Only Keyframes" mode, but unfortunately we are currently very busy with major changes in the 3.8-beta branch (format changes, examples overhaul and a soon planned release). So I can currently only offer you help in descriptive form instead of implementing it, sorry for that.

What would be necessary to be changed/extended are the following code parts:

SkeletonBaker.cs: line 1245 - 1283
spine-runtimes/SkeletonBaker.cs at 3.7

static void ParseRotateTimeline (Skeleton skeleton, RotateTimeline timeline, AnimationClip clip)
[part where Bezier curve is evaluated]

Here the fixed baking increment of 1/60 seconds is used, you could instead use the values of the frame entries. Note that these are not four values, but only 2 per control point (due to consistent in-out tangents at each control point, pointing in opposite direction).

CurveTimeline class, SetCurve demonstrates how these values 4 parameter values are transformed to the frame entry:
Animation.cs: line 250-274
spine-runtimes/Animation.cs at 3.7

/// <summary>Sets the specified key frame to Bezier interpolation. <code>cx1</code> and <code>cx2</code> are from 0 to 1,
/// representing the percent of time between the two key frames. <code>cy1</code> and <code>cy2</code> are the percent of the
/// difference between the key frame's values.</summary>
public void SetCurve (int frameIndex, float cx1, float cy1, float cx2, float cy2)

I hope this helps.