Dharengo / Spriter2UnityDX

Converts Spriter .scml files to Unity prefabs
155 stars 41 forks source link

Import keys/curves missmatch #6

Open grimmreefer opened 8 years ago

grimmreefer commented 8 years ago

Hy, so far you made great work with your plugin and you make the work with unity and spriter much easier but the problem is if you scale a bone or sprite inside spriter2d the animation get messed up.

https://brashmonkey.com/forum/applications/core/interface/imageproxy/imageproxy.php?img=http%3A%2F%2Fi.imgur.com%2FWDWfI0U.jpg&key=c1d6736da53dff1efac3ff787d4791143741c2bb7bb3298152c1bbe962f595f5

It would be realy great if you could fix this. Thank you!

Arakade commented 8 years ago

Hi, I'm seeing this too. It seems Spriter is using linear curves by default whereas, when imported into Unity, they're coming out as something like acceleration curves -- smoothed curves.

Seems it's happening automatically. Taking a look at the source, AnimationBuilder:250 is using AnimationCurve.AddKey(float, float) -- the doc for which says “Smooth tangents are automatically computed for the key.”

I'm seeing if I can calculate during import to approximate linear curves. If it works, I'll offer a Pull Request.

Dharengo commented 8 years ago

Animation math is not currently part of my skillset. Therefore I just set the tangents to positive infinity. If I knew more about it maybe I would fix it. That being said I'm currently focusing my efforts on extending SpriterDotNet since that tool is currently fully featured.

Arakade commented 8 years ago

Oh hey, didn't realize you'd actually reply! (browser only updated after I updated my post above!) Well, since I've done the base work, I'll see if I can finish it off (after 21:00 BST).

When you say SpriterDotNet is fully featured, does that mean Spirter2UnityDX is deprecated? Can it do the same job? (I'd noticed you'd posted on the prior's thread but got the impression it didn't do all the Mecanim stuff Spriter2UnityDX does -- incorrect?)

Thanks for the otherwise great too and replying, btw!

Dharengo commented 8 years ago

Currently, Spriter2Unity fills a niche that SpriterDotNet does not. Namely the fact that it converts spriter projects to native Unity animations. SpriterDotNet forgoes that in favor of a more C#-based animation scheme. I am trying to extend SpriterDotNet to add some Unity functionality (like the use of an AnimatorController), but it will never generate actual animations.

Arakade commented 8 years ago

Update:

The problem is due to 3 things:

  1. use of the AnimationClip.EnsureQuaternionContinuity() method which removes the provided curve tangents.
  2. Incorrect curve tangents being provided (none).
  3. the Spriter XML format including a “spin” value which isn’t being used in Spriter2UnityDX

With (1) enabled, the curve becomes a smoothed Cubic curve which results in lovely animations that are just not the same as the defaults in Spriter which uses Linear interpolation by default (although it’s importing as enum value Instantaneous).

In my workspace, I've disabled use of (1) and fixed (2). Lack of (3) means the rotations often loop the wrong way -- i.e. the rotations are not continuous without AC.EQC()! I think fixing (3) will fix this.

Sadly, I have to pause work on this for now. I will try to post a path / push a repository with these changes in a week or so. Perhaps someone can complete from this start.

FedericoCara commented 8 years ago

I didn't know that spriterdotnet is being made by the same person than spriter2unitydx. When I had to decide between those 2, I opted for spriter2unitydx becouse it creates animation clips, but it's a shame that it's discontinued. I'm facing the same issue with an animation rotating with strange curves. How can the animator prevent this from spriter at least?

Dharengo commented 8 years ago

Spriterdotnet is made by someone else. All I did was add a little animatorcontroller addon.

matiasticbits commented 7 years ago

If I understand correctly Spriter by default uses linear interpolation between keyframes? At the moment Spriter2UnityDX doesn't define tangent modes for key frames in curves, but this can be fixed using Unity's AnimationUtility. Using it's SetKeyLeftTangentMode and SetKeyRightTangentMode methods.

We had problems with couple keys not working as they were in Spriter, mostly with rapid direction changes in animation, and I made a fix for AnimationBuilder. I just iterate the keys through in SetKeys methods and set both tangent modes to linear for each key. Probably a proper fix would need the check the Scml file for the curve type and set the AnimatioCurve tangents based on that.

As this fix doesn't support other other than linear curve types from Spriter and I don't know the source code and Spriter well enough I don't want to make a pull request for this. Just wanted to give this information out there if someone was having similar problems and I'm open to help in case of creating a fix for this.

FedericoCara commented 7 years ago

Here's the solution I found. At the end of the Build method, in the AnimationBuilder class, I added these lines.

EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings (clip);
AnimationCurve animCurve;
foreach (EditorCurveBinding curveBinding in curveBindings) {
    animCurve = AnimationUtility.GetEditorCurve (clip, curveBinding);
    for (int i = 0; i < animCurve.length; i++) {
        AnimationUtility.SetKeyLeftTangentMode (animCurve, i, AnimationUtility.TangentMode.Linear);
        AnimationUtility.SetKeyRightTangentMode (animCurve, i, AnimationUtility.TangentMode.Linear);
    }
    AnimationUtility.SetEditorCurve (clip, curveBinding, animCurve);
}

Now when I import a scml file, all the curves have linear tangents on both sides.

Dharengo commented 7 years ago

If you can make it neat and pretty and make sure it only happens in situations where this is desirable, just make a pull request and I'll merge it.

FedericoCara commented 7 years ago

