Categories
Articles

Box2D Flash Puggle Lite Game Version of Peggle Nights


This is an update of Todd Kerpelman’s video tutorial of Puggle a game based on Peggle Nights using Box2DFlash, a free 2D physics engine for Flash that works in both Flash and Flex Builder 4. This is example involves a great deal of interaction of game components. For a simpler example of Box2d version 2.1 see my falling boxes example.

Todd’s Puggle game is really the example he uses to teach how to use Box2D. Todd’s version is based on Box2d 2.0 and I wanted to see how it applies to Box2D version 2.1. So I followed his tutorials and gleamed out the code and converted it. This was a painful process because the source code is not available and Todd spends a lot of time explaining concepts in his very helpful videos and went down some roads that he later changed perhaps to accentuate the right versus wrong way.

I did not reproduce all of the example Todd produced because I just ran out of time. However I got enough to put Puggle into working condition using Box2d 2.1 and also commented all the code extensively.

The Puggle Lite version composed from Todd’s work basically allows you to shoot balls from the gun at the top. You use the mouse to aim and fire. You can tune the code to determine the number of balls allowed in play at any one point in time. The source has this value set to 2.

A random selection of the pegs in the middle of the game are designated as goals. The remaining pegs are just that, pegs. They are colored to show goals as yellow and others as blue. They change color when hit and are removed once any ball in play leaves the game.

The moving chute at the bottom is a bonus if a ball falls into it. The game does not track points and perhaps something you may want to add.

You can build this with the free Flex SDK by using the code in the src folder and be sure to include a path to the Box2DFlash 2.1a library. For Flash CS3 and later versions, you need to create a Flash Document in the src folder and set the document class to HelloWorldFallingBoxes01 and then add a library path to the code or SWC if you created one. For your convenience the Flash CS4 example download is included with the needed SWC.

Also for Flash CS3 on up you will need some Flex library swc files because of the use of the Embed meta tag. Tareq AlJaber has good article on embedding meta data in Flash CS4.

Downloads

You can download Box2dFlash to work with the source, get the API documentation and more examples. I included a SWC library for version 2.1a in the example downloads so you will know they work despite what happens at Box2dFlash.org.

Another item used in this example is the Greensock Tweening library TweenLite for animations such as fade out. The SWC library is included in the downloads for your convenience.

This article shows the code for the Flex project. The main difference is how we stitch in the game sprites into the main class using the SpriteVisualElement.
[ad name=”Google Adsense”]
Application Class – HelloWorldPuggle01
There are a number of customization constants. I tried to extract all the data in buried in the code from Todd’s examples, but fell short for the side wall ramps. I prefer to have data either as members or other source and not buried in code.

The customization constants SHOW_BOX2D_DEBUG and SHOW_BOX2D_DEBUG_AS_OVERLAY on lines 127 and 134 respectively are useful in looking at the Box2D world visually. This helps in checking if the game assets, we call costumes, are synchronized with their physical representation in Box2D data.

The MAX_LAUNCED_BALLS_ALLOWED on line 57 is also helpful in testing. Run the number higher and more balls can be launched speeding up game action and shortening time to see coding changes.

Basically the Box2d world and the Shooter actor are created . The game is set up in the createLevel method we will visit in a later code block.

<?xml version="1.0" encoding="utf-8"?>
<!--
/**
* Demonstration of Box2D 2.1a converting Todd Kerpelman's Puggle Box2d 2.0 version 
of Peggle Nights.
* */
--><s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" 
			   applicationComplete="applicationCompleteHandler(event)"
			   width = "646"
			   height = "603"
			   frameRate="30"
			>
	   <fx:Script>
		   <![CDATA[
			   import Box2D.Collision.Shapes.b2CircleShape;
			   import Box2D.Common.Math.b2Vec2;
			   import Box2D.Dynamics.b2Body;
			   import Box2D.Dynamics.b2BodyDef;
			   import Box2D.Dynamics.b2DebugDraw;
			   import Box2D.Dynamics.b2FixtureDef;
			   import Box2D.Dynamics.b2World;
			   import mx.events.FlexEvent;
			   /**
				* Properties for background
				* @see background_bc
				*/
			   private static const backgroundColor:Number = 0x333333;
			   /**
				* Tracking of all actors to call their methods.
				* */
			   private var _actors:Array;
			   /**
				* Tracking of all actors that need to be removed after each update of the Box2dWorld.
				* */
			   private var _actorsTaggedForRemoval:Array;
			   /**
				* Tracking of all peg actors that have been hit.
				* */
			   private var _pegsLitUp:Array;
			   /**
				* Tracking of all peg actors that are considered goal targets.
				* */
			   private var _goalPegs:Array;
			   /**
				* Shooter actor.
				* */
			   private var _shooter:Shooter;
			   /**
				* Total balls launched.
				* */
			   private var _totalBallsLaunced:int = 0;
			   /**
				* Maximum balls that can be in play at a time. 
				* */
			   private const MAX_LAUNCED_BALLS_ALLOWED:int = 2;
			   /**
				* Time to simulate world. Set Box2D world time step to movie frame rate. 
				* Box2D manual recommends 1/60 seconds.
				* */
			   private const STEP_SIMULATION_TIME:Number = 1 / 30;
			   /**
				* Box2D interations for the velocity constraint solver
				* */
			   private const STEP_VELOCITY_ITERATIONS:int = 6;
			   /**
				* Box2D interations for the position constraint solver
				* */
			   private const POSITION_VELOCITY_ITERATIONS:int = 10;
			   /**
				* Horizontal spacing of PegActors.
				* */
			   private const PEG_H_SPACING:int = 36;
			   /**
				* Vertical spacing of PegActors.
				* */
			   private const PEG_V_SPACING:int = 36;
			   /**
				* Boundaries for the PegActors.
				* */
			   private const PEG_BOUNDARIES:Rectangle = new Rectangle(114, 226, 418, 255);
			   /**
				* Game side wall thickness.
				* */
			   private const SIDE_WALL_THICKNESS:Number = 10;
			   /**
				* Game side wall height.
				* */
			   private const SIDE_WALL_HEIGHT:Number = 603;
			   /**
				* Position of left wall.
				* */
			   private const LEFT_WALL_POSITION:Point = new Point(0,0);
			   /**
				* Position of right wall.
				* */
			   private const RIGHT_WALL_POSITION:Point = new Point(636,0);
			   /**
				* Left boundary of movable BonusChuteActor.
				* */
			   private const BONUS_CHUTE_LEFT_BOUNDS:int = 20;
			   /**
				* Right boundary of movable BonusChuteActor.
				* */
			   private const BONUS_CHUTE_RIGHT_BOUNDS:int = 433;
			   /**
				* Vertical position of the BonusChuteActor.
				* */
			   private const BONUS_CHUTE_VERTICAL_POSITION:int = 575;
			   /**
				* Position for the Shooter actor.
				* */
			   private const SHOOTER_POINT:Point = new Point(323,10);
			   /**
				* Velocity for the BallActors
				* */
			   private const LAUNCH_VELOCITY:Number = 90;
			   /**
				* Number of PegActors set as goal targets.
				* */
			   private const GOAL_PEG_NUM:int = 22;
			   /**
				* Developer testing for visualization of the Box2D elements using b2DebugDraw. 
				* Determines if b2DebugDraw is added to stage and called.
				* */
			   private const SHOW_BOX2D_DEBUG:Boolean = false;
			   /**
				* Developer b2DebugDraw visual is shown on top of visual actors. False the 
				* visual actors hide the b2DebugDraw visuals unless removed or are not occupying
				* the intended position of the actors the Box2d world represents. Can be useful
				* to see the removal process of actors and their Box2d counterparts when set to false.
				* */
			   private const SHOW_BOX2D_DEBUG_AS_OVERLAY:Boolean = true;
			   /**
				* Handler for applicationComplete event. Setup world and begin animation.
				* */
			   protected function applicationCompleteHandler(event:FlexEvent):void
			   {
				   trace("applicationCompleteHandler(...)");
				   // Initialize member variables.
				   _actors = new Array();
				   _actorsTaggedForRemoval = new Array();
				   _pegsLitUp = new Array();
				   _goalPegs = new Array();
				   _shooter = new Shooter();
				   // Add the Shooter actor
				   box2DContainer.addChild(_shooter);
				   _shooter.x = SHOOTER_POINT.x;
				   _shooter.y = SHOOTER_POINT.y;
				   // Create the Box2d world.
				   setupPhysicsWorld();
				   // Create the game elements other than the Shooter.
				   createLevel();
				   // The ENTER_FRAME event will drive the updates to the Box2d world.
				   addEventListener (Event.ENTER_FRAME, enterFrameHandler);
				   // Handle the mouse clicks to the stage.
				   stage.addEventListener(MouseEvent.CLICK, stageMouseClickHandler);

			   }

The setupPhysicsWorld method creates a Box2D 2.1 world and checks for the state to display the debug data to be shown under the game assets using the SHOW_BOX2D_DEBUG_AS_OVERLAY constant. In the createLevel method, there is the opposite case for the more likely need of showing the debug data over the assets.

Another debugging trick is to open Actor classes and set their “costume” visual visible property to false. Then you can see the Box2d world visual representation without the game visual representation.

Line 175 routes Box2d world body contact incidents to the PuggleContactListener class discussed in a later code block.

The addDebugDraw method determines if the debug data is shown at all.

			   /**
				* Creates the Box2dWorld
				* */
			   private function setupPhysicsWorld():void
			   {
				   // Create world. Gravity and let sleeping babies be.
				   PhysiVals.world = new b2World(new b2Vec2(0,9.8), true);
				   
				   // Add b2DebugDraw under the visual actors.
				   if (!SHOW_BOX2D_DEBUG_AS_OVERLAY)
				   {
					   addDebugDraw();
				   }
				   // Set the class to handle Box2d objects contacting each other.
				   PhysiVals.world.SetContactListener(new PuggleContactListener());
			   }
			   /**
				* b2DebugDraw visualization for Box2d. 
				* */
			   private function addDebugDraw():void
			   {
				   //Debugging visualization for Box2d. Will appear under the actors.
				   if (SHOW_BOX2D_DEBUG)
				   {
					   var debugSprite:Sprite = new Sprite();
					   box2DContainer.addChild(debugSprite);
					   var debugDraw:b2DebugDraw = new b2DebugDraw();
					   debugDraw.SetSprite(debugSprite);
					   debugDraw.SetDrawScale(PhysiVals.RATIO);
					   debugDraw.SetLineThickness( 1.0);
					   debugDraw.SetAlpha(1);
					   debugDraw.SetFillAlpha(0.4);
					   debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
					   PhysiVals.world.SetDebugDraw(debugDraw);
				   }
			   }

The createLevel method adds the PegActors in staggered rows and randomizes some of the PegActors to be goals. It also creates the game walls and ramps.

A big distinction that Box2D 2.1 uses over previous versions is the b2Fixture for each body to determine collisions.

			   /**
				* The only game level. Creates the walls, pegs, the ramps and the bonus chute.
				* */
			   private function createLevel():void
			   {
				   // Horizontal spacing of PegActors
				   var hSpacing:int = PEG_H_SPACING;
				   // Vertical spacing of PegActors
				   var vSpacing:int = PEG_V_SPACING;
				   // Rectangle boundaries of PegActors
				   var pegBounds:Rectangle = PEG_BOUNDARIES;
				   // Is odd numbered row
				   var oddRow:Boolean = false;
				   // Array of all PegActors created
				   var allPegs:Array = new Array();
				   // Loop from the top to the bottom of the PegActor boundaries until filled vertically.
				   for (var pegY:int = pegBounds.top; pegY < pegBounds.bottom; pegY += vSpacing)
				   {
					   // The starting x position of the PegActor is 0 or half of horizonal spacing 
					   // depending upon even or odd row state.
					   var startX:int = pegBounds.left + ((oddRow) ? 0: (hSpacing / 2));
					   // Flip odd row state.
					   oddRow = ! oddRow;
					   // Loop from the left to the right of the PegActor boundaries until filled horizontally.
					   for (var pegX:int = startX; pegX < pegBounds.right; pegX += hSpacing)
					   {
						   // Create new PegActor in PegActor.NORMAL state
						   var newPeg:PegActor = new PegActor(box2DContainer, new Point (pegX, pegY), PegActor.NORMAL);
						   // Add listener for PegEvent.PEG_LIT_UP.
						   newPeg.addEventListener(PegEvent.PEG_LIT_UP, pegEventHandler);
						   // Add listener for PegEvent.PEG_FADE_OUT_COMPLETE.
						   newPeg.addEventListener(PegEvent.PEG_FADE_OUT_COMPLETE, pegEventHandler);
						   // Add PegActor to actor tracking array.
						   _actors.push(newPeg);
						   // Add PegActor to local tracking array.
						   allPegs.push(newPeg);
					   }
				   }
				   // For the number of PegActors to set to PegActor.GOAL state.
				   for (var i:int = 0; i < GOAL_PEG_NUM; i++)
				   {
					   // Select a PegActor randomly from local tracking array
					   var randomPegNum:int = Math.floor(Math.random() * allPegs.length);
					   // Set the PegActor to PegActor.GOAL state.
					   PegActor(allPegs[randomPegNum]).setType(PegActor.GOAL);
					   _goalPegs.push(allPegs[randomPegNum]);
					   // Remove PegActor from local tracking array.
					   allPegs.splice(randomPegNum, 1);
				   }
				   // Dimensions for the walls
				   var wallShapeCoords:Array = new Array();
				   wallShapeCoords.push(
					   new Array(
						   new Point(0,0), 
						   new Point(SIDE_WALL_THICKNESS,0), 
						   new Point(SIDE_WALL_THICKNESS,SIDE_WALL_HEIGHT), 
						   new Point(0,SIDE_WALL_HEIGHT)
					   )
				   );
				   //Create the left wall.
				   var leftWall:ArbiStaticActor = new ArbiStaticActor(box2DContainer, LEFT_WALL_POSITION, wallShapeCoords);
				   _actors.push(leftWall);
				   //Create the right wall.
				   var rightWall:ArbiStaticActor = new ArbiStaticActor(box2DContainer, RIGHT_WALL_POSITION, wallShapeCoords);
				   _actors.push(rightWall);
				   // Dimensions for the left ramps.
				   var leftRampCoords:Array = new Array();
				   leftRampCoords.push(
					   new Array(
						   new Point(0,0), 
						   new Point(79,27), 
						   new Point(79,30), 
						   new Point(0,3)
					   )
				   );
				   // Add left ramps
				   var leftRamp1:ArbiStaticActor = new ArbiStaticActor(box2DContainer, new Point(0,265), leftRampCoords);
				   _actors.push(leftRamp1);
				   var leftRamp2:ArbiStaticActor = new ArbiStaticActor(box2DContainer, new Point(0,336), leftRampCoords);
				   _actors.push(leftRamp2);
				   var leftRamp3:ArbiStaticActor = new ArbiStaticActor(box2DContainer, new Point(0,415), leftRampCoords);
				   _actors.push(leftRamp3);
				   // Dimensions for the right ramps
				   var rightRampCoords:Array = new Array();
				   rightRampCoords.push(
					   new Array(
						   new Point(0,0), 
						   new Point(0, 3), 
						   new Point(-85,32), 
						   new Point(-85,29)
					   )
				   );
				   // Add the right ramps
				   var rightRamp1:ArbiStaticActor = new ArbiStaticActor(box2DContainer, new Point(646, 232), rightRampCoords);
				   _actors.push(rightRamp1);
				   var rightRamp2:ArbiStaticActor = new ArbiStaticActor(box2DContainer, new Point(646, 308), rightRampCoords);
				   _actors.push(rightRamp2);
				   var rightRamp3:ArbiStaticActor = new ArbiStaticActor(box2DContainer, new Point(646, 388), rightRampCoords);
				   _actors.push(rightRamp3);
				   // Create the bonus chute
				   var bonusChute:BonusChuteActor = new BonusChuteActor(box2DContainer, 
					   BONUS_CHUTE_LEFT_BOUNDS,
					   BONUS_CHUTE_RIGHT_BOUNDS, 
					   BONUS_CHUTE_VERTICAL_POSITION);
				   _actors.push(bonusChute);
				   // Add b2DebugDraw over the visual actors.
				   if (SHOW_BOX2D_DEBUG_AS_OVERLAY)
				   {
					   addDebugDraw();
				   }
			   }

Updating the world involves calling the Box2D world Step method. Then all the game Actor classes updateNow methods are called. Finally all the Actors tagged for removal are removed from the game. The tag and remove methodology is necessary to avoid instability in Box2D.

			   /**
				* Game ENTER_FRAME event handler.
				* */
			   private function enterFrameHandler(e:Event):void
			   {
				   updateWorld();
			   }
			   /**
				* .
				* */
			   private function updateWorld():void
			   {
				   // Step simulation time
				   var timeStep:Number = STEP_SIMULATION_TIME;
				   // Step velocity iterations
				   var velocityIterations:int = STEP_VELOCITY_ITERATIONS;
				   // Step position interactions
				   var positionIterations:int = POSITION_VELOCITY_ITERATIONS;
				   //Collision detection, integration, and constraint solutions performed.
				   PhysiVals.world.Step( timeStep, velocityIterations, positionIterations);
				   // As of version 2.1 we must clear the forces.
				   PhysiVals.world.ClearForces();
				   // You want to show debug visualizations.
				   if (SHOW_BOX2D_DEBUG)
				   {
					   PhysiVals.world.DrawDebugData();
				   }
				   // Update all the Actors.
				   for each (var actor:Actor in _actors)
				   {
					   actor.updateNow();
				   }
				   // Remove actors tagged for removal.
				   removeActors();
			   }
			   /**
				* Marks an actor for removal
				* */
			   public function markActorForRemoval(actor:Actor):void
			   {
				   if (_actorsTaggedForRemoval.indexOf(actor) < 0 )
				   {
					   _actorsTaggedForRemoval.push(actor);
				   }
			   }
			   /**
				* Remove actors. Calls their destroy methods.
				* */
			   private function removeActors():void
			   {
				   // Each Actor tagged for removal.
				   for each (var actor:Actor in _actorsTaggedForRemoval)
				   {
					   // This is a BallActor.
					   if (actor is BallActor)
					   {
						   // Reduce counter for number of BallActors in play.
						   _totalBallsLaunced --;
					   }
					   // Destroy the actor using its destroy() method.
					   actor.destroy();
					   // Remove Actor from the actors tracking array.
					   var actorIndex:int = _actors.indexOf(actor);
					   if (actorIndex > -1)
					   {
						   _actors.splice(actorIndex, 1);
					   }
				   }
				   // Reset the actors tagged for removal tracking array.
				   _actorsTaggedForRemoval = new Array();
			   }

This code handles the BallActor creation and launching. It also show handling the BallEvent.BALL_OFF_SCREEN and BALL_HIT_BONUS events.

			   
                            /**
			     * Handler for stage MouseEvent.CLICK.
			     * */
			   private function stageMouseClickHandler(e:MouseEvent):void
			   {
				   launchBall();
			   }
			   /**
				* Launch a new ball.
				* */
			   private function launchBall():void
			   {
				   
				   if (_totalBallsLaunced < MAX_LAUNCED_BALLS_ALLOWED)
				   {
					   // Get the launch position from the Shooter.
					   var launchPoint:Point = _shooter.getLaunchPosition();
					   // Subtract the coordinates of launch point from the mouse point to get direction.
					   var direction:Point = new Point(mouseX,mouseY).subtract(launchPoint);
					   // Scale by the LAUNCH_VELOCITY.
					   direction.normalize(LAUNCH_VELOCITY);
					   
					   // Create a BallActor
					   var ballActor:BallActor = new BallActor(box2DContainer, launchPoint, direction);
					   ballActor.addEventListener(BallEvent.BALL_OFF_SCREEN, ballOffScreenEventHandler);
					   ballActor.addEventListener(BallEvent.BALL_HIT_BONUS, ballHitBonusEventHandler);
					   _actors.push(ballActor);
					   _totalBallsLaunced++
				   }
			   }
			   /**
				* Handler for BallEvent.BALL_HIT_BONUS
				* */
			   private function ballHitBonusEventHandler(e:BallEvent):void
			   {
				   var ballActor:BallActor = BallActor(e.currentTarget);
				   markActorForRemoval(ballActor);
				   removePegActors();
				   ballActor.removeEventListener(BallEvent.BALL_HIT_BONUS,ballHitBonusEventHandler);
			   }
			   /**
				* Handler for BallEvent.BALL_OFF_SCREEN
				* */		
			   private function ballOffScreenEventHandler(e:BallEvent):void
			   {
				   var ballActor:BallActor = BallActor(e.currentTarget);
				   markActorForRemoval(ballActor);
				   removePegActors();
				   ballActor.removeEventListener(BallEvent.BALL_OFF_SCREEN,ballOffScreenEventHandler);
			   }

[ad name=”Google Adsense”]
Once a PegActor is hit, the process involves using Tweener to fade out the peg. The process starts in removePegActors method on line 433 and ends in the pegFadeOutState method on line 472 via the pegEventHandler method, on line 446, that handles the PegEvent.PEG_FADE_OUT_COMPLETE event .

The hit processing is handled in the pegEventHandler with PegEvent.PEG_LIT_UP event and adds the PegActor hit to the _pegsLitUp tracking array in the pegLitUpState method which you also see used in the removePegActors method to trigger the fade out process for PegActors in a lit up state.

	
			   /**
				* Fade out peg actors that have been hit (lit up). Removed from the array of lit up PegActors
				* and the fade out eventually leads to their removal.
				* */		
			   private function removePegActors():void
			   {
				   var pegNumber:int = 0;
				   for each (var pegActor:PegActor in _pegsLitUp)
				   {
					   pegActor.fadeOut(pegNumber);
					   pegNumber++;
				   }
				   _pegsLitUp = new Array();
			   }
			   /**
				* Handler for the PegActor PegEvent.PEG_LIT_UP and PegEvent.PEG_FADE_OUT_COMPLETE events.
				* */		
			   private function pegEventHandler(e:PegEvent):void
			   {
				   switch (e.type)
				   {
					   case PegEvent.PEG_LIT_UP:
						   pegLitUpState(PegActor(e.currentTarget));
						   break;
					   case PegEvent.PEG_FADE_OUT_COMPLETE:
						   pegFadeOutState(PegActor(e.currentTarget));
						   break;
				   }
			   }
			   /**
				* Add tracking for PegActors that are lit up.
				* */		
			   private function pegLitUpState(pegActor:PegActor):void
			   {
				   pegActor.removeEventListener(PegEvent.PEG_LIT_UP,pegEventHandler);
				   if (_pegsLitUp.indexOf(pegActor) < 0 )
				   {
					   _pegsLitUp.push(pegActor);
				   }
			   }
			   /**
				* Tag PegActors for removal if they have faded out.
				* */		
			   private function pegFadeOutState(pegActor:PegActor):void
			   {
				   _actorsTaggedForRemoval.push(pegActor);
			   }			   
		   ]]>
	   </fx:Script>

