I’ve been interested in creating a rolling shape type of sprite with ActionScript Physics Engine for use in a game. —A shape that could be moved around a landscape by rolling a weighted wheel particle around inside it. While building this, I discovered that the spring constraints in APE are really quite soft; requiring the diagonal constraints that support the structure to be doubled up. The result is a fairly stable structure that has quite a few more points than I’d like it to have.
As with most APE models, the less stable the model is, the more fun you can have with it. With this, it’s possible to run the elliptical scaffold over the obstacle, and get it airborne. As soon as the wheel particle looses contact with the scaffold, the model begins to oscillate.
To use: Click on the swf, and use the left/right arrows on your keyboard to control the wheel particle inside the scaffold. Source below or download source files.
main.as:
package
{
import flash.display.Sprite;
import flash.events.Event;
import flash.ui.Keyboard;
import flash.events.KeyboardEvent;
import org.cove.ape.*;
public class Main extends Sprite
{
private var defaultGroup:Group;
private var eScaffold:EllipticalScaffold;
private var outerPoints:Array; // points that define the outer ellipse
private var innerPoints:Array; // points that define the inner ellipse
private var wheel1:WheelParticle;
public function Main()
{
stage.frameRate = 30;
APEngine.init(1/4);
// set the default diplay container
APEngine.container = this;
APEngine.addMasslessForce(new Vector(0,1));
defaultGroup = new Group();
defaultGroup.collideInternal = true;
APEngine.addGroup(defaultGroup);
init();
}
public function init():void
{
drawFloor();
drawWheel();
drawObstacles();
eScaffold = new EllipticalScaffold(defaultGroup);
eScaffold.drawOuterEllipse(200, 50, 50, 40, -20, 20);
eScaffold.drawInnerEllipse(195, 80, 40, 30, -20, 20);
eScaffold.createScaffold();
stage.addEventListener(KeyboardEvent.KEY_DOWN, key_pressed);
stage.addEventListener(KeyboardEvent.KEY_UP, key_released);
stage.addEventListener(Event.ENTER_FRAME, run);
}
private function drawObstacles():void
{
var bump4:CircleParticle = new CircleParticle(480, 420, 100, true, 1, 0, 1);
defaultGroup.addParticle(bump4);
}
private function drawFloor():void
{
var floor:RectangleParticle = new RectangleParticle(260, 370, 600, 50, 0.0, true, 10, 0.4, 0.55);
floor.setStyle(0, 0, 0, Math.random() * 0x111111);
defaultGroup.addParticle(floor);
}
public function drawWheel():void
{
// WheelParticle(x, y, radius, fixed, mass, elasticity, friction, traction)
wheel1 = new WheelParticle(160, 210, 70, false, 10, 0.1, 1, 1);
defaultGroup.addParticle(wheel1);
}
// Events //////////////////////////////////
function key_pressed(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.RIGHT)
{
wheel1.angularVelocity = 1.2;
}
if (event.keyCode == Keyboard.LEFT)
{
wheel1.angularVelocity = -1.2;
}
}
private function key_released(event:KeyboardEvent):void
{
wheel1.angularVelocity = 0;
}
private function run(e:Event):void
{
APEngine.step();
APEngine.paint();
}
}
}
EllipticalScaffold.as:
package
{
import org.cove.ape.*;
/*
* EllipticalScaffold
*
* To create an EllipticalScaffold in ActionScript Physics Engine:
* 1. Draw the outer an inner ellipses by calling drawOuterEllipse() & drawInnerEllipse()
* 2. Join the 2 ellipses by calling createScaffold()
*/
public class EllipticalScaffold
{
private var outerPoints:Array; // point coordinates that define the outer ellipse
private var innerPoints:Array; // point coordinates that define the inner ellipse
private var outerCircles:Array; // Array of circle particles drawn on the stage that define the outer ellipse
private var innerCircles:Array; // Array of circle particles drawn on the stage that define the inner ellipse
private var defaultGroup:Group;
public function EllipticalScaffold(defaultGrp:Group)
{
defaultGroup = defaultGrp;
}
private function init():void
{
outerPoints = new Array();
innerPoints = new Array();
outerCircles = new Array();
innerCircles = new Array();
}
/*
* drawOuterEllipse.
*
* @param x {number} X coordinate
* @param y {number} Y coordinate
* @param a {number} Semimajor axis
* @param b {number} Semiminor axis
* @param steps {number} Number of points in the ellipse
*/
public function drawOuterEllipse(x:Number, y:Number, a:Number, b:Number, angle:Number, steps:Number):void
{
outerPoints = calculateEllipse(x, y, a, b, angle, steps);
outerCircles = drawEllipse(outerPoints);
}
public function drawInnerEllipse(x:Number, y:Number, a:Number, b:Number, angle:Number, steps:Number):void
{
innerPoints = calculateEllipse(x, y, a, b, angle, steps);
innerCircles = drawEllipse(innerPoints);
}
public function createScaffold():void
{
joinEllipses(outerCircles, innerCircles);
}
/*
* This functions returns an array containing the points to draw an
* ellipse.
*
* @param x {number} X coordinate
* @param y {number} Y coordinate
* @param a {number} Semimajor axis
* @param b {number} Semiminor axis
* @param angle {number} Angle of the ellipse
*/
private function calculateEllipse(x:Number, y:Number, a:Number, b:Number, angle:Number, steps:Number)
{
var points:Array = new Array();
// Angle is given by Degree Value
var beta = -angle * (Math.PI / 180); //(Math.PI/180) converts Degree Value into Radians
var sinbeta = Math.sin(beta);
var cosbeta = Math.cos(beta);
for (var i = 0; i < 360; i += 360 / steps)
{
var alpha = i * (Math.PI / 180) ;
var sinalpha = Math.sin(alpha);
var cosalpha = Math.cos(alpha);
var x = x + (a * cosalpha * cosbeta - b * sinalpha * sinbeta);
var y = y + (a * cosalpha * sinbeta + b * sinalpha * cosbeta);
var o:Object = new Object();
o.x = x;
o.y = y;
points.push(o);
}
return points;
}
/*
* drawEllipse
* Draw the points of the ellipse as circle particles.
* Return an array of those circle particles.
*/
private function drawEllipse(points:Array):Array
{
var circles:Array = new Array();
var i:Number;
for( i= 0; i < points.length; i++)
{
// CircleParticle(x, y, radius, fixed, mass, elasticity, friction)
var c:CircleParticle = new CircleParticle(points[i].x, points[i].y, 0, false, 0.4, 0, 1);
defaultGroup.addParticle(c);
circles.push(c);
if(i > 0)
{
var s:SpringConstraint = new SpringConstraint(circles[i-1], circles[i], 1, true, 1, 1, true);
defaultGroup.addConstraint(s);
}
}
// create the final constraint to close the ellipse:
var t:SpringConstraint = new SpringConstraint(circles[i-1], circles[0], 1, true, 1, 1, true);
defaultGroup.addConstraint(t);
return circles;
}
// join the ellipses with spring constraints:
private function joinEllipses(outerPoints:Array, innerPoints:Array):void
{
var i:Number;
// join each inner and outer ellipse point:
for(i = 0; i < outerPoints.length; i++)
{
var j:SpringConstraint = new SpringConstraint(outerPoints[i], innerPoints[i], 1, true, 3);
defaultGroup.addConstraint(j); //
}
// create diagonals to the right:
for(i = 0; i < outerPoints.length -1; i++)
{
// join each inner point to the next outer point:
var d1:SpringConstraint = new SpringConstraint(innerPoints[i], outerPoints[i +1], 1, true, 3);
defaultGroup.addConstraint(d1); //
}
var d1close:SpringConstraint = new SpringConstraint(innerPoints[innerPoints.length-1], outerPoints[0], 1, true, 3);
defaultGroup.addConstraint(d1close);
// create diagonals to the left:
for(i = outerPoints.length -1; i > 0; i--)
{
// join each innner point to the next outer point:
var d2:SpringConstraint = new SpringConstraint(innerPoints[i], outerPoints[i -1], 1, true, 3);
defaultGroup.addConstraint(d2);
}
var d2close:SpringConstraint = new SpringConstraint(innerPoints[0], outerPoints[outerPoints.length -1], 1, true, 3);
defaultGroup.addConstraint(d2close);
}
}
}
Recently I was using ActionScript Physics Engine to create a simple 2-D arcade-style game. I looked into building an elliptical spring constraint in APE to serve as the boundaries of the game. In the end I decided not to use the elliptical boundaries, but they are easy enough to code up. Here’s a quick demo, and the ActionScript source code:
package
{
import flash.display.Sprite;
import flash.events.Event;
import org.cove.ape.*;
public class Main extends Sprite
{
public var defaultGroup:Group;
var car:SpringConstraint;
var ellipsePoints:Array; // points that define the outer ellipse
var wheel1:WheelParticle;
public function Main()
{
stage.frameRate = 30;
APEngine.init(1/4);
// set the default diplay container
APEngine.container = this;
defaultGroup = new Group();
defaultGroup.collideInternal = true;
init();
APEngine.addGroup(defaultGroup);
stage.addEventListener(Event.ENTER_FRAME, run);
}
public function init():void
{
ellipsePoints = new Array();
ellipsePoints = drawEllipse(calculateEllipse(380, 80, 60, 36, -24, 32));
addBall();
}
public function addBall():void
{
var circle = new CircleParticle(
160, // x
210, // y
40, // radius
false, // fixed
10, // mass
1.0, // elasticty
0 // friction
);
circle.setStyle(0, 0, 0, Math.random() * 0xffffff);
defaultGroup.addParticle(circle);
circle.velocity = (new Vector(20, 1));
}
/*
* This functions returns an array containing the points to draw an
* ellipse.
*
* @param x {double} X coordinate
* @param y {double} Y coordinate
* @param a {double} Semimajor axis
* @param b {double} Semiminor axis
* @param angle {double} Angle of the ellipse
*/
private function calculateEllipse(x, y, a, b, angle, steps)
{
if (steps == null)
steps = 36;
var points:Array = new Array();
// Angle is given by Degree Value
var beta = -angle * (Math.PI / 180); //(Math.PI/180) converts Degree Value into Radians
var sinbeta = Math.sin(beta);
var cosbeta = Math.cos(beta);
for (var i = 0; i < 360; i += 360 / steps)
{
var alpha = i * (Math.PI / 180) ;
var sinalpha = Math.sin(alpha);
var cosalpha = Math.cos(alpha);
var x = x + (a * cosalpha * cosbeta - b * sinalpha * sinbeta);
var y = y + (a * cosalpha * sinbeta + b * sinalpha * cosbeta);
var o:Object = new Object();
o.x = x;
o.y = y;
points.push(o);
}
return points;
}
private function drawEllipse(points:Array):Array
{
var circles:Array = new Array();
var i:Number;
for( i= 0; i < points.length; i++)
{
// CircleParticle(x, y, radius, fixed, mass, elasticity, friction)
var c:CircleParticle = new CircleParticle(points[i].x, points[i].y, 0, true, 0.4, 0, 0);
defaultGroup.addParticle(c);
circles.push(c);
if(i > 0)
{
var s:SpringConstraint = new SpringConstraint(circles[i-1], circles[i], 1, true, 1, 1, true);
defaultGroup.addConstraint(s);
}
}
// create the final constraint to close the ellipse:
var t:SpringConstraint = new SpringConstraint(circles[i-1], circles[0], 1, true, 1, 1, true);
defaultGroup.addConstraint(t);
return circles;
}
private function run(e:Event):void
{
APEngine.step();
APEngine.paint();
}
}
}
Anatomy of an Ellipse:

