• Editor
  • After Effects and Photoshop export script for Spine

  • Editado

I've been tinkering with these scripts for the last few months as part of our game development. I've decided to make them available incase they're helpful to others using Spine.
They're up on github here:
https://github.com/Bee-Cave-Games/spine_export

From the README:

Spine Export Scripts
Scripts to export Adobe Photoshop and After Effects content to Esoteric Software's Spine animation tool JSON format.

After Effects Export with ae_to_spine.jsx
The ae_to_spine.jsx script exports a lot of animation data from After Effects, but not everything. Things that are supported:

  • Image layers

  • PNG sequence layers

  • Layer hierarchy (parenting)

  • Translation, rotation, scale, opacity keyframes (stepped or linear only)

  • Composition layers (nested compositions)

  • Per-layer in-point and out-point (visibility)

  • Time remapping

  • Additive blend mode

Things that are not supported:

  • Warp effects, puppet animation, etc. (no deformation)

  • Glows, shadows, etc. (no effects)

  • Masks of any kind

  • Color transformations of any kind

  • Plugin effects, like particles, etc.

Some of these limitations are easier to work around than others. For example, if you are warping an image, consider rendering out the warping animation to a PNG sequence and using that as a layer.

For particle effects, render those out as a separate PNG sequence as well. Use a lower resolution if you can to save texture space.

Same goes for glows, shadows, and other effects. They can be rendered out, added as another layer, and faded in and out using opacity, often at lower resolution.

Photoshop Export with psd_to_spine.jsx
There is an official photoshop export script from Esoteric Software. This one was written mainly to work with the new Photoshop CC 2013 image generator feature.
http://blogs.adobe.com/photoshopdotcom/2013/09/introducing-adobe-generator-for-photoshop-cc.html

psd_to_spine.jsx only exports layers that are named with ".png" in their name. It takes into account any declared scale, if there is one. For example, "25% foo.png" will use write out a PNG that is 25% of the layer size, but add "scaleX": 4.0, "scaleY": 4.0 to the Spine JSON output.

psd_to_spine.jsx also exports Groups as bones, and uses relative positions for the layers within the Group/bone.

The following Layer attributes are exported:

  • x, y, width, height

  • scale as declared in the Layer name

  • opacity

  • additive blend mode

Some notable things that aren't supported:

  • rotation (Even for smart objects, this just isn't something that's accessible through scripting)

  • blend modes other than "additive". (Would love for Spine to support more!)

  • Eliminating duplicate images

Regarding duplicate or mirrored images, the workflow we've adopted is to use duplcated and/or mirrored objects all we want in Photoshop, but once we export for Spine, edit the JSON and replace all duplicate image references with a single image reference. Various texture packer or atlas generators can do this job as well. The point is, look for opportunities to re-use the same texture in Spine if possible.

Support
If you run into something that should work but doesn't, feel free to comment here or post an issue on github.

Related Discussions
...
  • Editado

Re: The AfterEffects export script.

Are the keyframe curves/graphs exported correctly? Or at least approximated?

That's a good question. Currently only the key frame times and values are exported, not any curve info, so they're just linear. That's definitely something I'd like to look into. Could be done by sub-sampling and adding more keys, but if it's possible to approximate using the bezier interpolation available in Spine, that'd be better - or at least cooler. 🙂


So, I looked into it, and the Bezier data is there for the getting but I couldn't make sense of the values yet. It was easy to support the "stepped" curve type, though, so I've added that. To figure out the Bezier data, I'll need to make a simpler test animation in After Effects and see if I can figure out how to map their "influence" and "speed" to Spine's normalized Bezier curve values. (Anyone that has any insight on this, please let me know!)

I hope Nate can point you in the right direction.
This is great work as it is though. Almost there /o/

This is really important 'cause just for its controls, there are still some things like effects animations and 12/24fps things that are easier to animate in AE than in Spine. This would really fill in the gaps it has right now.

The other gotcha is the way that Spine handles rotation keyframes. In order to make my export work, I had to add keyframes if the difference between the After Effects keyframes was greater than 180. This is fine for linear interpolation, but making an intermediate bezier control point will be trickier.

Of course, all of this is a non issue if we don't mind adding a keyframe every nth of a second. Then you'd be getting really close to the After Effects animation curves at the expense of data size and edit-ability in Spine.


I see what I was not understanding now. After Effects has two different interpolatable things: spatial and temporal. Spine, on the other hand, has only temporal. The Bezier graph in Spine let's you adjust time, not position. I think the only solution for accurate export of "position" from After Effects to "translate" in Spine is to sample and add a keyframe every frame.

Since everything we've been using this script for so far does pretty well using linear spatial interpolation, I'd like to keep that functionality. Seems like this might be the first candidate for a UI for this script. One that let's you chose whether you want to add a keyframe per frame or not.

Any other ideas/thoughts?


Here's a possible solution for spatial bezier motion that strikes a good balance between fidelity and a ton of keyframes. Here's the sample I used:

Image removed due to the lack of support for HTTPS. | Show Anyway

It has 3 Position keyframes. When I'm processing the data for Spine, I still output a keyframe for each After Effects keyframe. But I also walk the Position values frame by frame between the keyframes. If the position ever strays from a linearly interpolated point by more than 20%, I emit another keyframe.

In Spine, the timeline ends up looking like this:

Image removed due to the lack of support for HTTPS. | Show Anyway

Notice how we end up with more keyframes where the curvature between the keyframes is greatest. Still doing some tweaking and testing but the results are promising. Unfortunately, the proliferation of keyframes (even minimally) makes for a not-so-editable Spine animation. That's only an issue if you need to tweak positions in Spine after export, though.

5 meses más tarde