SpriteVisualElement is a simple way to show Sprites in Flex.

	
	   <!--
	   Background for app 
	   --> 
	   <s:BorderContainer id = "background_bc"
						  width="{width}" height = "{height}"
						  backgroundColor="{backgroundColor}">
		   
		   <!--
		   Container for the Box2D world
		   -->
		   <s:SpriteVisualElement id = "box2DContainer" />
	   </s:BorderContainer> 
</s:Application>

Shooter Class
The functionality for the shooter is basically to rotate to aim towards the mouse and providing the starting point for the BallActors to launch. These are handled in the alignToMouse, on line 46, and getLaunchPosition, on line 57, methods respectively.

The graphics source and published files are included in the downloads. Many measurements are based on the graphic and so you can open them for viewing to see where values like BALL_OFFSET are derived.

The Shooter is not added to the Box2D world to simplify coding as there is no expectation of the Box2D world’s bodies coming into contact with it. You might want to include it if you expect collisions.

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Point;
	import mx.core.BitmapAsset;
	/**
	 * Defines the game shooter.
	 * */	
	public class Shooter extends Sprite
	{
		/**
		 * Using an embedded graphic.
		 * */
		[Embed(source="assets/shooter_published.png")] 
		private var ShooterGraphic:Class;		
		/**
		 * Starting stage position of BallActor relative to this Shooter.
		 * */
		private const BALL_OFFSET:Point = new Point(70,0);
		/**
		 * Constructor.
		 * */	
		public function Shooter()
		{
			// Create an instance for the graphic
			var shooterImg:BitmapAsset = new ShooterGraphic();
			// Center registration point.
			shooterImg.x -= shooterImg.width / 2;
			shooterImg.y -= shooterImg.height / 2;
			// Add the graphic
			addChild(shooterImg);
			// Listen for ENTER_FRAME events.
			this.addEventListener(Event.ENTER_FRAME, enterFrameEventHandler);
		}
		/**
		 * Handler for the ENTER_FRAME event.
		 * */
		private function enterFrameEventHandler(e:Event):void
		{
			alignToMouse();
		}
		/**
		 * Shooter aims at the mouse position.
		 * */
		private function alignToMouse():void
		{
			// Compute the angle to the mouse.
			var mouseAngle:Number = Math.atan2(
				this.stage.mouseY - this.y,
				this.stage.mouseX - this.x) * 180 / Math.PI;
			this.rotation = mouseAngle;
		}
		/**
		 * Provide the starting stage position for BallActors.
		 * */
		public function getLaunchPosition():Point
		{
			return (localToGlobal(BALL_OFFSET));
		}
	}
}

Actor Class
This defines the basic structure of the game pieces. It keeps a reference to game piece’s b2Body and the visual component called _costume.

Most of the methods are overridden in subclasses. However the updating of the costume positions with the Box2D world is done here in the updateMyLook method on line 59. Line 46 contains the pubic interface for updating Actors which first calls updateMyLook and then allows subclasses to perform any child specific tasks.

Line 368 of the HelloWorldPuggle01 class removeActors method call the destroy method you see here on line 75. That method then calls cleanUpBeforeRemoving method, on line 82, that removes the costume and the b2Body for the Actor.

package
{
	import Box2D.Dynamics.b2Body;
	import flash.display.DisplayObject;
	import flash.events.EventDispatcher;
	import flash.events.IEventDispatcher;
	/**
	 * Defines requirements for all game actors and retains references to Box2d body and the actor's costume.
	 * */	
	public class Actor extends EventDispatcher
	{
		protected var _body:b2Body;
		protected var _costume:DisplayObject;
		/**
		 * Constructor
		 * @param body Box2D body for Actor
		 * @param costume is the visual for the Actor.
		 * */	
		public function Actor(body:b2Body, costume:DisplayObject)
		{
			_body = body;
			_costume = costume;
			// Store instance in the Box2D body for use as needed.
			_body.SetUserData(this);
			// Update the visual costume.			
			updateMyLook();
		}
		/**
		 * Handle being hit by another actor. 
		 * @param actor The actor hiting this actor.
		 * */	
		public function hitByActor(actor:Actor):void
		{
			// OVERRIDE IN EXTENDED ACTOR CLASS.
		}
		/**
		 * Handle hitting the bonus chute. 
		 * */	
		public function hitBonusTarget():void
		{
			// OVERRIDE IN EXTENDED ACTOR CLASS.
		}
		/**
		 * Updates the actor.
		 * */	
		public function updateNow():void
		{
			// This is a dynamic Actor needing its costume synchronized.
			if ( _body.GetType() == b2Body.b2_dynamicBody)
			{
				updateMyLook();
			}
			// Include any extended class updating specific to that class.
			childSpecificUpdating();
		}
		/**
		 * Updates the actor's costume position and angle.
		 * */	
		private function updateMyLook():void
		{
			_costume.x = _body.GetPosition().x * PhysiVals.RATIO;
			_costume.y = _body.GetPosition().y * PhysiVals.RATIO;
			_costume.rotation = _body.GetAngle() * 180 / Math.PI;
		}		
		/**
		 * Expected to be overridden by children
		 * */
		protected function childSpecificUpdating():void
		{
			// OVERRIDE IN EXTENDED ACTOR CLASS.
		}
		/**
		 * Handle permanent remove of Actor from game.
		 * */
		public function destroy():void
		{
			cleanUpBeforeRemoving();
		}
		/**
		 * Handles removal of Actor's costume and removal from the Box2D world.
		 * */
		protected function cleanUpBeforeRemoving():void
		{
			// Remove the costume.
			_costume.parent.removeChild(_costume);
			// Remove the Box2D representation.
			PhysiVals.world.DestroyBody(_body);
		}
	}
}	

BallShape Class
This is a simple graphic to depict a ball for the BallActor and PegActor. You could embellish it with bitmaps or more enhanced vector drawing.

package
{
	import flash.display.Sprite;
	/**
	 * Draws a filled circle to represent a ball
	 * */
	public class BallShape extends Sprite
	{
		/**
		 * The circle radius.
		 * */
		private var _radius:Number;
		/**
		 * The circle color.
		 * */
		private var _color:uint;
		/**
		 * param radius Radius of ball in pixels.
		 * param color Color of ball.
		 * */
		public function BallShape(radius:Number = 30, color:uint=0xff0000)
		{
			// Consume the parameters into member variables.
			_radius = radius;
			_color = color;
			// Draw the circle.
			draw();
		}
		/**
		 * Draws the circle
		 * */
		private function draw():void
		{
			graphics.clear();
			graphics.beginFill(_color);
			graphics.drawCircle(0, 0, _radius);
			graphics.endFill();
		}
		/**
		 * Set the fill color.
		 * */
		public function set color(colorVal:uint):void
		{
			_color = colorVal;
			draw();
		}
	}
}

BallActor Class
The class defines the Box2D representation of our ball bullets.

Two other functions are the dispatches of the BallEvent.BALL_HIT_BONUS and BallEvent.BALL_OFF_SCREEN events on line 67 and 78 respectively.

The PuggleContactListener class calls the hitBonusTarget method on line 64 for collisions.

The BallEvent.BALL_OFF_SCREEN allows the BallActor to handled for removal in the HelloWorldPuggle01 class ballOffScreenEventHandler method.

The BallEvent.BALL_HIT_BONUS currently just results in the BallActor targeting for removal in the HelloWorldPuggle01 class ballHitBonusEventHandler method. However, you can consider adding scoring logic there if you like.