I’ve created a quick demo with APE that allows the user to draw rectangle particles with the mouse. New wheel particles are created every 2 seconds. Particles are removed if they fall outside the stage area so that the demo will not leak memory.
Main.as:
package {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.events.Event;
import flash.utils.Timer;
import flash.events.TimerEvent;
import org.cove.ape.*;
// Draw APE Rectangle Particles
// built with Actionscript Physics Engine v0.45
// http://www.cove.org/ape/
public class Main extends Sprite
{
private var myRectangle:RectangleParticle
private var wheel:WheelParticle;
private var defaultGroup:Group;
private var beginX:Number; // mouse drag top left x position
private var beginY:Number; // mouse drag top left y position
private var endX:Number; // mouse drag bottom right x position
private var endY:Number; // mouse drag bottom right y position
private const bound:Number = 600;
public function Main()
{
init();
}
private function init():void
{
stage.frameRate = 65;
// Initialize the engine. The argument here is the time step value.
// Higher values scale the forces in the sim, making it appear to run
// faster or slower. Lower values result in more accurate simulations.
APEngine.init(1/4);
APEngine.damping = 0.98;
// set the default diplay container
APEngine.container = this;
APEngine.addMasslessForce(new Vector(0,8));
defaultGroup = new Group();
defaultGroup.collideInternal = true;
//RectangleParticle(x, y, width, height, rotation, fixed, mass, elasticity, friction)
var floor:RectangleParticle = new RectangleParticle(300,490, 600, 20, 0.0, true, 10, 0.4, 0.55);
floor.setStyle(0, 0, 0, Math.random() * 0x111111);
defaultGroup.addParticle(floor);
APEngine.addGroup(defaultGroup);
addEventListener(Event.ENTER_FRAME, run);
stage.addEventListener(MouseEvent.MOUSE_DOWN, beginDrawRect);
stage.addEventListener(MouseEvent.MOUSE_UP, endDrawRect);
setTimer(); // start creating wheel particles
}
private function beginDrawRect(m:MouseEvent):void
{
beginX = mouseX;
beginY = mouseY;
}
private function endDrawRect(m:MouseEvent):void
{
endX = mouseX;
endY = mouseY;
// if we have 2 positive numbers, draw:
if (endX - beginX > 1 && endY - beginY > 1)
{
drawRectangle();
}
}
private function drawRectangle():void
{
//RectangleParticle(x, y, width, height, rotation, fixed, mass, elasticity, friction)
// the more area a rectangle has the more mass we give it:
myRectangle = new RectangleParticle(beginX + ( (endX - beginX)/2 ), beginY + ((endY - beginY)/2), endX - beginX, endY - beginY, 0 , false, (endX - beginX) * (endY - beginY) , 0.4, 0.1);
myRectangle.setStyle(0, 0, 0, Math.random() * 0xffffff);
defaultGroup.addParticle(myRectangle);
}
private function createParticle(e:TimerEvent):void
{
//WheelParticle(x, y, radius, fixed, mass, elasticity, friction, traction)
var rand:Number = Math.random();
// the more area a wheel has the more mass we give it:
wheel = new WheelParticle(rand * stage.stageWidth, -20, rand * 55, false, rand * 55, 0.4, 0.1);
wheel.setStyle(0, 0, 0, Math.round(Math.random() * 0xffffff));
defaultGroup.addParticle(wheel);
checkForBounds();
}
// Check location of particles & remove if out of bounds so that we don't leak memory:
function checkForBounds()
{
/* particles:Array [read-only] The Array of all AbstractParticle
instances added to the AbstractCollection */
for (var i:Number = defaultGroup.particles.length -1; i > 0 ; --i){
/* Check to see if particles is past the 'bound' number, if is then remove it from system
py & px give the X & Y location of the partical, view AbstractParticle in APE api */
if ( defaultGroup.particles[i].py > bound )
{
defaultGroup.removeParticle( defaultGroup.particles[i] ); // Removes an AbstractParticle from the AbstractCollection.
}
}
}
private function setTimer():void
{
var myTimer:Timer= new Timer(2000, 0);
myTimer.addEventListener("timer", createParticle);
myTimer.start();
}
private function run(e:Event):void
{
APEngine.step();
APEngine.paint();
}
}
}
I don’t do a lot of web design these days. I spend most of my time coding. But I was recently at a client site finishing up an ActionScript 3 project. They were in a pinch and I was asked to whip up some quick screen designs for a web-based business process automation solution my client was submitting a proposal for. These were created very quickly in Adobe Fireworks. I’ve removed information here that might be that my client might consider sensitive, changed the name of the project etc. etc.
The final images were dropped into web pages and linked together with image maps. Not exactly an elegant solution, but more than adequate for a proposal. See the full-sized click through.
Here’s a game built with the AS3 ActionScript Physics Engine. It’s easy to create a simple a car physics game with APE. Here’s my go at it: Kinetic Car Park. Make the car fly through the air and land in the parking space. Click the game to give it focus, then use the left/right arrow keys on your keyboard to control the game. It’s possible to put the car on every ramp in the game. Not too hard really.
This is a very rough go at a game so no scoring etc. at this point. You can download the source AS3 & Fla files here. You’ll also need the ActionScript Physics Engine. This little demo was created with APE v0.45.
Here’s the document class:
package
{
import org.cove.ape.*;
import flash.display.Sprite;
import flash.events.Event;
import flash.ui.Keyboard;
import flash.events.KeyboardEvent;
public class APEcar extends Sprite
{
private var wheel1:WheelParticle = new WheelParticle(460,20,10,false,100,0.05);
private var wheel2:WheelParticle = new WheelParticle(500,20,10,false,100,0.05);
public function APEcar()
{
addEventListener(Event.ENTER_FRAME, run);
APEngine.init(1/4);
APEngine.container = this;
APEngine.addMasslessForce(new Vector(0,2));
var defaultGroup:Group = new Group();
defaultGroup.collideInternal = true;
defaultGroup.addParticle(wheel1);
defaultGroup.addParticle(wheel2);
var leftWall:RectangleParticle = new RectangleParticle(5,0,990,10,4.712389,true);
defaultGroup.addParticle(leftWall);
var rightWall:RectangleParticle = new RectangleParticle(695,0,990,10,4.712389,true);
defaultGroup.addParticle(rightWall);
// Left //
var planeLeft1:RectangleParticle = new RectangleParticle (10, 140, 200, 10, -0.2, true);
defaultGroup.addParticle(planeLeft1);
var planeLeft2:RectangleParticle = new RectangleParticle(10,200,200,10,-0.1,true);
defaultGroup.addParticle(planeLeft2);
var planeLeft3:RectangleParticle = new RectangleParticle(10,300,300,10,0.2,true);
defaultGroup.addParticle(planeLeft3);
// Center //
var planeCenter1:RectangleParticle = new RectangleParticle(420,245,200,10,0.1,true);
defaultGroup.addParticle(planeCenter1);
var planeCenter2:RectangleParticle = new RectangleParticle(503,240,50,10,-0.6,true);
defaultGroup.addParticle(planeCenter2);
var planeCenter4:RectangleParticle = new RectangleParticle(440,350,180,10,0.1,true);
defaultGroup.addParticle(planeCenter4);
var planeCenter5:RectangleParticle = new RectangleParticle(240,490,200,10,-0.4,true);
defaultGroup.addParticle(planeCenter5);
var planeCenter6:RectangleParticle = new RectangleParticle(380,483,80,10,0.6,true);
defaultGroup.addParticle(planeCenter6);
var planeCenter3:RectangleParticle = new RectangleParticle(340,330,50,10,0.7,true);
defaultGroup.addParticle(planeCenter3);
// Right //
var planeRight1:RectangleParticle = new RectangleParticle(670,160,70,10,0,true);
defaultGroup.addParticle(planeRight1);
var planeRight2:RectangleParticle = new RectangleParticle(620,360,70,10, 0.1,true);
defaultGroup.addParticle(planeRight2);
var planeRight4:RectangleParticle = new RectangleParticle(570,380,40,10, -0.8,true);
defaultGroup.addParticle(planeRight4);
var planeRight3:RectangleParticle = new RectangleParticle(560,390,260,10, -0.3,true);
defaultGroup.addParticle(planeRight3);
var speedBump:RectangleParticle = new RectangleParticle(670,156,4,10,0,true);
defaultGroup.addParticle(speedBump);
var floor:RectangleParticle = new RectangleParticle(300,500,1000,10,0,true);
defaultGroup.addParticle(floor);
// put the car together: 2 wheel particles joined by a spring constraint: //
var car:SpringConstraint = new SpringConstraint(wheel1, wheel2, 0.3, true, 3);
defaultGroup.addConstraint(car);
APEngine.addGroup(defaultGroup);
stage.addEventListener(KeyboardEvent.KEY_DOWN, key_pressed);
stage.addEventListener(KeyboardEvent.KEY_UP, key_released);
}
function key_pressed(event:KeyboardEvent):void
{
if (event.keyCode == Keyboard.RIGHT)
{
wheel1.angularVelocity = 0.3;
wheel2.angularVelocity = 0.3;
}
if (event.keyCode == Keyboard.LEFT)
{
wheel1.angularVelocity = -0.3;
wheel2.angularVelocity = -0.3;
}
}
function key_released(event:KeyboardEvent):void
{
wheel1.angularVelocity = 0;
wheel2.angularVelocity = 0;
}
private function run(evt:Event):void
{
APEngine.step();
APEngine.paint();
}
}
}
Here’s a Flash MVC slideshow in ActionScript 3. My goal here was to keep the code clean while adding the features the customer wanted: unobtrusive controls, drop shadows, optional info text, and smooth cross-fades between images. The MVC pattern makes keeping the code organized easy while TweenLite makes all the transitions super easy to manage.
See it in action at poseygirlflowers. Or download the source Flash files.
I’ve posted some of the code below. There are several additional classes you’ll need + the .Fla in the .zip archive.
SlideModel.as
package slideshow
{
import flash.events.Event;
public class SlideshowModel extends Model
{
public var images:XMLList;
public var currentImage:XML;
public var currentTitle:String;
public var currentFile:String;
public function SlideshowModel()
{
}
override protected function updateData():void
{
currentImage = images[currentIndex];
totalItems = images.length();
currentTitle = currentImage.title;
currentFile = currentImage.file;
}
override protected function dataLoaded(event:Event):void
{
data = new XML(loader.data);
images = data.image;
setCurrentIndex(currentIndex);
}
}
}
SlideView.as
package slideshow
{
import flash.display.*;
import flash.events.Event;
import flash.net.URLRequest;
import flash.filters.DropShadowFilter;
import gs.TweenLite;
// only need this for debug:
import flash.text.*;
public class SlideshowView extends View
{
private var loader:Loader;
private var bmp:Bitmap;
public var container:Sprite;
public var buttonsMc:MovieClip;
// store a reference to the _controller:
private var _controller:SlideshowController;
private var stageWidth:Number = 580;
private var stageHeight:Number = 520;
// we will only allow the user to getNext/Prev image if we
// are not currentlyanimating a transition between 2 images:
private var isAnimating:Boolean;
private var baseURL:String = "http://www.stevenloe.com/projects/flash/mvc-slideshow/";
public function SlideshowView(c:Sprite)
{
container = c;
container.buttonMode = true;
bmp = new Bitmap();
loader = new Loader();
container.addChild(bmp);
container.addChild(loader);
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, imageLoaded);
}
override public function update(event:Event = null):void
{
if(loader.width > 0)
{
container.addChild(bmp);
bmp.alpha = 1;
bmp.x = loader.x;
bmp.y = loader.y;
bmp.bitmapData = new BitmapData(loader.content.width, loader.content.height);
bmp.bitmapData.draw(loader);
}
trace("IMAGE URL : " + baseURL + model.currentFile );
loader.load(new URLRequest(baseURL + model.currentFile));
//display the file name of the image being requested:
//buttonsMc.debug_txt.text = "\nLoad:: File: " + model.currentFile + " " + model.currentIndex;
}
private function imageLoaded(event:Event):void
{
var myDropShadowFilter = new DropShadowFilter (12,45,0x000000,0.5,10,10,1,2,false,false,false);
container.filters = [myDropShadowFilter];
container.addChild(loader);
loader.x = (stageWidth / 2) - (loader.width / 2);
loader.y = (stageHeight / 2) - (loader.height / 2) -30;
loader.alpha = 0;
TweenLite.to(loader,0.7,{alpha:1,onStart:onTweenStart, onComplete:onTweenComplete});
TweenLite.to(bmp, 0.7, {alpha:0});
_controller.startTimer();
//display request success:
//buttonsMc.debug_txt.text += "\nLoaded!:: Index: " + model.currentIndex;
}
public function getIsAnimating():Boolean
{
return isAnimating;
}
// set a flag that will allow us to ignore user input while
// an animation is in progress
private function onTweenStart():void
{
isAnimating = true;
}
private function onTweenComplete():void
{
isAnimating = false;
}
// show/hide the play & pause buttons to reflect the action currently avalible to the user:
// the two buttons are stacked on the stage
public function showButton(b:String):void
{
if(b == "play")
{
buttonsMc.play_btn.visible = true;
buttonsMc.pause_btn.visible = false;
}
else
{
buttonsMc.play_btn.visible = false;
buttonsMc.pause_btn.visible = true;
}
}
// pass in a reference to the controller so that it can call getIsAnimating:
public function setControllerReference(c:SlideshowController):void
{
_controller = c;
}
// pass in a reference to the MC that contains our buttons on stage
public function setButtonsReference(b:MovieClip):void
{
buttonsMc = b;
}
}
}
SlideController.as
package slideshow
{
import flash.display.DisplayObject;
import flash.events.*;
import flash.utils.Timer;
public class SlideshowController extends Controller
{
private var autoPlayEnabled:Boolean = true;
// set time to display each image here:
private var autoPlayTimer = new Timer(7000, 1);
// store a reference to the view:
private var _slideView:SlideshowView;
public function SlideshowController(m:Model)
{
super(m);
autoPlayTimer.addEventListener(TimerEvent.TIMER_COMPLETE, nextItem);
}
public function addNextButton(btn:DisplayObject):void
{
btn.addEventListener(MouseEvent.CLICK, nextItem);
btn.addEventListener(MouseEvent.CLICK, continueShow);
}
public function addPrevButton(btn:DisplayObject):void
{
btn.addEventListener(MouseEvent.CLICK, prevItem);
btn.addEventListener(MouseEvent.CLICK, continueShow);
}
public function addPlayBtn(btn:DisplayObject):void
{
btn.addEventListener(MouseEvent.CLICK, play);
}
public function addPauseBtn(btn:DisplayObject):void
{
btn.addEventListener(MouseEvent.CLICK, pause);
}
// ----- begin slideshow control functions -----
override public function nextItem(event:Event = null):void
{
if(!_slideView.getIsAnimating())
{
model.setCurrentIndex(model.currentIndex +1);
}
}
override public function prevItem(event:Event = null):void
{
if(!_slideView.getIsAnimating())
{
model.setCurrentIndex(model.currentIndex -1);
}
}
private function play(event:Event):void
{
if(!_slideView.getIsAnimating())
{
trace("play");
nextItem();
autoPlayTimer.start();
autoPlayEnabled = true;
_slideView.showButton("pause");
}
}
private function pause(event:Event):void
{
if(!_slideView.getIsAnimating())
{
trace("pause");
autoPlayTimer.stop();
autoPlayEnabled = false;
_slideView.showButton("play");
}
}
// when the user presses Next/Prev we want to reset the timer and start a new timer running
private function continueShow(event:Event):void
{
if(!_slideView.getIsAnimating())
{
trace("pause");
autoPlayTimer.reset();
autoPlayTimer.start();
autoPlayEnabled = true;
_slideView.showButton("pause");
}
}
public function startTimer():void
{
trace("start timer");
if(autoPlayEnabled)
{
autoPlayTimer.start();
trace("START");
}
}
public function setViewReference(v:SlideshowView):void
{
_slideView = v;
}
}
}
I recently broke my “don’t buy old Macs” rule. I picked up a Power Mac G4 “Quicksilver” Dual 800 on craigslist for $130 for the system, complete with keyboard, mouse, and monitor. Ordinarily I wouldn’t go vintage mac like this, but this machine will be for the neighbor kids to play with when they are over here visiting. They enjoy playing Flash games, and it will I hope (please!!) keep them away from my precious Macbook Pro.
All’s well with this old mac. It’s surprisingly peppy given its minimal memory and dual G4s. Or so it seems ’till I launch Flash Player 10 for the first time and try to play an arcade-style game. Hmmm, maybe this wasn’t such a great idea…
Anyway, the Power Mac desperately needs 802.11g wifi. I googled around and didn’t see much by way of mainstream Mac USB wifi solutions that are 802.11g and low cost. Then I discovered the Newer Technologies MaxPower 802.11g/b USB wireless stick, and decided to give it a shot. I found this one over at Other World Computing at a pretty fair price.
Features and specs:
- It offers 802.11g and b and various flavors of security: 64/128/152 bit WEP, WPA, WPA2, WPA-PSK/WPA-PSK2
- Operating Channels: 1 thru 11
- It’s a little longer than your average USB thumb drive at 3.5″ in length
Box Contents:
- USB WiFi Stick
- Driver CD with OSX 10.3, 10.4 and 10.5 drivers (also includes Windows & linux drivers –which I did not test)
- Instruction booklet

