MollyPages.org
"You were wrong case. To live here is to live." |
Pages /
Database /
Forms /
Servlet /
Javadocs
![]() ![]() |
Some good flash references:
Each timeline has 1 or more layers. Each layer has frames
,
which are like one frame of a movie reel. Like a movie, a series of frames
can be played at speed - and presto ! - we get animation
and movement.
Movies typically play at 24 frames/sec. Flash can play at any frame rate but is typically set to 12-18 fps.
A frame by default has no content. This is like an empty picture reel. This is called a regular frame.
Any frame that has (or can have) content is called a keyframe. (I would have called it content-frame myself but whatever, that's the terminology).
If the frame can have content (but has none to start out with, purposefully), then it's called a blank keyframe. If you draw on a blank keyframe, it becomes a non-empty keyframe.
This is relevant, since in Flash, to add content to any non-keyframe, it is not sufficient to just select that frame and start drawing. One must manually convert that frame to a keyframe first. Else, if one selects as regular frame and starts drawing/adding content to it, the content is added to the first prior keyframe behind that frame.
A new flash document starts with one blank keyframe as Frame #1. If that initial frame is deleted, then one must first create at least one new keyframe before content can be added to that document.
As it moves down the timeline, Flash will keep displaying the content of the last keyframe it encountered until it comes to the next keyframe, whereby it will show the contents of that next keyframe. Animation is done by slightly modifying the content betweeen keyframe to keyframe, much like the 24/second snap shots of a real movie.
This doesn't mean that one must use strong-typing. I personally prefer (for smaller projects, at least), the simpler, traditional JS syntax and shy away from AS extensions (including strong typing).
For larger projects, strong typing is very useful. (remember: the compiler is always your best friend).
Traditional syntax: | var foo =
"hello"; |
AS-extended (optional): | var foo:String =
"hello"; |
Actionscript variables can be declared on any frame. They are only available to the frame in which they are declared and subsequent frames. The code is re-run every time the frame is revisted (in the normal looping movie, the first frame is revisted at the beginning of another loop).
var n = 1; |
movie 'flash_1.swf' // flash 8, total frames: 1, frame rate: 12 fps, 550x400 px frame 0 push 'n', 1 varEquals end // of frame 0 end |
var n = 1; n++; trace(n); |
movie 'flash_1.swf' // flash 8, total frames: 1, frame rate: 12 fps, 550x400 px frame 0 constants 'n' push 'n', 1 varEquals push 'n', 'n' getVariable increment setVariable push 'n' getVariable trace end // of frame 0 end |
Output:2 |
2 is output only once.
The flash player optimizes the timeline, such that, it does not loop over frames, if there is only 1 frame defined. This means that the code in that single frame will be called/execute just once, and not over and over again.
This is non-intuitive and poorly documented. One expects the flash player to keep looping, even over a single frame, at the movie fps setting.
This is the same as the previous example, but with another (albiet empty) keyframe added.
var n = 1; n++; trace(n); |
movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate: 12 fps, 550x400 px frame 0 constants 'n' push 'n', 1 varEquals push 'n', 'n' getVariable increment setVariable push 'n' getVariable trace end // of frame 0 end |
Output:2 2 2 ... |
The flash player now loops across all frames (since there is more than 1 frame) and '2' is printed forever
Note, interestingly, when the flash player loops back to the
first frame, the variables are initialized again and the code
behaves as if we were running the entire code (including
initializers) for the first time. So we don't see 2, 3,
4,...
but rather 2, 2, 2...
.
There is no way to initialize global state just once in a
simple manner. (however, the example below shows how to do this in
a non-simple manner).
gotoAndStop
) or (b) we can put in a check to see if the
variable has been initialized already.
This latter example is shown below.
var n; if (! n) { n = 1; } n++; (n); |
movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate: 12 fps, 550x400 px frame 0 constants 'n' push 'n' var push 'n' getVariable not not branchIfTrue label1 push 'n', 1 setVariable label1: push 'n', 'n' getVariable increment setVariable push 'n' getVariable trace end // of frame 0 end |
Output:2 3 4 5 ... |
The variable n is now initialized just once (if it's not
already initialized). We see the expected sequence
2, 3, 4, 5...
Actionscript, like Javascript has the defined
keyword.
Variables are defined
the first time flash runs through the
loop although they are re-initialized
every subsequent time
through the loop. In the above example, we could also have said:
if (n == undefined) { //(same as saying: ! n)
n = 1;
}
var n = 1; trace (n === _root.n); |
movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate: 12 fps, 550x400 px frame 0 constants 'n', '_root' push 'n', 1 varEquals push 'n' getVariable push '_root' getVariable push 'n' getMember strictEquals trace end // of frame 0 end |
Output:true true ... |
All variables are stored in the per-movie clip _root
global
object.
In Actionscript3, they are stored in the MainTimeline
object, which is the same as stage.getChild(0)
. Generally,
in AS3, static class variables are the recommended way to store global
variables, these can be accessed via
the_classname.the_variable
(since class names have global
scope).
Scope Resolution
Like Javascript, Actionscript has a scope chain. At the
top is _global
, movie clip (for all movie clips),
a per-movie clip global (_root
) is somewhere in the middle
and various sundry objects along the chain (that are not
interesting and/or I don't feel like documenting).
On Frame 1: trace (n); On Frame 5: var n = 1; |
movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate: 12 fps, 550x400 px frame 0 push 'n' getVariable trace end // of frame 0 frame 4 push 'n', 1 varEquals end // of frame 4 end |
Output:undefined 1 1 ... |
As expected, the variable is first defined and put in the
_root
object (via push
) in frame #5 (
the bytecode is 0-indexed so frame #5 shows up as #4).
The first time through, in frame #1 the variable is not defined
so undefined
is printed.
Then flash, gets to frame #5 defines the variable and next time through the loop, frame #1 sees the variable set to the previous value (1 in this case).
Basically, each frame is setting/getting/creating variables
somewhere in the scope chain (by default, the _root
or
MainTimeline
via code in each frame. This is akin
to the following Javascript.
<script> //frame 1 trace(n); //frame 2 var n = 1; //frame 3 n++ //etc. </script>
Note: Layer 2 is above layer 1 in this example.
On Frame 1, layer 2: var n = 100; On Frame 1, layer 1: trace (n); |
movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate: 12 fps, 550x400 px frame 0 push 'n', 100 varEquals end // of frame 0 frame 0 push 'n' getVariable trace end // of frame 0 end |
Output:100 100 ... |
Code in layers are converted in the layer order. On each frame, code on top most layers is executed first and code on bottom layers is executed later.
Note: Layer 2 is below layer 1 in this example.
On Frame 1, layer 1: trace (n); On Frame 1, layer 2: var n = 100; |
movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate: 12 fps, 550x400 px frame 0 push 'n' getVariable trace end // of frame 0 frame 0 push 'n', 100 varEquals end // of frame 0 end |
Output:undefined 100 100 ... |
As expected, the code in frame #1, layer 1 is executed first.
Since n
is undefined
the first time
around, "undefined" is printed. Then frame #2, layer 2 code
runs and initializes n
. Thereafter, the next time
around, this value of n
is printed. (compare this
to the previous example).
var n = 1; n++; function foo() { trace(n); } foo(); |
movie 'flash_1.swf' compressed // flash 8, total frames: 5, frame rate: 12 fps, 550x400 px frame 0 constants 'n', 'foo' function foo () push 'n' getVariable trace end // of function foo push 'n', 1 varEquals push 'n', 'n' getVariable increment setVariable push 0.0, 'foo' callFunction pop end // of frame 0 end |
button_1.onPress = foo; function foo() { trace("button_1 clicked"); } |
movie 'flash_1.swf' compressed // flash 8, total frames: 1, frame rate: 12 fps, 550x400 px frame 0 function foo () push 'button_1 clicked' trace end // of function foo push 'button_1' getVariable push 'onPress', 'foo' getVariable setMember end // of frame 0 end |
Output:button_1 clicked |
When the button is clicked, button_1 clicked
is
output to the console.
Flash has a single-threaded interpreter. Code in frames in executed, frame by frame and (unless stopped), the interpreter loops back to the first frame and starts from the first frame again.
So how are events handled ? Events are asynchronous, in that they can happen anytime the user clicks on a button or types a keystroke or the flash VM received network data. If the flash interpreter is single threaded, and chugging along merrily, from frame to frame, then how does it:
Flash documentation is sorely lacking in this regard. As of Sept. 20, 2008, there is not even a single (!) public adobe/macromedia document that talks about how flash event dispatching is implemented. I've downloaded the tamarin source code and also the gnash open source flash-player source code to investigate this issue. The gnash player code (in particular) is very nicely written.
From what I can gather, from reading the source and by this post (link opens in new window) on the gnash mailing list, this is how things work:
-- The operating system informs the flash player/VM about events such as user click, network actity, etc. These events are internally (by another thread, or the main thread when it's free, or by the operating system, or whatever) put in an event-buffer
-- The main execution thread, periodically looks at the event buffer and invokes functions that (we in actionscript have registered to) handle that event. This polling of the event buffer is done at each frame boundary (before the interpreter moves from the current frame to the next frame) and/or at some fixed interval (say every 10ms), which is an internal setting in the flash player implementation.
Closures in ActionScript 3 are not compatible with Javascript.
var str = "bad dog"; function foo() { var hoopty = "hoopty"; trace(this.str + "; " + hoopty); } var joe = new Object(); joe.str = "i am joe"; joe.foo = foo; joe.foo();
In both ActionScript 2 and Javascript (replace "trace" with "alert" to run this in a browser), this prints/alerts:
i am joe; hoopty
In ActionScript 3, this same example prints:
bad dog; hoopty
It's expected that all of AS2/AS3/Javascript save the local variable
hoopty
via a closure mechanism. That does happen
and so far, well and good.
However, in AS3, note, the problem is that the "this"
pointer is
also getting saved as part of the closure in: function foo() {...}
.
This is incompatible with JS. (AS3 calls capturing the "this" pointer as part of closure, a bound method). However, in Javascript, closures are strictly over local function variables/parameters, in lexical scope. AS3's behavior is extremely non-intuitive and bad, because it completely breaks from what Javascript does. In Javascript, the "this" pointer is never part of any closure and "this" always refers to the object through which a method was invoked. (update: similar to the "=>" operator available in recent versions of Javascript)