Introduction
Why use SSWF?
Flash Basics
SSWF BasicsHow do I show a bitmap image?
How do I spin a box?
How do I move a spinning box?
How do I fade a shape over time?
How do I make a slideshow?
How do I make a movie link to another Web page when clicked?
IntroductionSSWF is a scripting language that lets one make Flash animations by describing them as text files. After preparing your .SSWF file, the SSWF compiler converts it to a Flash (.SWF) file which can be displayed in a Web browser.
This site was created because although SSWF is very powerful, it can be confusing to learn and deploy its scripting language. The reader is not expected to be an expert Flash user.
If you have used POV-Ray (a script-based raytracing renderer) then many concepts in SSWF will be familiar. Flash (and therefore SSWF) is of course more two-dimensional and has more features related to animation.
Why Use SSWF?Most Flash animations are produced using visual tools such as Macromedia Director, Flash MX, etc. This is reasonable since most artists are comfortable working visually.
A scripting approach is more efficient for those who need to produce animations procedurally; for example, producing slideshows for news sites from picture feeds that arrive frequently. Once a basic animation sequence has been developed, its construction can be expressed as software code and the computer can take over producing subsequent animations -- all you have to do is provide the input data it will display (and that can probably be automated as well). The idea is similar to the batch or macro/scripting facilities in some visual tools but is simply provided in standalone form in the case of SSWF.
SSWF includes a library that lets your programs generate Flash files directly. However, the scripting system nicely handles the problem of prototyping what code your program should generate. Usually, automating a Flash animation comprises these steps:
When writing a script, disassembling the Flash file produced by a visual tool is often handy (for simple tasks, it is often easier to skip using a visual tool). Conversely, even diehard scripters will rarely produce complex shapes/fonts without a visual tool (such as Adobe Illustrator). For this reason, it is good to have a vector editing program and some way to convert its output to SSWF script form. For bitmap images, SSWF refers to them using filename paths.
- Make the animation visually.
- Make a SSWF script version.
- Write program code (usually C/C++) to directly generate the Flash file, using the script as a pseudocode guide.
You can also have your programs emit SSWF script code and then invoke the SSWF compiler to generate the Flash file(s). This is useful when you want to tweak some of the script files separately. Another reason might be to forward the scripts to a second program that modifies them, like the way mailmerge programs replace generic tags like {FIRST_NAME} with text from database records.
Procedural effects within an animation are also easier to code in script format. In a visual tool, one often has to anyway access a scripting facility -- SSWF simply cuts to the chase.
People who like having full control over their Flash file production also find scripting preferable. It is similar to WordPerfect users who like its "Reveal Codes" feature, or POV-Ray users who like knowing precisely what geometry transforms are occurring. Scripts, being text files, can also be easily searched for various items and some things, like globally searching and replacing text, are likewise easy.
Being text, SSWF scripts are easily displayed, exchangeable with and edited by other people. Anyone with a text editor and the SSWF compiler can make Flash movies.
Finally, SSWF is also a good way to learn Flash, as it more greatly exposes the underlying details of the Flash file format. With a script file, you get to see how everything works.
Flash BasicsFlash files (movies) are built from records called tags. A tag is basically a verb token specifying some action along with related data on how to do the action. The SSWF script language somewhat mimics the Flash tags but is not meant to have a 1:1 correspondance. Directly coding Flash files is too onerous due to the format's complexity; SSWF provides a higher and more convenient level of abstraction.
The first tag in a Flash file is the header, which defines the movie's frame rate and default display size. Since Flash elements are usually vector-based, movies can be resized in one's browser (and it gives users more flexibility if you support this feature). Bitmap images not only take more bandwidth to describe, they lose detail when enlarged or zoomed in. If your movie uses a lot of bitmaps, you might want to consider a pure bitmap animation format such as QuickTime, MPEG, AVI, etc.
There are two kinds of tags. Definition tags let you describe reusable entities such as shapes, fonts and bitmaps, while control tags manipulate these entities to produce the animation. Definition tags often refer to other definition tags, e.g., a bitmap fill style referring to a bitmap.
Since Flash is a streaming format, all the definition tags needed by a control tag must be defined first. By the time the Flash player encounters a control tag, it can assume that any needed objects have been created.
Animations in Flash can be broken up into multiple, simultaneously running movie clips. This matches the movie- or song-production metaphor where a director defines several tracks of audio or video and plays them together to produce the full movie. Each clip is called a sprite and a sprite tag is used to define each one.
By default, only one track exists at the start, which is the main sequence. If your movie is complex, it is usually good form to have the main sequence spawn sprites, each one handling its part of the overall movie.
The display list is the set of currently visible objects, and it is empty at the beginning of a Flash file. The SSWF show frame command is used to define a frame of animation as being the contents of the display list up to that point. In Flash parlance, display list entities are called characters, as if they were actors in a movie.
Sprites and other objects are placed on and removed from the display list as required. The same object can be placed multiple times, visually duplicating it, or it can be removed and placed elsewhere, making it appear to move. When an object is placed, you can specify a transform matrix governing its location, size, rotation, etc. Placed objects also have a depth controlling whether they appear behind or in front of other placed objects.
Sprites provide a divide-and-conquer strategy: the main control tag sequence can place a sprite on the display list, and then the sprite can place and remove whatever objects it wants. Removing a sprite from the display list causes its animation to end.
Sprites also make grouped transforms easy. Each placed object in a sprite has its own transform (location, size, etc.). which is concatenated with the transform of the sprite itself. So if you move, scale, or rotate a sprite, it automatically applies that change to all of its child objects.
SSWF BasicsWriting SSWF script follows a few rules. SSWF is based on a C-like language so if you know C it will not be too hard.
Each SSWF tag or command follows a keyword-and-particulars form. The syntax of what follows the keyword depends on what keyword was chosen. Since most tags are built out of smaller tags, the learning curve rests on familiarizing oneself with the syntax of the smaller tags. Below is a sample SSWF fill style tag:
fill style "fill_mybitmap" { img : image_mybitmap; type: "clipped"; matrix { scale: 20, 20; }; };The keywords img, type, and scale specify simple datums and require only a colon (:) between them and the related data.
The fill style and matrix keywords specify compound structures and use curly braces ( { and } ) to contain their member elements. Both simple and compound statements must be terminated with semicolons (;). One can omit a semicolon if a closing brace immediately follows it, but it is recommended form to always include them.
The fill style keyword also has a string label preceding the opening curly brace; this label acts to name the fill style for later use by another object.
Some script keywords do not correspond to Flash tags at all; these are like POV-Ray macros in that they let you easily generate large quantities of Flash tags using loops and other control structures. Here's an example of a simple loop:
This loop moves and resizes all the display list characters on the second depth level. With each step through the loop, it invokes "show frame" to define the step as a presentable animation frame.max = 50; for(idx = 1; idx <= max; idx + 1) { replace object { depth: 2; matrix { scale: 1.0 +(idx*5.0/max), 1.0 +(idx*3.0/max); translate: idx*5, idx*1; }; }; show frame; };Such loops increase the size of the Flash file and are meant mostly as a convenience to avoid having to manually script repetitive tag sequences. Beyond a certain point (e.g., 100 tags) it is more efficient to use action scripts (although those are understandably more cumbersome to write). More advanced effects, however, are only possible (or are overwhelmingly efficient) with action scripts.
To make character motions easier, the replace object keyword can be used instead of doing a remove-and-place. When only a depth level is specified, the transform is simply applied to all of the characters at that depth and they are all implicitly replaced with transformed versions of themselves.
SSWF uses pixel coordinates instead of Flash's default twips units (which are twenty times smaller). Since SSWF allows floating-point numbers, it is easy to perform subpixel operations without resorting to a finer coordinate system like twips. The coordinate system has the origin in the upper left corner of the movie frame.
To define an "entrypoint" to your movie, use the sequence "main" statement. All commands issued within this sequence will define the root causality of the movie. Normally, you'll define the various elements (definition tags) your animations need, then the sprites that use them, and in the main sequence, you'll instantiate the definitions and place the sprites on the display list.
Numeric variable types are defined implicitly by the values assigned to them. For example, the following expression
will put zero instead of 1/50 into n, because both idx and max are integers. To force n to be floating point, do this:max = 50; idx = 1; n = idx / max;
max = 50; idx = 1; n = (0.0 + idx) / max;Rotations are in radians. To rotate an object about its origin 180 degrees, use the predefined constant "pi" (3.1415926).
The "scale" keyword accepts one value if you want to do a uniform scaling; e.g., "scale: 10;" is equivalent to "scale: 10, 10;".
Coding ActionScript in SSWFTo really unlock the full potential of Flash, you'll want to use ActionScript. This is the Flash equivalent of the PostScript page description language, and it works in a similar manner. With ActionScript, you can access sprite properties and modify them in interesting ways.
SSWF is a lightweight wrapper around Flash tag syntax and so SSWF ActionScript coding looks similar to what you would see if you disassembled the compiled animation file.
Action scripts are defined in sprites and are performed at the next occurring "show frame" instruction. A simple action script modifies the shape or other properties of the sprite for just one frame, while a more complex script can loop and generate a movie clip, handle button events, etc.
The ActionScript language offers basic facilities to manipulate variables and the stack, and to perform conditional processing.
SSWF's script facilities make coding ActionScript a little easier. Defining constants, for example, can be done using C-like expressions that reference SSWF variables.
Action Blocks and Action StatementsAn action block is a related sequence of ActionScript instructions that are meant to be executed as a single unit. Once an action block is finished, a "show frame" can be issued to perform the block's code. In SSWF, a block is defined by issuing a "do action" statement. In simple sprites, one action block is usually sufficient.
Each instruction statement inside an action block starts with the keyword "action"; e.g.:
do action { action "push data" { integer: 3; float: 1.4142; }; action "multiply"; // 3 * 1.4142 is now on the top of the stack. };Data Types
ActionScript supports booleans, integers, floats, strings, objects, and arrays. Objects are similar to arrays except that each element (object member) requires a name. Datums can exist in named variables or on the stack. There is also a four-element register array for temporary value storage.
Datums can be equal even if they are of different types; e.g., the string "3" is equal to integer 3 and the float 3.0. If you want to test for datums' type equality as well as content matching, use the 'strictly equals' instruction.
String value declarations need to watch out for special characters (like double quotemarks) because, like C, they are "escaped" using the backslash character. The backslash itself is defined using '\\', not '\'.
Sprite Properties
These are some of the properties that an ActionScript can access:
x Horizontal position, in pixels. y Vertical position. xscale Horizontal scale, in percentage (e.g., 500 = 5x) yscale Vertical scale rotation Rotation angle, in degrees counterclockwise. alpha Transparency percentage (0 = opaque) visibility Whether sprite is visible or hidden width Width of sprite height Height of sprite current frame The current frame number number of frames Total frame count in sprite target Full pathname of sprite name Name of sprite url URL associated with the sprite quality Display quality (0, 1, or 2) x mouse Horizontal position of mouse pointer y mouse Vertical position of mouse pointer drop target Object over which sprite was last droppedThe Stack
Like PostScript, the stack is used to store instruction operands, and its critical to push operands onto the stack in the correct order. Even if you use variables, you must still push their values onto the stack in order to use them as instruction arguments. This is what makes Flash safe -- there's no way to store things at a particular memory address. Each action block, upon finishing execution, should leave the stack empty. Flash v6+ players now explicitly empty the stack for you.
You push values onto the stack using the "push data" instruction. You can push one or several values at a time. Each value needs to have its type specified. Example:
The last datum pushed in the above example is a sprite property identifier. It is normally preceded by a sprite name because those two things are required to access a sprite's property.action "push data" { string: "hello"; integer: 3; float: 1.4142; string: "mysprite"; property: "_xscale"; };You can save time and bandwidth by using as few "push data" instructions as possible -- operands will just sit on the stack until later instructions consume them, no matter how far down they are in the action block. However, it makes the block less readable since the operands required for late instructions are defined far apart. In some situations it's unavoidable; the best thing to do is to comment liberally.
The other stack manipulations are "duplicate" and "swap", which respectively copies the topmost element (so a stack of '1 2 3' is now '1 2 3 3') and swaps the two top elements (so '1 2 3' becomes '1 3 2'). Unlike PostScript, there is no 'rot' (rotate stack) instruction.
Dictionaries
Also known as the constant pool, a dictionary is just a list of often-used string values. It is defined at the start of a frame and is available to every function in the frame. Without dictionaries, repeated strings would consume more memory than they have to. The SSWF compiler does not create dictionaries automatically. To declare a dictionary, use the "dictionary" statement, e.g.:
Dictionary element indices are zero-based and are accessed by using a "push data" instruction with a "lookup" type:action "dictionary" { "hello"; // element 0 "world"; // element 1 };
action "push data" { lookup: 0; // "hello" gets pushed lookup: 1; // "world" gets pushed };Apparently you can redefine dictionaries within the same action block but realworld examples are rare.
Functions
Functions let you compartmentalize computations into reusable areas. They are defined using the 'function' keyword inside an action block and are called using the 'call function' instruction.
Unlike Postscript, function arguments are not taken from the stack but passed in through named variables. SSWF lets you define these argument variables in C fashion:
function "foo(n)" { action "push data" { string: "n"; }; action "get variable"; // The value of the function arg 'n' is now on the stack. action "push data" { float: 2.5; }; action "multiply"; action "return"; };The above function multiplies its argument by 2.5 and leaves the result on the stack for the caller to read.
Calling a function requires pushing any arguments, then the number of arguments the function takes, and finally the function's name, like this:
action "push data" { float: 3.0; integer: 1; string: "foo"; }; action "call function";Upon executing which, the value 7.5 would be on the stack.
How do I show a bitmap image?First, prepare your bitmap image in 32-bit Targa (.TGA) format. We'll assume that it measures 640 x 480 pixels and is stored at the folder C:\My Pictures\MyBitmap.tga.
The following .SSWF file shows how to display it. For simplicity we will have the bitmap take the entire area of a non-animated movie.
screen_width = 640; screen_height = 480; rectangle "screen" { 0, 0, screen_width, screen_height }; edges "edges_mybitmap" { // Define a path that the bitmap will be clipped against. screen_width, 0; 0, screen_height; -screen_width, 0; 0, -screen_height; }; image "image_mybitmap" { // Define an image entity. The 'jpeg' keyword // means that the image will be stored in JPEG format // inside the final Flash movie. We use forward slashes even // on Windows because backward slashes are interpreted as // escape sequence delimiters. jpeg : "C:/My Pictures/MyBitmap.tga"; }; fill style "fill_mybitmap" { // Define a fill style based on the bitmap. img : image_mybitmap; type: "clipped"; // Unlike other coordinates, SSWF leaves bitmap dimensions // in Flash's default twips units, so we have to scale up 20x. matrix { scale: 20, 20; }; }; shape "shape_mybitmap" { // Now define a shape filled with the bitmap fill style. rect { -1, -1, screen_width+1, screen_height+1 }; fill_mybitmap; move: 0, 0; // The 'rect' keyword only defines the shape's // bounding rectangle; for the actual shape outline we need our edge list. edges_mybitmap; }; sprite "sprite_mybitmap" { place object { id: shape_mybitmap; depth: 1; }; }; sequence "main" { frame_rate = 10; compress = false; screen; set background color { sswf.col.black }; image_mybitmap; fill_mybitmap; shape_mybitmap; sprite_mybitmap; place object { depth: 1; id: sprite_mybitmap; }; show frame; };
How do I spin a box?This script shows how to make a lone cyan box spin in the center of the movie. We make a box shape of unit size (1 pixel square) and scale it to be bigger. Unit size shapes are handy because one can reliably calculate displayed sizes solely from transforms.
screen_width = 500; screen_height = 200; rectangle "screen" { 0, 0, screen_width, screen_height }; fill style "fill_black" { sswf.col.black; }; fill style "fill_cyan" { color cyan { 0, 1, 1, 1 }; }; shape "shape_box" { // Make a small cyan box. // Make the origin in the center of the box. rect { -0.5, -0.5, 0.5, 0.5 }; fill_cyan; // The shape is just four straight edges. // First, move to upper left of the box. move: -0.5, -0.5; // Since the box is unit-sized, the edges // need to have unit length. edges { 1, 0; 0, 1; -1, 0; 0, -1; }; }; sprite "sprite_spinning_box" { // This sprite implements a movie clip to show // the spinning box. place object { id: shape_box; depth: 1; }; max = 20; for(idx = 0; idx < max; idx + 1) { t = (0.0 + idx) / max; replace object { depth: 1; matrix { rotate: pi * t; scale: 50, 50; } }; show frame; }; }; sequence "main" { frame_rate = 15; compress = false; screen; set background color { sswf.col.black }; shape_box; sprite_spinning_box; // Put the anim clip of the spinning box into // the center of the movie. place object { depth: 1; id: sprite_spinning_box; matrix { translate: screen_width/2, screen_height/2; }; }; };
How do I move a spinning box?The script below takes our lone cyan box and moves it across the display frame while it is spinning. To make things more interesting, we have squeezed the box to be a rectangle and made its length oscillate.
The key to more complex motion is to apply transformations in the correct place -- the spinning and length oscillation are handled by the box sprite itself, while its motion across the screen is done by the main sequence. We could have the sprite move the box too, but that would "tightly couple" such motion into the sprite making it harder to re-use just the spinning box. One often needs to distinguish between basic reusable behaviors and more "one-off" behavior such as making a character walk along a path. It is also good form to make "basic" sprites and higher-level "directing" sprites which simply act to control the basic sprites.
screen_width = 500; screen_height = 200; rectangle "screen" { 0, 0, screen_width, screen_height }; fill style "fill_black" { sswf.col.black; }; fill style "fill_cyan" { color cyan { 0, 1, 1, 1 }; }; shape "shape_box" { // Make a small cyan box. // Make the origin in the center of the box. rect { -0.5, -0.5, 0.5, 0.5 }; fill_cyan; // The shape is just four straight edges. // First, move to upper left of the box. move: -0.5, -0.5; // Since the box is unit-sized, the edges // need to have unit length. edges { 1, 0; 0, 1; -1, 0; 0, -1; }; }; sprite "sprite_spinning_slimbox" { // This sprite implements a movie clip to show // the spinning rectangle. We use the sin() function // to smoothly vary the rectangle's length. place object { id: shape_box; depth: 1; }; max = 20; for(idx = 0; idx < max; idx + 1) { t = (0.0 + idx) / max; replace object { depth: 1; matrix { rotate: 2.0 * pi * t; scale: 10, 80 * sin(t * pi); } }; show frame; }; }; sequence "main" { frame_rate = 25; compress = false; screen; set background color { sswf.col.black }; shape_box; sprite_spinning_slimbox; place object { depth: 1; id: sprite_spinning_slimbox; matrix { translate: screen_width/4, screen_height/2; }; }; // This loop moves the spinning rectangle across // the display frame. After placing the sprite at depth 1, // we simply translate the entire depth more horizontally // with each step. max = 50; for(idx = 0; idx < max; idx + 1) { replace object { depth: 1; matrix { translate: screen_width/4 + 300.0 * idx / max, screen_height/2; }; }; show frame; }; };Both the sprite and the main sequence invoke the "show frame" instruction (in fact, sprites always have an implicit "show frame" terminating them whether you specify it or not). Why are both needed?
It helps to consider "show frame" not as displaying a frame but simply marking the current state of the display list as being the output of the next drawn frame. Each sprite does a "show frame" to tell the player that its object placements (onto its part of the display list) constitute a valid animation step. The main sequence does the same for its object placements. Once the player has processed all the sprites and the main sequence, the display list is in the desired state and is correctly output.
Since the loops are just preprocessor directives, our resulting Flash file actually contains this:
define shape "shape_box" define sprite "sprite_spinning_slimbox" { place object { shape_box } show frame replace object { shape_box, slight rotation } show frame replace object { shape_box, greater rotation } show frame replace object { shape_box, even greater rotation } show frame ... sixteen more times } // main sequence starts here place object { sprite_spinning_slimbox at frame center } show frame replace object { sprite_spinning_slimbox slightly moved } show frame replace object { sprite_spinning_slimbox moved more } show frame ... forty-seven more timesThe sprite definition doesn't "do" anything except define the sprite. It isn't until the player encounters the "show frame" instructions in the main sequence that things get drawn.
When the main sequence places the sprite, the player then consults the sprite's definition to let the sprite modify the display list. The sprite places the box shape and calls "show frame" to indicate that the box is all it is contributing to the current animation step. The player then pops back out to the main sequence and encounters its "show frame" and since there are no higher levels, the animation step is rendered.
On the next object placement (or replacement, rather) in the main sequence, the sprite is positioned a little to the right. When the player consults the sprite definition, any geometry in the sprite will be modified by this displacement. Since we are now processing animation step #2, the player starts reading the sprite past its previous invocation of "show frame" and the second step of the sprite (which places the box with a slight rotation) occurs.
Eventually all twenty steps in the sprite are used even though the main sequence is still less than halfway through its fifty steps. The player simply loops back to the start of the sprite so the rectangle appears to spin continually while it moves across the frame.
If you omit "show frame" from the main sequence, you'll wind up with the rectangle spinning at the far right of the display frame. This happens because the player is being told "Move the spinning box here, and then move it farther, farther still, etc." all in one animation step. The sprite won't appear because the player cannot conclude the display list until the main sequence issues a "show frame" or comes to an end. By the time it does, it has positioned the sprite all the way to the right. The player restarts the main sequence, and the main sequence again moves the sprite (inefficiently) to the far right in a single animation step, while the sprite rotates the box correctly for each step. Instead of move-rotate-draw we wind up with move-all-the-way-to-the-right-rotate-draw. Without "show frame" in the main sequence, each sprite replacement winds up overriding the previous replacement without drawing anything.
How do I fade a shape over time?The script below shows how to encode ActionScript to make our lone cyan box fade in and out.
The sprite sprite_fading_box actually performs the whole animation since it issues "previous frame" instructions causing it to loop. The instruction actually causes the current frame to be replayed, but since the ActionScript code is incrementing a frame counter variable which controls the box's transparency, the current frame changes its appearance to what appears to be the next frame of animation.
ActionScript can appear daunting because it is low-level, similar to machine language. It also uses a stack-like operating context like PostScript; it's easy to make script that reads like RPN (reverse Polish notation), although I try to minimize that here. SSWF does not currently define a high-level C-like interface to ActionScript; instead, you need to break apart C-style instructions such as "x = n + 2" into "push 2, push 'n', get var, add, push 'x', set var" and so on.
screen_width = 500; screen_height = 200; rectangle "screen" { 0, 0, screen_width, screen_height }; fill style "fill_black" { sswf.col.black; }; fill style "fill_cyan" { color cyan { 0, 1, 1, 1 }; }; shape "shape_box" { // Make a small cyan box. // Make the origin in the center of the box. rect { -0.5, -0.5, 0.5, 0.5 }; fill_cyan; move: -0.5, -0.5; edges { 1, 0; 0, 1; -1, 0; 0, -1; }; }; sprite "sprite_box" { place object { id: shape_box; depth: 1; }; }; sprite "sprite_fading_box" { // This sprite implements a movie clip to show // a box fading from transparent to opaque and back again. fade_max = 50; replace object { "n"; // We name this sprite so we can refer to it later. id: sprite_box; depth: 1; matrix { scale: 50; }; }; // This action script block sets up some initial data. do action { // Declare a list of often-used strings; in our // case, the names of the iteration variable and // the sprite's name. Dictionary element indices are // zero-based. action "dictionary" { "i"; "n"; }; // Set the sprite's initial transparency. action "push data" { lookup: 1; // "n" property: "_alpha"; // "alpha" integer: 0; // = 0 }; action "set property"; // Set our frame counter to zero. action "push data" { lookup: 0; // "i" integer: 0; // = 0 }; action "set variable"; // The above two steps would be more efficiently // encoded as "push data { alpha stuff, counter stuff }" // and then "set variable, set property", but as // the stack contents get longer it gets harder to // tell which actions are using which stack elements. // We need a function that returns a number's // absolute value, so declare it here. Note that all // this does is _define_ the function, it does not // perform it. function "abs(a)" { // Returns the absolute value of . // Flash is very compact and things like abs() // are considered "high-level" functions. SSWF // apparently provides math functions but it's // also nice to see how they can be coded directly. action "push data" { string: "a"; }; action "get variable"; action "duplicate"; action "push data" { float: 0; }; action "less than"; action "if true" { "negate"; }; action "return"; label { "negate" }; action "push data" { float: -1; }; action "multiply"; action "return"; }; // Although a function takes named arguments, // it does not return them. The return data is // whatever is left on top of the stack. This makes // it possible to easily return multiple values, however. // Now we define a function that modifies the // sprite's transparency (alpha level). function "fade" { // Our fade function maps the frame index (i) // to a 0 .. 100 .. 0 range, causing the sprite // to fade in, then fade out. // We push a reference to the sprite's alpha property // first because the replacement property value takes // a long set of steps to compute and must be pushed // onto the stack afterwards. If we wanted to group // the property ref with the "set property" command // way below (to increase readability), the stack // operands would be out-of-order because we would // have: // value "n" prop:"alpha" // instead of: // "n" prop:"alpha" value // // and the only way to keep things straight would be // to do a push-swap, push-swap. action "push data" { lookup: 1; property: "_alpha"; }; // "ts" = top of stack. action "push data" { lookup: 0; }; action "get variable"; // ts = i. action "push data" { float: 200.0 / fade_max; }; action "multiply"; // ts = i * 200 / fade_max = 0 .. 200 action "push data" { float: -100; }; action "add"; // ts += -100 = -100 .. +100 action "push data" { integer: 1; string: "abs"; }; action "call function"; // ts = 100 .. 0 .. 100 action "push data" { float: -100; }; action "add"; // ts = 0 .. -100 .. 0 action "push data" { integer: 1; string: "abs"; }; action "call function"; // ts = 0 .. 100 .. 0 action "set property"; // alpha = ts // Return true if (i < fade_max). // We compute a boolean value and leave it on // the stack for the caller to process. action "push data" { integer: fade_max; lookup: 0; }; action "get variable"; action "equal"; action "logical not"; action "return"; }; }; // This "show frame" enacts the action script above, // which sets up initial conditions. We delay for // another frame because the loop below continues by // jumping to the previous frame, and we don't want it // to jump back to the initialization frame, since that // would cause the animation to appear halted. show frame { 2 }; // This action block loops and repeatedly calls our // fade() function to change the sprite's transparency. do action { action "push data" { string: "i"; }; action "duplicate"; // (void) i++ action "get variable"; action "increment"; action "set variable"; action "push data" { integer: 0; string: "fade"; }; action "call function"; // (bool) (*f)() action "if true" { "repeat"; }; // if (*f)() then goto repeat action "next frame"; // Process the next frame action "branch" { "done" }; // Carry on label { "repeat" }; action "previous frame"; // !(*f)(): stay in loop label { "done" }; action "play"; }; show frame; }; sequence "main" { frame_rate = 30; compress = false; screen; set background color { sswf.col.black }; shape_box; sprite_box; sprite_fading_box; replace object { "n"; depth: 2; id: sprite_fading_box; matrix { translate: screen_width/2, screen_height/2; }; }; show frame; };
How do I make a slideshow?A slideshow is a great place to start with Flash because it covers the essence of animation, which is to show one frame after another and transition between them. Also, by using color transforms, we can do nice fading transitions without ActionScript. Slideshows are great for simple ads or presentations, and with a bitmap editor you can add slides containing text to help annotate things.
For the sake of simplicity, our slides will be bitmaps of equal size, which is what most people have in mind. If you have a bunch of photos taken with a digicam and just want to stitch them together into a Flash clip, then this tutorial will do that (but you should downsample them first to fit on the screen; say, to 320 x 240 pixels).
Our slideshow script will offer customizable transition timing (how long it takes one slide to fade away and reveal the next) and presentation timing (how long, after a slide is shown, it continues to remain visible before the next one fades in). It also offers a definable border thickness.
You can make movies by dispensing with transitions and setting the presentation delay to a tiny fraction of a second (or remove the delay and let the clip's frame rate set the speed). However, JPEG bitmap storage (which is what we're using) can't match QuickTime or other true pixel-based movie file formats. For short clips or small ones, it might not matter, but you wouldn't want to make a real pixel-based movie using Flash.
fps_ = 60; // Our frame rate. transition_length = 1.0; // Time to transition one slide, in seconds. display_length = 2.0; // Time to show a frame, in seconds. // The dimensions of our picture data. pic_width = 320; pic_height = 240; // Optional border thickness, in pixels. // Set to zero if you don't want a border. border_width = 2; // Our slideshow's size is the picture size plus // any border width. screen_width = pic_width + (border_width * 2); screen_height = pic_height + (border_width * 2); rectangle "screen" { 0, 0, screen_width, screen_height }; // Define shape paths for the screen and the pictures. edges "edges_screen" { screen_width, 0; 0, screen_height; -screen_width, 0; 0, -screen_height; }; edges "edges_pic" { pic_width, 0; 0, pic_height; -pic_width, 0; 0, -pic_height; }; // Load up our picture files. // Our method here is to have a folder holding all the slides, // and to give each slide (a JPEG file) a simple numbered filename. image "img_01" { jpeg : "slides/01.jpg"; }; image "img_02" { jpeg : "slides/02.jpg"; }; image "img_03" { jpeg : "slides/03.jpg"; }; // Make fill styles for each image. fill style "fill_01" { img : img_01; type: "clipped"; matrix { scale: 20, 20; }; }; fill style "fill_02" { img : img_02; type: "clipped"; matrix { scale: 20, 20; }; }; fill style "fill_03" { img : img_03; type: "clipped"; matrix { scale: 20, 20; }; }; // Make shapes for each image by containing the fills. shape "shape_01" { rect { -1, -1, pic_width+1, pic_height+1 }; fill_01; move: 0, 0; edges_pic; }; shape "shape_02" { rect { -1, -1, pic_width+1, pic_height+1 }; fill_02; move: 0, 0; edges_pic; }; shape "shape_03" { rect { -1, -1, pic_width+1, pic_height+1 }; fill_03; move: 0, 0; edges_pic; }; // Make sprites for each shape. sprite "sprite_01" { replace object { id: shape_01; depth: 1; }; }; sprite "sprite_02" { replace object { id: shape_02; depth: 1; }; }; sprite "sprite_03" { replace object { id: shape_03; depth: 1; }; }; // Make a sprite to present the slideshow. sprite "sprite_anim" { // Convert our presentation timings into frame counts. transition_frames = transition_length * fps_; display_frames = display_length * fps_; // Place and show the first picture and wait. // To make the border, we simply shift the picture by the border width. place object { id: sprite_01; depth: 10; matrix { translate: border_width, border_width; }; }; show frame { display_frames }; // Place the second picture behind the first. place object { id: sprite_02; depth: 9; matrix { translate: border_width, border_width; }; }; // Make the first picture fade out, gradually revealing the second. // We do this by placing a color transform at the same depth // as the first picture and lowering the color's alpha component. for(idx = 0; idx < transition_frames; idx + 1) { replace object { depth: 10; color transform { scale: 1, 1, 1, 1.0 - (idx * 1.0 / (transition_frames - 1)); }; }; show frame; }; // Remove the first picture. Even though it is has been // faded to invisibility, leaving pictures around will // eventually slow the Flash player to a crawl. remove { depth: 10; }; // Let the second picture be visible for a while. show frame { display_frames }; // Repeat the above steps for the third picture. // Notice that the placement depth goes down by one each time. // Therefore, the starting depth should be at least equal to // the number of pictures in the slideshow. place object { id: sprite_03; depth: 8; matrix { translate: border_width, border_width; }; }; for(idx = 0; idx < transition_frames; idx + 1) { replace object { depth: 9; color transform { scale: 1, 1, 1, 1.0 - (idx * 1.0 / (transition_frames - 1)); }; }; show frame; }; remove { depth: 9; }; show frame { display_frames }; // Eventually all the slides are shown. // To make for nice looping, we show the first slide // again, this time placing it underneath the last slide // to get the proper transition from last to first. place object { id: sprite_01; depth: 7; matrix { translate: border_width, border_width; }; }; for(idx = 0; idx < transition_frames; idx + 1) { replace object { depth: 8; color transform { scale: 1, 1, 1, 1.0 - (idx * 1.0 / (transition_frames - 1)); }; }; show frame; }; remove { depth: 8; }; // Note: don't remove the first slide (depth 7) // here because then there will be nothing in the // display list, and the implict 'show frame' at // the end of the sprite will cause a full view of // the background color to appear before the show // starts again, causing an annoying flicker. // The player will reset the display list anyway. }; sequence "main" { frame_rate = fps_; compress = false; screen; // Set the background color. // This also defines the border color (if you are using a border) // because the pictures are simply shifted to reveal a thin // strip of background along the edges. set background color { sswf.col.black }; img_01; img_02; img_03; fill_01; fill_02; fill_03; shape_01; shape_02; shape_03; sprite_01; sprite_02; sprite_03; sprite_anim; replace object { id: sprite_anim; depth: 1; }; };
How do I make a movie link to another Web page when clicked?With normal images (like GIF or JPEG), making them "clickable" simply requires wrapping the IMG tag in your HTML code with an A HREF= tag or by referencing a MAP tag. Sadly, this won't work with the OBJECT or EMBED tags used to place Flash movies, because the Flash player overrides the mouse's behavior when the pointer is over the movie. So we have to insert the behavior into the movie itself. Below is a minimal script that shows an empty blue-colored frame and then adds an invisible button to make it clickable.
For flexibility, we pass the link's URL and target frame to the compiled Flash movie as arguments from the containing HTML page. For example, if you placed the movie inside a subframe but wanted the visted URL to take the entire browser window, you would set your EMBED tag's SRC attribute to something like this:
mymovie.swf?url=http://sswf.m2osw.com&target=_top
To make the linked URL appear in the same frame as the movie, just use:
mymovie.swf?url=http://sswf.m2osw.com&target=_self
fps_ = 30; // Our frame rate. screen_width = 250; screen_height = 60; rectangle "screen" { 0, 0, screen_width, screen_height }; edges "inside" { screen_width, 0; 0, screen_height; -screen_width, 0; 0, -screen_height; }; fill style "fill_content" { sswf.col.blue; }; Include a "stub" fill style for the invisible button, since the shape that defines the button's active area cannot be declared without a fill. fill style "fill_stub" { sswf.col.cyan; }; This is the shape of our content, which also covers the entire movie frame. shape "shape_content" { rect { -1, -1, screen_width+1, screen_height+1 }; fill_content; move: 0, 0; inside; }; sprite "sprite_content" { place object { id: shape_content; depth: 1; }; }; This is the shape of our button. For simplicity, we make it cover the entire movie frame. shape "shape_link" { rect { -1, -1, screen_width+1, screen_height+1 }; fill_stub; move: 0, 0; inside; }; button "button_link" { This is the button. The state block(s) below tell the Flash player what to display depending on what the mouse pointer is doing. state { flags: 0x08; // Hit test; use our button's shape depth: 1; id: shape_link; }; // What to do when the button is clicked. // We just push the passed-in variables from the HTML page // (url and target) and invoke the player's url command. action "push data" { string: "/:url"; }; action "get variable"; action "push data" { string: "/:target"; }; action "get variable"; action "url"; }; sequence "main" { frame_rate = fps_; screen; set background color { sswf.col.white }; shape_link; shape_content; sprite_content; button_link; place object { depth: 1; id: sprite_content; }; place object { depth: 1; id: button_link; }; };
Latest News
Sep 30/2004: Added simple URL tutorial. Sep 10/2004: Added "Why use SSWF?" section and some Flash movies. Sep 5/2004: Added slideshow tutorial. Aug 23/2004: Added fade tutorial. Aug 12/2004: Added box tutorials. Aug 9/2004: Site started.