|
MollyPages.org
"You were wrong case. To live here is to live." |
Pages /
Database /
Forms /
Servlet /
Javadocs
/
License & Download /
Tutorials /
Cookbook
/
Contact & Hire
|
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)