package
{
	import Box2D.Collision.Shapes.b2CircleShape;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2FixtureDef;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.geom.Point;
	/**
	 * Defines ball game elements that user shoots.
	 * */
	public class BallActor extends Actor
	{
		/**
		 * The diameter for the ball
		 * */
		public static const BALL_DIAMETER:int = 12;
		/**
		 * Constructor
		 * @param parent Display container for the BallActor
		 * @param location Starting position in Box2dWorld
		 * @param initVel The starting velocity.
		 * */
		public function BallActor(parent:DisplayObjectContainer, location:Point, initVel:Point)
		{
			// Create the costume.
			var ballSprite:Sprite = new BallShape();
			ballSprite.scaleX = BALL_DIAMETER / ballSprite.width;
			ballSprite.scaleY = BALL_DIAMETER / ballSprite.height;
			// Add costume to display container.
			parent.addChild(ballSprite);
			
			// Create a b2BodyDef
			var bodyDef:b2BodyDef = new b2BodyDef();
			// This is a dynamic Box2D body.
			bodyDef.type = b2Body.b2_dynamicBody;
			// Position the BallActor body.
			bodyDef.position.Set(location.x / PhysiVals.RATIO,location.y / PhysiVals.RATIO);
			// Create the b2Body to represent the ballSprite
			var ballBody:b2Body= PhysiVals.world.CreateBody(bodyDef);
			// Body is to be treated for multiple collisions.
			ballBody.SetBullet(true);
			// Create b2CircleShape to represent this body. 
			var dynamicCircle:b2CircleShape = new b2CircleShape();
			dynamicCircle.SetRadius(BALL_DIAMETER / 2 / PhysiVals.RATIO);
			// Define b2FixtureDef - the collision detection wrapper. 
			var fixtureDef:b2FixtureDef = new b2FixtureDef();
			fixtureDef.shape = dynamicCircle;
			fixtureDef.density = 1.5;
			fixtureDef.friction = 0;
			fixtureDef.restitution = 0.45;
			// Create b2FixtureDef - the collision detection wrapper. 
			ballBody.CreateFixture(fixtureDef);
			// Set velocity
			var velocityVector:b2Vec2 = new b2Vec2( initVel.x / PhysiVals.RATIO, initVel.y / PhysiVals.RATIO);
			ballBody.SetLinearVelocity(velocityVector); 
			super(ballBody, ballSprite);
		}
		/**
		 * Bonus target hit by this BallActor.
		 * */
		override public function hitBonusTarget():void
		{
			// Broadcast the BallEvent.BALL_HIT_BONUS event.
			dispatchEvent(new BallEvent(BallEvent.BALL_HIT_BONUS));
		}
		/**
		 * Handle the specific items for updates.
		 * */
		override protected function childSpecificUpdating():void
		{
			// Off the stage.
			if (_costume.y > PhysiVals.STAGE_HEIGHT )
			{
				// Broadcast the BallEvent.BALL_OFF_SCREEN event.
				dispatchEvent(new BallEvent(BallEvent.BALL_OFF_SCREEN));
			}
		}
	}
}

BallEvent Class
Just a basic Actionscript event class for BallActor events.

package
{
	import flash.events.Event;
	/**
	 * Defines events for BallActors
	 * */	
	public class BallEvent extends Event
	{
		public static const BALL_OFF_SCREEN:String = "ballOffScreen";
		public static const BALL_HIT_BONUS:String = "ballHitBonus";
		public function BallEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}
		public override function clone():Event
		{
			return new BallEvent(type,bubbles,cancelable);
		}
	}
}

PegActor Class
The first main responsibility is to create both the costume and b2Body for the peg in the constructor.

The HelloWorldPuggle01 class removePegActors method calls the fadeOut method on line 101 where Tweener does the fade out.

The completion of fade out then triggers the PegEvent.PEG_FADE_OUT_COMPLETE event in fadeOutCompleteHandler on line 110.

The hitByActor method on line 134 is called by the PuggleContactListener for collisions. Here the state is updated and the PegEvent.PEG_LIT_UP is fired for handling in the HelloWorldPuggle01 pegEventHandler and pegLitUpState methods.

The checkPegType method on line 124 is guarantees correct values in setting NORMAL or GOAL states.

The setPegState method on line 149 sets the costume state of the PegActor.

package
{
	import Box2D.Collision.Shapes.b2CircleShape;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2FixtureDef;
	import com.greensock.TweenLite;
	import flash.display.DisplayObjectContainer;
	import flash.geom.Point;
	/**
	 * Defines circle peg actor.
	 * */
	public class PegActor extends Actor
	{
		/**
		 * Normal state for PegActor
		 * */
		public static const NORMAL:int = 1;
		/**
		 * Goal state for PegActor
		 * */
		public static const GOAL:int = 2;
		/**
		 * Color for NORMAL PegActor hit state.
		 * */
		private const NORMAL_HIT_COLOR:Number = 0xff0000;
		/**
		 * Color for NORMAL PegActor not hit state.
		 * */
		private const NORMAL_NOT_HIT_COLOR:Number = 0x0000ff;
		/**
		 * Color for GOAL PegActor hit state.
		 * */
		private const GOAL_HIT_COLOR:Number = 0x00ff00;
		/**
		 * Color for GOAL PegActor not hit state.
		 * */
		private const GOAL_NOT_HIT_COLOR:Number = 0xffff00;
		/**
		 * Diameter for PegActor
		 * */
		private static const PEG_DIAMETER:int = 19;
		/**
		 * Hit state.
		 * */
		private var _beenHit:Boolean = false;
		/**
		 * Type of NORMAL or GOAL.
		 * */
		private var _pegType:int;
		/**
		 * Costume as a BallShape.
		 * */
		private var _pegSprite:BallShape;
		/**
		 * Constructor
		 * @param parent Display container for the PegActor
		 * @param location Location of the PegActor.
		 * @param pegType Valid _pegType.
		 * */
		public function PegActor(parent:DisplayObjectContainer, location:Point, pegType:int)
		{
			// Consume parameters to member variables.
			_pegType = pegType;
			
			// Create the costume
			_pegSprite = new BallShape();
			_pegSprite.scaleX = PEG_DIAMETER / _pegSprite.width;
			_pegSprite.scaleY = PEG_DIAMETER / _pegSprite.height;
			// Add to display container.
			parent.addChild( _pegSprite );
			// Create the body definitions.
			var bodyDef:b2BodyDef = new b2BodyDef();
			// Set the body as static.
			bodyDef.type = b2Body.b2_staticBody;
			bodyDef.position.Set( location.x / PhysiVals.RATIO, location.y / PhysiVals.RATIO );
			// Create the b2Body to represent the pegSprite.
			var pegBody:b2Body= PhysiVals.world.CreateBody( bodyDef );
			// Create a b2CircleShape for the Box2D world.
			var b2_circleShape:b2CircleShape = new b2CircleShape();
			b2_circleShape.SetRadius( PEG_DIAMETER / 2 / PhysiVals.RATIO );
			// Define the collision detection fixture.
			var fixtureDef:b2FixtureDef = new b2FixtureDef();
			// Set the fixture shape to the circle.
			fixtureDef.shape = b2_circleShape;
			fixtureDef.density = 0;
			fixtureDef.friction = 0;
			fixtureDef.restitution = .45;
			// Add the fixture to the BonusChute b2Body.
			pegBody.CreateFixture(fixtureDef);
			// Call super before throw statements
			super(pegBody, _pegSprite);
			// Check the pegType value.
			checkPegType(pegType);
			// Update the state.			
			setPegState();
		}
		/**
		 * Fade out the costume.
		 * */
		public function fadeOut(pegNumber:int):void
		{
			TweenLite.to(_costume, 0.3, {alpha:0, delay: .08 * pegNumber, onComplete: fadeOutCompleteHandler});
		}
		/**
		 * Handler for TweenLite onComplete event. Costume fade out complete.
		 * */
		private function fadeOutCompleteHandler():void
		{
			dispatchEvent(new PegEvent(PegEvent.PEG_FADE_OUT_COMPLETE));
		}
		/**
		 * Set the pegType.
		 * */
		public function setType(pegType:int):void
		{
			checkPegType(pegType);
			_pegType = pegType;
			setPegState();
		}
		/**
		 * Validator for pegType input.
		 * */
		private function checkPegType(pegType:int):void
		{
			if (pegType != NORMAL && pegType != GOAL)
			{
				throw(new Error("PegActor setType should be PegActor.NORMAL || PegActor.GOAL"));
			}
		}
		/**
		 * Process a collision with other Actors.
		 * */
		public override function hitByActor(actor:Actor):void
		{
			// Not in hit state.
			if (! _beenHit) 
			{
				_beenHit = true;
				setPegState();
				// Broadcase the change to a hit state.
				dispatchEvent(new PegEvent(PegEvent.PEG_LIT_UP));
			}
		}
		/**
		 * Set the state of the peg based on hit state and peg type. 
		 * Set the costume look to represent the state.
		 * */
		private function setPegState():void
		{
			if (_pegType == NORMAL)
			{
				if (_beenHit)
				{
					_pegSprite.color = NORMAL_HIT_COLOR;
				}
				else
				{
					_pegSprite.color = NORMAL_NOT_HIT_COLOR;
				}
			}
			else if (_pegType == GOAL)
			{
				if (_beenHit)
				{
					_pegSprite.color = GOAL_HIT_COLOR;
				}
				else
				{
					_pegSprite.color = GOAL_NOT_HIT_COLOR;
				}
			}
		}
	}
}

PegEvent Class
Defines the events for PegActors.

package
{
	import flash.events.Event;
	/**
	 * Defines events for PegActors
	 * */	
	public class PegEvent extends Event
	{
		public static const PEG_LIT_UP:String = "pegLitUp";
		public static const PEG_FADE_OUT_COMPLETE:String = "pegFadeOutComplete";
		public function PegEvent(type:String, bubbles:Boolean=false, cancelable:Boolean=false)
		{
			super(type, bubbles, cancelable);
		}
		public override function clone():Event
		{
			return new PegEvent(type,bubbles,cancelable);
		}
	}
}

BonusChuteActor Class
Most of the code is consumed by creating the b2Body. The b2Body creation on line 99. The b2Body is made of three parts. There are two ramps on the left and right side that BallActor will bounce off. The center marks the point the BallActor can pass into the BonusChute. I broke the creation of these parts into separate methods on lines 127, 170 and 213 respectively.

The work is done in the childSpecificUpdating method on line 263. This mainly positions and moves the BonusChute in the HelloWorldPuggle01 updateWorld method. It also keeps the BonusChute within the boundaries.

package
{
	import Box2D.Collision.Shapes.b2PolygonShape;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2FixtureDef;
	import flash.display.DisplayObjectContainer;
	import flash.geom.Point;
	import mx.core.BitmapAsset;
	/**
	 * Defines bonus chute element. This moves left and right automatically.
	 * */
	public class BonusChuteActor extends Actor
	{
		/**
		 * Using an embedded graphic.
		 * */
		[Embed(source="assets/bonus_chute_published.png")] 
		private var BonusChute:Class;
		
		/**
		 * Container for the graphic.
		 * */
		private var _bonusChute:BitmapAsset
		/**
		 * Identifies this as a bonus target and is passed along in the b2FixtureDef as user data.
		 * */
		public static const BONUS_TARGET:String = "BonusTarget";
		/**
		 * Moving left.
		 * */
		private static const LEFT:int = -1;
		/**
		 * Moving right.
		 * */
		private static const RIGHT:int = 1;
		/**
		 * Index in _bounds for the left boundary.
		 * */
		private static const LEFT_BOUND_INDEX:int = 0;
		/**
		 * Index in _bounds for the right boundary.
		 * */
		private static const RIGHT_BOUND_INDEX:int = 1;
		/**
		 * Speed in pixels.
		 * */
		private static const TRAVEL_SPEED:int = 2;
		/**
		 * The b2Body.
		 * */
		private var _chuteBody:b2Body
		/**
		 * Boundary data.
		 * */
		private var _bounds:Array;
		/**
		 * Vertical position.
		 * */
		private var _yPos:int;
		/**
		 * Current direction.
		 * */
		private var _direction:int;
		/**
		 * Display container
		 * */
		private var _displayContainer:DisplayObjectContainer;
		/**
		 * Constructor
		 * @param parent Display container for the BonusChuteActor
		 * @param leftBounds Left boundary.
		 * @param rightBounds Right boundary.
		 * @param yPos Verticle position.
		 * */
		public function BonusChuteActor(parent:DisplayObjectContainer, leftBounds:int, rightBounds:int, yPos:int)
		{
			// Consume arguments.
			_bounds = [leftBounds, rightBounds];
			_yPos = yPos;
			_direction = RIGHT; 
			_displayContainer = parent;
			
			// Create the Box2D body.
			create_b2Body();
			// Add the left ramp b2Polygon.
			createLeftRamp();
			// Add the right ramp b2Polygon.
			createRightRamp();
			// Add the center hole b2Polygon.
			createCenterHole()
			
			super(_chuteBody, _bonusChute);
		}
		/**
		 * Create the Box2d body for BonusChute.
		 * */
		private function create_b2Body():void
		{
			var leftBounds:int = _bounds[0];
			var rightBounds:int = _bounds[1];
			// Costume
			_bonusChute = new BonusChute();
			// Add costume to display container.
			_displayContainer.addChild(_bonusChute);
			// Set center as registration point.
			_bonusChute.x -= _bonusChute.width / 2;
			_bonusChute.y -= _bonusChute.height / 2;
			//_bonusChute.visible = false;  // For testing just the b2Body
			// Create b2BodyDef.
			var chuteBodyDef:b2BodyDef = new b2BodyDef();
			// This is a dynamic b2Body.
			chuteBodyDef.type = b2Body.b2_dynamicBody;
			// Does not rotate.
			chuteBodyDef.fixedRotation = true;
			// Set position converted into the physical values.
			chuteBodyDef.position.Set(
				(leftBounds + rightBounds) / 2 / PhysiVals.RATIO, 
				_yPos / PhysiVals.RATIO);
			// Create the b2Body.
			_chuteBody = PhysiVals.world.CreateBody(chuteBodyDef);
		}
		/**
		 * Create the left ramp to deflect BallActors that miss the chute opening
		 * */
		private function createLeftRamp():void
		{
			// Box2d polygon shape.
			var polyShape:b2PolygonShape;
			// Define b2FixtureDef - the collision detection wrapper. 
			var fixtureDef:b2FixtureDef;
			// Polygon points.
			var listOfPointVectors:Array;
			// Each polygon point.
			var v1:b2Vec2;
			var v2:b2Vec2;
			var v3:b2Vec2;
			// Left ramp pixels converted into the b2World dimensions.
			// Need to explore the BonusChute graphic for pixel dimensions.
			listOfPointVectors = new Array();
			v1 = new b2Vec2();
			v1.x = 1 / PhysiVals.RATIO;
			v1.y = 25 / PhysiVals.RATIO;
			listOfPointVectors.push(v1);
			v2 = new b2Vec2();
			v2.x = 19 / PhysiVals.RATIO;
			v2.y = 10 / PhysiVals.RATIO;
			listOfPointVectors.push(v2);
			v3 = new b2Vec2();
			v3.x = 18 / PhysiVals.RATIO;
			v3.y = 25 / PhysiVals.RATIO;
			listOfPointVectors.push(v3);
			// Create the b2PolygonShape.
			polyShape = new b2PolygonShape();
			polyShape.SetAsArray( listOfPointVectors, listOfPointVectors.length);
			// Define the collision detection fixture.
			fixtureDef = new b2FixtureDef();
			// Assign the b2Polygon shape.
			fixtureDef.shape = polyShape;
			fixtureDef.density = 1;
			fixtureDef.friction = .1;
			fixtureDef.restitution = 0.6;
			// Add the fixture to the BonusChute b2Body.
			_chuteBody.CreateFixture(fixtureDef);
		}
		/**
		 * Create the right  ramp to deflect BallActors that miss the chute opening
		 * */
		private function createRightRamp():void
		{
			// Box2d polygon shape.
			var polyShape:b2PolygonShape;
			// Define b2FixtureDef - the collision detection wrapper. 
			var fixtureDef:b2FixtureDef;
			// Polygon points.
			var listOfPointVectors:Array;
			// Each polygon point.
			var v1:b2Vec2;
			var v2:b2Vec2;
			var v3:b2Vec2;
			// Right ramp pixels converted into the b2World dimensions.
			// Need to explore the BonusChute graphic for pixel dimensions.
			listOfPointVectors = new Array();
			v1 = new b2Vec2();
			v1.x = 150 / PhysiVals.RATIO;
			v1.y = 25 / PhysiVals.RATIO;
			listOfPointVectors.push(v1);
			v2 = new b2Vec2();
			v2.x = 150 / PhysiVals.RATIO;
			v2.y = 10 / PhysiVals.RATIO;
			listOfPointVectors.push(v2);
			v3 = new b2Vec2();
			v3.x = 167 / PhysiVals.RATIO;
			v3.y = 25 / PhysiVals.RATIO;
			listOfPointVectors.push(v3);
			// Create the b2PolygonShape.
			polyShape = new b2PolygonShape();
			polyShape.SetAsArray( listOfPointVectors, listOfPointVectors.length);
			// Define the collision detection fixture.
			fixtureDef = new b2FixtureDef();
			// Assign the b2Polygon shape.
			fixtureDef.shape = polyShape;
			fixtureDef.density = 1;
			fixtureDef.friction = .1;
			fixtureDef.restitution = 0.6;
			// Add the fixture to the BonusChute b2Body
			_chuteBody.CreateFixture(fixtureDef);
		}
		/**
		 * Create the center chute opening
		 * */
		private function createCenterHole():void
		{
			// Box2d polygon shape.
			var polyShape:b2PolygonShape;
			// Define b2FixtureDef - the collision detection wrapper. 
			var fixtureDef:b2FixtureDef;
			// Polygon points.
			var listOfPointVectors:Array;
			// Each polygon point.
			var v1:b2Vec2;
			var v2:b2Vec2;
			var v3:b2Vec2;
			var v4:b2Vec2;
			// Right ramp pixels converted into the b2World dimensions.
			// Need to explore the BonusChute graphic for pixel dimensions.
			listOfPointVectors = new Array();
			v1 = new b2Vec2();
			v1.x = 19 / PhysiVals.RATIO;
			v1.y = 10 / PhysiVals.RATIO;
			listOfPointVectors.push(v1);
			v2 = new b2Vec2();
			v2.x = 150 / PhysiVals.RATIO;
			v2.y = 10 / PhysiVals.RATIO;
			listOfPointVectors.push(v2);
			v3 = new b2Vec2();
			v3.x = 150 / PhysiVals.RATIO;
			v3.y = 25 / PhysiVals.RATIO;
			listOfPointVectors.push(v3);
			v4 = new b2Vec2();
			v4.x = 18 / PhysiVals.RATIO;
			v4.y = 25 / PhysiVals.RATIO;
			listOfPointVectors.push(v4);
			// Create the b2PolygonShape.
			polyShape = new b2PolygonShape();
			polyShape.SetAsArray( listOfPointVectors, listOfPointVectors.length );
			// Define the collision detection fixture.
			fixtureDef = new b2FixtureDef();
			// Assign the b2Polygon shape.
			fixtureDef.shape = polyShape;
			fixtureDef.density = 1;
			fixtureDef.friction = .1;
			fixtureDef.restitution = 0.6;
			fixtureDef.isSensor = true;
			fixtureDef.userData = BONUS_TARGET;
			// Add the fixture to the BonusChute b2Body
			_chuteBody.CreateFixture(fixtureDef);
		}
		/**
		 * Handle the specific items for updates.
		 * */
		override protected function childSpecificUpdating():void
		{
			// Hit right boundary.
			if (_costume.x >= _bounds[RIGHT_BOUND_INDEX] )
			{
				_direction = LEFT;
			}
			// Hit left boundary.
			else if (_costume.x <= _bounds[LEFT_BOUND_INDEX] )
			{
				_direction = RIGHT;
			}
			// New location in pixels.
			var idealLocation:b2Vec2 = new b2Vec2(
				_costume.x + ( _direction * TRAVEL_SPEED),
				_yPos);
			// Distance to travel in one frame in pixels.
			var directionToTravel:b2Vec2 = new b2Vec2(
				idealLocation.x - _costume.x, 
				idealLocation.y - _costume.y);
			// Distance to travel in one frame in meters
			directionToTravel.Multiply( 1 / PhysiVals.RATIO );
			// The distance in one second in meters.
			directionToTravel.Multiply(PhysiVals.FRAME_RATE);
			// Set linear velocity of the center of mass.
			_chuteBody.SetLinearVelocity(directionToTravel);
			// Peform any generic child specific updating.
			super.childSpecificUpdating();
		}
	}
}

