Hi everyone. John Mayhew from Flash authoring here. This is my first blog entry here and I wanted to take some time to describe the XML schema we use to store motion presets in Flash CS4. First I’d like to give you some background. The new animation model in Flash CS4 was built from the ground up over the last several years. It started as an idea I pitched at the end of our Flash 8 cycle at Macromedia. We assembled a team and started development in Flash 9 and continued through into Flash 10 and eventually shipped it as part of CS4. It was the culmination of tons of hard work by many, many people including our very own Jen DeHaan! We are all very proud of the feature and hope our users find that it does indeed "kick ass" as Jen likes to put it.
Early on in the development we decided that all of the motion data in the new model would be stored in XML. There were many reasons for this but the top two were 1) the ability to easily store this within our planned XFL file format and 2) to allow users to easily create their own presets from code if they wished. Robert Penner developed an XML representation for the classic tweens in Flash 9. For the new motion model we developed a separate schema that suited its property independence and new easing model. The two are very different in every way. I’ll be covering the new motion XML schema here. My hope with describing this XML format is that it will enable you all will start creating presets either from code or plugins. Once you understand the format of the presets its easy to create them yourself outside of the authoring environment. Now for some key concepts.
First of all we need ot discuss the concept of time as it relates to the new motion model. Traditionally Flash motion tweens stored keyframes as frame indexes. This made sense since keyframes could only exist on frames in the main timeline. A traditional keyframe in Flash represents a new instance of some object or symbol in the motion case.
The new model however supports keyframes per symbol property. Instead of tweening between two symbol instances, the new engine simply tweens a single symbols properties. Therefore, a keyframe in the new motion model is a concept that exists on symbol properties such as x, width, blur X, alpha, etc. More details on property keyframes can be found in Jen’s blog entry here.
Further, the spatial properties (x, y, z) in the new motion model can contain keyframes that do not fall on frame boundaries. These special keyframes are called roving keyframes. They are used to simplify the process of making motion across a bunch of spatial keyframes constant. So the new model requires a timing model that supports true time vs. frame numbers. We chose a time construct similar to what After Effects uses. In the new model keyframe times for a property are stored as a rational number, i.e. a fraction. One major benefit of this model is that it is not subject to any rounding errors which makes actions like scaling property keyframes less error prone. The denominator of the fraction represents our time scale which is set to 1000 times the frame rate of the movie. So for a 24 fps FLA file all motions will use a time scale of 24000. The numerator represents the time value in ticks. Since the time scale is 1000 times the frame rate, there are essentially 1000 time ticks per frame in the new timing model. Frames are easily represented in this model. 0/24000 would be the first frame, 1000/24000 would be the second frame, etc.. You can think of the keyframe time as the time of the start of the frame rectangle in the Flash timeline. Tween durations can also be expressed this way. A one second tween would have a duration of 24000/24000 for a 24 fps movie, a 1.5 second tween at 12 fps would have a duration of 18000/12000.
So, with this time structure, you can see that its possible for property keyframes to exist on frame boundaries or between frames. As far as how times are used with property keyframes we impose some restrictions. One is that every tweenable property must have a property keyframe at time zero. The second is that all property keframes fall on frame boundaries. The only exception to the last one is for roving keyframes in the spatial properties. These restirctions were put in simply because our users were already familiar with those restrictions of keyframe placement in Flash and the timeline already enforced those from a UI perspective. The underlying model doesn’t have those restrictions so in the future you could imagine a Flash timeline where keyframes don’t snap to frames. Who knows? Anything is possible now.
Properties and Property Keyframes
As mentioned before the new model ties keyframes to individual animation properties. The model treats every property (except the spatial properties x, y, z) as independent entities. There are essentially two types of properties in a new motion tween; keyframeable and constant. A constant property has one single value for the duration of the tween. An example of a constant property is the Quality setting in the Flash filters such as Drop Shadow. A keyframeable property is one that can have more than one value over the duration of the tween. Each value is stored as a property keyframe. Property keyframes contain a value and a time. Examples of keyframeable properties are x, y, z, witdh, rotation, skew X, and so on. The value types for property keyframes can vary since different keyframeable properties have different values. For example, drop shadow color stores an RGBA value for each keyframe. The Type property of the Gradient Bevel filter stores values of Inner, Outer and Full.
Some keyframeable properties can store values that represent cubic bezier points. Those properties can then be represented as a graph with a cubic bezier. Examples of the graphable properties are x, y, z, scale, etc., which you can see and interact with their graphs using the Motion Editor. The key thing to remember here is that each property keyframe for a keyframeable property stores a value of some type and a keyframe time as a fraction. Keyframeable properties must have a start keyframe at time zero as mentioned earlier, but they do not have to have any keyframes after that start frame. All keyframes after the starting keyframe are optional.
Eases (a.k.a Time Maps)
The new model has a brand new and very unique easing model. The eases are called Time Maps internally. They essentially remap time in the properties to achieve easing across the property keyframe values. I won’t go into detail on Time Maps here as it will be covered in much more detail in some upcoming blogs, so stay tuned! Flash CS4 includes many new ease types. In fact there are sixteen simple eases, and of course custom ease. The simple eases take a single value and produce various easing curves for you. More on each of those in some upcoming blogs as well. Custom ease is very similar to the custom ease in Flash 8 and 9. It essentially lets you create your own easing curve. The main difference in how time maps are used vs. pre-CS4 eases is that time maps are applied to an individual property vs. a traditional keyframe in the timeline. You apply eases to individual properties in the Motion Editor.
Okay, last concept and we will get to some actual Motion XML. The core of the new animation engine, which we call the animation core (very clever huh?), was built to handle properties in a completely generic manner. Properties to the core are stored in property containers. Each container can contain other containers and/or properties. So, essentially its a tree structure. Properties and property containers can have names. The structure is generic but Flash adds specific properties to the core for a tween. These properties happen to be the things that Flash can tween like x, y, scale, etc. Since the core is generic, you can imagine many, many more properties being available in the future! I’ll let your imagination run wild with the possibilities but I’ll just say that anythign is possible now.
Okay, with all that fully digested lets talk motion XML. I won’t really go into details on creating tweens in this article, but you can get detailed steps on that here or in the Flash help. In Flash CS4 I created a new document, and set the frame rate to 24 fps, which is the default. I created a very simple motion tween that moves an object from the left side of the stage to the right side of the stage in a straight line. Then I right clicked the span and selected "Save as Motion Preset" and named it "Simple". Then I went to my user folder (C:Documents and Settings<your user name>Local SettingsApplication DataAdobeFlash CS4enConfigurationMotion Presets on windows or \Users<your user name>LibraryApplication SupportAdobeFlash CS4enConfigurationMotion Presets" on mac) and opened the XML file. The motion xml for the simple tween I created is shown below.
Note: To view the XML in a browser, click here.
Now lets break this down. I’ll go through it section by section. The first line is
AnimationCore is the base tag for all the animation core data. The time scale entry is as we discussed earlier, the time scale for this preset. In this case it is 24000 which means the preset was created in a FLA set to 24 fps. This time scale is the scale for all times throughout this preset. The version number is the format number of the XML schema and is reserved for future use. The version for Flash CS4 is always 1. The duration is the total duration of this tween. As discussed above, 24000 / 1000 means the tween is 24 frames long. So at 24 fps, this tween is 1 second in duration. It is important to note that just because this preset was saved with a time scale of 24000, it doesn’t mean you cannot apply this preset in any document that has a frame rate different than 24 fps. The animation core will simply rebase the time scale when it is applied and the duration of the tween in frames will be maintained. All entries in this tag are required.
The next line is
<TimeMap strength="0" type="Quadratic"/>. This section below the main AnimationCore tag is reserved for all of the eases or time maps. By default every motion has a default simple Quadratic ease. This is required. It is the ease that is controlled by the Motion Property inspector. Every time map has two parameters, strength and type. All of the simple eases use the strength param to store the ease strength. The type is the type of the ease. The time maps are included one after another. All ease types are shown below as they relate to the name we use in the user interface and their strength values.
|Ease Type||UI Name||Strength Ranges|
|Quadratic||Simple (Slow)||-100 to 100|
|Cubic||Simple (Medium)||-100 to 100|
|Quartic||Simple (Fast)||-100 to 100|
|Quintic||Simple (Fastest)||-100 to 100|
|DualQuadratic||Stop and Start (Slow)||-100 to 100|
|DualCubic||Stop and Start (Medium)||-100 to 100|
|DualQuartic||Stop and Start (Fast)||-100 to 100|
|DualQuintic||Stop and Start (Fastest)||-100 to 100|
|Bounce||Bounce||-100 to 100|
|BounceIn||Bounce In||-100 to 100|
|Spring||Spring||0 to 100|
|SineWave||Sine Wave||0 to 100|
|SawtoothWave||Sawtooth Wave||1 to 100|
|SquareWave||Sine Wave||2 to 100|
|RandomSquareWave||Random||3 to 100|
|DampedWave||Damped Wave||0 to 100|
|Custom||Custom||Strength is ignored and must be 0|
Custom eases are specified as a cubic bezier curve and their format is slightly different. You can create a preset that has a custom ease and save it to view its XML. An example of a custom ease time map XML description is shown below:
As you can see the custom eases have extra data associated with them. This data specifies the actual bezier curve for the ease. I won’t go into detail on cubic bezier curves here, but basically they are described a series of points that specify the location of an anchor point, its next handle, the handle of the subsequent anchor point (that anchors previous handle), then the final anchor. That is a single cubic bezier spline. Every anchor point has a previous and a next handle. Custom eases in the animation core list each previous / anchor / next set as a CustomTimeMapPoint that specifies the three points as simple x, y pairs. Both x and y are floating point numbers. Anchor point y values are limited between 0 and 1 while the handles y values can extend beyond that range. All x values in custom eases are limited from 0 to 1. It is important to note that the previous handle of the first anchor and the next handle of the last anchor are completely ignored. If you are writing out motion XML you should make those two match the x, y of their anchor. Finally, all of the CustomTimeMapPoint entries must be tween the TimeMap tags for that particular custom ease.
Okay on to the metadata section shown below.
The metadata section is intended to hold some generic extra data, and initially also used to handle localized preset names. The localization aspect is no longer used but we do still save out the tween preset name in the language of the user who saved it. You can ignore this section for the most part. If you are writing out a preset, you can just ignore the names section but remember that the name in the presets panel is actually determined another way. It’s basically determined by the actual XML file name for the custom presets. The built in presets have yet another mechanism that I won’t go into here. Again, if you are writing out XML for a preset, you could just ignore the name section as shown above.
The important part of the metadata tag is the Settings section. This stores two pieces of data. First, it stores whether or not orient to path is on (selected). If orientToPath is 1, orient is on; if its 0, it is off. Simple enough. The second piece of data describes exactly where the optimal transform point for this motion should be. We store the transform point as a percentage offset from the untransformed objects top left corner of its bounding box. We store it as a percentage to allow it to more easily be applied to a target object of any size.
Why is the transform point so important? Well, one example to show why is a tween of a bouncing ball that squishes at the bottom of each bounce. To make the squish work the transform point must be at the bottom of the object. So if you save that preset and want to have other users be able to apply it and have it tween properly, you must also include the transform point within the preset data. We store the percentage offset for x, y, and z but note that the z offset should be 0 for all 2D tweens. The z parameter is only for use in 3D tweens where the transform point has x, y, and z
Properties in XML
Okay, we have finally made it to the property data section. As mentioned before the animation core stores properties in containers. Think of each container as a folder that can contain other folders and/or other properties. It’s a basic tree structure. The property data section of the sample xml is shown again below.
Note: To view this XML in a browser, click here.
The animation core keeps a root property container which serves as the parent to all property containers. This exists as the head container. So you can see the tag for a container is named PropertyContainer. This tag contains one parameter that is the ID of the container. The root containers ID is "headContainer", which is set and must exist. Under the head container are four child containers, "Basic_Motion", "Transformation", "Colors" and "Filters". The structure of all of these is set. The XML must contain the head container and it must contain these child containers. Under each container can be other containers or properties. The current structure for CS4 is set so "Basic_Motion" contains the x, y, z, rotation x, rotation y and rotation z. For 2D tweens, z and rotation x, and rotation y are not added. Transformation contains skew x and y as well as scale x and y.
Each property is specified in the "Property" tag. That tag has five parameters. The "id" parameter identifies the property for the core. Those ids are pretty self explanatory. The "enabled" parameter is reserved for future use and will allow us to enable and disable properties. In CS4 you should always set this to 1. The "readonly" param is also for future use and will allow us to set a property to an un-editable stage. For CS4 this should always be 1. The same is true for the "visible" parameter. This will allow us to hide certain properties in the future. For CS4, always set this to 1. Finally there is the "ignoreTimeMap" parameter that tells the property whether it should ignore its ease or not. This parameter is 1 when the ease checkbox in the Motion Editor is unchecked and is 0 when it is checked.
Now the sample for this article doesn’t apply any eases to a property. If an ease was applied to a property, that property tag would also contain a parameter named "TimeMapIndex" that would specify the zero based index of the ease which this property has applied. It is a zero based index into the list of "TimeMap" entries at the top of the XML file.
All of the properties mentioned above (x, y, z, rotation z, y and z, skew and scale) are keyframeable and graphable so each property stores a collecion of property keyframes that describe its cubic bezier curve. Each property keyframe stores a time and a value that is a cubic bezier point (prev, anchor and next). The time value of the property keyframes are fractions, as mentioned above, but because every property keyframe in the entire motion must have the same time scale, we avoid writing out the time scale (the denominator) for each property keyframe. Instead, all property keyframes use the time scale specified in the AnimationCore tag we discussed above. This avoids having to deal with XML that has mixed time scales, which would be an error condition. Because the time scale appears only once in the XML we know that situation cannot occur. So, each property keyframe only stores the numerator or time value of the time fraction. That is the "timevalue" parameter, which is in ticks as described at the beginning of this article. This value actually specifies the time of the anchor point of the property keyframe. Next is the "roving" parameter which simply specifies whether this property keyframe is roving. It is a simple Boolean: 1 or 0. Only keyframes of the spatial (x, y, z) properties can be roving keyframes. Of the spatial property keyframes the first and last keyframes cannot be roving, only the keyframes inbetween can be roving. This parameter corresponds to the roving setting in the motion editor UI.
Finally, there are the value parameters for each property keyframe. Because these are all graphable they are specified as cubic bezier prev, anchor, and next values. Each param "previous", "anchor", and "next" is specified in an x, y value pair. The x value of each of these parameters is specified first and represents the time value (numerator) of the point relative to the anchor time, which is specified in "timevalue" mentioned above. The previous point can have negative x values to specify it is before the anchor in time. The x component of the "anchor" parameter should always be zero. Finally, the y portion of the value pair for each parameter specifies the actual property value in its native units. So for x, y, and z, the y portion is in floating point pixels. For skew it is in degrees and for scale it is in percent. So for example lets take this property keyframe tag.
<Keyframe anchor="0,3.8512" next="1150,2.5" previous="-7658,5.4" roving="0" timevalue="23000" />
This is not a roving keyframe because "roving" is set to zero. Its time value is 23000 so that means it is at frame 23 of the span. As mentioned before, time values are always relative to the start of the span. The anchor time value offset is 0 which it always should be. The anchor y value is 3.8512. The previous handle time value is -7658 ticks so the handle is about 7.7 frames before the anchor in time. The previous handle value is 5.4. The next handle is 1150 time ticks after the anchor and its value is 2.5.
The best way to try to familiarize yourself with how these values are determined is to veiw a motion in the Motion Editor while looking at its preset XML. This will help you get a feel for how the values are stored.
This article is getting rather long and there is still more to cover. This covered basic tweens. I’ll save Colors and Filters for my next article. There we will investigate their properties, and so on. The concepts here carry over directly to those but there are more property types there to discuss. Until next time….