Introduction
Why use SSWF?
Flash Basics
SSWF Basics
Coding ActionScript in SSWF
How 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?
Introduction
SSWF 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:
- 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.
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.
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 Basics
Flash 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 Basics
Writing 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:
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;
};
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.
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
max = 50;
idx = 1;
n = idx / max;
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 = (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 SSWF
To 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 Statements
An 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 dropped
The 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:
action "push data"
{
string: "hello";
integer: 3;
float: 1.4142;
string: "mysprite";
property: "_xscale";
};
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.
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.:
action "dictionary"
{
"hello"; // element 0
"world"; // element 1
};
Dictionary element indices are zero-based and are accessed by
using a "push data" instruction with a "lookup" type:
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?
Example Flash movie
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 times
The 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?
Example Flash movie
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.