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();
		}
	}
}