ArbiStaticActor Class
A generic class for creating static Actors like walls and ramps.

package
{
	import Box2D.Collision.Shapes.b2PolygonShape;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2FixtureDef;
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.geom.Point;
	/**
	 * Defines generic static actors such as walls and ramps.
	 * */
	public class ArbiStaticActor extends Actor
	{
		/**
		 * Constructor
		 * @param parent Display container for the ArbiStaticActor
		 * @param location Location of the ArbiStaticActor.
		 * @param coordinates Point coordinates for the ArbiStaticActor.
		 * */
		public function ArbiStaticActor(parent:DisplayObjectContainer, location:Point, coordinates:Array)
		{
			// Create the b2Body.
			var myBody:b2Body = createBodyFromCoordinates(coordinates, location);
			// Create the costume.
			var mySprite:Sprite = createSpriteFromCoordinates( coordinates, location, parent);
			super (myBody, mySprite);
		}
		/**
		 * Create the costume for the AribStaticActor.
		 * @param coordinates Point coordinates for the ArbiStaticActor.
		 * @param location Location of the ArbiStaticActor.
		 * @param parent Display container for the ArbiStaticActor
		 * */
		private function createSpriteFromCoordinates(coordinates:Array, location:Point, parent:DisplayObjectContainer):Sprite
		{
			// Create a Sprite to represent the ArbiStaticActor.
			var newSprite:Sprite = new Sprite();
			newSprite.graphics.lineStyle(2, 0x00bb00);
			// Each point array.
			for each ( var listOfPoints:Array in coordinates )
			{
				var firstPoint:Point = listOfPoints[0];
				newSprite.graphics.moveTo(firstPoint.x, firstPoint.y);
				newSprite.graphics.beginFill(0x00bb00);
				// Each point.
				for each (var newPoint:Point in listOfPoints)
				{
					newSprite.graphics.lineTo(newPoint.x,newPoint.y);
				}
				newSprite.graphics.lineTo(firstPoint.x, firstPoint.y);
				newSprite.graphics.endFill();
			}
			newSprite.x = location.x;
			newSprite.y = location.y;
			parent.addChild(newSprite);
			return newSprite;
		}
		/**
		 * Create the b2Body for the AribStaticActor.
		 * @param coordinates Point coordinates for the ArbiStaticActor.
		 * @param location Location of the ArbiStaticActor.
		 * @param parent Display container for the ArbiStaticActor
		 * */
		private function createBodyFromCoordinates(coordinates:Array, location:Point):b2Body
		{	
			// Create the b2Body definitions.
			var bodyDef:b2BodyDef = new b2BodyDef();
			bodyDef.type = b2Body.b2_staticBody;
			bodyDef.position.Set(location.x / PhysiVals.RATIO, location.y / PhysiVals.RATIO);
			// Create the b2Body.
			var arbiBody:b2Body= PhysiVals.world.CreateBody(bodyDef);
			// Each point array.
			for each (var listOfPoints:Array in coordinates)
			{
				var listOfPointVectors:Array = new Array();
				// Each point.
				for (var i:int = 0; i < listOfPoints.length; i++)
				{
					var v:b2Vec2 = new b2Vec2();
					var nextPoint:Point = listOfPoints[i];
					// Convert to b2World measurements.
					v.x = nextPoint.x / PhysiVals.RATIO;
					v.y = nextPoint.y / PhysiVals.RATIO;
					listOfPointVectors.push(v);
				}
				// Create polygon shape.
				var polyShape:b2PolygonShape = new b2PolygonShape();
				polyShape.SetAsArray( listOfPointVectors, listOfPointVectors.length);
				// Define the collision detection fixture.
				var fixtureDef:b2FixtureDef = new b2FixtureDef();
				// Assign the b2Polygon shape.
				fixtureDef.shape = polyShape;
				fixtureDef.density = 0;
				fixtureDef.friction = .2;
				fixtureDef.restitution = 0.3;
				// Add the fixture to the b2Body.
				arbiBody.CreateFixture(fixtureDef);
			}
			return arbiBody;
		}
	}
}

[ad name=”Google Adsense”]
PuggleContactListener Class
On line 175 of the HelloWorldPuggle01 setupPhysicsWorld() method, this class is assigned as the b2worlds’s contact listener.

Collisions occurring during the Step method updating of the b2World fire contact events and they are received here. The BeginContact event handler on line 21 does the work we need. We are either calling the Actor’s hitByActor method as shown on lines 31 and 36 or the hitBonusTarget method on lines 43 and 50.

The BeginContact method provides GetFixtureA and GetFixtureB to represent the collision. From them we use data stored in b2Bodies or b2Fixturesto determine the Actors involved with collisions.

For example the BonusChute class createCenterHole method stores user data on line 256: fixtureDef.userData = BONUS_TARGET;. Here we see on line 40 and 47 testing for that value when user data is a String.

The other case is when the user data contains an Actor we can determine if the Actor is a PegActor or BallActor as on lines 29 and 34.

You can work out your own method of supplying and testing for user data depending on your own code.

package
{
	import Box2D.Collision.b2Manifold;
	import Box2D.Dynamics.Contacts.b2Contact;
	import Box2D.Dynamics.b2ContactListener;
	/**
	 * Handler for collisions in b2World.
	 * */	
	public class PuggleContactListener extends b2ContactListener
	{
		/**
		 * Constructor.
		 * */	
		public function PuggleContactListener()
		{
			super();
		}
		/**
		 * Contact started. Notify actors involved.
		 * */	
		override public function BeginContact(contact:b2Contact):void
		{
			// Extract user data from contacting Actors.
			var actorA:Actor = contact.GetFixtureA().GetBody().GetUserData();
			var actorB:Actor = contact.GetFixtureB().GetBody().GetUserData();
			var actorAFixtureUserData:* = contact.GetFixtureA().GetUserData();
			var actorBFixtureUserData:* = contact.GetFixtureB().GetUserData();
			// BallActor and PegActor contacted.
			if (actorA is PegActor && actorB is BallActor)
			{
				actorA.hitByActor(actorB);
			}
			// BallActor and PegActor contacted.
			else if (actorB is PegActor && actorA is BallActor)
			{
				actorB.hitByActor(actorA);
			} 
			// BallActor contacted BonusChuteActor.BONUS_TARGET.
			else if (actorAFixtureUserData is String 
				&& actorAFixtureUserData == BonusChuteActor.BONUS_TARGET
				&& actorB is BallActor)
			{
				actorB.hitBonusTarget()
			}
			// BallActor contacted BonusChuteActor.BONUS_TARGET.
			else if (actorBFixtureUserData is String 
				&& actorBFixtureUserData == BonusChuteActor.BONUS_TARGET
				&& actorA is BallActor)
			{
				actorA.hitBonusTarget()
			}
			super.BeginContact(contact);
		}
		/**
		 * Contact ended.
		 * */	
		override public function EndContact(contact:b2Contact):void
		{
			super.EndContact(contact);
		}
	}
}

PhysiValsClass

A place to store some of the values used across classes. The key value is the RATIO that converts the pixel world into the physics world’s meters.

package
{
	import Box2D.Dynamics.b2World; 

	public class PhysiVals
	{
		public static const STAGE_HALF_WIDTH:int = 323;
		public static const STAGE_HEIGHT:int = 603;
		private static var _instance:PhysiVals;			// Singleton instance
		public static const RATIO:Number = 30;
		public static const FRAME_RATE:Number = 30;
		private static var _world:b2World;
		
		public function PhysiVals(pvt:PhysiValsPrivateClass)
		{
		}
		/**
		 * Singleton instantiation method
		 * */
		public static function getInstance():PhysiVals
		{
			if (PhysiVals._instance == null)
			{
				PhysiVals._instance = new PhysiVals( new PhysiValsPrivateClass() );

			}
			return PhysiVals._instance;
		}
		public static function get world():b2World	
		{
			return _world
		}
		public static function set world(world:b2World):void	
		{
			_world = world;
		}
	}
}

/**
 * Singleton enforcer class
 * */
class PhysiValsPrivateClass
{
	public function PhysiValsPrivateClass()
	{
		
	}
}

Categories
Articles

Box2D 2.1 Flash Hello World Falling Boxes

I decided it was time to try out Box2DFlash, a free 2D physics engine for Flash that works in both Flash and Flex Builder 4.

I found most examples were for version 2.02 and earlier and there is now a version 2.1a (alpha) which it appears everyone is treating as a release. I downloaded the 2.1a version and immediately was struggling to find good starting examples since the developer documentation is not ready for 2.1a save scant update notes.

I started out with a video tutorial from Todd Kerpelman who did a great job for anyone starting cold turkey. His examples are for Box2D version 2.0 and he has a note that he will update for 2.1. I also converted his Puggle version of Peggle Nights.

Todd may hold out revising videos as I read forum rumors that more major code revisions are possible with Box2D and judging from this update, there is no tolerance for maintaining backwards compatibility other than use the older version. Todd’s videos are longish and would be served better if he added code to copy for each video. However his explanations of Bxo2d in the videos are extremely helpful.

I found by taking Todd’s work and a Box2d 2.1a example by Allan Bishop, I was able to revise Todd’s example in 2.1a. Allan’s example is more basic than is Todd’s example. I also would like to point out Allan has several more 2.1a version examples that are intriguing that I intend to explore.

You can build this with the free Flex SDK by using the code in the src folder and be sure to include a path to the Box2DFlash 2.1a library. For Flash CS3 and later versions, you need to create a Flash Document in the src folder and set the document class to HelloWorldFallingBoxes01 and then add a library path to the Box2DFlash 2.1a code or SWC if you created one. For your convenience the Flash CS4 example download is included.

You can download Box2dFlash to work with the source, get the API documentation and more examples. I included a SWC library for version 2.1a in the example downloads so you will know they work despite what happens at Box2dFlash.org.

This article shows the code for the Flex project.

Application Class – HelloWorldFallingBoxes01
All the code is contained in this class except for the Box2d library. The applicationCompleteHandler creates the Box2D world. The world contains a floor and left and right walls. It also will contain a bunch of falling rectangles that I am calling falling boxes. It sets a debug feature so we can visualize the physic data computed and starts an ENTER_FRAME event to make updates.

All the measurements are in meters so there is ratio PIXELS_TO_METERS constant on line 60 to handle that conversion when you want to think in pixels.

The Box2D world works with “center” registration points and so the measurements for the container, in this case the Flash movie, are provided as constants on line 44 and 48 to create the offsets.

The MAX_WORLD_BODIES constant on line 68 is used as an upper limit in adding the falling boxes. The walls, floor and falling boxes are referred to as bodies in Box2D. The MAX_WORLD_BODIES constant along with the NEXT_BOX_IN_INTERVAL constant on line 56 will control the addition of falling boxes.