If the importer could read and interpret the tangents from scml file, this would not be necessary. Since that's not the case, it's always better to have linear interpolations by default (smooth tangents make highly undesirable behaviors). About making it neat and pretty, I'm not sure how you want it. That's how I program :( If you are referring to commenting the code, I'm not that good writing in English (is not my native language), so you probably can do it better.

Dharengo commented 7 years ago

I just mean that it fits into the code without breaking anything and creating behavior that not everyone might desire.

And sure the importer could read and interpret that stuff... Just write the code that does it.

CarlosHBP commented 7 years ago

I used the solution from @FedericoCara and works for "weird" imported animations like "skid" (Run N' Gun Platformer Pack\PlayerCharacter\gunner_player_smaller_head.scml).

But others like "to_Ladder" and "block_hit" became weird.

The problem is the Displayed Sprite value. Its a float number and sometimes don't corresponding to the correct Sprite. And the value of Displayed Sprite is "animating"... For example, from 0 to 2.

And the "to_block" and "from_block" animation the sword goes to wrong direction (using or not this solution). I tried change to Quartenion, but I don't understand about curves. Someone knows what to do?

Thanks

CarlosHBP commented 7 years ago

I think I did (for my cases). I added an "if" to not enter when DisplayedSprite and m_IsActive:

EditorCurveBinding[] curveBindings = AnimationUtility.GetCurveBindings(clip);
AnimationCurve animCurve;
foreach (EditorCurveBinding curveBinding in curveBindings)
{
    if (curveBinding.propertyName != "DisplayedSprite" && curveBinding.propertyName != "m_IsActive")
    {
        animCurve = AnimationUtility.GetEditorCurve(clip, curveBinding);
        for (int i = 0; i < animCurve.length; i++)
        {
            AnimationUtility.SetKeyLeftTangentMode(animCurve, i, AnimationUtility.TangentMode.Linear);
            AnimationUtility.SetKeyRightTangentMode(animCurve, i, AnimationUtility.TangentMode.Linear);
        }
        AnimationUtility.SetEditorCurve(clip, curveBinding, animCurve);
    }
}

The other problems, about the rotation ("to_block" and "to_Ladder") I think is an Unity problem. This has nothing to do with importing.

CarlosHBP commented 7 years ago

Another change that help the "to_Ladder" animation. Yet has a strange flip but its nice now.

I dont know how to use "instant" (stepped?) in Unity, but people are saying to use "constant"/math.infinite for inTangent and onTangent.

I changed the SetKeys function (no Sprite parameter) to this:

private void SetKeys (AnimationCurve curve, TimeLine timeLine, Func infoValue, Animation animation) {
    foreach (var key in timeLine.keys) { //Create a keyframe for every key on its personal TimeLine
        if (key.curve_type != CurveType.instant)
            curve.AddKey (key.time, infoValue (key.info));
        else
            curve.AddKey(new Keyframe(key.time, infoValue(key.info), inf, inf));
    }
    var lastIndex = (animation.looping) ? 0 : timeLine.keys.Length - 1; //Depending on the loop type, duplicate the first or last frame

    if (timeLine.keys[lastIndex].curve_type != CurveType.instant)
        curve.AddKey (animation.length, infoValue (timeLine.keys [lastIndex].info)); //At the end of the curve
    else
        curve.AddKey(new Keyframe(animation.length, infoValue(timeLine.keys[lastIndex].info), inf, inf));
}

It's doing like SetKeys for Sprites when curves are "instant".

The only problems now:

1) The rotation going to wrong direction in some animations. Unity problem? 2) Some animations z-order are wrong, but it's only a few (about 5 in 163 animations tests) and easy to adjust manually.

CarlosHBP commented 7 years ago

After more testing...

Changes I made in SpriterPro animations to work better in Unity:

1) Animations with sprite deformation (negative-x-scale):

If in SpriterPro the object has the negative x-scale only to reposition according to the angle, subtract 180 from the angle value and leave the x-scale positive. Thus the sprite will not be deformed in Unity.

In the case I think the animation was wrong, because it did not have to "flip" the sword:

Incorrect: untitled-1

Correct: untitled-2

2) Since Unity has no "instant" (no animation):

If the animation is rotating to the wrong side, it probably would also be in the SpriterPro, which is probably using "instant" (no animation). If you change to "linear" you will see that it goes to the "wrong" side also in SpriterPro.

So to solve, I add a new keyframe between the other two. I reposition the bones to the position they should have in that keyframe.

I think it will not make a difference to leave "instant" or "linear" in this case.

Wrong: wrong

Correct: correct

CarlosHBP commented 7 years ago

This change solved all my problems with z-order: (AnimationBuilder -> GetCurves)

Original:

if (!rv.ContainsKey (ChangedValues.PositionX) && (defaultInfo.x != info.x || defaultInfo.y != info.y)) {
    rv [ChangedValues.PositionX] = new AnimationCurve (); //There will be irregular behaviour if curves aren't added for all members  
    rv [ChangedValues.PositionY] = new AnimationCurve (); //in a group, so when one is set, the others have to be set as well
    rv [ChangedValues.PositionZ] = new AnimationCurve ();
}

Changed:

if (!rv.ContainsKey (ChangedValues.PositionX) && (defaultInfo.x != info.x || defaultInfo.y != info.y) || key.id == 0) {
    rv [ChangedValues.PositionX] = new AnimationCurve (); //There will be irregular behaviour if curves aren't added for all members  
    rv [ChangedValues.PositionY] = new AnimationCurve (); //in a group, so when one is set, the others have to be set as well
    rv [ChangedValues.PositionZ] = new AnimationCurve ();
}

Every first keyframe of each animation will have the position of the object.

Slowed the import.