What's in the box
Simple OSX WiFi Setup:
- Load the CD, install the driver for the version of mac OSX you’re running
- Reboot
- Open System Preferences and choose “Network”
- Click “OK” in the “Detected a new network port” dialog box
- Click “Apply” and close system preferences
- Plug the USB WiFi stick into a USB port, and the Wireless Utility application launches
- Click on the name of the Wifi network you want to use, click “Add Profile”, enter your WEP/WPA password
- Click “Activate”
- Start surfing the internet
The install process was that simple.

Network Utility displaying the site survey
Hardware:
The unit is built like a standard memory stick. It feels sturdy and has a tiny LED that flashes to indicate data is being transmitted.
Software:
3rd party devices like this live or die based on the quality of their drivers and end user software. I’m happy to report that Newer seems to have done a good job here. The install went very smoothly. The Wireless Utility is easy to understand and use. When you start your mac, the Wireless Utility launches, and connects to your network. The connection is reliable and as fast as I can hope for given that I’m doing this all through old school USB 1.1.
Minor niggles: Every time you boot up your mac, the Wireless Utility pops up front and center and shows you that it has connected to the network. Not a very Mac-like user experience. Bugs: I did experience one instance where I booted up the mac and the Wireless Utility had forgotten my network’s WEP.
Performance:
I did a quick speed test using www.speakeasy.net. Results are equivalent to the speed I see using my Macbook Pro when It’s plugged into the cable modem powered ethernet here (~4.4 Mbps down, ~350 Kbps up). Web surfing is still noticeably slower than on my Macbook Pro. However, I currently have the wifi stick plugged into the stock USB 1.1 port on my Power Mac. Newer Technology warns that throughput will be slower using a 1.1 port. Next upgrade: an inexpensive USB 2.0 card. Either that or a shiny new Mac Mini!
Conclusion:
Overall, I’m very happy with the performance, ease of set-up and cost.
Test System:
Power Mac G4 Dual 800 Running OSX 10.4.11. Wireless base station: Apple Airport Express, version 6.3.