<?xml version="1.0" encoding="utf-8"?>
<!--
/**
* Demonstration of Box2D 2.01a dropping boxes into container.
* */
--><s:Application 
   		xmlns:fx="http://ns.adobe.com/mxml/2009" 
		xmlns:s="library://ns.adobe.com/flex/spark" 
		xmlns:mx="library://ns.adobe.com/flex/mx" 
		applicationComplete="applicationCompleteHandler(event)"
		width = "550"
		height = "400"
		>
	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>
	<fx:Script>
		<![CDATA[
			import Box2D.Collision.Shapes.b2PolygonShape;
			import Box2D.Collision.b2AABB;
			import Box2D.Common.Math.b2Vec2;
			import Box2D.Dynamics.b2Body;
			import Box2D.Dynamics.b2BodyDef;
			import Box2D.Dynamics.b2DebugDraw;
			import Box2D.Dynamics.b2FixtureDef;
			import Box2D.Dynamics.b2World;
			import mx.events.FlexEvent;
			/**
			 * Properties for background
			 * @see background_bc
			 */
			private static const backgroundColor:Number = 0x333333;
			/**
			 * The Box2D world
			 */
			private var _world:b2World;
			/**
			 * Frames per second. 
			 */
			private const FPS:uint = 30;
			/**
			 * Movie width 
			 */
			private const SWF_WIDTH:int = 550;
			/**
			 * Movie height
			 */
			private const SWF_HEIGHT:int = 400;
			/**
			 * Wall thickness
			 */
			private const WALL_THICKNESS:int = 5;
			/**
			 * Interval, in frames, between adding boxes to fall.
			 */
			private const NEXT_BOX_IN_INTERVAL:int = 5;
			/**
			 * Ratio of pixels to meters. 30 is a defacto standard.
			 */
			private static const PIXELS_TO_METER:Number = 40;
			/**
			 * Number of frames until next box is added.
			 * */
			private var _nextBoxIn:int = 0;
			/**
			 * Maximum bodies including boxes, floor and walls.
			 * */
			private const MAX_WORLD_BODIES:int = 80;
			/**
			 * Handler for applicationComplete event. Setup world and begin animation.
			 * */
			protected function applicationCompleteHandler(event:FlexEvent):void
			{
				trace("applicationCompleteHandler(...)");
				// Setup the world
				setupWorld();
				// Create walls and floor
				createWallsAndFloor();
				// Visualize with debugDraw
				setupDebugDraw();
				// Listen to ENTER_FRAME event.
				addEventListener(Event.ENTER_FRAME, enterFrameHandler);
			}

[ad name=”Google Adsense”]
This section updates the world. The world updates with the Step method shown on line 89.

There is also logic on line 111 – 118 to add falling boxes at the top of the animation using randomized sizes and positions we will see in the addBox() method later. Here the coding is simply trying to time the frame intervals when a new falling box is added and to deal with some of the new requirements in Box2D 2.1a for updating the world.

			/**
			 * Handler for ENTER_FRAME event.
			 * */
			private function enterFrameHandler(e:Event):void
			{
				updateWorld();
			}
			/**
			 * Update world. Add additional boxes.
			 * */
			private function updateWorld():void
			{
				//trace("Main.updateWorld(...)");
				// Box2D manual recommends 1/60 seconds 
				// set it to the SWF framerate
				var timeStep:Number = 1 / FPS;
				// velocityIterations and positionIteration with 10 being the 
				// suggested count for each. 
				// Fewer iterations boosts performances 
				// but comes at the cost of accuracy.
				var velocityIterations:int = 6;
				var positionIterations:int = 2;
				// Update the world
				_world.Step(timeStep, velocityIterations, positionIterations);
				// As of version 2.1 we must clear the forces.
				_world.ClearForces();
				// Reduce frame delay for adding box
				_nextBoxIn--;
				// Time to add another box
				if ( _nextBoxIn <= 0 && _world.GetBodyCount() < MAX_WORLD_BODIES)
				{
					addBox();
					// Reset the box frame delay
					_nextBoxIn = NEXT_BOX_IN_INTERVAL;
				}
				// Draw the debug data.
				_world.DrawDebugData();
			}

This function creates the world. The 2.1a b2Word constructor no longer requires a universe size. The constructor arguments are now down to two.

			/**
			 * Setup the world.
			 * */
			private function setupWorld():void
			{
				trace("Main.setupWorld()");
				// Define gravity. Y = 9.8 meters per second.
				var gravity:b2Vec2 = new b2Vec2( 0, 9.8);
				// Ignore sleeping babies
				var ignoreSleeping:Boolean = true;
				_world = new b2World( gravity, ignoreSleeping );
			}

[ad name=”Google Adsense”]
In the createFloor() method, the groundBody b2Body is created and represent the floor. Version 2.1a uses b2FixtureDef. You create fixture definition for material properties. You provide the shape to the fixture, and call b2Body.CreateFixture.

			/**
			 * Create the walls and floor.
			 * */
			private function createWallsAndFloor():void
			{
				trace("Main.createWallsAndFloor()");
				createFloor();
				createWalls();
			}
			/**
			 * Create the floor.
			 * */
			private function createFloor():void
			{
				trace("Main.createFloor()");
				// Define the ground body.
				var groundBodyDef:b2BodyDef= new b2BodyDef();
				// Set position
				groundBodyDef.position.Set(	(SWF_WIDTH / 2) / PIXELS_TO_METER, 
					(SWF_HEIGHT - WALL_THICKNESS) / PIXELS_TO_METER );
				// Factory design pattern to create the body.
				var groundBody:b2Body = _world.CreateBody(groundBodyDef);
				// Create our shape.
				var groundBox:b2PolygonShape = new b2PolygonShape();
				// Set the box dimensions. 
				// Width 1/2 of MovieClip less offset for left and right wall thicknesses 
				// Height = wall thickness.
				groundBox.SetAsBox( 
					( (SWF_WIDTH ) / 2) / PIXELS_TO_METER - ( WALL_THICKNESS * 2) / PIXELS_TO_METER, 
					WALL_THICKNESS / PIXELS_TO_METER);
				// Create a fixture definition for body
				var groundFixtureDef:b2FixtureDef = new b2FixtureDef();
				// Set shape
				groundFixtureDef.shape = groundBox;
				// Set density
				groundFixtureDef.density = 1;
				// Set friction 100%
				groundFixtureDef.friction = 1;
				// Create fixture
				groundBody.CreateFixture(groundFixtureDef);
			}

Creating the walls mirrors the code for creating the floor. The b2BodyDef is reused for both wall b2Body bodies because the word CreateBody method copies and does not provide a pointer to a b2BodyDef. There are computations for position and box sizes stand next to the floor.

			/**
			 * Create the walls.
			 * */
			private function createWalls():void
			{
				trace("Main.createWalls() - left wall");
				// Define the wall body.
				var wallBodyDef:b2BodyDef= new b2BodyDef();
				
				wallBodyDef.position.Set( WALL_THICKNESS / PIXELS_TO_METER, ( SWF_WIDTH - WALL_THICKNESS ) / PIXELS_TO_METER );
				// Factory design pattern to create the body.
				var leftWallBody:b2Body = _world.CreateBody(wallBodyDef);
				// Create shape.
				var leftWallBox:b2PolygonShape = new b2PolygonShape();
				// Set as a box.
				leftWallBox.SetAsBox(WALL_THICKNESS / PIXELS_TO_METER, ( SWF_WIDTH - WALL_THICKNESS )  / PIXELS_TO_METER);
				// Create a fixture definition for left and right wall bodies
				var wallFixtureDef:b2FixtureDef = new b2FixtureDef();
				// Set shape 
				wallFixtureDef.shape = leftWallBox;
				// Set density
				wallFixtureDef.density = 0;
				// Set friction 50%
				wallFixtureDef.friction = 0.5;
				// Set restitution to 30%
				wallFixtureDef.restitution = 0.3;
				// Create fixture
				leftWallBody.CreateFixture(wallFixtureDef);
				trace("Main.createWalls() - right wall");
				wallBodyDef.position.Set(
					545 / PIXELS_TO_METER, 
					( SWF_WIDTH - WALL_THICKNESS ) / PIXELS_TO_METER );
				// Factory design pattern to create the body.
				var rightWallBody:b2Body = _world.CreateBody(wallBodyDef);
				// Create our shape.
				var rightWallBox:b2PolygonShape = new b2PolygonShape();
				// Redefine shape
				wallFixtureDef.shape = rightWallBox;
				// Set as a box.
				rightWallBox.SetAsBox(
					WALL_THICKNESS / PIXELS_TO_METER, 
					( SWF_WIDTH - WALL_THICKNESS ) / PIXELS_TO_METER);
				// Create fixture
				rightWallBody.CreateFixture(wallFixtureDef);
			}

[ad name=”Google Adsense”]
The addBox method creates a new falling box. It follows the same code steps as the floor. Differences include that the boxBodyDef type is dynamic to allow for movement, the use of SetAsOrientedBox on line 238 instead of SetAsBox used for floor and walls. The SetAsOrientedBox allows for rotation. To get to the angle argument you need to provide a b2Vec2 center point.

Randomizing the box sizes and starting positions is included for some variety.

			/**
			 * Add a falling box.
			 * */
			private function addBox():void
			{
				trace("Main.addBox()");
				var boxBodyDef:b2BodyDef= new b2BodyDef();
				boxBodyDef.position.Set( 
					randomInt(15, 530) / PIXELS_TO_METER, 
					randomInt(-100, -50) / PIXELS_TO_METER );
				// Crates move.
				boxBodyDef.type = b2Body.b2_dynamicBody;
				// Here we can see the factory design pattern being used to create the body.
				var boxBody:b2Body = _world.CreateBody(boxBodyDef);
				// Create our shape.
				var boxShape:b2PolygonShape = new b2PolygonShape();
				// Randomize the crate size and convert to meters.
				var hx:Number = randomInt(5, 40) / PIXELS_TO_METER;
				var hy:Number = randomInt(5, 40) / PIXELS_TO_METER;
				// Set as an oriented box
				boxShape.SetAsOrientedBox(
					hx, 
					hy,
					new b2Vec2(hx / 2, hy / 2), 		     // Center
					(Math.random() * 360) * (Math.PI / 180)	// Rotation random degrees converted to radians
				);	
				
				// Create a fixture definition for crate
				var boxFixtureDef:b2FixtureDef = new b2FixtureDef();
				// Set shape
				boxFixtureDef.shape = boxShape;
				// Set density
				boxFixtureDef.density = 0.7;
				// Set friction 80%
				boxFixtureDef.friction = 0.8;
				// Set restitution to 30%
				boxFixtureDef.restitution = 0.3;
				// Create fixture
				boxBody.CreateFixture(boxFixtureDef);
			}

Here we have some utilities. First is the b2DebugDraw to make the physics data visual and a method to compute a random number from a range.

The b2DebugDraw is added to the SpriteVisualElement box2DContainer on line 265.

			/**
			 * Setup the b2DebugDraw
			 * */
			private function setupDebugDraw():void
			{
				trace("Main.setupDebugDraw()");
				var spriteToDrawOn:Sprite = new Sprite();
				box2DContainer.addChild(spriteToDrawOn);
				var debugDraw:b2DebugDraw = new b2DebugDraw();
				debugDraw.SetSprite(spriteToDrawOn);
				debugDraw.SetDrawScale(PIXELS_TO_METER);
				debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
				debugDraw.SetLineThickness(2.0);
				debugDraw.SetFillAlpha(0.6);
				_world.SetDebugDraw(debugDraw);
			}
			/**
			 * Return a random int from a range.
			 * @param lowVal  Start of the range.
			 * @param highVal  End of the range.
			 * @return Random int from lowVal to highVal
			 * */
			private function randomInt(lowVal:int, highVal:int):int
			{
				if ( lowVal <= highVal)
				{
					return (lowVal + Math.floor(Math.random() * ( highVal - lowVal + 1)));
				}
				else
				{
					throw (new Error("Main.randomInt(...) lowVal exceeds highVal"));
				}
			}

		]]>
	</fx:Script>

These are the Flex visual components. Simply a background using the BorderContainer and a the Flex 4 SpriteVisualElement to contain the b2DebugDraw.

	   <!--
	   Background for app 
	   --> 
	   <s:BorderContainer id = "background_bc"
						  width="{width}" height = "{height}"
						  
						  backgroundColor="{backgroundColor}">
		   
		   <!--
		   Container for the Box2D world
		   -->
		   <s:SpriteVisualElement id = "box2DContainer" />
	   </s:BorderContainer> 
</s:Application>
Categories
Articles

Away3D Hello World Rotating Sphere in Flex 4 and Flash

I was looking around for an updated “Hello World” Away3D example and found many examples out of date, unnecessarily complex or not proofed for orphan code.

The unnecessarily complex examples attempted explaining more than was necessary and neglected clarifying the code.

Many examples used the “open ended object” parameters for methods and constructors. These types of parameters are the root cause for code failing both for the developers of the api and the users. A class property changes and someone forgets that the constructor has a loose compile time parameter. Thus we users are left scratching heads. Magic is cool but should be restricted to side shows. I recommend sticking with setting individual properties and avoiding these types of parameters unless there is a class to define an object to pass so the compiler can reduce the mishaps.

Learn More About Away3D from Away3D Team Members

I am using the Away3D version 3.5 code. I included that code in these examples since open source code tends to be subject to change and volatile. Many of the Away3D examples I have seen are broken with this version and the author failed to clarify the exact version they were using or take them down.

You can build this with the free Flex SDK by using the code in the src folder and be sure to include a path to the Away3D library. For Flash CS3 and later versions, you need to create a Flash Document in the src folder and set the document class to Away3D_HelloWorld_Rotating_Sphere_AS3 and then add a library path to the Away3d code or SWC if you created one. For your convenience the Flash CS4 example download is included.

This article shows the code for the Flex project.

Application Class – Away3D_HelloWorld_Rotating_Sphere_Flex
All the code is contained in this class except for the Away3D library. The applicationCompleteHandler creates the Away3D objects and event registration. This uses the Sphere primitive included in Away3D. Only the wire frame is shown so that you can see the x, y and z axes to help get a perspective. The sphere object is dead center in its container for all three axes. You are viewing the sphere from a negative position on the z axis.

<?xml version="1.0" encoding="utf-8"?>
<!--
/**
* Away3D 3.5.0 basic example showing centering a wireframe sphere in a View3D all centered on stage.
* */
-->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" 
			   applicationComplete="applicationCompleteHandler(event)"
			   width="500" height="500">
	<fx:Declarations>
		<!-- Place non-visual elements (e.g., services, value objects) here -->
	</fx:Declarations>
	<fx:Script>
		<![CDATA[
			import away3d.containers.View3D;
			import away3d.materials.WireColorMaterial;
			import away3d.primitives.Sphere;
			import away3d.primitives.Trident;
			
			import mx.events.FlexEvent;
			
			// Properties for background
			private static const backgroundColor:Number = 0xcccccc;
			private static const backgroundBorderColor:Number = 0x666666;
			private static const backgroundBorderWeight:Number = 2;

			private var view:View3D;						// View to contain the Sphere
			private var sphere:Sphere;					// Sphere to animate and position.
			private var speed:Number = .1;				// Speed for sphere rotationY property

			protected function applicationCompleteHandler(event:FlexEvent):void
			{
				var wireColorMaterial:WireColorMaterial;	// Optional colors for Sphere.
								
				// Create a View3D and center on stage
				view = new View3D();
				view.x = stage.stageWidth / 2;
				view.y = stage.stageHeight / 2;
				// Add viewport to the container
				away3DContainer.addChild(view);
				
				// Create a Sphere. Default position is 0,0,0;
				sphere = new Sphere();
				sphere.radius = 250;
				sphere.segmentsW = 12; 
				sphere.segmentsH = 9;
				
				// Create a color for the sphere wire model. Optional.
				wireColorMaterial = new WireColorMaterial();
				wireColorMaterial.alpha = 0;	// Default wireColorMaterial.color is transparent.;
				wireColorMaterial.wireColor = 0x0000ff;
				sphere.material = wireColorMaterial;
				
				// Add the sphere to the scene
				view.scene.addChild(sphere);
				
				// Add x, y and z axes for position perspective.
				var axis:Trident = new Trident(250, false);
				view.scene.addChild(axis);
				
				// Register listener for the ENTER_FRAME event.
				addEventListener(Event.ENTER_FRAME, enterFrameEventHandler);
			}

[ad name=”Google Adsense”]
This code is simply the ENTER_FRAME event handler and the function for updating the view and the state of the 3D objects to create the animation. The sphere rotationY property is animated.

			/**
			 * ENTER_FRAME event handler. 
			 * */
			public function enterFrameEventHandler(e:Event):void
			{
				updateView();
			}
			/**
			 * Computes the animation changes and updates view.
			 * */
			public function updateView():void
			{
				// Rotate sphere around the Y-axis. 
				sphere.rotationY += speed;
				// Render the view.
				view.render();
			} 

		]]>
	</fx:Script>

[ad name=”Google Adsense”]
This Flex version is a spark implementation. The SpriteVisualElement is used as the container. Older versions of Flex will require creating a UIComponent.

