- Düzenlendi
[AS3/Starling] Runtime modifications needed for optimization
Hi Nate,
In our late game http://www.esotericsoftware.com/forum/v ... f=5&t=2118 we use a lot Spine animations with a main character which has almost 60 skins and 60 animations - beside this character we have a lot of animated props in the background. The initialization time lasts for seconds and seconds - like 10-20 or more on iphone 4. The reason is very simple - the main character Spine file is about 9.6 Mo - even if the file is embedded the JSON.parse() method take a very long time and the animations and skins initialization too (animations more than skins).
After some tests and research I ended up with a method used by the developers of Bardbarian http://treefortress.com/app/_bardbarian/ . They write classes instances in binary format using the ByteArray class which provides much lighter files and the decompression method is very fast. So I give it a try. With some Spine runtime classes modifications I succeeded to write SkeletonData instances in binary format into one single file that weight about 682 Ko which is way less of the 11 Mo of my Spine JSON files. But the much important thing is that the decompression of the SkeletonData instances is very very very fast. Now our Spine animation initialization take 4 or 5 time less in iphone 4 which is great for us.
I really want to use this method for our next project - but in order to keep this method compatible with the AS3/Starling runtime some modifications are needed. Those modifications are very light - actually the main problem is that serialized/unserialized class must have empty constructors - that's all. So I had to comment some constructors and add getters/setters in order to allow properties initialization. Obviously I had to change all the places where those classes were instantiated too. But after all it wasn't a big deal. There was a second issue - skin attachment images are initialized at the same time as skin - but images can't be serialized, So I needed to initialized skin attachment after SkeletonData decompression. In order to achieve that I needed to be able to know how to create skin attachment directly from a SkeletonData instance - so I wen that way :
-Create a class named SkinAttachmentData
package spine
{
public class SkinAttachmentData
{
public var name:String;
public var slotIndex:int;
public var map:Object;
}
}
Add a vector.<SkinAttachmentData> in the Skin class
public var attachmentData:Vector.<SkinAttachmentData> = new Vector.<SkinAttachmentData>();
And finally in the Skeletonjson class readSkeletonData method :
for (var skinName:String in skins) {
var skinMap:Object = skins[skinName];
//var skin:Skin = new Skin(skinName);
var skin:Skin = new Skin();
skin.name = skinName;
for (var slotName:String in skinMap) {
var slotIndex:int = skeletonData.findSlotIndex(slotName);
var slotEntry:Object = skinMap[slotName];
for (var attachmentName:String in slotEntry) {
var attachment:Attachment = readAttachment(skin, attachmentName, slotEntry[attachmentName]);
if (attachment != null){
skin.addAttachment(slotIndex, attachmentName, attachment);
skinAttachmentData = new SkinAttachmentData();
skinAttachmentData.name = attachmentName;
skinAttachmentData.slotIndex = slotIndex;
skinAttachmentData.map = slotEntry[attachmentName];
skin.attachmentData.push(skinAttachmentData);
}
}
}
skeletonData.addSkin(skin);
if (skin.name == "default")
skeletonData.defaultSkin = skin;
}
With those modifications I was able to create a class that load a bin file, write the content into a ByteArray, decompress the ByteArray, iterate through all objects and save each one as a SkeletonData instances. Then for each SkeletonInstances I can iterate trough all skins and for each skins retrieve skin attachment data and create them if needed - with that approach is even possible to decide when to initialize a skin attachments.
What do you think about that ? Would it be possible to make some AS3/Starling runtime classes serializable and add skin attachment infos like I did ? The point is not to make people use this approach but let it possible without having modifying the runtime each time it is updated.
Thank you
JSON is always very slow, especially when there are many numbers to parse. ByteArray serializes objects using AS3's AMF format. I have a feeling this won't be as small as Spine's own binary format. Can you export your skeleton as binary and report the same and the AMF size? AMF may have the advantage that loading it is somehow optimized by AS3. I would like to see how fast loading Spine's binary format is before officially supporting AMF.
Because the binary format is more fragile than JSON regarding schema changes and the schema is still expected to change to support meshes and skinning, I haven't ported the binary loader to all runtimes yet. If you'd like to port it to AS3, you can find reference code here:
https://github.com/EsotericSoftware/spi ... inary.java
Some of the code needed is here:
https://github.com/libgdx/libgdx/blob/m ... Input.java
The rest can be ported from Java's DataInputStream class (I'm sure that has been done for AS3 somewhere on the Internet).
Hi Nate,
I'm not quite sure to understand your request when you ask to export my skeleton as a binary. Do you mean the binary file I have generated from my action-script code or is there a binary export directly from Spine editor ?
I'm not asking to support AMF - not at all - I was wondering if you could consider to change AS3 runtime classes in order they can be serialized. That way I would be able to write a little AIR application that take a folder containing json file and convert all of them in one AMF binary file. I will never ask you to support built-in export AMF binary file from the editor - I guess it would be to much work. But I will take a look at those 2 links you shared because the idea is very interesting !!!!
You said AMF may have the advantage that loading it is somehow optimized - I'm don't know about that - I simply know that the binary size is very small but the most important things is that the byteArray uncompress method is very fast so the skeletonData creation is too.
Thank you very much.
Spine has a binary export.
If the changes needed to use AMF/ByteArray are small, they can probably be made. I would rather support Spine's format though, as it is smaller and possibly as fast or faster.
Ok - and how can we compare Spine binary export and AMF ? I'm ok to make tests but not sure how to proceed.
Thanks
You can compare size by exporting binary from Spine (it's next to exporting JSON) and comparing with your AMF file. To compare speed you'd have to port the spine-libgdx SkeletonBinary class, or wait for me to get to it sometime after skinning.
Hi Nate,
The AMF binary file I have created is done from an action-script class and only contains the SkeletonData instance - would it be the same as the binary exported from the Editor - I mean, does that make sense to compare those file size ? Anyway, I'll do it because I'm very interested with that format !!!
Thank you
Yes, a Spine skeleton file from Spine also only contains data for a SkeletonData instance.