Hack 84 Obscure Operators 
Undocumented ActionScript
isn't the only fertile hunting ground for the
curious coder. Discover nonobvious uses for several obscure
ActionScript operators.
Although ActionScript is large,
typical developers can solve 90% of their problems using 10% of the
available features. The remaining ActionScript is used only rarely or
for very specific purposes. Some ActionScript is used rarely because
developers don't know how to use it or it has a
nonobvious use.
Here are a few alternative uses for obscure
ActionScript commands and operators.
Modulo: Clamp and Snap
The modulo operator, %,
returns the remainder of a division operation assuming no decimal
part. For example, the result of 15 modulo 6 is 3, which is the
remainder of 15 divided by 6:
trace(15 % 6); // Displays: 3
What is it good for? Because the result of a%b can
never be greater than b, you can use modulo
division to limit (clamp or
clip) an expression a to the
range 0 to b.
The following animation helps visualize the results. If you attach
the following code to the first frame of a new movie, you will see
that the center point of the ball movie clip never
goes beyond the line at x = 300. The
clamp( ) function, which acts as the
onEnterFrame( ) handler for the
ball clip, uses (this._x +
speed)%300 to set the position of the ball. This limits
ball._x to the range 0 to 300.
function clamp( ):Void {
this._x = (this._x + speed)%300;
}
// Create ball
var ball:MovieClip = this.createEmptyMovieClip("ball",
this.getNextHighestDepth( ));
ball.lineStyle(20, 0x0, 100);
ball.lineTo(0, 1);
ball._x = 0;
ball._y = 200;
// Create line
this.lineStyle(null, 0x0, 100);
this.moveTo(300, 190);
this.lineTo(300, 210);
var speed = 4;
// Set up animation
ball.onEnterFrame = clamp;
Another use of modulo is in snapping a value to a particular multiple
of a number. If x = a%b, you can constrain
x to whole values that are divisible by
b by either adding or subtracting
x%b from x. These lines display
300 and 400:
x = 350;
trace(x - x%100); // Next closest lower number divisible by 100
trace(x + x%100); // Next closest higher number divisible by 100
The following code uses snapping in a simple drag-and-drop example.
The ball movie clip follows the mouse, snapping to
the nearest 30-pixel multiple.
ball.onMouseMove = function( ) {
this._x = _xmouse - _xmouse%size;
this._y = _ymouse - _ymouse%size;
updateAfterEvent( );
};
size = 30;
The dragger.fla file
on this book's web site uses the snapping code to
create a series of tiles that can be arranged only along a grid. If
you run the example and drag and drop the colored squares, you will
see that they snap to the nearest whole position, allowing you to
stack them edge to edge, as seen on the right side of Figure 10-10.

This sort of feature would be useful in:
Flash puzzle games, especially sliding block games and jigsaws Flash tile-based action games (platform games) Flash board games Icon-driven Flash OS-type interfaces (in which you create a site that
looks like an operating system desktop, typically driven by point,
click, and drag-and-drop icons)
Optimize Range Checking
The Math.abs(
) method returns an
expression's absolute value.
Math.abs(5) and Math.abs(-5)
both return 5. Not the most exciting method, but it is an efficient
way to limit a number at both ends of a scale. Simply offset the
scale so that the top and bottom ends of the scale are the same
absolute number (such as -5 and +5 instead of 0 and 10).
Checking the upper and lower limits for the x and y coordinates of a
clip traditionally requires four conditionals. You can hack this down
to two conditionals by moving the origin to the center of the Stage.
In the case of a default Stage size (550 400), the leftmost and
rightmost x positions are -275 and 275 (instead of 0 and 550) and the
topmost and bottommost y positions are -200 and 200 (instead of 0 and
400).
This optimization isn't apparent unless you need
things to run really fast, as in the following
code. It uses setInterval( ) with a
one-millisecond interval to force Flash to run the animation as fast
as it can. The optimized range-checking functions are shown in bold.
function anim(target:MovieClip):Void {
if (Math.abs(target._x) > 275) {
target.xSpeed = -target.xSpeed;
}
if (Math.abs(target._y) > 200) {
target.ySpeed = -target.ySpeed;
}
target._x += target.xSpeed;
target._y += target.ySpeed;
updateAfterEvent( );
}
// Create balls
for (var i = 0; i < 20; i++) {
var ball:MovieClip = this.createEmptyMovieClip("ball"+i,
this.getNextHighestDepth( ));
ball.lineStyle(5, 0x0808AF, 100);
ball.lineTo(0, 1);
ball.xSpeed = Math.random( ) * 10;
ball.ySpeed = Math.random( ) * 10;
this["fastAnim"+i] = setInterval(anim, 1, ball);
}
// Capture screen extents
var temp:String = Stage.scaleMode;
Stage.scaleMode = "exactFit";
var sWidth:Number = Stage.width;
var sHeight:Number = Stage.height;
Stage.scaleMode = temp;
// Move Stage's original origin to center
this._x = sWidth / 2;
this._y = sHeight / 2;
// Mark out Stage
this.lineStyle(undefined, 0x0, 100);
this.beginFill(0xF0FAFF, 100);
this.moveTo(-sWidth, sHeight), this.lineTo(sWidth, sHeight);
this.lineTo(sWidth, -sHeight), this.lineTo(-sWidth, -sHeight);
this.endFill( );
The still image of the output shown in Figure 10-11
doesn't do the effect justice. On a modern computer,
the dots fly quickly around the Stage. If you do not have a high
enough monitor refresh rate, you will not see many of the dots at
all, but rather the trails they leave behind!