A background is included to help see the impact of changing positioning properties of the View3d and the Sphere should you want to play.

	<!--
	Background for app 
	--> 
	<s:BorderContainer id = "background_bc"
					   width="{width}" height = "{height}"
					   borderWeight="{backgroundBorderWeight}"
					   borderColor="{backgroundBorderColor}"
					   backgroundColor="{backgroundColor}">
		
		<!--
			Container for the Away3D Sprite objects
		-->
		<s:SpriteVisualElement id = "away3DContainer" />
	</s:BorderContainer> 
</s:Application>

References
Away3d.com/

Categories
Articles

Flex Asteriods Game Ship Animation

By Lon (Alonzo) Hosford

This example is based on Keith Peters ground breaking book on animation for ActionScript 3, Foundation Actionscript 3.0 Animation: Making Things Move.

Keith lays out the basic fundamentals of motion. A great start point example to understand velocity, friction, acceleration and direction is the Asteroids game style ship moving through space. I also reproduced a similar example using HTML5 canvas you can view here: HTML5 Canvas Based Animation Asteriods Space Ship Movement

Keith Peters AS3 Animation
Learn More

Many similar examples just assume you are going to use the keyboard to control the ship. This example decouples the ship acceleration and movement from keyboard. However we then double back and use the keyboard to control the ship. You will see in the MXML Application code, the keyboard events call methods on the ArrowShip class. However you could provide buttons for mouse clicks or even go crazy with a multi-touch application.

The example is created in Flex Builder Running Eclipse but can be adapted to AIR or Flash. Download the example code. You can also use Flash CS3 and CS4. You need create a Flash document in the src directory and set AsteriodsShipMovement as you document class. You also need to include the Flex 3 library, the Flex.swc file, into your Actionscript 3 settings. Tareq AlJaber article shows how that is done on page two of his article Embedding metadata with Flash. For your convenience you can download a Flash CS4 file that is set up, but still you may need to check the Actionscript 3 settings if the movie will not play because you might have a non-standard installation. Download the Flash CS4 example.

You can use any graphic for the ship. More on how to do that is noted on the comments for ArrowShip class code.

I added lots of comments in the code. The following is an overview of each code piece.

The AsteroidsShipMovement class is the main class to start the example. Line 17 is where you set the size and speed of your Flash movie.

The background is a simple shape. You can embellish to your preference. See line 34.

The ship needs a mask to define when it leaves view. That can be any size but generally is the same size as the stage or smaller. Thus your flying area can be smaller. The mask must be a rectangle. See line 38.

AsteroidsShipMovement.as

package
{
.
.
.
	import com.alh.ui.ships.ArrowShip;
	import com.alh.ui.ships.Ship;
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.StageScaleMode;
	import flash.events.KeyboardEvent;
	import flash.geom.Rectangle;
	import flash.ui.Keyboard;

	// SET GAME SIZE AND SPEED HERE
	[SWF(width=600, height = 600, frameRate = "30")]

	public class AsteriodsShipMovement extends Sprite
	{
		// Basic game background - set your preference here
		public static const backgroundColor:Number = 0x0000ff;
		public static const backgroundBorderColor:Number = 0x666666;
		public static const backgroundBorderWidth:Number = 2;

		// Create the ship
		private var arrowShip:ArrowShip;
		public function AsteriodsShipMovement()
		{
			// Set stage options
			initStage();

			// Create a background
			var backgroundRect:Shape = getRectShape(backgroundColor, backgroundBorderColor, backgroundBorderWidth, stage.stageWidth, stage.stageHeight)
			addChild(backgroundRect);

			// Create the boundaries for the arrowShip, create the arrowShip.
			var shipMaskRect:Shape = getRectShape(0x000000, 0x000000, backgroundBorderWidth, stage.stageWidth, stage.stageHeight)
			arrowShip = new ArrowShip(shipMaskRect);
			addChild(arrowShip);
			arrowShip.x = shipMaskRect.width / 2;
			arrowShip.y = shipMaskRect.height / 2;

			// Use keyboard events to control the ship
			stage.addEventListener( KeyboardEvent.KEY_DOWN, keyPressed);
			stage.addEventListener( KeyboardEvent.KEY_UP, keyReleased);
		}
		/**
		 * Set any stage options per your needs
		 * */
		private function initStage():void
		{
			stage.scaleMode = StageScaleMode.NO_SCALE;
		}
		/**
		 * Handler for KeyboardEvent.KEY_DOWN
		 * */
		private function keyPressed(evt:KeyboardEvent):void
		{
			// Either accelerate or turn the ship
			switch (evt.keyCode)
			{
				case Keyboard.UP:
					arrowShip.accelerate(Ship.START);
					break;
				case Keyboard.LEFT:
					arrowShip.turn(Ship.LEFT, Ship.START);
					break;
				case Keyboard.RIGHT:
					arrowShip.turn(Ship.RIGHT, Ship.START);
					break;
			}
		}

		/**
		 * Handler for KeyboardEvent.KEY_UP
		 * */

		private function keyReleased(evt:KeyboardEvent):void
		{
			// Either stop accelerating or stop turning the ship
			switch (evt.keyCode)
			{
				case Keyboard.UP:
					arrowShip.accelerate(Ship.STOP);
					break;
				case Keyboard.LEFT:
					arrowShip.turn(Ship.LEFT, Ship.STOP);
					break;
				case Keyboard.RIGHT:
					arrowShip.turn(Ship.RIGHT, Ship.STOP);
					break;
			}
		}
		/**
		 * Utility to draw a rectangle Shape object
		 * */
		private function getRectShape(bgColor:uint, borderColor:uint, borderSize:uint, width:uint, height:uint):Shape
		{
			var newShape:Shape = new Shape();
			newShape.graphics.beginFill(bgColor);
			newShape.graphics.lineStyle(borderSize, borderColor);
			newShape.graphics.drawRect(0, 0, width, height);
			newShape.graphics.endFill();
			return newShape;
		}

	}
}

The ArrowShip class is a subclass of the Ship class. The Ship class does all the common functions for ship objects. The ArrowShip class is basically the skin. You subclass the Ship class and add your ship image on line 11.

ArrowShip.as

.
.
.
package com.alh.ui.ships
{
	import flash.display.DisplayObject;

	public class ArrowShip extends Ship
	{

		[Embed(source="/assets/ArrowShip.png")]
		private var ShipImg:Class;
		private var a:int;
		/**
		 * Constructor
		 * param maskRect represents the container boundaries for the ship and its mask
		 * */
		public function ArrowShip(maskRect:DisplayObject) : void
		{
			super(maskRect);
			super.addShipImage(new ShipImg(),270);

		}
	}
}

The Ship class implements the animation direction, velocity, friction, and acceleration principles set out in Keith Peters’ book, Foundation Actionscript 3.0 Animation: Making Things Move!.
Instead of including Keyboard movement in the class as many animators do, the accelerate and turn methods were included. The UI or any code can call these methods to control the ship. On a more advanced level you could create an interface to further specify these methods in all types of objects that move forward and turn.

Ship.as

.
.
.
package com.alh.ui.ships
{

	import flash.display.DisplayObject;
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;

	/**
	 * Generic class for a space ship moving through space
	 * This class is intended to be abstract
	 * */
	public class Ship extends MovieClip
	{

		private var _shipDirectionOffset:Number = 0; 	// Offset front of ship in artwork in degrees.
														// Artwork may have the ship facing in different
														// directions and this makes the adjustment
														// 0 = artwork ship faces right. 270 faces up.
		private var _speed:Number = 0.3;				// Acceleration increment
		private var _rotateSpeed:Number = 3;			// Turning speed in degrees
		private var _vx:Number = 0;						// Velocity for x (direction and speed)
		private var _vy:Number = 0;						// Velocity for y(direction and speed)
		private var _friction:Number = 0.95;			// Percent reduction in _vx and _vy
		private var _accelerate:Boolean = false;		// True if increasing _vx and _vy, false if not
		private var _turnLeft:Boolean = false;			// Right turn direction request
		private var _turnRight:Boolean = false;			// Object has right turn direction request

		public static const START:int = 0;				// Start moving or turning
		public static const STOP:int = 1;				// Stop moving or turning
		public static const LEFT:int = 0;				// Turn left
		public static const RIGHT:int = 1;				// Turn right

		/**
		 * Constructor
		 * param maskRect represents the container boundaries for the ship and its mask
		 * */
		public function Ship(maskRect:DisplayObject) : void
		{
			mask = maskRect;
			// Register handler for Event.ENTER_FRAME
			addEventListener(Event.ENTER_FRAME, enterFrameEventHandler, false, 0, true);
		}
		/**
		 * Add the ship image and its directional offset
		 * param imgClass is the artwork
		 * param shipDirectionOffset see _shipDirectionOffset for notes
		 * */
		internal function addShipImage(imgClass:DisplayObject, shipDirectionOffset:Number):void
		{
			_shipDirectionOffset = shipDirectionOffset;
			// Add in ship image
			var shipSprite:Sprite = new Sprite();
			shipSprite.addChild(imgClass);

			// Add ship sprite and center
			addChild(shipSprite);
			shipSprite.x = shipSprite.width / 2* -1;
			shipSprite.y = shipSprite.height / 2 * -1;

		}
		/**
		 * Accelerates ship or stops acceleration
		 * param startStopState valid values START and STOP
		 * */
		public function accelerate(startStopState:int):void
		{
			// Set the accelerating state
			_accelerate = startStopState == START;
		}
		/**
		 * Turn ship
		 * param turnDirection valid values LEFT and RIGHT
		 * param startStopState valid values START and STOP
		 * */
		public function turn(turnDirection:int, startStopState:int):void
		{
			// Set the left turn state
			if (turnDirection == LEFT)
			{
				_turnLeft = startStopState == START;
			}
			// Set the right turn state
			if (turnDirection == RIGHT)
			{
				_turnRight = startStopState == START;
			}
		}
		/**
		 * Event.ENTER_FRAME event handler
		 * */
		protected function enterFrameEventHandler(e:Event) : void
		{
			// Acceleration is on
			if (_accelerate )
			{
				// Conversion of rotation degrees (rotation + _shipDirectionOffset) and speed to velocity
				_vy += Math.sin(degreesToRadians(rotation + _shipDirectionOffset)) * _speed;
				_vx += Math.cos(degreesToRadians(rotation + _shipDirectionOffset)) * _speed;
			}
			// Acceleration is off
			else
			{
				// reduce velocity by friction
				_vy *= _friction;
				_vx *= _friction;
			}

			// Change direction right
			if (_turnRight)
			{
				rotation += _rotateSpeed;
			}
			// Change direction left
			else if (_turnLeft)
			{
				rotation += -_rotateSpeed;
			}

			// Move position by velocity
			y += _vy;
			x += _vx;

			// Exiting right side of mask
			if (x - width / 2 > mask.width)
			{
				x = 0;
			}
			// Exiting left side of mask
			else if (x + width / 2 < 0)
			{
				x = mask.width;
			}

			// Exiting top of mask
			if (y - height / 2 > mask.height)
			{
				y = 0;
			}
			// Exiting bottom of mask
			else if (y + height / 2 < 0)
			{
				y = mask.height;
			}
		}
		/**
		 * Change the default friction value
		 * */
		protected function set friction(friction:Number):void
		{
			_friction = friction;
		}
		/**
		 * Change the default speed value
		 * */
		protected function set speed(speed:Number):void
		{
			_speed = speed;
		}
		/**
		 * Change the default rotateSpeed value
		 * */
		protected function set rotateSpeed(rotateSpeed:Number):void
		{
			_rotateSpeed = rotateSpeed;
		}
		/**
		 * Utility to convert Actionscript degrees (0-360) to Trig radians (57.2958 degrees).
		 * */
		private function degreesToRadians(degrees:Number) : Number
		{
			return degrees * Math.PI / 180;
		}

	}

}

Categories
Articles

Actionscript 3 Animating Sprite Rotation Following Mouse Movement

By Lon (Alonzo) Hosford

This my own version of of Keith Peter’s Foundation Actionscript 3.0 Animation: Making Things Move chapter 3 implementation of sprite rotation following a mouse.

Download files
[August 10 2010 – I updated this to an Actionscript project in Flex Builder 4. ]

Keith Peters AS3 Animation
Learn More

You can build this with the free Flex SDK by using the code in the src folder. Same for Flash CS3 and later versions. You need to create a Flash Document in the src folder and set the document class to Chapter03_Rotation_AS3. For your convenience the Flash CS4 example download is included.

This article shows the code for the Flex project.

Application Class – Chapter03_Rotation_Flex
This Flex version is a spark implementation. The SpriteVisualElement component shown on line 51 is used to add the code to the application display on line 31.

<?xml version="1.0" encoding="utf-8"?>
<!--
	Application class for showing sprite rotation following mouse movement. 
	<p>Author: Lon Hosford https://www.lonhosford.com 908 996 3773</p>
    <p>Reference: Keith Peter's Actionscript 3.0 Animation Chapter 3</p>
	
-->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="400"
			   creationComplete="creationCompleteHandler(event)" 
			   viewSourceURL="srcview/index.html">
	<fx:Script>
		<![CDATA[
			import mx.events.FlexEvent;
			
			// Properties for background
			private static const backgroundColor:Number = 0x0000ff;
			private static const backgroundBorderColor:Number = 0x666666;
			private static const backgroundBorderWeight:Number = 2;
			/**
			 * Handler for Application creationComplete event
			 * */
			protected function creationCompleteHandler(event:FlexEvent):void
			{
				// Create an Arrow object
				var arrow:Arrow = new Arrow();
				// Wrap arrow object into a RotateSpriteToMouse object
				var rotateToMouse:RotateSpriteToMouse = new RotateSpriteToMouse(arrow);
				// Add rotateToMouse Sprite to a SpriteVisualElement
				arrowVisualElement.addChild(rotateToMouse);
				// Center the SpriteVisualElement
				arrowVisualElement.x = background_bc.width / 2;
				arrowVisualElement.y = background_bc.height / 2;
			}

		]]>
	</fx:Script>
	<!--- 
	Background for app 
	--> 
	<s:BorderContainer id = "background_bc"
					   width="{width}" height = "{height}"
					   borderWeight="{backgroundBorderWeight}"
					   borderColor="{backgroundBorderColor}"
					   backgroundColor="{backgroundColor}">

		<!--- 
		Spark container for Sprite 
		--> 
		<s:SpriteVisualElement id="arrowVisualElement" />

	</s:BorderContainer>
					   
</s:Application>

[ad name=”Google Adsense”]
RotateSpriteToMouse Class
This is the class that does the work. Listening to the Event.ENTER_FRAME event leads to the update_rotation() method that does the work of updating the rotation.

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	/**
	 * Rotates sprite to mouse position
	 * */
	public class RotateSpriteToMouse extends Sprite
	{
		private var _sprite_to_rotate:Sprite;	// Sprite to rotate to mouse
		/**
		 * Constructor 
		 * @param sprite_to_rotate The sprite to rotate
		 * */
		public function RotateSpriteToMouse(sprite_to_rotate:Sprite)
		{
			_sprite_to_rotate = sprite_to_rotate;
			addChild(_sprite_to_rotate);
			addEventListener(Event.ENTER_FRAME, enterFrameEventHandler);
		}
		/**
		 * The event handler for Event.ENTER_FRAME
		 * */
		private function enterFrameEventHandler(event:Event):void
		{
			update_rotation();
		}
		/**
		 * Updates the rotation of the _sprite_to_rotate
		 * */
		private function update_rotation():void
		{
			// Triangle adjacent angle side distance for the x value.
			var dx:Number = mouseX - _sprite_to_rotate.x;
			// Triangle opposite angle side distance for the y value.
			var dy:Number = mouseY - _sprite_to_rotate.y;
			// Compute angle in radians from the sprite to the mouse position.
			var radians:Number = Math.atan2(dy, dx);
			// Convert radians to degrees
			_sprite_to_rotate.rotation = radians * 180 / Math.PI;
		}
	}
}