Thank you very much for the script
But how i can export multiple composition in After Effect as Animations in Spine?.

I usually use Comps as every animation status in AE, but the script only allow export one Comps at once. Then, when import JSONs to Spine, I have alot of Hierarchy and can't combine they together

Hi Nimai,

I'm very happy to hear that you have a script for AE to Spine. I was trying to write one for my self as well. I hope we can share ideas and features to make the script better and better.

About stepped and linear interpolation only, I have a solution to support bezier. Here is how I did it:

Translate:

if (key <= thisLayer.position.numKeys - 1 && thisLayer.position.keyOutInterpolationType(key) == KeyframeInterpolationType.BEZIER){
   curve1U = Math.round(position.keyOutTemporalEase(key)[0].influence)/100;
   curve2U = 1 - Math.round(position.keyInTemporalEase(key + 1)[0].influence)/100;
   curve1V = 0;
   curve2V = 1;
   f.write(", \"curve\": [" + curve1U + ", " + curve1V + ", " + curve2U + ", " + curve2V + "]");
}

Rotate:

if (key <= thisLayer.rotation.numKeys - 1 && thisLayer.rotation.keyOutInterpolationType(key) == KeyframeInterpolationType.BEZIER){
   curve1U = Math.round(rotation.keyOutTemporalEase(key)[0].influence)/100;
   curve2U = 1 - Math.round(rotation.keyInTemporalEase(key + 1)[0].influence)/100;
   curve1V = 0;
   curve2V = 1;
   f.write(", \"curve\": [" + curve1U + ", " + curve1V + ", " + curve2U + ", " + curve2V + "]");
}

Scale:

if (key <= thisLayer.scale.numKeys - 1 && thisLayer.scale.keyOutInterpolationType(key) == KeyframeInterpolationType.BEZIER){
   curve1U = Math.round(scale.keyOutTemporalEase(key)[0].influence)/100;
   curve2U = 1 - Math.round(scale.keyInTemporalEase(key + 1)[0].influence)/100;
   curve1V = 0;
   curve2V = 1;
   f.write(", \"curve\": [" + curve1U + ", " + curve1V + ", " + curve2U + ", " + curve2V + "]");
}

Opacity:

if (key <= thisLayer.opacity.numKeys - 1 && thisLayer.opacity.keyOutInterpolationType(key) == KeyframeInterpolationType.BEZIER){
   curve1U = Math.round(opacity.keyOutTemporalEase(key)[0].influence)/100;
   curve2U = 1 - Math.round(opacity.keyInTemporalEase(key + 1)[0].influence)/100;
   curve1V = 0;
   curve2V = 1;
   f.write(", \"curve\": [" + curve1U + ", " + curve1V + ", " + curve2U + ", " + curve2V + "]");
}

The limitation is that in AE we should always make horizontal bezier control points (which I think is enough).

I hope you find this useful. It's nice to meet you!

Quang

Fantastic! I'll definitely have a look at that approach, and I agree that simple horizontally offset control points should be fine.

This should work great for temporal easing, but curved paths that control the value itself would need to be supported in Spine.

Nimai

Awesome!

Hi Nimai!

I've just found a way to correctly convert speed and influence into x and y. For example for rotation:

if (key <= thisLayer.rotation.numKeys - 1 && thisLayer.rotation.keyOutInterpolationType(key) == KeyframeInterpolationType.BEZIER){
   duration = rotation.keyTime(key + 1) - rotation.keyTime(key);
   amount = Math.abs(rotation.keyValue(key + 1) - rotation.keyValue(key));
   averageSpeed = amount/duration;
   curve1U = rotation.keyOutTemporalEase(key)[0].influence/100;
   curve1V = rotation.keyOutTemporalEase(key)[0].speed/averageSpeed * curve1U;
   curve2U = rotation.keyInTemporalEase(key + 1)[0].influence/100;
   curve2V = rotation.keyInTemporalEase(key + 1)[0].speed/averageSpeed * curve2U;
   f.write(", \"curve\": [" + curve1U + ", " + curve1V + ", " + (1 - curve2U) + ", " + (1 - curve2V) + "]");
}

We just need to round the results to make them shorter. :rofl:

Very cool stuff. Yes, I know what you mean about those giant floating point numbers in JSON! I'm sure 8 places would be fine, but more accuracy isn't a bad thing either.

By the way, do you have a plan to convert puppet pins in AE into mesh deform in Spine?
Moreover, in AE, I see a very cool feature. It lets us transform a layer in 3D. I think it is possible to simulate that perspective look into Spine using mesh deform.

How do you think? Do you have time to further develop the script? I see that your thread was not updated in the last 5 months, so I just wonder.

Great questions. I've pretty much taken this script as far as it needs to go for our purposes.

I have given some thought to what it would take to support "standard" deformations in many Adobe products. (See here).

Puppet support would be amazing! I don't have the time to pursue it, though.

Puppet support would be amazing! I don't have the time to pursue it, though.

The last time I approached that issue (before Spine existed) it was not possible as the mesh data was never serialized in a way that could be exported. It was a proprietary mesh-generating algorithm that only took in a few parameters.

Hi Mitch. Thanks for the info. I have an idea of even simulating that algorithm and adapt it to Spine, although I know this will be difficult.


Nimai, is it possible to make several animations in AE (each animation is a composition), and then export all of them to only one json file?

@famevn - I assume you mean that multiple compositions would be output as one JSON file, and each composition would be it's own animation? That would be pretty useful. Each composition would have to have the exact same layers. Is that what you're thinking?

Yes exactly!

I don't see any technical reason why that couldn't be done. For now, you should be able to copy-paste and consolidate animations from multiple exports into one.

Do you mean I should manipulate the JSON files?