On my machine, I see around 15% to 20% faster animation using
optimized range checking with 5-pixel wide circular movie clips. The
speed increase depends heavily on the speed of your computer and the
complexity of the movie clip graphics (remembering that screen
redraw, not the code, is usually the performance bottleneck).
The following code uses the Math.min(
) and Math.max( )
methods to "clip" a value to a
particular range. It skips the conditional expression and simply
forces the value into the desired range. (The code assumes you
haven't shifted the origin as in the previous
example.) It works well when you want to restrict an
object's range of motion, say a puck along a slider,
but not for objects that need to "reverse
course," such as a bouncing ball. Here, the
calculation prevents the movie clip from traveling off stage:
function anim(target:MovieClip):Void {
target._x = Math.max (0, Math.min (target._x + target.xSpeed, 550));
target._y = Math.max (0, Math.min (target._y + target.ySpeed, 400));
updateAfterEvent( );
}
Or, more generally:
function clip(input:Number, minVal:Number, maxVal:Number):Number {
return Math.max (minVal, Math.min (input, maxVal));
}
function anim(target:MovieClip):Void {
target._x = clip(target._x + target.xSpeed, 0, 550);
target._y = clip(target._y + target.ySpeed, 0, 400);
updateAfterEvent( );
}
Optimize Inertial Motion with Bit Shifting
Flash's base unit of
measure is a twentieth of a point (a
"twip"). Flash allows you to define
pixel values with precision down to .05 pixels (smaller increments
are ignored), so drawing a pixel at the point (10.5, 10.5) is legal.
Flash displays graphics at fractional locations using antialiasing.
By blurring the pixel, Flash attempts to give the impression that the
pixel is at the fractional position, even though the video card can
address pixels only at integer positions, such as (10, 10). The
problem is that you can lose detail through the blurring effect, so
it is best to position graphics at whole-pixel intervals.
Inertial motion [Hack #39] is one
of the most oft-used tricks in Flash. However, the standard inertia
algorithm returns fractional pixel values. For example, the following
code creates the ubiquitous ball movie clip and
animates it with an inertial motion toward the mouse position (a.k.a.
the Cheesy Flash Mouse Follower effect):
inertia = function ( ):Void {
this._x -= (this._x - _xmouse) / 4;
this._y -= (this._y - _ymouse) / 4;
trace(this._x)
};
// Create ball
var ball:MovieClip = this.createEmptyMovieClip("ball",
this.getNextHighestDepth( ));
ball.lineStyle(20, 0x0, 100);
ball.lineTo(0, 1);
ball.onEnterFrame = inertia;
Almost none of the traced values (this._x) give an
integer, which means that the clip can appear blurry because it is
positioned on the Stage between physical pixel positions. More
typically, if you are adding inertia effects to drop-down menus or
scrollbars, it will make any associated text fuzzy and wreak havoc
with pixel fonts.
Instead of dividing by 4 in the preceding example (which,
incidentally, is an arbitrary value, chosen because it gives a
pleasing inertial effect at my chosen frame rate of 18 fps), you can
instead bit-shift the value to the right by 2 binary digits via the
following:
inertia = function ( ):Void {
this._x -= (this._x - _xmouse) >> 2;
this._y -= (this._y - _ymouse) >> 2;
trace(this._x)
};
The >> 2 operation tells
Flash to shift the bits in the answer 2 positions to the right,
effectively dividing the result by 22, or
4, using integer division. This is faster than the standard
ActionScript division operator, /, which performs floating-point
division. Now the trace( ) statement displays
integer values only, which keeps your animated content much sharper,
assuming that the content is snapped to the nearest pixel
(View Snapping Snap to Pixels) and all your
graphics are rendered in exact pixel sizes (you can ensure this by
specifying integer values for Width and Height in the Properties
panel).
Final Thoughts
As you can see, there are many ways to use the more obscure features
of ActionScript, which are often more optimized or shorter than
mainstream solutions. It pays to keep an eye out for alternative
ActionScript when performance is at a premium.
|