Arrow Class
Simple arrow sprite that Keith wrote. Key here is the center registration point.

package
{
	import flash.display.Sprite;
	/**
	 * Creates an arrow sprite with fixed dimensions
	 * */
	public class Arrow extends Sprite
	{
		public function Arrow() 
		{
			draw();
		}
		/**
		 * Draw the arrow
		 * */
		private function draw():void
		{
			graphics.lineStyle(1, 0, 1);
			graphics.beginFill(0xffff00);
			graphics.moveTo(-50, -25);
			graphics.lineTo(0, -25);
			graphics.lineTo(0, -50);
			graphics.lineTo(50, 0);
			graphics.lineTo(0, 50);
			graphics.lineTo(0, 25);
			graphics.lineTo(-50, 25);
			graphics.lineTo(-50, -25);
			graphics.endFill();
		}
	}
	
}
Categories
Articles

Actionscript 3 Animation Following the Mouse to Show Velocity at an Angle

By Lon (Alonzo) Hosford

See also the same done in HTML5 canvas HTML5 Canvas Based Animation Rotate Arrow To Mouse Position

In chapter 5 of Keith Peter’s Foundation Actionscript 3.0 Animation: Making Things Move velocity at an angle is explained. I took Keith’s mouse following example demonstrating velocity at an angle and punched it up a small notch.

Download files
[August 10 2010 – I updated this to an Actionscript project in Flex Builder 4. ]

You can build this with the free Flex SDK by using the code in the src folder. Same for Flash CS3 and later versions. You need to create a Flash Document in the src folder and set the document class to Chapter05_FollowMouse. For your convenience the Flash CS4 example download is included.

Keith Peters AS3 Animation
Learn More

This article shows the code for the Flex project. This Flex version is a spark implementation.

The arrow will follow the mouse around the stage. It will stop following when requested and start again with a mouse click on the stage. It also waits patiently when the mouse leaves the stage. The arrow gets real frustrated when you put the mouse over it. It always points towards the mouse when the mouse is over the stage.

Application Class – Chapter04_SprayPaint
I added user mouse click interaction to control the start and stop movement of the _arrow sprite. See line 40 and lines 48 to 51.

The movement also stops and starts when the mouse enters and leaves the stage. To accomplish that the stage needed to register a Event.MOUSE_LEAVE event to detect the mouse leaving the stage. The stage object is available when the applicationComplete(...) event occurs. The MouseEvent.MOUSE_MOVE sufficed to detect the mouse back over the stage. The _mouseOverStage variable carries the state of the mouse over the stage.

The rotation of the _arrow sprite is continuous. Gives it a mouse awareness state.

<?xml version="1.0" encoding="utf-8"?>
<!--
	Application class to demonstrate animation trigonometry to show velocity at an angle. 
    Mouse position determines the angle. 
	<p>Author: Lon Hosford https://www.lonhosford.com 908 996 3773</p>
    <p>Reference: Keith Peter's Actionscript 3.0 Animation Chapter 5</p>
	
-->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="400"
			   applicationComplete="applicationCompleteHandler(event)"
			   viewSourceURL="srcview/index.html">
	<fx:Script>
		<![CDATA[
			import mx.events.FlexEvent;
			public static const backgroundColor:Number = 0x0000ff;
			public static const backgroundBorderColor:Number = 0x666666;
			public static const backgroundBorderWeight:Number = 2;
			
			private var _instructions_tf:TextField;						// Instructions for user
			[Bindable]
			private var _lang_instructions:String = "Click to start and stop. Move mouse for animation."
			private var _arrow:Arrow;									// Animated sprite is an Arrow 
			private var _speed:Number = 5;								// Movement speed
			private var _allowMoving:Boolean = false;					// Use allowing moving state
			private var _mouseOverStage:Boolean = true;					// Mouse on the stage state
			
			/**
			 * Handler for Application applicationComplete event
			 * */
			protected function applicationCompleteHandler(event:FlexEvent):void
			{
				// Create Arrow object and add to stage. This is animated.
				_arrow = new Arrow();
				arrowVisualElement.addChild(_arrow);
				_arrow.x = (width - _arrow.width ) / 2;
				_arrow.y = (height - _arrow.height ) / 2;
				
				
				stage.addEventListener(MouseEvent.CLICK, mouseClickEventHandler);
				stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveEventHandler);
				stage.addEventListener(Event.ENTER_FRAME, enterFrameEventHandler);
				stage.addEventListener(Event.MOUSE_LEAVE, mouseLeaveEventHandler);
			}
			/**
			 * MouseEvent.CLICK handler
			 * */
			private function mouseClickEventHandler(event:MouseEvent):void
			{
				_allowMoving = !_allowMoving;
			}
			/**
			 * MouseEvent.MOUSE_MOVE handler
			 * */
			private function mouseMoveEventHandler(event:MouseEvent):void
			{
				_mouseOverStage = true;
			}
			/**
			 * Event.MOUSE_LEAVE handler
			 * */
			private function mouseLeaveEventHandler(event:Event):void
			{
				_mouseOverStage = false;
			}
			
			/**
			 * Event.ENTER_FRAME handler
			 * */
			private function enterFrameEventHandler(event:Event):void
			{
				draw();
			}

[ad name=”Google Adsense”]
The draw() method on line 77 updates the animation. The dx and dy variables are the distance from the mouse to the registration point of the _arrow sprite. Radians for the angle are computed using the Math.atan2(...) method. The angle in radians is converted for Actionscript rotation property to degrees. These trigonometry formulas are covered in Keith’s book .

The state the user choose to have the mouse follow is tested and if true the moveTo() method on line 98 computes the velocity at an angle discussed in chapter 5.

The _arrow sprite is designed with a center registration point to facilitate the rotation. As a result the _arrow sprite will move until the mouse coordinates reach the registration point. I added the hitTestPoint(...) method on line 105 to adjust so that the movement stops at the edge of the pixels in the _arrow sprite. The result the _arrow sprite’s point comes very close to the mouse versus the center of the _arrow sprite.

			/**
			 * Draw
			 * */
			private function draw():void
			{		
				// Distance of arrow registration point from mouse
				var dx:Number = mouseX - _arrow.x ;
				var dy:Number = mouseY - _arrow.y ;
				
				// Get angle in radians
				var angle:Number = Math.atan2(dy, dx);
				// Rotate converting radians to degrees
				_arrow.rotation = angle * 180 / Math.PI;
				
				// Is in a mouse following state
				if (_allowMoving)
				{
					// Move based on angle
					moveTo(angle);
				}
			}
			/**
			 * Move arrow
			 * */
			private function moveTo(angle:Number):void
			{
				// Velocity based on angle
				var vx:Number = Math.cos(angle) * _speed;
				var vy:Number = Math.sin(angle) * _speed;
				
				// Mouse position overlaps shape and mouse is over a pixel in the object
				if (!_arrow.hitTestPoint(mouseX, mouseY, true) && _mouseOverStage)
				{
					// Add velocity to position
					_arrow.x += vx ;
					_arrow.y += vy ;
				}
			}
			/**
			 * Set any stage options per your needs
			 * */
			private function initStage():void 
			{
				stage.scaleMode = StageScaleMode.NO_SCALE;
			}
			/**
			 * Instructions for user
			 * */
			private function getInstructions_tf():TextField 
			{
				var tf:TextField = new TextField();
				tf.autoSize = TextFieldAutoSize.LEFT;			
				tf.background = true;
				var textFormat:TextFormat = new TextFormat();
				textFormat.font = "_typewriter";
				tf.defaultTextFormat = textFormat;
				tf.text = _lang_instructions;
				return tf;
			}
		]]>
	</fx:Script>

[ad name=”Google Adsense”]
This part of the application represents the Flex UI based in Spark components.

	<!--- 
	Background for app 
	--> 
	<s:BorderContainer id = "background_bc"
					   width="{width}" height = "{height}"
					   borderWeight="{backgroundBorderWeight}"
					   borderColor="{backgroundBorderColor}"
					   backgroundColor="{backgroundColor}">

		<!--- 
		Spark container for Sprite 
		--> 
		<s:SpriteVisualElement id="arrowVisualElement" />

	</s:BorderContainer>
	<!--- 
	Instructions 
	--> 
	<s:HGroup  horizontalAlign="center" x="0" y="378" width = "100%">
		<s:Label text="{_lang_instructions}" color="0xffffff"/>
	</s:HGroup>
					   
</s:Application>

[ad name=”Google Adsense”]

Categories
Articles

Actionscript 3 Spray Paint with BitmapData

By Lon (Alonzo) Hosford

This my own version of of Keith Peter’s Foundation Actionscript 3.0 Animation: Making Things Move chapter 4 use of the BitmapData class to create a spray paint example.

Download files
[August 10 2010 – I updated this to an Actionscript project in Flex Builder 4. ]

You can build this with the free Flex SDK by using the code in the src folder. Same for Flash CS3 and later versions. You need to create a Flash Document in the src folder and set the document class to Chapter04_SprayPaint. For your convenience the Flash CS4 example download is included.

Keith Peters AS3 Animation
Learn More

This article shows the code for the Flex project. This Flex version is a spark implementation.

Application Class – Chapter04_SprayPaint
The canvas instance of the BitmapData class is instantiated line 32. The BitmapData class setPixel32(...) method on line 83 is used to set the color and alpha transparency values of a single 32 bit pixel.

The overall user interaction is mouse down start the enter frame events that call the draw() method and also to select a random color.

<?xml version="1.0" encoding="utf-8"?>
<!--
	Application class to demonstrate use of the BitmapData class along with animation trigonometry. 
	<p>Author: Lon Hosford https://www.lonhosford.com 908 996 3773</p>
    <p>Reference: Keith Peter's Actionscript 3.0 Animation Chapter 4</p>
	
-->
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" width="400" height="400"
			   creationComplete="creationCompleteHandler(event)" 
			   viewSourceURL="srcview/index.html">
	<fx:Script>
		<![CDATA[
			import mx.events.FlexEvent;
			
			// Properties for background
			private static const backgroundColor:Number = 0xffffff;
			private static const backgroundBorderColor:Number = 0x666666;
			private static const backgroundBorderWeight:Number = 2;
			private var canvas:BitmapData;			// Pixel level access
			private var color:uint;					// Randomly generated spray brush colors
			private var size:Number = 20;			// Size of the spray brush
			private var density:Number = 50;		// Number of pixels sprayed per frame
			/**
			 * Handler for Application creationComplete event
			 * */
			protected function creationCompleteHandler(event:FlexEvent):void
			{
				
				// Create BitmapData object allowing use to work with pixels
				canvas = new BitmapData( background_bc.width,background_bc.height, true, 0x00000000);
				var bmp:Bitmap =  new Bitmap(canvas);
				
				// Add itmapData object to a SpriteVisualElement
				arrowVisualElement.addChild(bmp);
				// Handlers for mouse events to signal drawing.
				addEventListener(MouseEvent.MOUSE_DOWN, mouseDownEventHandler);
				addEventListener(MouseEvent.MOUSE_UP, mouseUpEventHandler);
			}
			/**
			 * Event handler for MouseEvent.MOUSE_DOWN
			 * */
			private function mouseDownEventHandler(event:Event):void 
			{
				// Random color from 0 to highest color value which is white
				// and then add alpha channel of opaque
				color = Math.random() * 0xffffff + 0xff000000;
				// Start enter frame events
				stage.addEventListener(Event.ENTER_FRAME, enterFrameEventHandler);
			}
			/**
			 * Event handler for MouseEvent.MOUSE_UP
			 * */
			private function mouseUpEventHandler(event:Event):void 
			{
				stage.removeEventListener(Event.ENTER_FRAME, enterFrameEventHandler);
			}
			/**
			 * Event handler for Event.ENTER_FRAME
			 * */
			private function enterFrameEventHandler(event:Event):void 
			{
				draw();
			}

[ad name=”Google Adsense”]
The draw method uses the density variable on line 72 to determine the number of pixels to color using randomization to compute an angle and a radius within the size of the brush.

Each pixel is basically a point on its own circle. The mouse is becomes a center point for a random circle with a diameter up to the size variable. Line 77 computes the radius for that circle.

Keith’s formulas for computing points on a circle math from chapter 3 are then used to compute the position of the pixel on lines 79 and 81.


			/**
			 * Draw
			 * */
			private function draw():void 
			{
				// Repeat for number of pixels - density.
				for (var i:int = 0; i < density; i++)
				{
					// Random angle 0 - 6.2832 radians  (0 - 360)
					var angle:Number = Math.random() * Math.PI * 2;
					// Radius is random percentage of maximum radius (size / 2)
					var radius:Number = Math.random() * size / 2;
					// Center is mouseX to compute the x point
					var xpos:Number = mouseX + Math.cos(angle) * radius;
					// Center is mouseY to compute the y point
					var ypos:Number = mouseY + Math.sin(angle) * radius;
					//Set the color and alpha transparency values of a single 32 bit pixel.
					canvas.setPixel32(xpos, ypos, color);
					
				}
			}

		]]>
	</fx:Script>

[ad name=”Google Adsense”]
The SpriteVisualElement component allows adding the canvas BitmapData object to the display list.

	<!--- 
	Background for app 
	--> 
	<s:BorderContainer id = "background_bc"
					   width="{width}" height = "{height}"
					   borderWeight="{backgroundBorderWeight}"
					   borderColor="{backgroundBorderColor}"
					   backgroundColor="{backgroundColor}">

		<!--- 
		Spark container for Sprite 
		--> 
		<s:SpriteVisualElement id="arrowVisualElement" />

	</s:BorderContainer>
	<!--- 
	Instructions 
	--> 
	<s:Label x="96" y="378" text="Click and drag mouse to spray paint."/>
					   
</s:Application>
Categories
Articles

Actionscript 3 Revolving Animation Around a Center Point

By Lon (Alonzo) Hosford

I was working through chapter 3 of Keith Peter’s Foundation Actionscript 3.0 Animation: Making Things Move and came up with this Rotator class that will revolve any sprite around a given point.

Keith Peters AS3 Animation
Learn More

[August 18 2010] I updated this to an ActionScript project to Flex Builder 4. Download the example code. You can build this with the free Flex SDK by using the code in the src folder. Same for Flash CS3 and CS4. You need to create a Flash Document in the src folder and set the document class to Chapter02_Factory_PrintCenters. For your convenience you can download a Flash CS4 ready to go example.

Application Class – Animation_Circular_Rotation
This is the application class. The actual use of the Rotator class is all done on lines 81 to 86 with the _rotator instance and the Circle class circle instance. The rest of this class is dedicated to user interaction with the keyboard to start, stop and step the animation and with informational feedback using the DebugConsole class.

The circle created on line 80 is what the Rotator class instance _rotator revolves. The Rotator class constructor requires the Sprite object it will revolve and the radius as shown on line 85.

/**
 *  Purpose: Animating a rotation motion in Actionscript 3
 *  <p>Author: Lon Hosford https://www.lonhosford.com 908 996 3773</p>
 *  <p>Version history:</p>
 *  <p>	Number:			1.00.00</p>
 *  <p>	Date:			06/15/2007</p>
 *  <p>	Programmer:		Lon Hosford: https://www.lonhosford.com</p>
 *  <p>	Changes:		Original programming</p>
 * */	

