Importing Flash into Torque (Tech Demo 4)
by Charlie Patterson · 05/05/2012 (4:32 pm) · 6 comments
Hey everyone,
I thought I'd document a few of my tech successes updating the Torque2D engine. This one is about Importing Flash animations into a block of t2d scene objects. There's a movie at the end, as usual.
(Also, if you enjoy this tech demo, please consider "paying forward" with a click on my game's like button, here: facebook.com/RednecksVsRobots. Gonna look for some funding soon and likes will help, I hope!)
One of my favorite aspects of Torque is that it has (almost) everything you need to base a game, but it does not get bogged down in tons of special one-off cases. It has basic sprites, collisions, scripts, editors, etc. I also think the animations should have been kept relatively simple and they were. It's a subtle line. However, I had been fretting animation for a while.
In my opinion for my game plans, rotating scene object and linear motion wouldn't be enough. (Hence the Twillex project for starters.) Flipping through animations is better, and Torque has that too. However, I'm on the verge of needing a few more things:
I think motion editing is an entire tool in itself; much like Photoshop or the Gimp makes more sense that adding a built-in image editor to Torque. Flash is a fairly well-respected animation tool. Meanwhile the artist on my project was experimenting with puppet animations in Flash anyway.
So I set out on a 5-week project to see if I could get some form of Flash reader into my Torque scenes.
For Flash animations, the only tool in town is Adobe Flash Professional. You'll here of Flash Builder and Flash Developer, but these are developer tools -- very text-based. Adobe Flash Pro is the artist's tool for creating graphics and animating them frame by frame.
Flash keeps all the source files (images, vector graphics, paths, etc.) in a proprietary format known as FLA. (There is actually another newer format, but that's too confusing for now.) An artist opens his FLA file and updates the graphics and paths. He sets "tweens" and creates multiple dynamic scenes called movieclips. This name is semi-accurate. By and large, you don't interact with movieclips, but they aren't like an MPEG video either. A movieclip is made of individual symbols (graphics, text, vectors) that are moved frame by frame, and it is the symbols and motion data that make up the final movieclip.
Once the output is ready, the artist compiles to a SWF file from his FLA file. The SWF file is what you known in your browser as a "flash file," and it has a frame rate, a scene size, and it compiles out much of the hierarchical information and unused other stuff added while creating. The SWF file is stripped down to its bare minimum for efficiency. These SWF files can also contain scripts in a proprietary language called ActionScript, but that isn't necessary for basic animations.
There is no *natural* way to read or connect to a FLA or a SWF file. At least SWF has open documentation if you are brave enough to try and read the files as binary. However, new formats come out at a pretty snappy pace. While they are backwards compatible, it is something to be concerned about.
After a few false starts, the best option I found for extracting the animations from Flash was Grapefrukt, a free and open-source trojan program written by Martin Jonasson. The idea is that you include Grapefrukt as a script in your SWF, and at run-time it watches the animation play and creates an XML file and some PNG files of what it sees. The SWF file isn't necessarily anything you plan to release in this case. You just create it to give Grapefrukt a place to live.
While Grapefrukt is not perfect (which is more Flash's fault), it is reliable because it's written in ActionScript and it's using the rules of SWF files, and those aren't going anywhere.
It take a little time to learn what Grapefrukt needs as for modifications in the SWF. Because a SWF file is so minimal, you must give "linkage names" to all your movieclips, name all your symbol *instances*, add labels to your movies, etc, etc. This is because unnamed items can't be accessed from script. Then you must write a little 10-line program using the Grapefrukt API, but that's not too bad either. The hard part is that it's not always easy to know what went wrong. However, after a little playing, it gets pretty easy.
Actually Grapefrukt is written to be easy as possible, I believe. It does a few things for you that I didn't want. For instance, it "flattens" any movieclips inside of other movieclips. So if I have a robot animation, and it has a tread animation inside of it, Grapefrukt will convert the treads to a series of image frames in a texture sheet. Yuck for my case. So I ended up adding a handful of, in my opinion, features (such as "sub animations") that seem to be the opposite of the author's intent. Nevertheless, I'm more than happy to pass around any changes.
Once Grapefrukt has made two XML files (and some PNGs) the work is just beginning! Flash is really, at its core, a mathematically-intensive 2D transformation system, with data written as scaling, rotating and *skewing*. Even deeper, movieclips are made of "sub" movieclips in a hierarchy. Each level can be scaled and translated to its own coordinate system.
There is even some screwy stuff where you can rotate around a transformation point, instead of the origin. That's normal enough, but ultimately all that Flash really does is twist the origin (called the registration point) and throw out the transformation point from the SWF file. This requires a bit of reverse transforming to get right.
So I went about making a sweet-enough Matrix class in TorqueScript. Should I have used C++? Well more on that soon, but I try to live by the idea that pre-optimization is evil. Nine times out of ten I'm surprised how optimization isn't needed.
Then I used Torque's XML reader classes and SimObjects and SimSets to import the entire tree structure of an animation. Every symbol in my puppet is listed and for each symbol each frame of animation is defined, with the position/size/skew/scale.
I'm happy to report this was pretty darn easy in a dynamic language like TorqueScript. If I needed a new object variable I just created it. For example:
Finally, once I had the data in, I set about creating one sceneObject per movieclip symbol. Then the plan is to use the frame updates for each part and move the part into position per frame. This only took about 1000 tries! :) I'd draw a picture loaded with offsets, origins and skews, understand as best as I could, then offset things to their center for Torque, fail, then try again. Flash changed the 2d concepts just enough that most of what I knew was slightly askew for each step.
Speaking of askew, I had to add skew to Torque, which was super easy. Skewing is where a part shears or twists -- a great trick for animators to make things look larger than life, or wiggly, or to make inanimate objects look like they are anticipating or acting. To skew, I simply added a method to the t2dMatrix.
Each puppet can have "sub assemblies" such as the treads on a robot. And each puppet is really a series of "surrogate" sceneObjects that are held together by nothing more than their spatial relations.
Hmm. Remember that part about not optimizing prematurely? OK. Now I know I need to optimize. Each robot in the following movie is approximately 30 sceneObjects. And each one requires, for each frame of the animation, several parameters such as position, scale, skew, etc. All this work can really take its toll using a scripting language!
So how many of these robots do you think I can run around? Actually, I was surprised how few. By the second robot in a scene, my FPS is visibly down to 15fps. At 6 robots the FPS is... 1! Ugh. On a second experiment I didn't actually try to move the robot, which skips any searching for frames or setting up the surrogate parts. All I do is let Torque[/] draw all the [i]sceneObjects as fast as it can. So how fast now? Now I get 6 robots at 94fps. Much better. However, 15 robots and I'm already down to 66fps. If I want lots of puppets, say 100, I'll need to come up with an engine solution.
But quickly, I was kinda surprised that 1000 sceneObjects could slow Torque down! I believe the reason is that it must collect them all, sort them by Y-axis (in my case), and maybe some other stuff. This adds up when you did it frame by frame.
Here's the stats in a kinda table. Remember that every robot has about 30 sceneObjects
to be continued...
That's it. Here's a video.
I thought I'd document a few of my tech successes updating the Torque2D engine. This one is about Importing Flash animations into a block of t2d scene objects. There's a movie at the end, as usual.
(Also, if you enjoy this tech demo, please consider "paying forward" with a click on my game's like button, here: facebook.com/RednecksVsRobots. Gonna look for some funding soon and likes will help, I hope!)
why
One of my favorite aspects of Torque is that it has (almost) everything you need to base a game, but it does not get bogged down in tons of special one-off cases. It has basic sprites, collisions, scripts, editors, etc. I also think the animations should have been kept relatively simple and they were. It's a subtle line. However, I had been fretting animation for a while.
In my opinion for my game plans, rotating scene object and linear motion wouldn't be enough. (Hence the Twillex project for starters.) Flipping through animations is better, and Torque has that too. However, I'm on the verge of needing a few more things:
- Sometimes you want cartoon paths, too. I want a character to jump almost straight up, hang with a little vibration, and then pounce forward quickly. Doing this with scripting seems unnecessarily complicated. It's also not really possible for the artist. What you need is some kind of "motion editor."
- Also, there is a good chance I want some "puppet" animations or characters that move in a efficient but funny way like South Park. The same kind of multi-pieces motion could be used for other things like...
- There may be some animated intro and outro cut scenes, even if they are just flying logos.
I think motion editing is an entire tool in itself; much like Photoshop or the Gimp makes more sense that adding a built-in image editor to Torque. Flash is a fairly well-respected animation tool. Meanwhile the artist on my project was experimenting with puppet animations in Flash anyway.
So I set out on a 5-week project to see if I could get some form of Flash reader into my Torque scenes.
way too quick explanation of Flash
For Flash animations, the only tool in town is Adobe Flash Professional. You'll here of Flash Builder and Flash Developer, but these are developer tools -- very text-based. Adobe Flash Pro is the artist's tool for creating graphics and animating them frame by frame.
Flash keeps all the source files (images, vector graphics, paths, etc.) in a proprietary format known as FLA. (There is actually another newer format, but that's too confusing for now.) An artist opens his FLA file and updates the graphics and paths. He sets "tweens" and creates multiple dynamic scenes called movieclips. This name is semi-accurate. By and large, you don't interact with movieclips, but they aren't like an MPEG video either. A movieclip is made of individual symbols (graphics, text, vectors) that are moved frame by frame, and it is the symbols and motion data that make up the final movieclip.
Once the output is ready, the artist compiles to a SWF file from his FLA file. The SWF file is what you known in your browser as a "flash file," and it has a frame rate, a scene size, and it compiles out much of the hierarchical information and unused other stuff added while creating. The SWF file is stripped down to its bare minimum for efficiency. These SWF files can also contain scripts in a proprietary language called ActionScript, but that isn't necessary for basic animations.
grapefrukt
There is no *natural* way to read or connect to a FLA or a SWF file. At least SWF has open documentation if you are brave enough to try and read the files as binary. However, new formats come out at a pretty snappy pace. While they are backwards compatible, it is something to be concerned about.
After a few false starts, the best option I found for extracting the animations from Flash was Grapefrukt, a free and open-source trojan program written by Martin Jonasson. The idea is that you include Grapefrukt as a script in your SWF, and at run-time it watches the animation play and creates an XML file and some PNG files of what it sees. The SWF file isn't necessarily anything you plan to release in this case. You just create it to give Grapefrukt a place to live.
While Grapefrukt is not perfect (which is more Flash's fault), it is reliable because it's written in ActionScript and it's using the rules of SWF files, and those aren't going anywhere.
It take a little time to learn what Grapefrukt needs as for modifications in the SWF. Because a SWF file is so minimal, you must give "linkage names" to all your movieclips, name all your symbol *instances*, add labels to your movies, etc, etc. This is because unnamed items can't be accessed from script. Then you must write a little 10-line program using the Grapefrukt API, but that's not too bad either. The hard part is that it's not always easy to know what went wrong. However, after a little playing, it gets pretty easy.
Actually Grapefrukt is written to be easy as possible, I believe. It does a few things for you that I didn't want. For instance, it "flattens" any movieclips inside of other movieclips. So if I have a robot animation, and it has a tread animation inside of it, Grapefrukt will convert the treads to a series of image frames in a texture sheet. Yuck for my case. So I ended up adding a handful of, in my opinion, features (such as "sub animations") that seem to be the opposite of the author's intent. Nevertheless, I'm more than happy to pass around any changes.
xml in
Once Grapefrukt has made two XML files (and some PNGs) the work is just beginning! Flash is really, at its core, a mathematically-intensive 2D transformation system, with data written as scaling, rotating and *skewing*. Even deeper, movieclips are made of "sub" movieclips in a hierarchy. Each level can be scaled and translated to its own coordinate system.
There is even some screwy stuff where you can rotate around a transformation point, instead of the origin. That's normal enough, but ultimately all that Flash really does is twist the origin (called the registration point) and throw out the transformation point from the SWF file. This requires a bit of reverse transforming to get right.
So I went about making a sweet-enough Matrix class in TorqueScript. Should I have used C++? Well more on that soon, but I try to live by the idea that pre-optimization is evil. Nine times out of ten I'm surprised how optimization isn't needed.
Then I used Torque's XML reader classes and SimObjects and SimSets to import the entire tree structure of an animation. Every symbol in my puppet is listed and for each symbol each frame of animation is defined, with the position/size/skew/scale.
I'm happy to report this was pretty darn easy in a dynamic language like TorqueScript. If I needed a new object variable I just created it. For example:
%animation = new SimObject(); // new object just like that! %animation.actor = %actor; // back reference; just add it at any variable name // a method to read xml attributes off the "top of the XML stack" into object fields. // so, if xml has <animation name="attack" frames="32"> then this next method // will create %animation.name = "attack" and %animation.frames = 32; YepUtil::convertXMLAttributesToFields(%xml, %animation); // create an "array" of object "parts" for the animation, just like that :) %animation.parts = new SimSet(); YepUtil::callPerXMLChild(%xml, "Texture", %this, loadPartPhasesManifest, %animation);
scene objects out
Finally, once I had the data in, I set about creating one sceneObject per movieclip symbol. Then the plan is to use the frame updates for each part and move the part into position per frame. This only took about 1000 tries! :) I'd draw a picture loaded with offsets, origins and skews, understand as best as I could, then offset things to their center for Torque, fail, then try again. Flash changed the 2d concepts just enough that most of what I knew was slightly askew for each step.
Speaking of askew, I had to add skew to Torque, which was super easy. Skewing is where a part shears or twists -- a great trick for animators to make things look larger than life, or wiggly, or to make inanimate objects look like they are anticipating or acting. To skew, I simply added a method to the t2dMatrix.
Each puppet can have "sub assemblies" such as the treads on a robot. And each puppet is really a series of "surrogate" sceneObjects that are held together by nothing more than their spatial relations.
next
Hmm. Remember that part about not optimizing prematurely? OK. Now I know I need to optimize. Each robot in the following movie is approximately 30 sceneObjects. And each one requires, for each frame of the animation, several parameters such as position, scale, skew, etc. All this work can really take its toll using a scripting language!
So how many of these robots do you think I can run around? Actually, I was surprised how few. By the second robot in a scene, my FPS is visibly down to 15fps. At 6 robots the FPS is... 1! Ugh. On a second experiment I didn't actually try to move the robot, which skips any searching for frames or setting up the surrogate parts. All I do is let Torque[/] draw all the [i]sceneObjects as fast as it can. So how fast now? Now I get 6 robots at 94fps. Much better. However, 15 robots and I'm already down to 66fps. If I want lots of puppets, say 100, I'll need to come up with an engine solution.
But quickly, I was kinda surprised that 1000 sceneObjects could slow Torque down! I believe the reason is that it must collect them all, sort them by Y-axis (in my case), and maybe some other stuff. This adds up when you did it frame by frame.
Here's the stats in a kinda table. Remember that every robot has about 30 sceneObjects
robot count scene objects fps for animate fps for just draw
6 300 1 94
15 600 - 66
33 1200 - 48to be continued...
demo
That's it. Here's a video.
#2
By the way, what's the song of the video ? (16 horsepower ?)
05/09/2012 (2:11 am)
WoW !!! That's great !By the way, what's the song of the video ? (16 horsepower ?)
#3
05/09/2012 (9:25 am)
Haha @Frank. I was pronouncing Grapefrukt like "grapefruit" with a k in it, but I like your way much better.
#4
I don't have to go look it up because YouTube recognizes it, blocks my video from certain countries, but *also* sticks and ad in the comments. :P Oh well, I did just stick a song in it to see what would happen. Fair enough but funny to me.
05/09/2012 (9:28 am)
Hi @Grugin, You tube helpfully reports the song as "Brimstone Rock" by 16 HorsePower. :PI don't have to go look it up because YouTube recognizes it, blocks my video from certain countries, but *also* sticks and ad in the comments. :P Oh well, I did just stick a song in it to see what would happen. Fair enough but funny to me.
#5
05/11/2012 (12:28 pm)
Very sick tech demo Charlie! I'm loving your posts. I'm also jealous, as you are free of productization shackles which allows you to push the boundaries. Keep up the good work!
#6
05/12/2012 (4:34 pm)
@Mich. Thanks again! FWIW, I'm jealous of your job, too. Well, I *think*. :P
Torque Owner Demolishun
DemolishunConsulting Rocks!