package
{
	import com.lonhosford.util.debug.lite.DebugConsole;
	
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.display.StageScaleMode;
	import flash.events.Event;
	import flash.events.KeyboardEvent;
	import flash.events.TimerEvent;
	import flash.system.Capabilities;
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.text.TextFormatAlign;
	import flash.ui.Keyboard;
	import flash.utils.Timer;
	

	[SWF(width=600, height = 770, frameRate = 30)]
	/**
	 *  Application class
	 * */	
	public class Animation_Circular_Rotation extends Sprite
	{
		private var _debugConsole:DebugConsole = DebugConsole.getInstance();
		public var maxDebugConsoleLines:Number = 500;				// Maximum lines in debug console.
																	// More line lowers performance.
		// Basic background - set your preference here
		public static const backgroundColor:Number = 0xcccccc;
		public static const backgroundBorderColor:Number = 0x666666;
		public static const backgroundBorderWidth:Number = 2;
		
		private var _rotator:Rotator;								// Rotation animator
		
		// Language
		private var _lang_clickStage:String = "Click stage to get keyboard focus.";
		private var _lang_enterKey:String = "ENTER starts and stops animation.";
		private var _lang_spacebar:String = "SPACE BAR increments animation one step. Auto repeats after delay.";
		private var _lang_debugCleared:String = "Debug window cleared every";
		private var _lang_debugClearedWhen:String = "lines when animating to maintain low memory.";
		private var _lang_traceDisabled:String = "Trace output suppressed when animation plays in Flash Control->Test Movie.";
		private var _lang_keyboardShortcuts:String = "In Flash after Control->Test Movie, select Control->Disable Keyboard Shortcuts.";
		// Space bar repeat timer
		private var _keySpacebarTimeDelay:Number = 350;					// Delay between each Keyboard.SPACE event.
		private var _keySpacebarTimer:Timer; 							// Timer for repeating Keyboard.SPACE events.

		public function Animation_Circular_Rotation()
		{
			// Set stage options
			initStage();
			// Create a background
			var backgroundRect:Shape = getRectShape(backgroundColor, backgroundBorderColor, backgroundBorderWidth, stage.stageWidth, stage.stageHeight)
			addChild(backgroundRect);
			
			// Add output monitor
			stage.addChild(_debugConsole);
			_debugConsole.width = stage.stageWidth;
			_debugConsole.height = stage.stageHeight * .25;
			_debugConsole.y = stage.stageHeight - _debugConsole.height;
			// Disable tracing to Flash External player. Tracing impairs performance.
			if ( Capabilities.playerType == "External")
			{
				_debugConsole.isTracing = false;
			}
			// Instructions
			var instruction_tf:TextField = instructionDisplay();
			addChild(instruction_tf);
			instruction_tf.x = (stage.stageWidth - instruction_tf.width) / 2;
			
			// Create sprite to rotate
			var circle:Circle = new Circle();
			
			// Create a rotator
			_rotator = new Rotator(circle, backgroundRect.width * .3);
			_rotator.x = (stage.stageWidth - _rotator.width) / 2;
			_rotator.y = instruction_tf.y + instruction_tf.height + 5;
			addChild(_rotator);
			_rotator.addEventListener(Event.ENTER_FRAME,rotatorEnterFrameEventHandler);
			
			// Use keyboard events to control the animation
			stage.addEventListener( KeyboardEvent.KEY_DOWN, keyPressed);
			stage.addEventListener( KeyboardEvent.KEY_UP, keyReleased);
			
			_keySpacebarTimer = new Timer(_keySpacebarTimeDelay,1); 
			_keySpacebarTimer.addEventListener(TimerEvent.TIMER, keySpacebarTimerEventHandler);
		}

[ad name=”Google Adsense”]


		/**
		 * Set any stage options per your needs
		 * */
		private function initStage():void 
		{ 
			stage.scaleMode = StageScaleMode.NO_SCALE;
		}
		/**
		 * Instructions display.
		 * */
		private function instructionDisplay():TextField 
		{
			var instructions_tf:TextField = new TextField();		
			var format:TextFormat = new TextFormat();
			format.font = "_typewriter";
			format.color = 0x000000;
			format.size = 12;
			format.indent = 2;
			instructions_tf.defaultTextFormat = format;
			instructions_tf.border = true;
			instructions_tf.borderColor = 0x000000;
			instructions_tf.background = true;
			instructions_tf.backgroundColor = 0xffffff;
			instructions_tf.autoSize = TextFormatAlign.LEFT
			instructions_tf.appendText(_lang_clickStage + "\n");
			instructions_tf.appendText(_lang_enterKey + "\n");
			instructions_tf.appendText(_lang_spacebar + "\n");
			instructions_tf.appendText(_lang_debugCleared + " " + maxDebugConsoleLines + " " + _lang_debugClearedWhen + "\n");
			instructions_tf.appendText(_lang_traceDisabled + "\n");
			instructions_tf.appendText(_lang_keyboardShortcuts);
			
			return instructions_tf;
		}
		/**
		 * Handler for KeyboardEvent.KEY_DOWN
		 * */
		private function keyPressed(evt:KeyboardEvent):void 
		{
			
			switch (evt.keyCode)
			{
				case Keyboard.SPACE:
					
					if (!_keySpacebarTimer.running)
					{
						_keySpacebarTimer.start();
					}	
					break;
			}
		}
		/**
		 * Handler for KeyboardEvent.KEY_UP 
		 * */
		private function keyReleased(evt:KeyboardEvent):void 
		{
			switch (evt.keyCode)
			{
				case Keyboard.SPACE:
					if (_keySpacebarTimer.running)
					{
						_keySpacebarTimer.stop();
						_keySpacebarTimer.reset();
					}
					if (_rotator.isPlaying)
					{
						_rotator.stop();
					}
					else
					{
						_rotator.step();
					}
					
					break;
				case Keyboard.ENTER:
					if (_rotator.isPlaying)
					{
						_rotator.stop();
					}
					else
					{
						_rotator.start();
					}
					break;
			}
		}
		/**
		 * Handler for TimerEvent.TIMER event. Resets the delay interval once the default delay 
		 * is reached.
		 * */
		private function keySpacebarTimerEventHandler(event:TimerEvent):void 
		{
			_rotator.start();
		}
		/**
		 * Handler for _rotator Event.ENTER_FRAME event. 
		 * */
		private function rotatorEnterFrameEventHandler(event:Event):void 
		{
			clearDebugConsoleFlashExternalPlayer();
		}
		/**
		 * Handles slower processing in Flash external player Control->Test Movie
		 * and clears debugConsole when 100 lines are reached.
		 * */
		
		private function clearDebugConsoleFlashExternalPlayer():void
		{
			if (_debugConsole.contentLineCount > maxDebugConsoleLines && _rotator.isPlaying )
			{
				_debugConsole.clear();
				
			}
		}
		/**
		 * Utility to draw a rectangle Shape object 
		 * */
		private function getRectShape(bgColor:uint, borderColor:uint, borderSize:uint, width:uint, height:uint):Shape 
		{
			var newShape:Shape = new Shape();
			newShape.graphics.beginFill(bgColor);
			newShape.graphics.lineStyle(borderSize, borderColor);
			newShape.graphics.drawRect(0, 0, width, height);
			newShape.graphics.endFill();
			return newShape;
		}
	}
}

Rotator Class
This is the class that does the rotation. The main work is done in the move() method on lines 154 – 187. Comments tell what each step accomplishes. The lines past 187 in the move() method are for information only and can be deleted. The move() method only advances the animation one step defined by the _vr variable on line 38. Increase the _vr variable will speed up the animation and decreasing slows the animation down. As an exercise you could make this a speed option via a setter method and the constructor.

The init() method on lines 74 to 94 set the sprite to animate inside a rectangular boundary.

package
{
	import com.lonhosford.util.debug.lite.DebugConsole;
	import com.lonhosford.util.debug.lite.Debugger;
	
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	
	/**
	 *  Purpose: Circular rotation of a sprite around a radius
	 *  <p>Author: Lon Hosford www.lonhosford.com 908 996 3773</p>
	 *  <p>Version history:</p>
	 *  <p>	Number:			1.00.00</p>
	 *  <p>	Date:			06/15/2007</p>
	 *  <p>	Programmer:		Lon Hosford: https://www.lonhosford.com</p>
	 *  <p>	Changes:		Original programming</p>
	 * */	
	public class Rotator extends Sprite
	{
		/**
		 * A sprite object to rotate
		 * */
		private var _spriteToRotate:Sprite;
		/**
		 * Current angle in radians. 
		 * */
		private var _angle:Number = 0;  
		/**
		 * Radius of rotation. 
		 * @default 150
		 * */
		private var _radius:Number;
		/**
		 * Velocity of the angle change in radians. 
		 * @default .05
		 * */
		private var _vr:Number = .03; // Radians
		/**
		 * Center of rotation for x.
		 * */
		private var _centerX:Number;
		/**
		 * Center of rotation for y.
		 * */
		private var _centerY:Number;
		/**
		 * Coordinate x for _spriteToRotate.
		 * */
		private var _coordinateX:Number;
		/**
		 * Coordinate y for _spriteToRotate.
		 * */
		private var _coordinateY:Number;
		/**
		 * Animation is playing.
		 * */
		private var _isPlaying:Boolean = false;
		private var debugConsole:DebugConsole = DebugConsole.getInstance();
		/**
		 * Constructor
		 * @param radius see _radius
		 * */
		public function Rotator(spriteToRotate:Sprite, radius:uint = 150)
		{
			_spriteToRotate = spriteToRotate;
			_radius = radius;
			
			init();
		}

[ad name=”Google Adsense”]

		/**
		 * Initialize the class
		 * */
		private function init():void
		{
			var boundaryBorderWidth:Number = 1
			// Create a boundaries object to set container height and width 
			var boundaries:Shape = new Shape();
			boundaries.graphics.beginFill(0xcccccc, 0);
			boundaries.graphics.lineStyle(boundaryBorderWidth, 0x000000, 0);
			boundaries.graphics.drawRect(0, 0, _radius * 2 + _spriteToRotate.width , _radius * 2 + _spriteToRotate.height );
			boundaries.graphics.endFill();
			addChild(boundaries);
			
			// Compute the center of container
			_centerX = (boundaries.width - boundaryBorderWidth)/2 ;
			_centerY = (boundaries.height - boundaryBorderWidth)/2  ;
			
			// Set starting position for sprite to rotate
			
			// Add sprite
			_spriteToRotate.alpha = .5;
			addChild(_spriteToRotate);
			move();
			
			/*----------------------------------------------------------------------
			Start statistical data for information only.
			Can delete for actual implementations
			----------------------------------------------------------------------*/		
			debugConsole.write("\n");
			debugConsole.write("Rotator.init() - width:" + width);
			debugConsole.write("Rotator.init() - height:" + height);
			debugConsole.write("Rotator.init() - _angle:" + _angle);
			debugConsole.write("Rotator.init() - _radius:" + _radius);
			debugConsole.write("Rotator.init() - Math.cos(_angle):" + Math.cos(_angle));
			debugConsole.write("Rotator.init() - Math.sin(_angle):" + Math.sin(_angle));
			/*----------------------------------------------------------------------
			End statistical data for information only.
			Can delete above for actual implementations
			----------------------------------------------------------------------*/		
		}
		/**
		 * Handler for Event.ENTER_FRAME events
		 * */
		private function onEnterFrame(event:Event):void
		{
			move();
		}
		/**
		 * Get the playing status
		 * */
		public function get isPlaying():Boolean
		{
			return _isPlaying;
		}
		/**
		 * Starts the animation.
		 * */
		public function start():void
		{
			_isPlaying = true;
			addEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		/**
		 * Stops the animation.
		 * */
		public function stop():void
		{
			_isPlaying = false;
			removeEventListener(Event.ENTER_FRAME, onEnterFrame);
		}
		/**
		 * Animates one unit of velocity.
		 * */
		public function step():void
		{
			stop();
			move();
		}

[ad name=”Google Adsense”]

		/**
		 * Animates one velocity unit.
		 * <p>Performs computations for rotation and moves sprite object</p>
		 * */
		private function move():void
		{
			/*----------------------------------------------------------------------
			Sprite coordinates
			----------------------------------------------------------------------*/		
			var _coordinateX:Number = _centerX + Math.cos(_angle) * _radius;
			var _coordinateY:Number = _centerY + Math.sin(_angle) * _radius;
			/*----------------------------------------------------------------------
			Position sprite object
			----------------------------------------------------------------------*/	
			_spriteToRotate.x = _coordinateX;
			_spriteToRotate.y = _coordinateY;
			/*----------------------------------------------------------------------
			Augment the angle by the velocity
			----------------------------------------------------------------------*/		
			_angle += _vr;
			/*----------------------------------------------------------------------
			Draw triangle
			----------------------------------------------------------------------*/		
			graphics.clear();
			graphics.lineStyle(1);
			/*----------------------------------------------------------------------
			Draw hypotenuse
			----------------------------------------------------------------------*/		
			graphics.moveTo( _coordinateX, _coordinateY );
			graphics.lineTo(_centerX, _centerY);
			/*----------------------------------------------------------------------
			Draw adjacent leg
			----------------------------------------------------------------------*/		
			graphics.lineTo(_coordinateX , _centerY);
			/*----------------------------------------------------------------------
			Draw opposite leg
			----------------------------------------------------------------------*/		
			graphics.lineTo(_coordinateX, _coordinateY);
			
			/*----------------------------------------------------------------------
			Start statistical data for information only.
			Can delete for actual implementations.
			dx and dy are not used in the class.
			----------------------------------------------------------------------*/		
			debugConsole.write("\n");
			debugConsole.write("Rotator.move() - _vr:" + _vr);
			debugConsole.write("Rotator.move() - _angle:" + _angle);
			debugConsole.write("Rotator.move() - _coordinateX:" + _coordinateX);
			debugConsole.write("Rotator.move() - _coordinateY:" + _coordinateY);
			debugConsole.write("Rotator.move() - _centerX:" + _centerX);
			debugConsole.write("Rotator.move() - _centerY:" + _centerY);
			var dx:Number = _centerX - _coordinateX;
			var dy:Number = _centerY - _coordinateY;
			debugConsole.write("Rotator.move() - Length: hypotenuse:" + ( Math.sqrt(dx * dx + dy * dy)  ));
			dx = _centerX - _coordinateX;
			dy = 0;
			debugConsole.write("Rotator.move() - Length: adjacent leg:" + ( Math.sqrt(dx * dx + dy * dy)  ));
			dx = 0;
			dy = _centerY - _coordinateY;
			debugConsole.write("Rotator.move() - Length: adjacent leg:" + ( Math.sqrt(dx * dx + dy * dy)  ));
		
			/*----------------------------------------------------------------------
			End statistical data for information only.
			Can delete above for actual implementations
			----------------------------------------------------------------------*/		
			
		}
	}
}

Circle Class
This is the sprite that is revolving in the animation.

package {
	import flash.display.Sprite;
	/**
	 *  Purpose: Draw a circle in a Sprite
	 *  <p>Author: Lon Hosford www.lonhosford.com 908 996 3773</p>
	 *  <p>Version history:</p>
	 *  <p>	Number:			1.00.00</p>
	 *  <p>	Date:			06/15/2007</p>
	 *  <p>	Programmer:		Lon Hosford: https://www.lonhosford.com</p>
	 *  <p>	Changes:		Original programming</p>
	 * */	
	public class Circle extends Sprite {
		/**
		 * Radius of the circle. 
		 * @default 40	
		 * */
		private var _radius:Number;
		/**
		 * Fill color. 
		 * @default 0x000000	
		 * */
		private var _color:uint;
		/**
		 * Constructor.
		 * @param radius See _radius
		 * @param color See _color
		 * */
		public function Circle(radius:Number=40, color:uint=0x000000) 
		{
			_radius = radius;
			_color = color;
			
			init();
			
		}
		/**
		 * Initialize the class
		 * */
		public function init():void 
		{
			draw();
		}
		/**
		 * Draw class graphics
		 * */
		public function draw():void 
		{
			graphics.clear();
			graphics.beginFill(_color,alpha);
			graphics.drawCircle(0, 0, _radius);
			graphics.endFill();
		}
	}
}