logo
468x60-2-495


  • Home
  • Privacy Policy
  • About
search
top
Apr 17, 2012 Posted on Apr 17, 2012 in Hints and Tips | 10 comments

ActionScript 3.0 Optimization: A Practical Example

Code optimization aims to maximize the performance of your Flash assets, while using as little of the system’s resources – RAM and CPU – as possible. In this tutorial, starting off with a working but resource-hogging Flash app, we will gradually apply many optimization tweaks to its source code, finally ending up with a faster, leaner SWF.


Final Result Preview

Let’s take a look at the final result we will be working towards:

Note that the “Memory Used” and “CPU Load” stats are based on all the SWFs you have open across all browser windows, including Flash banner ads and the like. This may make the SWF appear more resource intensive than it actually is.


Step 1: Understanding the Flash Movie

The Flash movie has two main elements: a particle simulation of fire, and a graph showing the animation’s resource consumption over time. The graph’s pink line tracks the total memory consumed by the movie in megabytes, and the green line plots CPU load as a percentage.

ActionScript objects take up most of the memory allocated to the Flash Player, and the more ActionScript objects a movie contains, the higher its memory consumption. In order to keep a program’s memory consumption low, the Flash Player regularly does some garbage collection by sweeping through all ActionScript objects and releasing from memory those no longer in use.

A memory consumption graph normally reveals a hilly up-down pattern, dipping each time garbage collection is performed, then slowly rising as new objects are created. A graph line that’s only going up points to a problem with garbage collection, as it means new objects are being added to memory, while none are being removed. If such a trend continues, the Flash player may eventually crash as it runs out of memory.

The CPU load is calculated by tracking the movie’s frame rate. A Flash movie’s frame rate is much like its heartbeat. With each beat, the Flash Player updates and renders all on-screen elements and also runs any required ActionScript tasks.

It is the frame rate that determines how much time Flash Player should spend on each beat, so a frame rate of 10 frames per second (fps) means at least 100 milliseconds per beat. If all the required tasks are performed within that time, then Flash Player will wait for the remaining time to pass before moving on to the next beat. On the other hand, if the required tasks in a particular beat are too CPU intensive to be completed within the given time frame, then the frame rate automatically slows down to allow for some extra time. Once the load lightens, the frame rate speeds up again, back to the set rate.

(The frame rate may also be automatically throttled down to 4fps by the Flash Player when the program’s parent window looses focus or goes offscreen. This is done to conserve system resources whenever the user’s attention is focused elsewhere.)

What this all means is that there are actually two kinds of frame rates: the one you originally set and hope your movie always runs at, and the one it actually runs at. We’ll call the one set by you the target frame rate, and the one it actually runs at the actual frame rate.

The graph’s CPU load is calculated as a ratio of actual to target frame rate. The formula used to calculate this is:

CPU load = ( target frame rate - actual frame rate ) / actual frame rate * 100

For example, if the target frame rate is set to 50fps but the movie actually runs at 25fps, the CPU load will be 50% – that is, ( 50 - 25 )/ 50 * 100.

Please note that this is not the actual percentage of system CPU resources used by the running movie, but rather a rough estimate of the actual value. For the optimization process outlined here, this estimate is a good enough metric for the task at hand. To get the actual CPU usage, use the tools provided by your operating system, e.g. the Task Manager in Windows. Looking at mine it right now, it shows the unoptimized movie is using 53% of CPU resources, while the movie’s graph shows a CPU load of 41.7%.

a screenshot of Flames.swf after all the steps above have been applied.

PLEASE NOTE: All the movie screenshots in this tutorial were taken from the standalone version of Flash Player. The graph will most likely show different numbers on your system, depending on your operating system, browser, and Flash Player version. Having any other currently running Flash apps in different browser windows or flash players may also affect the memory use reported by some systems. When analyzing the perfomance of your program, always ensure that no other Flash programs are running as they may corrupt your metrics.

With the CPU load, expect it to shoot up to over 90% whenever the movie goes off screen – for example if you switch to another browser tab or scroll down the page. The lower frame rate that causes this will not be caused by CPU intensive tasks, but by Flash throttling down the frame rate whenever you look elsewhere. Whenever this happens, wait a few seconds for the CPU load graph to settle to its proper CPU load values after the normal frame rate kicks in.


Step 2: Does This Code Make My Flash Look Fat?

The movie’s source code is shown below and contains just one class, named Flames, which is also the document class. The class contains a set of properties to keep track of the movie’s memory and CPU load history, which is then used to draw a graph. The memory and CPU load statistics are calculated and updated in the Flames.getStats() method, and the graph is drawn by calling Flames.drawGraph() on each frame. To create the fire effect, the Flames.createParticles() method first generates hundreds of particles each second, which are stored in the fireParticles array. This array is then looped through by Flames.drawParticles() , which uses each particle’s properties to create the effect.

Take some time to study the Flames class. Can you already spot any quick changes that will go a long way in optimizing the program?


package com.pjtops{
import flash.display.MovieClip;
import flash.events.Event;
import fl.motion.Color;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.text.TextField;
import flash.system.System;
import flash.utils.getTimer;

public class Flames extends MovieClip{

	private var memoryLog = new Array(); // stores System.totalMemory values for display in the graph
	private var memoryMax = 0; // the highest value of System.totalMemory recorded so far
	private var memoryMin = 0;	// the lowest value of System.totalMemory recorded so far
	private var memoryColor; // the color used by text displaying memory info

	private var ticks = 0; // counts the number of times getStats() is called before the next frame rate value is set
	private var frameRate = 0;	//the original frame rate value as set in Adobe Flash
	private var cpuLog = new Array(); // stores cpu values for display in the graph
	private var cpuMax = 0; // the highest cpu value recorded so far
	private var cpuMin = 0; // the lowest cpu value recorded so far
	private var cpuColor;	// the color used by text displaying cpu
	private var cpu; // the current calculated cpu use	

	private var lastUpdate = 0; // the last time the framerate was calculated
	private var sampleSize = 30; // the length of memoryLog & cpuLog
	private var graphHeight;
	private var graphWidth;

	private var fireParticles = new Array(); // stores all active flame particles
	private var fireMC = new MovieClip(); // the canvas for drawing the flames
	private var palette = new Array(); // stores all available colors for the flame particles
	private var anchors = new Array(); // stores horizontal points along fireMC which act like magnets to the particles
	private var frame; // the movieclips bounding box

	// class constructor. Set up all the events, timers and objects
	public function Flames() {
		addChildAt( fireMC, 1 );
		frame = new Rectangle( 2, 2, stage.stageWidth - 2, stage.stageHeight - 2 );

		var colWidth = Math.floor( frame.width / 10 );
		for( var i = 0; i < 10; i++ ){
			anchors[i] = Math.floor( i * colWidth );
		}

		setPalette();

		memoryColor = memoryTF.textColor;
		cpuColor = cpuTF.textColor;
		graphHeight = graphMC.height;
		graphWidth = graphMC.width;

		frameRate = stage.frameRate;

		addEventListener( Event.ENTER_FRAME, drawParticles );
		addEventListener( Event.ENTER_FRAME, getStats );
		addEventListener( Event.ENTER_FRAME, drawGraph );
	}

	//creates a collection of colors for the flame particles, and stores them in palette
	private function setPalette(){
		var black = 0x000000;
		var blue = 0x0000FF;
		var red = 0xFF0000;
		var orange = 0xFF7F00;
		var yellow = 0xFFFF00;
		var white = 0xFFFFFF;
		palette = palette.concat( getColorRange( black, blue, 10 ) );
		palette = palette.concat( getColorRange( blue, red, 30 ) );
		palette = palette.concat( getColorRange( red, orange, 20 ) );
		palette = palette.concat( getColorRange( orange, yellow, 20 ) );
		palette = palette.concat( getColorRange( yellow, white, 20 ) );
	}

	//returns a collection of colors, made from different mixes of color1 and color2
	private function getColorRange( color1, color2, steps){
		var output = new Array();
		for( var i = 0; i < steps; i++ ){
			var progress = i / steps;
			var color = Color.interpolateColor( color1, color2, progress );
			output.push( color );
		}
		return output;
	}

	// calculates statistics for the current state of the application, in terms of memory used and the cpu %
	private function getStats( event ){
		ticks++;
		var now = getTimer();

		if( now - lastUpdate < 1000 ){
			return;
		}else {
			lastUpdate = now;
		}

		cpu = 100 - ticks / frameRate * 100;
		cpuLog.push( cpu );
		ticks = 0;
		cpuTF.text = cpu.toFixed(1) + '%';
		if( cpu > cpuMax ){
			cpuMax = cpu;
			cpuMaxTF.text = cpuTF.text;
		}
		if( cpu < cpuMin || cpuMin == 0 ){
			cpuMin = cpu;
			cpuMinTF.text = cpuTF.text;
		}

		var memory = System.totalMemory / 1000000;
		memoryLog.push( memory );
		memoryTF.text = String( memory.toFixed(1) ) + 'mb';
		if( memory > memoryMax ){
			memoryMax = memory;
			memoryMaxTF.text = memoryTF.text;
		}
		if( memory < memoryMin || memoryMin == 0 ){
			memoryMin = memory;
			memoryMinTF.text = memoryTF.text;
		}
	}

	//render's a graph on screen, that shows trends in the applications frame rate and memory consumption
	private function drawGraph( event ){
		graphMC.graphics.clear();
		var ypoint, xpoint;
		var logSize = memoryLog.length; 

		if( logSize > sampleSize ){
			memoryLog.shift();
			cpuLog.shift();
			logSize = sampleSize;
		}
		var widthRatio = graphMC.width / logSize;	

		graphMC.graphics.lineStyle( 3, memoryColor, 0.9 );
		var memoryRange = memoryMax - memoryMin;
		for( var i = 0; i < memoryLog.length; i++ ){
			ypoint = ( memoryLog[i] - memoryMin ) / memoryRange * graphHeight;
			xpoint = (i / sampleSize) * graphWidth;
			if( i == 0 ){
				graphMC.graphics.moveTo(  xpoint, -ypoint );
				continue;
			}
			graphMC.graphics.lineTo( xpoint, -ypoint );
		}

		graphMC.graphics.lineStyle( 3, cpuColor, 0.9 );
		for( var j = 0; j < cpuLog.length; j++ ){
			ypoint = cpuLog[j] / 100 * graphHeight;
			xpoint = ( j / sampleSize ) * graphWidth;
			if( j == 0 ){
				graphMC.graphics.moveTo(  xpoint, -ypoint );
				continue;
			}
			graphMC.graphics.lineTo( xpoint, -ypoint );
		}
	}

	//renders each flame particle and updates it's values
	private function drawParticles( event ) {
		createParticles( 20 ); 

		fireMC.graphics.clear();
		for ( var i in fireParticles ) {
			var particle = fireParticles[i]; 

			if (particle.life == 0 ) {
				delete( fireParticles[i] );
				continue;
			}

			var size = Math.floor( particle.size * particle.life/100 );
			var color = palette[ particle.life ];
			var transperency = 0.3;								

			if( size < 3 ){
				size *= 3;
				color = 0x333333;
				particle.x += Math.random() * 8 - 4;
				particle.y -= 2;
				transperency = 0.2;
			}else {
				particle.y = frame.bottom - ( 100 - particle.life );

				if( particle.life > 90 ){
					size *= 1.5;
				}else if( particle.life > 45){
					particle.x += Math.floor( Math.random() * 6 - 3 );
					size *= 1.2;
				}else {
					transperency = 0.1;
					size *= 0.3;
					particle.x += Math.floor( Math.random() * 4 - 2 );
				}
			}				

			fireMC.graphics.lineStyle( 5, color, 0.1 );
			fireMC.graphics.beginFill( color, transperency );
			fireMC.graphics.drawCircle( particle.x, particle.y, size );
			fireMC.graphics.endFill();
			particle.life--;
		}
	}

	//generates flame particle objects
	private function createParticles( count ){
		var anchorPoint = 0;
		for(var i = 0; i < count; i++){
			var particle = new Object();
			particle.x = Math.floor( Math.random() * frame.width / 10 ) + anchors[anchorPoint];
			particle.y = frame.bottom;
			particle.life = 70 + Math.floor( Math.random() * 30 );
			particle.size = 5 + Math.floor( Math.random() * 10 );
			fireParticles.push( particle );	

			if(particle.size > 12){
				particle.size = 10;
			}
			particle.anchor = anchors[anchorPoint] + Math.floor( Math.random() * 5 );

			anchorPoint = (anchorPoint == 9)? 0 : anchorPoint + 1;
		}
	}

}
}

It’s a lot to take in, so don’t worry – we’ll go through the various improvements in the rest of this tutorial.


Step 3: Use Strong Typing by Assigning Data Types to All Variables

The first change we’ll make to the class is to specify the data type of all declared variables, method parameters, and method return values.

For example, changing this

protected var memoryLog = new Array();
protected var memoryMax = 0; // yes, but what exactly are you?

to this.

protected var memoryLog:Array = new Array();
protected var memoryMax:Number = 0; // memoryMax the Number, much better!

Whenever declaring variables, always specify the data type, as this allows the Flash compiler to perform some extra optimizations when generating the SWF file. This alone can lead to big performance improvements, as we’ll soon see with our example. Another added benefit of strong typing is that the compiler will catch and alert you of any data-type related bugs.


Step 4: Examine Results

a screenshot of Flames.swf after all the steps above have been applied.

This screen shot shows the new Flash movie, after applying strong typing. We can see that while it’s had no effect on the current or maximum CPU load, the minimum value has dropped from 8.3% to 4.2%. The maximum memory consumed has gone down from 9MB to 8.7MB.

The slope of the graph’s memory line has also changed, compared to the one shown in Step 2. It still has the same jagged pattern, but now drops and rises at a slower rate. This is a good thing, if you consider that the sudden drops in memory consumption are caused by Flash Player’s garbage collection, which is usually triggered when allocated memory is about to run out. This garbage collection can be an expensive operation, since Flash Player has to traverse through all the objects, looking for those that are no longer needed but still taking up memory. The less often it has to do this, the better.


Step 5: Efficiently Store Numeric Data

Actionscript provides three numeric data types: Number , uint and int . Of the three types, Number consumes the most memory as it can store larger numeric values than the other two. It is also the only type able to store numbers with decimal fractions.

The Flames class has many numeric properties, all of which use the Number data type. As int and uint are more compact data types, we can save some memory by using them instead of Numbers in all situations where we don’t need decimal fractions.

A good example is in loops and Array indexes, so for example we are going to change

for( var i:Number = 0; i < 10; i++ ){
	anchors[i] = Math.floor( i * colWidth );
}

into

for( var i:int = 0; i < 10; i++ ){
	anchors[i] = Math.floor( i * colWidth );
}

The properties cpu , cpuMax and memoryMax will remain Numbers, as they will most likely store fractional data, while memoryColor , cpuColor and ticks can be changed to uints, as they will always store positive, whole numbers.


Step 6: Minimize Method Calls

Method calls are expensive, especially calling a method from a different class. It gets worse if that class belongs to a different package, or is a static method. The best example here is the Math.floor() method, used throughout the Flames class to roundoff fractional numbers. This method call can be avoided by using uints instead of Numbers to store whole numbers.

// So instead of having:
anchors[i] = Math.floor( i * colWidth );
// we instead cast the value to a uint
anchors[i] = uint( i * colWidth );

// The same optimization can be performed by simply assigning the uint data type, for example changing
var size:uint = Math.floor( particle.size * particle.life/100 );
// into
var size:uint = particle.size * particle.life/100;

In the example above, the call to Math.floor() is unnecessary, since Flash will automatically round off any fractional number value assigned to a uint.


Step 7: Multiplication Is Faster Than Division

Flash Player apparently finds multiplication easier than division, so we’ll go through the Flames class and convert any division math into the equivalent multiplication math. The conversion formula involves getting the reciprocal of the number on the right side of the operation, and multiplying it with the number on the left. The reciprocal of a number is calculated by dividing 1 by that number.

var colWidth:uint = frame.width / 10; //division by ten
var colWidth:uint = frame.width * 0.1; //produces the same result as multiplication by 0.1

Lets take a quick look at the results of our recent optimization efforts. The CPU load has finally improved by dropping from 41.7% to 37.5%, but the memory consumption tells a different story. Maximum memory has risen to 9.4MB, the highest level yet, and the graph’s sharp, saw-tooth edges shows that garbage collection is being run more often again. Some optimization techniques will have this inverse effect on memory and CPU load, improving one at the expense of the other. With memory consumption almost back to square one, a lot more work still needs to be done.

a screenshot of Flames.swf after all the steps above have been applied.

Step 8: Recycling Is Good for the Environment

You too can play your part in saving the environment. Recycle your objects when writing your AS3 code reduce the amount of energy consumed by your programs. Both the creation and destruction of new objects are expensive operations. If your program is constantly creating and destroying objects of the same type, big performance gains can be achieved by recycling those objects instead. Looking at the Flames class, we can see that a lot of particle objects are being created and destroyed every second:

private function drawParticles( event ):void {
	createParticles( 20 ); 

	fireMC.graphics.clear();
	for ( var i:* in fireParticles ) {
		var particle:Object = fireParticles[i]; 

		if (particle.life == 0 ) {
			delete( fireParticles[i] );
			continue;
		}

There are many ways to recycle objects, most involve creating a second variable to store unneeded objects instead of deleting them. Then when a new object of the same type is required, it is retrieved from the store instead of creating a new one. New objects are only created when the store is empty. We are going to do something similar with the particle objects of the Flames class.

First, we create a new array called inactiveFireParticles[] , which stores references to particles whose life property is zero (dead particles). In the drawParticles() method, instead of deleting a dead particle, we add it to the inactiveFireParticles[] array.

private function drawParticles( event ):void {
	createParticles( 20 ); 

	fireMC.graphics.clear();
	for ( var i:* in fireParticles ) {
		var particle:Object = fireParticles[i]; 

		if( particle.life <= 0 ) {
			if( particle.life == 0 ){
				particle.life = -1;
				inactiveFireParticles.push( particle );
			}
			continue;
		}

Next, we modify the createParticles() method to first check for any stored particles in the inactiveFireParticles[] array, and use them all before creating any new particles.

private function createParticles( count ):void{
	var anchorPoint = 0;
	for(var i:uint = 0; i < count; i++){

		var particle:Object;
		if( inactiveFireParticles.length > 0 ){
			particle = inactiveFireParticles.shift();
		}else {
			particle = new Object();
			fireParticles.push( particle );
		}				

		particle.x = uint( Math.random() * frame.width * 0.1 ) + anchors[anchorPoint];
		particle.y = frame.bottom;
		particle.life = 70 + uint( Math.random() * 30 );
		particle.size = 5 + uint( Math.random() * 10 );

		if(particle.size > 12){
			particle.size = 10;
		}
		particle.anchor = anchors[anchorPoint] + uint( Math.random() * 5 );

		anchorPoint = (anchorPoint == 9)? 0 : anchorPoint + 1;
	}
}

Step 9: Use Object and Array Literals Whenever Possible

When creating new objects or arrays, using the literal syntax is faster than using the new operator.

private var memoryLog:Array = new Array(); // array created using the new operator
private var memoryLog:Array = []; // array created using the faster array literal

particle = new Object(); // object created using the new operator
particle = {}; // object created using the faster object literal

Step 10: Avoid Using Dynamic Classes

Classes in ActionScript can either be sealed or dynamic. They’re sealed by default, meaning the only properties and methods an object derived from it can have must have been defined in the class. With dynamic classes, new properties and methods can be added at runtime. Sealed classes are more efficient than dynamic classes, because some Flash Player performance optimizations can be done when all the possible functionality that a class can ever have are known beforehand.

Within the Flames class, the thousands of particles extend the built-in Object class, which is dynamic. Since no new properties need to be added to a particle at runtime, we’ll save up more resources by creating a custom sealed class for the particles.

Here is the new Particle, which has been added to the same Flames.as file.

class Particle{
	public var x:uint;
	public var y:uint;
	public var life:int;
	public var size:Number;
	public var anchor:uint;
}

The createParticles () method will also be adjusted, changing the line

var particle:Object;
particle = {};

to instead read:

var particle:Particle;
particle = new Particle();

Step 11: Use Sprites When You Don’t Need the Timeline

Like the Object class, MovieClips are dynamic classes. The MovieClip class inherits from the Sprite class, and the main difference between the two is that MovieClip has a timeline. Since Sprites have all the functionality of MovieClips minus the timeline, use them whenever you need a DisplayObject that does not need the timeline. The Flames class extends the MovieClip but it does not use the timeline, as all its animation is controlled through ActionScript. The fire particles are drawn on fireMC , which is also a MovieClip that does not make use of its timeline.

We change both Flames and fireMC to extend Sprite instead, replacing:

import flash.display.MovieClip;
private var fireMC:MovieClip = new MovieClip();
public class Flames extends MovieClip{

with

import flash.display.Sprite;
private var fireMC:Sprite = new Sprite();
public class Flames extends Sprite{

Step 12: Use Shapes Instead of Sprites When You Don’t Need Child Display Objects or Mouse Input

The Shape class is even lighter than the Sprite class, but it cannot support mouse events or contain child display objects. As the fireMC requires none of this functionality, we can safely turn it into a Shape.

import flash.display.Shape;
private var fireMC:Shape = new Shape();
a screenshot of Flames.swf after all the steps above have been applied.

The graph shows big improvements in memory consumption, with it dropping and remaining stable at 4.8MB. The saw-tooth edges have been replaced by an almost straight horizontal line, meaning garbage collection is now rarely run. But the CPU load has mostly gone back again to its original high level of 41.7%.


Step 13: Avoid Complex Calculations Inside Loops

They say over 50% of a program’s time is spent running 10% of its code, and most of that 10% is most likely to be taken up by loops. Many loop optimization techniques involve placing as much of the CPU intensive operations outside the body of a loop. These operations include object creation, variable lookups and calculations.

for( var i = 0; i < memoryLog.length; i++ ){
	// loop body
}

The first loop in the drawGraph() method is shown above. The loop runs through every item of the memoryLog array, using each value to plot points on the graph. At the start of each run, it looks up the length of the memoryLog array and compares it with the loop counter. If the memoryLog array has 200 items, the loop runs 200 times, and performs this same lookup 200 times. Since the length of memoryLog does not change, the repeated lookups are wasteful and unnecessary. It’s better to look up the value of memoryLog.length just once before the lookup begins and store it in a local variable, since accessing a local variable will be faster than accessing an object’s property.

var memoryLogLength:uint = memoryLog.length;
for( var i = 0; i < memoryLogLength; i++ ){
	// loop body
}

In the Flames class, we adjust the two loops in the drawGraph() method as shown above.


Step 14: Place Conditional Statements Most Likely to Be True First

Consider the block of if..else conditionals below, derived from the drawParticles () method:

if( particle.life > 90 ){ // a range of 10 values, between 91 - 100
	size *= 1.5;
}else if( particle.life > 45){ // a range of 45 values, between 46 - 90
	particle.x += Math.random() * 6 - 3;
	size *= 1.2;
}else {	// a range of 45 values, values between 0 - 45
	transperency = 0.1;
	size *= 0.3;
	particle.x += Math.random() * 4 - 2;
}

A particle’s life value can be any number between 0 and 100. The if clause tests whether the current particle’s life is between 91 to 100, and if so it executes the code within that block. The else-if clause tests for a value between 46 and 90, while the else clause takes the remaining values, those between 0 and 45. Considering the first check is also the least likely to succeed as it has the smallest range of numbers, it should be the last condition tested. The block is rewritten as shown below, so that the most likely conditions are evaluated first, making the evaluations more efficient.

if( particle.life < 46 ){
	transperency = 0.1;
	size *= 0.3;
	particle.x += Math.random() * 4 - 2;
}else if( particle.life < 91 ){
	particle.x += Math.random() * 6 - 3;
	size *= 1.2;
}else {
	size *= 1.5;
}

Step 15: Add Elements to the End of an Array Without Pushing

The method Array.push() is used quite a lot in the Flames class. It will be replaced by a faster technique that uses the array’s length property.

cpuLog.push( cpu ); // slow and pretty
cpuLog[ cpuLog.length ] = cpu; // fast and ugly

When we know the length of the array, we can replace Array.push() with an even faster technique, as shown below.

var output:Array = [];	//output is a new, empty array. Its length is 0
for( var i:uint = 0; i < steps; i++ ){ // the value of i also starts at zero. Each loop cycle increases both i and output.length by one
	var progress:Number = i / steps;
	var color:uint = Color.interpolateColor( color1, color2, progress );
	output[i] = color;	// faster than cpuLog[ cpuLog.length ] = cpu;
}

Step 16: Replace Arrays With Vectors

The Array and Vector classes are very similar, except for two major differences: Vectors can only store objects of the same type, and they’re more efficient and faster than arrays. Since all the arrays in the Flames class either store variables of only one type – ints, uints or Particles, as required – we shall convert them all to Vectors.

These arrays:

private var memoryLog:Array = [];
private var cpuLog:Array = [];
private var fireParticles:Array = [];
private var palette:Array = [];
private var anchors:Array = [];
private var inactiveFireParticles:Array = [];

…are replaced with their Vector equivalents:

private var memoryLog:Vector.<Number> = new Vector.<Number>();
private var cpuLog:Vector.<Number> = new Vector.<Number>();
private var fireParticles:Vector.<Particle> = new Vector.<Particle>();
private var palette:Vector.<uint> = new Vector.<uint>();
private var anchors:Vector.<uint> = new Vector.<uint>();
private var inactiveFireParticles:Vector.<Particle> = new Vector.<Particle>();

Then we modify the getColorRange() method to work with Vectors rather than arrays.

private function getColorRange( color1, color2, steps):Vector.<uint>{
	var output:Vector.<uint> =  new Vector.<uint>();
	for( var i:uint = 0; i < steps; i++ ){
		var progress:Number = i / steps;
		var color:uint = Color.interpolateColor( color1, color2, progress );
		output[i] = color;
	}
	return output;
}

Step 17: Use the Event Model Sparingly

While very convenient and handy, the AS3 Event Model is built on top of an elaborate setup of event listeners, dispatchers and objects; then there is event propagation and bubbling and much more, all of which a book can be written about. Whenever possible, always call a method directly rather than through the event model.

addEventListener( Event.ENTER_FRAME, drawParticles );
addEventListener( Event.ENTER_FRAME, getStats );
addEventListener( Event.ENTER_FRAME, drawGraph );

The Flames class has three event listeners calling three different methods, and all bound to the ENTER_FRAME event. In this case, we can keep the first event listener and get rid of the other two, then have the drawParticles () method call getStats() , which in turn calls drawGraph() . Alternatively, we can simply create a new method that calls the getStats(), drawGraph() and drawParticles () for us directly, then have just one event listener that’s bound to the new method. The second option is more expensive however, so we’ll stick with the first.

// this line is added before the end of the <code> drawParticles </code>() method
getStats();
// this line is added before the end of the <code> getStats() </code> method
drawGraph();

We also remove the event parameter (which holds the Event object) from both the drawGraph() and getStats() , as they are no longer needed.


Step 18: Disable All Mouse Events for Display Objects That Do Not Need It

Since this Flash animation does not require any user interaction, we can free its display object from dispatching unnecessary mouse events. In the Flames class, we do that by setting its mouseEnabled property to false. We also do the same for all its children by setting the mouseChildren property to false. The following lines are added to the Flames constructor:

mouseEnabled = false;
mouseChildren = false;

Step 19: Use the Graphics.drawPath() Method to Draw Complex Shapes

The Graphics.drawPath() is optimized for performance when drawing complex paths with many lines or curves. In the Flames.drawGraph() method, the CPU load and memory consumption graph lines are both drawn using a combination of Graphics.moveTo() and Graphics.lineTo() methods.

for( var i = 0; i < memoryLogLength; i++ ){
	ypoint = ( memoryLog[i] - memoryMin ) / memoryRange * graphHeight;
	xpoint = (i / sampleSize) * graphWidth;
	if( i == 0 ){
		graphMC.graphics.moveTo(  xpoint, -ypoint );
		continue;
	}
	graphMC.graphics.lineTo( xpoint, -ypoint );
}

We replace the original drawing methods with calls to Graphics.drawPath(). An added advantage of the revised code below is that we also get to remove the drawing commands from the loops.

var commands:Vector.<int> = new Vector.<int>();
var data:Vector.<Number> = new Vector.<Number>();

for( var i = 0; i < memoryLogLength; i++ ){
	ypoint = ( memoryLog[i] - memoryMin ) / memoryRange * graphHeight;
	xpoint = (i / sampleSize) * graphWidth;

	if( i == 0 ){
		data[ data.length ] = xpoint;
		data[ data.length ] = -ypoint;
		commands[ commands.length ] = 1;
	}

	data[ data.length ] = xpoint;
	data[ data.length ] = -ypoint;
	commands[ commands.length ] = 2;
}
graphMC.graphics.drawPath( commands, data );

Step 20: Make the Classes Final

The final attribute specifies that a method cannot be overridden or that a class cannot be extended. It can also make a class run faster, so we’ll make both the Flames and Particle classes final.

Edit: Reader Moko pointed us to this great article by Jackson Dunstan, which remarks that the final keyword does not actually have any effect on performance.

a screenshot of Flames.swf after all the steps above have been applied.

The CPU load is now 33.3%, while the total memory used stays between 4.8 and 5MB. We’ve come a long way from the CPU load of 41.7% and peak memory size of 9MB!

Which brings us to one of the most important decisions to be made in an optimization process: knowing when to stop. If you stop too early, your game or application may perform poorly on low end systems, and if you go too far, your code may get more obfuscated and harder to maintain. With this particular application, the animation looks smooth and fluid while CPU and memory usage are under control, so we’ll stop here.


Summary

We have just looked at the process of optimization, using the Flames class as an example. While the many optimization tips were presented in a step by step fashion, the order doesn’t really matter. What’s important is being aware of the many issues that can slow down our program, and taking measures to correct them.

But remember to watch out for premature optimization; focus first on building your program and making it work, then start tweaking performance.



View full post on Activetuts+

banner ad

10 Responses to “ActionScript 3.0 Optimization: A Practical Example”

  1. Patrick Jaoko says:
    April 17, 2012 at 1:03 am

    Code optimization aims to maximize the performance of your Flash assets, while using as little of the system’s resources – RAM and CPU – as possible. In this tutorial, starting off with a working but resource-hogging Flash app, we will gradually apply many optimization tweaks to its source code, finally ending up with a faster, leaner SWF.


    Final Result Preview

    Let’s take a look at the final result we will be working towards:

    Note that the “Memory Used” and “CPU Load” stats are based on all the SWFs you have open across all browser windows, including Flash banner ads and the like. This may make the SWF appear more resource intensive than it actually is.


    Step 1: Understanding the Flash Movie

    The Flash movie has two main elements: a particle simulation of fire, and a graph showing the animation’s resource consumption over time. The graph’s pink line tracks the total memory consumed by the movie in megabytes, and the green line plots CPU load as a percentage.

    ActionScript objects take up most of the memory allocated to the Flash Player, and the more ActionScript objects a movie contains, the higher its memory consumption. In order to keep a program’s memory consumption low, the Flash Player regularly does some garbage collection by sweeping through all ActionScript objects and releasing from memory those no longer in use.

    A memory consumption graph normally reveals a hilly up-down pattern, dipping each time garbage collection is performed, then slowly rising as new objects are created. A graph line that’s only going up points to a problem with garbage collection, as it means new objects are being added to memory, while none are being removed. If such a trend continues, the Flash player may eventually crash as it runs out of memory.

    The CPU load is calculated by tracking the movie’s frame rate. A Flash movie’s frame rate is much like its heartbeat. With each beat, the Flash Player updates and renders all on-screen elements and also runs any required ActionScript tasks.

    It is the frame rate that determines how much time Flash Player should spend on each beat, so a frame rate of 10 frames per second (fps) means at least 100 milliseconds per beat. If all the required tasks are performed within that time, then Flash Player will wait for the remaining time to pass before moving on to the next beat. On the other hand, if the required tasks in a particular beat are too CPU intensive to be completed within the given time frame, then the frame rate automatically slows down to allow for some extra time. Once the load lightens, the frame rate speeds up again, back to the set rate.

    (The frame rate may also be automatically throttled down to 4fps by the Flash Player when the program’s parent window looses focus or goes offscreen. This is done to conserve system resources whenever the user’s attention is focused elsewhere.)

    What this all means is that there are actually two kinds of frame rates: the one you originally set and hope your movie always runs at, and the one it actually runs at. We’ll call the one set by you the target frame rate, and the one it actually runs at the actual frame rate.

    The graph’s CPU load is calculated as a ratio of actual to target frame rate. The formula used to calculate this is:

    CPU load = ( target frame rate - actual frame rate ) / actual frame rate * 100

    For example, if the target frame rate is set to 50fps but the movie actually runs at 25fps, the CPU load will be 50% – that is, ( 50 - 25 )/ 50 * 100.

    Please note that this is not the actual percentage of system CPU resources used by the running movie, but rather a rough estimate of the actual value. For the optimization process outlined here, this estimate is a good enough metric for the task at hand. To get the actual CPU usage, use the tools provided by your operating system, e.g. the Task Manager in Windows. Looking at mine it right now, it shows the unoptimized movie is using 53% of CPU resources, while the movie’s graph shows a CPU load of 41.7%.

    a screenshot of Flames.swf after all the steps above have been applied.

    PLEASE NOTE: All the movie screenshots in this tutorial were taken from the standalone version of Flash Player. The graph will most likely show different numbers on your system, depending on your operating system, browser, and Flash Player version. Having any other currently running Flash apps in different browser windows or flash players may also affect the memory use reported by some systems. When analyzing the perfomance of your program, always ensure that no other Flash programs are running as they may corrupt your metrics.

    With the CPU load, expect it to shoot up to over 90% whenever the movie goes off screen – for example if you switch to another browser tab or scroll down the page. The lower frame rate that causes this will not be caused by CPU intensive tasks, but by Flash throttling down the frame rate whenever you look elsewhere. Whenever this happens, wait a few seconds for the CPU load graph to settle to its proper CPU load values after the normal frame rate kicks in.


    Step 2: Does This Code Make My Flash Look Fat?

    The movie’s source code is shown below and contains just one class, named Flames, which is also the document class. The class contains a set of properties to keep track of the movie’s memory and CPU load history, which is then used to draw a graph. The memory and CPU load statistics are calculated and updated in the Flames.getStats() method, and the graph is drawn by calling Flames.drawGraph() on each frame. To create the fire effect, the Flames.createParticles() method first generates hundreds of particles each second, which are stored in the fireParticles array. This array is then looped through by Flames.drawParticles() , which uses each particle’s properties to create the effect.

    Take some time to study the Flames class. Can you already spot any quick changes that will go a long way in optimizing the program?

    
    
    
    package com.pjtops{
    import flash.display.MovieClip;
    import flash.events.Event;
    import fl.motion.Color;
    import flash.geom.Point;
    import flash.geom.Rectangle;
    import flash.text.TextField;
    import flash.system.System;
    import flash.utils.getTimer;
    
    public class Flames extends MovieClip{
    
    	private var memoryLog = new Array(); // stores System.totalMemory values for display in the graph
    	private var memoryMax = 0; // the highest value of System.totalMemory recorded so far
    	private var memoryMin = 0;	// the lowest value of System.totalMemory recorded so far
    	private var memoryColor; // the color used by text displaying memory info
    
    	private var ticks = 0; // counts the number of times getStats() is called before the next frame rate value is set
    	private var frameRate = 0;	//the original frame rate value as set in Adobe Flash
    	private var cpuLog = new Array(); // stores cpu values for display in the graph
    	private var cpuMax = 0; // the highest cpu value recorded so far
    	private var cpuMin = 0; // the lowest cpu value recorded so far
    	private var cpuColor;	// the color used by text displaying cpu
    	private var cpu; // the current calculated cpu use	
    
    	private var lastUpdate = 0; // the last time the framerate was calculated
    	private var sampleSize = 30; // the length of memoryLog & cpuLog
    	private var graphHeight;
    	private var graphWidth;
    
    	private var fireParticles = new Array(); // stores all active flame particles
    	private var fireMC = new MovieClip(); // the canvas for drawing the flames
    	private var palette = new Array(); // stores all available colors for the flame particles
    	private var anchors = new Array(); // stores horizontal points along fireMC which act like magnets to the particles
    	private var frame; // the movieclips bounding box
    
    	// class constructor. Set up all the events, timers and objects
    	public function Flames() {
    		addChildAt( fireMC, 1 );
    		frame = new Rectangle( 2, 2, stage.stageWidth - 2, stage.stageHeight - 2 );
    
    		var colWidth = Math.floor( frame.width / 10 );
    		for( var i = 0; i < 10; i++ ){
    			anchors[i] = Math.floor( i * colWidth );
    		}
    
    		setPalette();
    
    		memoryColor = memoryTF.textColor;
    		cpuColor = cpuTF.textColor;
    		graphHeight = graphMC.height;
    		graphWidth = graphMC.width;
    
    		frameRate = stage.frameRate;
    
    		addEventListener( Event.ENTER_FRAME, drawParticles );
    		addEventListener( Event.ENTER_FRAME, getStats );
    		addEventListener( Event.ENTER_FRAME, drawGraph );
    	}
    
    	//creates a collection of colors for the flame particles, and stores them in palette
    	private function setPalette(){
    		var black = 0x000000;
    		var blue = 0x0000FF;
    		var red = 0xFF0000;
    		var orange = 0xFF7F00;
    		var yellow = 0xFFFF00;
    		var white = 0xFFFFFF;
    		palette = palette.concat( getColorRange( black, blue, 10 ) );
    		palette = palette.concat( getColorRange( blue, red, 30 ) );
    		palette = palette.concat( getColorRange( red, orange, 20 ) );
    		palette = palette.concat( getColorRange( orange, yellow, 20 ) );
    		palette = palette.concat( getColorRange( yellow, white, 20 ) );
    	}
    
    	//returns a collection of colors, made from different mixes of color1 and color2
    	private function getColorRange( color1, color2, steps){
    		var output = new Array();
    		for( var i = 0; i < steps; i++ ){
    			var progress = i / steps;
    			var color = Color.interpolateColor( color1, color2, progress );
    			output.push( color );
    		}
    		return output;
    	}
    
    	// calculates statistics for the current state of the application, in terms of memory used and the cpu %
    	private function getStats( event ){
    		ticks++;
    		var now = getTimer();
    
    		if( now - lastUpdate < 1000 ){
    			return;
    		}else {
    			lastUpdate = now;
    		}
    
    		cpu = 100 - ticks / frameRate * 100;
    		cpuLog.push( cpu );
    		ticks = 0;
    		cpuTF.text = cpu.toFixed(1) + '%';
    		if( cpu > cpuMax ){
    			cpuMax = cpu;
    			cpuMaxTF.text = cpuTF.text;
    		}
    		if( cpu < cpuMin || cpuMin == 0 ){
    			cpuMin = cpu;
    			cpuMinTF.text = cpuTF.text;
    		}
    
    		var memory = System.totalMemory / 1000000;
    		memoryLog.push( memory );
    		memoryTF.text = String( memory.toFixed(1) ) + 'mb';
    		if( memory > memoryMax ){
    			memoryMax = memory;
    			memoryMaxTF.text = memoryTF.text;
    		}
    		if( memory < memoryMin || memoryMin == 0 ){
    			memoryMin = memory;
    			memoryMinTF.text = memoryTF.text;
    		}
    	}
    
    	//render's a graph on screen, that shows trends in the applications frame rate and memory consumption
    	private function drawGraph( event ){
    		graphMC.graphics.clear();
    		var ypoint, xpoint;
    		var logSize = memoryLog.length; 
    
    		if( logSize > sampleSize ){
    			memoryLog.shift();
    			cpuLog.shift();
    			logSize = sampleSize;
    		}
    		var widthRatio = graphMC.width / logSize;	
    
    		graphMC.graphics.lineStyle( 3, memoryColor, 0.9 );
    		var memoryRange = memoryMax - memoryMin;
    		for( var i = 0; i < memoryLog.length; i++ ){
    			ypoint = ( memoryLog[i] - memoryMin ) / memoryRange * graphHeight;
    			xpoint = (i / sampleSize) * graphWidth;
    			if( i == 0 ){
    				graphMC.graphics.moveTo(  xpoint, -ypoint );
    				continue;
    			}
    			graphMC.graphics.lineTo( xpoint, -ypoint );
    		}
    
    		graphMC.graphics.lineStyle( 3, cpuColor, 0.9 );
    		for( var j = 0; j < cpuLog.length; j++ ){
    			ypoint = cpuLog[j] / 100 * graphHeight;
    			xpoint = ( j / sampleSize ) * graphWidth;
    			if( j == 0 ){
    				graphMC.graphics.moveTo(  xpoint, -ypoint );
    				continue;
    			}
    			graphMC.graphics.lineTo( xpoint, -ypoint );
    		}
    	}
    
    	//renders each flame particle and updates it's values
    	private function drawParticles( event ) {
    		createParticles( 20 ); 
    
    		fireMC.graphics.clear();
    		for ( var i in fireParticles ) {
    			var particle = fireParticles[i]; 
    
    			if (particle.life == 0 ) {
    				delete( fireParticles[i] );
    				continue;
    			}
    
    			var size = Math.floor( particle.size * particle.life/100 );
    			var color = palette[ particle.life ];
    			var transperency = 0.3;								
    
    			if( size < 3 ){
    				size *= 3;
    				color = 0x333333;
    				particle.x += Math.random() * 8 - 4;
    				particle.y -= 2;
    				transperency = 0.2;
    			}else {
    				particle.y = frame.bottom - ( 100 - particle.life );
    
    				if( particle.life > 90 ){
    					size *= 1.5;
    				}else if( particle.life > 45){
    					particle.x += Math.floor( Math.random() * 6 - 3 );
    					size *= 1.2;
    				}else {
    					transperency = 0.1;
    					size *= 0.3;
    					particle.x += Math.floor( Math.random() * 4 - 2 );
    				}
    			}				
    
    			fireMC.graphics.lineStyle( 5, color, 0.1 );
    			fireMC.graphics.beginFill( color, transperency );
    			fireMC.graphics.drawCircle( particle.x, particle.y, size );
    			fireMC.graphics.endFill();
    			particle.life--;
    		}
    	}
    
    	//generates flame particle objects
    	private function createParticles( count ){
    		var anchorPoint = 0;
    		for(var i = 0; i < count; i++){
    			var particle = new Object();
    			particle.x = Math.floor( Math.random() * frame.width / 10 ) + anchors[anchorPoint];
    			particle.y = frame.bottom;
    			particle.life = 70 + Math.floor( Math.random() * 30 );
    			particle.size = 5 + Math.floor( Math.random() * 10 );
    			fireParticles.push( particle );	
    
    			if(particle.size > 12){
    				particle.size = 10;
    			}
    			particle.anchor = anchors[anchorPoint] + Math.floor( Math.random() * 5 );
    
    			anchorPoint = (anchorPoint == 9)? 0 : anchorPoint + 1;
    		}
    	}
    
    }
    }
    

    It’s a lot to take in, so don’t worry – we’ll go through the various improvements in the rest of this tutorial.


    Step 3: Use Strong Typing by Assigning Data Types to All Variables

    The first change we’ll make to the class is to specify the data type of all declared variables, method parameters, and method return values.

    For example, changing this

    
    
    protected var memoryLog = new Array();
    protected var memoryMax = 0; // yes, but what exactly are you?
    

    to this.

    
    
    protected var memoryLog:Array = new Array();
    protected var memoryMax:Number = 0; // memoryMax the Number, much better!
    

    Whenever declaring variables, always specify the data type, as this allows the Flash compiler to perform some extra optimizations when generating the SWF file. This alone can lead to big performance improvements, as we’ll soon see with our example. Another added benefit of strong typing is that the compiler will catch and alert you of any data-type related bugs.


    Step 4: Examine Results

    a screenshot of Flames.swf after all the steps above have been applied.

    This screen shot shows the new Flash movie, after applying strong typing. We can see that while it’s had no effect on the current or maximum CPU load, the minimum value has dropped from 8.3% to 4.2%. The maximum memory consumed has gone down from 9MB to 8.7MB.

    The slope of the graph’s memory line has also changed, compared to the one shown in Step 2. It still has the same jagged pattern, but now drops and rises at a slower rate. This is a good thing, if you consider that the sudden drops in memory consumption are caused by Flash Player’s garbage collection, which is usually triggered when allocated memory is about to run out. This garbage collection can be an expensive operation, since Flash Player has to traverse through all the objects, looking for those that are no longer needed but still taking up memory. The less often it has to do this, the better.


    Step 5: Efficiently Store Numeric Data

    Actionscript provides three numeric data types: Number , uint and int . Of the three types, Number consumes the most memory as it can store larger numeric values than the other two. It is also the only type able to store numbers with decimal fractions.

    The Flames class has many numeric properties, all of which use the Number data type. As int and uint are more compact data types, we can save some memory by using them instead of Numbers in all situations where we don’t need decimal fractions.

    A good example is in loops and Array indexes, so for example we are going to change

    
    
    for( var i:Number = 0; i < 10; i++ ){
    	anchors[i] = Math.floor( i * colWidth );
    }
    

    into

    
    
    for( var i:int = 0; i < 10; i++ ){
    	anchors[i] = Math.floor( i * colWidth );
    }
    

    The properties cpu , cpuMax and memoryMax will remain Numbers, as they will most likely store fractional data, while memoryColor , cpuColor and ticks can be changed to uints, as they will always store positive, whole numbers.


    Step 6: Minimize Method Calls

    Method calls are expensive, especially calling a method from a different class. It gets worse if that class belongs to a different package, or is a static method. The best example here is the Math.floor() method, used throughout the Flames class to roundoff fractional numbers. This method call can be avoided by using uints instead of Numbers to store whole numbers.

    
    
    // So instead of having:
    anchors[i] = Math.floor( i * colWidth );
    // we instead cast the value to a uint
    anchors[i] = uint( i * colWidth );
    
    // The same optimization can be performed by simply assigning the uint data type, for example changing
    var size:uint = Math.floor( particle.size * particle.life/100 );
    // into
    var size:uint = particle.size * particle.life/100;
    

    In the example above, the call to Math.floor() is unnecessary, since Flash will automatically round off any fractional number value assigned to a uint.


    Step 7: Multiplication Is Faster Than Division

    Flash Player apparently finds multiplication easier than division, so we’ll go through the Flames class and convert any division math into the equivalent multiplication math. The conversion formula involves getting the reciprocal of the number on the right side of the operation, and multiplying it with the number on the left. The reciprocal of a number is calculated by dividing 1 by that number.

    
    
    var colWidth:uint = frame.width / 10; //division by ten
    var colWidth:uint = frame.width * 0.1; //produces the same result as multiplication by 0.1
    

    Lets take a quick look at the results of our recent optimization efforts. The CPU load has finally improved by dropping from 41.7% to 37.5%, but the memory consumption tells a different story. Maximum memory has risen to 9.4MB, the highest level yet, and the graph’s sharp, saw-tooth edges shows that garbage collection is being run more often again. Some optimization techniques will have this inverse effect on memory and CPU load, improving one at the expense of the other. With memory consumption almost back to square one, a lot more work still needs to be done.

    a screenshot of Flames.swf after all the steps above have been applied.

    Step 8: Recycling Is Good for the Environment

    You too can play your part in saving the environment. Recycle your objects when writing your AS3 code reduce the amount of energy consumed by your programs. Both the creation and destruction of new objects are expensive operations. If your program is constantly creating and destroying objects of the same type, big performance gains can be achieved by recycling those objects instead. Looking at the Flames class, we can see that a lot of particle objects are being created and destroyed every second:

    
    
    private function drawParticles( event ):void {
    	createParticles( 20 ); 
    
    	fireMC.graphics.clear();
    	for ( var i:* in fireParticles ) {
    		var particle:Object = fireParticles[i]; 
    
    		if (particle.life == 0 ) {
    			delete( fireParticles[i] );
    			continue;
    		}
    

    There are many ways to recycle objects, most involve creating a second variable to store unneeded objects instead of deleting them. Then when a new object of the same type is required, it is retrieved from the store instead of creating a new one. New objects are only created when the store is empty. We are going to do something similar with the particle objects of the Flames class.

    First, we create a new array called inactiveFireParticles[] , which stores references to particles whose life property is zero (dead particles). In the drawParticles() method, instead of deleting a dead particle, we add it to the inactiveFireParticles[] array.

    
    
    private function drawParticles( event ):void {
    	createParticles( 20 ); 
    
    	fireMC.graphics.clear();
    	for ( var i:* in fireParticles ) {
    		var particle:Object = fireParticles[i]; 
    
    		if( particle.life <= 0 ) {
    			if( particle.life == 0 ){
    				particle.life = -1;
    				inactiveFireParticles.push( particle );
    			}
    			continue;
    		}
    

    Next, we modify the createParticles() method to first check for any stored particles in the inactiveFireParticles[] array, and use them all before creating any new particles.

    
    
    private function createParticles( count ):void{
    	var anchorPoint = 0;
    	for(var i:uint = 0; i < count; i++){
    
    		var particle:Object;
    		if( inactiveFireParticles.length > 0 ){
    			particle = inactiveFireParticles.shift();
    		}else {
    			particle = new Object();
    			fireParticles.push( particle );
    		}				
    
    		particle.x = uint( Math.random() * frame.width * 0.1 ) + anchors[anchorPoint];
    		particle.y = frame.bottom;
    		particle.life = 70 + uint( Math.random() * 30 );
    		particle.size = 5 + uint( Math.random() * 10 );
    
    		if(particle.size > 12){
    			particle.size = 10;
    		}
    		particle.anchor = anchors[anchorPoint] + uint( Math.random() * 5 );
    
    		anchorPoint = (anchorPoint == 9)? 0 : anchorPoint + 1;
    	}
    }
    

    Step 9: Use Object and Array Literals Whenever Possible

    When creating new objects or arrays, using the literal syntax is faster than using the new operator.

    
    
    private var memoryLog:Array = new Array(); // array created using the new operator
    private var memoryLog:Array = []; // array created using the faster array literal
    
    particle = new Object(); // object created using the new operator
    particle = {}; // object created using the faster object literal
    

    Step 10: Avoid Using Dynamic Classes

    Classes in ActionScript can either be sealed or dynamic. They’re sealed by default, meaning the only properties and methods an object derived from it can have must have been defined in the class. With dynamic classes, new properties and methods can be added at runtime. Sealed classes are more efficient than dynamic classes, because some Flash Player performance optimizations can be done when all the possible functionality that a class can ever have are known beforehand.

    Within the Flames class, the thousands of particles extend the built-in Object class, which is dynamic. Since no new properties need to be added to a particle at runtime, we’ll save up more resources by creating a custom sealed class for the particles.

    Here is the new Particle, which has been added to the same Flames.as file.

    
    
    class Particle{
    	public var x:uint;
    	public var y:uint;
    	public var life:int;
    	public var size:Number;
    	public var anchor:uint;
    }
    

    The createParticles () method will also be adjusted, changing the line

    
    
    var particle:Object;
    particle = {};
    

    to instead read:

    
    
    var particle:Particle;
    particle = new Particle();
    

    Step 11: Use Sprites When You Don’t Need the Timeline

    Like the Object class, MovieClips are dynamic classes. The MovieClip class inherits from the Sprite class, and the main difference between the two is that MovieClip has a timeline. Since Sprites have all the functionality of MovieClips minus the timeline, use them whenever you need a DisplayObject that does not need the timeline. The Flames class extends the MovieClip but it does not use the timeline, as all its animation is controlled through ActionScript. The fire particles are drawn on fireMC , which is also a MovieClip that does not make use of its timeline.

    We change both Flames and fireMC to extend Sprite instead, replacing:

    
    
    import flash.display.MovieClip;
    private var fireMC:MovieClip = new MovieClip();
    public class Flames extends MovieClip{
    

    with

    
    
    import flash.display.Sprite;
    private var fireMC:Sprite = new Sprite();
    public class Flames extends Sprite{
    

    Step 12: Use Shapes Instead of Sprites When You Don’t Need Child Display Objects or Mouse Input

    The Shape class is even lighter than the Sprite class, but it cannot support mouse events or contain child display objects. As the fireMC requires none of this functionality, we can safely turn it into a Shape.

    
    
    import flash.display.Shape;
    private var fireMC:Shape = new Shape();
    
    a screenshot of Flames.swf after all the steps above have been applied.

    The graph shows big improvements in memory consumption, with it dropping and remaining stable at 4.8MB. The saw-tooth edges have been replaced by an almost straight horizontal line, meaning garbage collection is now rarely run. But the CPU load has mostly gone back again to its original high level of 41.7%.


    Step 13: Avoid Complex Calculations Inside Loops

    They say over 50% of a program’s time is spent running 10% of its code, and most of that 10% is most likely to be taken up by loops. Many loop optimization techniques involve placing as much of the CPU intensive operations outside the body of a loop. These operations include object creation, variable lookups and calculations.

    
    
    for( var i = 0; i < memoryLog.length; i++ ){
    	// loop body
    }
    

    The first loop in the drawGraph() method is shown above. The loop runs through every item of the memoryLog array, using each value to plot points on the graph. At the start of each run, it looks up the length of the memoryLog array and compares it with the loop counter. If the memoryLog array has 200 items, the loop runs 200 times, and performs this same lookup 200 times. Since the length of memoryLog does not change, the repeated lookups are wasteful and unnecessary. It’s better to look up the value of memoryLog.length just once before the lookup begins and store it in a local variable, since accessing a local variable will be faster than accessing an object’s property.

    
    
    var memoryLogLength:uint = memoryLog.length;
    for( var i = 0; i < memoryLogLength; i++ ){
    	// loop body
    }
    

    In the Flames class, we adjust the two loops in the drawGraph() method as shown above.


    Step 14: Place Conditional Statements Most Likely to Be True First

    Consider the block of if..else conditionals below, derived from the drawParticles () method:

    
    
    if( particle.life > 90 ){ // a range of 10 values, between 91 - 100
    	size *= 1.5;
    }else if( particle.life > 45){ // a range of 45 values, between 46 - 90
    	particle.x += Math.random() * 6 - 3;
    	size *= 1.2;
    }else {	// a range of 45 values, values between 0 - 45
    	transperency = 0.1;
    	size *= 0.3;
    	particle.x += Math.random() * 4 - 2;
    }
    

    A particle’s life value can be any number between 0 and 100. The if clause tests whether the current particle’s life is between 91 to 100, and if so it executes the code within that block. The else-if clause tests for a value between 46 and 90, while the else clause takes the remaining values, those between 0 and 45. Considering the first check is also the least likely to succeed as it has the smallest range of numbers, it should be the last condition tested. The block is rewritten as shown below, so that the most likely conditions are evaluated first, making the evaluations more efficient.

    
    
    if( particle.life < 46 ){
    	transperency = 0.1;
    	size *= 0.3;
    	particle.x += Math.random() * 4 - 2;
    }else if( particle.life < 91 ){
    	particle.x += Math.random() * 6 - 3;
    	size *= 1.2;
    }else {
    	size *= 1.5;
    }
    

    Step 15: Add Elements to the End of an Array Without Pushing

    The method Array.push() is used quite a lot in the Flames class. It will be replaced by a faster technique that uses the array’s length property.

    
    
    cpuLog.push( cpu ); // slow and pretty
    cpuLog[ cpuLog.length ] = cpu; // fast and ugly
    

    When we know the length of the array, we can replace Array.push() with an even faster technique, as shown below.

    
    
    var output:Array = [];	//output is a new, empty array. Its length is 0
    for( var i:uint = 0; i < steps; i++ ){ // the value of i also starts at zero. Each loop cycle increases both i and output.length by one
    	var progress:Number = i / steps;
    	var color:uint = Color.interpolateColor( color1, color2, progress );
    	output[i] = color;	// faster than cpuLog[ cpuLog.length ] = cpu;
    }
    

    Step 16: Replace Arrays With Vectors

    The Array and Vector classes are very similar, except for two major differences: Vectors can only store objects of the same type, and they’re more efficient and faster than arrays. Since all the arrays in the Flames class either store variables of only one type – ints, uints or Particles, as required – we shall convert them all to Vectors.

    These arrays:

    
    
    private var memoryLog:Array = [];
    private var cpuLog:Array = [];
    private var fireParticles:Array = [];
    private var palette:Array = [];
    private var anchors:Array = [];
    private var inactiveFireParticles:Array = [];
    

    …are replaced with their Vector equivalents:

    
    
    private var memoryLog:Vector.<Number> = new Vector.<Number>();
    private var cpuLog:Vector.<Number> = new Vector.<Number>();
    private var fireParticles:Vector.<Particle> = new Vector.<Particle>();
    private var palette:Vector.<uint> = new Vector.<uint>();
    private var anchors:Vector.<uint> = new Vector.<uint>();
    private var inactiveFireParticles:Vector.<Particle> = new Vector.<Particle>();
    

    Then we modify the getColorRange() method to work with Vectors rather than arrays.

    
    
    private function getColorRange( color1, color2, steps):Vector.<uint>{
    	var output:Vector.<uint> =  new Vector.<uint>();
    	for( var i:uint = 0; i < steps; i++ ){
    		var progress:Number = i / steps;
    		var color:uint = Color.interpolateColor( color1, color2, progress );
    		output[i] = color;
    	}
    	return output;
    }
    

    Step 17: Use the Event Model Sparingly

    While very convenient and handy, the AS3 Event Model is built on top of an elaborate setup of event listeners, dispatchers and objects; then there is event propagation and bubbling and much more, all of which a book can be written about. Whenever possible, always call a method directly rather than through the event model.

    
    
    addEventListener( Event.ENTER_FRAME, drawParticles );
    addEventListener( Event.ENTER_FRAME, getStats );
    addEventListener( Event.ENTER_FRAME, drawGraph );
    

    The Flames class has three event listeners calling three different methods, and all bound to the ENTER_FRAME event. In this case, we can keep the first event listener and get rid of the other two, then have the drawParticles () method call getStats() , which in turn calls drawGraph() . Alternatively, we can simply create a new method that calls the getStats(), drawGraph() and drawParticles () for us directly, then have just one event listener that’s bound to the new method. The second option is more expensive however, so we’ll stick with the first.

    
    
    // this line is added before the end of the <code> drawParticles </code>() method
    getStats();
    // this line is added before the end of the <code> getStats() </code> method
    drawGraph();
    

    We also remove the event parameter (which holds the Event object) from both the drawGraph() and getStats() , as they are no longer needed.


    Step 18: Disable All Mouse Events for Display Objects That Do Not Need It

    Since this Flash animation does not require any user interaction, we can free its display object from dispatching unnecessary mouse events. In the Flames class, we do that by setting its mouseEnabled property to false. We also do the same for all its children by setting the mouseChildren property to false. The following lines are added to the Flames constructor:

    
    
    mouseEnabled = false;
    mouseChildren = false;
    

    Step 19: Use the Graphics.drawPath() Method to Draw Complex Shapes

    The Graphics.drawPath() is optimized for performance when drawing complex paths with many lines or curves. In the Flames.drawGraph() method, the CPU load and memory consumption graph lines are both drawn using a combination of Graphics.moveTo() and Graphics.lineTo() methods.

    
    
    for( var i = 0; i < memoryLogLength; i++ ){
    	ypoint = ( memoryLog[i] - memoryMin ) / memoryRange * graphHeight;
    	xpoint = (i / sampleSize) * graphWidth;
    	if( i == 0 ){
    		graphMC.graphics.moveTo(  xpoint, -ypoint );
    		continue;
    	}
    	graphMC.graphics.lineTo( xpoint, -ypoint );
    }
    

    We replace the original drawing methods with calls to Graphics.drawPath(). An added advantage of the revised code below is that we also get to remove the drawing commands from the loops.

    
    
    var commands:Vector.<int> = new Vector.<int>();
    var data:Vector.<Number> = new Vector.<Number>();
    
    for( var i = 0; i < memoryLogLength; i++ ){
    	ypoint = ( memoryLog[i] - memoryMin ) / memoryRange * graphHeight;
    	xpoint = (i / sampleSize) * graphWidth;
    
    	if( i == 0 ){
    		data[ data.length ] = xpoint;
    		data[ data.length ] = -ypoint;
    		commands[ commands.length ] = 1;
    	}
    
    	data[ data.length ] = xpoint;
    	data[ data.length ] = -ypoint;
    	commands[ commands.length ] = 2;
    }
    graphMC.graphics.drawPath( commands, data );
    

    Step 20: Make the Classes Final

    The final attribute specifies that a method cannot be overridden or that a class cannot be extended. It can also make a class run faster, so we’ll make both the Flames and Particle classes final.

    Edit: Reader Moko pointed us to this great article by Jackson Dunstan, which remarks that the final keyword does not actually have any effect on performance.

    a screenshot of Flames.swf after all the steps above have been applied.

    The CPU load is now 33.3%, while the total memory used stays between 4.8 and 5MB. We’ve come a long way from the CPU load of 41.7% and peak memory size of 9MB!

    Which brings us to one of the most important decisions to be made in an optimization process: knowing when to stop. If you stop too early, your game or application may perform poorly on low end systems, and if you go too far, your code may get more obfuscated and harder to maintain. With this particular application, the animation looks smooth and fluid while CPU and memory usage are under control, so we’ll stop here.


    Summary

    We have just looked at the process of optimization, using the Flames class as an example. While the many optimization tips were presented in a step by step fashion, the order doesn’t really matter. What’s important is being aware of the many issues that can slow down our program, and taking measures to correct them.

    But remember to watch out for premature optimization; focus first on building your program and making it work, then start tweaking performance.


  2. David Appleyard says:
    April 17, 2012 at 1:29 am

    Over the past few weeks, we’ve been working hard behind the scenes to create a central place for our Tuts+ readers to call “home”. Somewhere that shows you the latest content from across all our sites, allows you to filter only those tutorials that interest you, and shares a few statistics about the Tuts+ network.

    Today, we’re very excited to introduce the Tuts+ Hub — read on to find out more about it!


    All Our Latest Tutorials in One Place

    The Tuts+ Hub is a new way to see all the latest content across our whole network. Any new tutorials that have been published since your last visit will be given a “New” banner, and you can quickly filter out the sites that don’t interest you — just toggle the checkboxes at the top of the screen to show/hide content for each site.

    Tuts+ Hub

    We’re also showcasing all our new Tuts+ Premium tutorials — these are shown with a yellow border, so it’s worth keeping an eye out for these if you’re one of our awesome Tuts+ Premium members!


    A Handful of Statistics

    We also thought it would be fun to share a few of our statistics with you. Did you know we’ve published over 11,500 tutorials? Or that we’re approaching half a million comments across the network? It’s a fun way to keep an eye on how Tuts+ is growing (and these stats are updated every day).

    Tuts+ Hub

    What Do You Think?

    I’d love to hear your feedback on Tuts+ Hub, and any thoughts or comments on what works well/what you’d like to see improved. We’re really happy with how the project turned out and I think you’re going to love it!

    Thanks for being one of our readers and, if you’ve somehow resisted all the way down to this point, head over and check out the Tuts+ Hub!


  3. Carlos Yanez says:
    April 17, 2012 at 2:02 am

    In this Premium Flash tutorial, you’ll learn how to create a simple wordsearch with a neat “highlighter” interface, which is easy to modify with your own words and layout. It’s a neat diversion you could add to any website, or something you could extend to make into a full game.


    Premium Preview

    Let’s take a look at the result we will be working towards:

    In this tutorial we’ll use a series of ActionScript classes to create a classic Alphabet Soup wordsearch game. The objective of the game is to highlight letters to form a word. You will be able to create your own alphabet soup and include your own words.


    Read the Full Tutorial

    Premium members can access the full tutorial right away!

    If you’re not yet a Premium member, you can still read the first few steps for free.


    Tuts+ Premium Membership

    We run a Premium membership system which periodically gives members access to extra tutorials, like this one, from across the whole Tuts+ network. If you’re a Premium member, you can log in and read the tutorial. If you’re not a member, you can of course join today!

    Also, don’t forget to follow @envatoactive on twitter, circle us on Google+, like us on Facebook, and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.


  4. Dru Kepple says:
    April 17, 2012 at 2:08 am

    Dart is a pretty cool new language that could mean a change in the way you write web applications. Google provides an Eclipse-based Dart Editor that provides a handy means to get started. But TextMate users usually find a way to bend TextMate to their will, and this Quick Tip will get you started a-bending to use Dart with TextMate.


    Prerequisites

    I assume you are familiar with TextMate bundles to at least the degree of what they are and that they add functionality (usually language-specific) to TextMate. I also assume you are familiar with and are capable of checking code out of a Subversion repository. Lastly, I assume you have a little Dart experience. You don’t need much, but having some Dart files around to open in TextMate will make this go much smoother.

    If you’re not familiar with Dart at this point, I can point you to the official source of information: http://www.dartlang.org/. Beyond that, Activetuts+ has previously published my introductory tutorial on the language, What is Dart, and Why Should You Care?


    Install Dart Editor

    We won’t really be using the Dart Editor (the point of this tutorial is to use TextMate for Dart development), but the download includes the Dart SDK, which is really what we’re after. Even if you’re a hardcore TextMate fanatic (like I am), it’s still not a terrible idea to have the “official” Dart Editor installed and handy.

    On the official Dart site (see the previous section), you can download the Dart Editor from the following link: http://www.dartlang.org/docs/getting-started/editor/index-macos.html

    If you’re on Windows or Linux, yet are reading this tutorial despite its Mac-centric nature, you can download the Dart Editor for those platforms from the Dart Editor for Windows and Linux pages.

    Under “Step 1” of that page, you’ll find a link to a ZIP file containing the Dart Editors. It’s around 40 MB, so it’s not a terribly heavy download.

    The Dart Editor is based on Eclipse, so if you’ve used that you’ll be right at home with the Dart Editor. I won’t be getting into details on using it in this tutorial, but feel free to play around with it. The current Activetuts+ Facebook Fan Bonus takes you through the basic usage of the Dart Editor.

    (Note that if you don’t want to install the Dart Editor, you can download just the Dark SDK for your OS at this URL (it’s only 2 or 3 MB): http://www.dartlang.org/docs/getting-started/sdk/index.html


    Install frogc

    frogc is the Dart-to-JavaScript compiler. It’s a command line tool, but it’s thankfully easy to use. We’ll be using it in a TextMate command later to turn our Dart file(s) into JavaScript so we can actually use our Dart code today.

    Open up Terminal (found in your /Applications/Utilities/ folder). Type the following:

    
    
    nano ~/.bash-profile
    

    If you already have some PATH customizations going on, place your cursor after these lines.

    Type:

    
    
    export PATH=$PATH:
    

    And then drag the bin folder, which should be located at /Applications/dart/dart-sdk/bin, into the Terminal window. If it’s not in that location, look for a dart-sdk folder in something that you downloaded (if you downloaded the SDK by itself, this should be that download, unzipped). You should end up with something like this:

    
    
    export PATH=$PATH:/Applications/dart/dart-sdk/bin
    

    To save this file, press Control-O (that’s Control, not Command), press Return to confirm the file to save, and then press Control-X to exit nano.

    Almost ready; I’ve found a problem with frogc if you happen to have spaces in your file or folder names. This can be easily fixed, though. Open up frogc. It’s an executable shell script, so don’t double-click it. Instead, drag it to your TextMate icon, and you may be presented with a warning dialog but you should be able to see the short script. You don’t need to understand what this does, just change the last line from this:

    
    
    $SCRIPTPATH/dart --new_gen_heap_size=128 $SCRIPTPATH/frogc.dart --libdir=$LIBPATH $@
    

    …to this:

    
    
    "$SCRIPTPATH/dart" --new_gen_heap_size=128 "$SCRIPTPATH/frogc.dart" --libdir="$LIBPATH" "$@"
    

    Notice that basically I’ve surrounded every path with quotes, which helps avoid the space problem.


    Install Google’s Dart TMBundle

    You can find the .tmbundle on this Google Code page.

    You can either check out the entire Dart source, which might be interesting to poke through, or you can simply check out the .tmbundle. Using the Terminal, navigate to the location in which you’d like to have the code (type cd then drag the destination folder into the Terminal window again — note that there’s a space after cd). Once Terminal is in your desired location, type this is in for a full checkout:

    
    
    svn checkout http://dart.googlecode.com/svn/trunk/ dart-read-only
    

    …or this for just the .tmbundle:

    
    
    svn checkout http://dart.googlecode.com/svn/branches/bleeding_edge/dart/tools/utils/textmate/Dart.tmbundle
    

    If you’ve checked out the entire project, you can navigate to the .tmbundle by following this path from the project root: [dart-read-only]/dart/tools/utils/textmate/Dart.tmbundle. Either way, double-click on the .tmbundle to have TextMate install it.

    A lot of TextMate users like to simply check .tmbundles out directly to their bundle directory. To do this, navigate to that directory in Terminal (this should do it: cd "~/Library/Application Support/TextMate/Pristine Copy/Bundles") and then run the second svn checkout line above (the one that checks out just the .tmbundle). This way you can easily update the bundle in-place with svn up "~/Library/Application Support/TextMate/Pristine Copy/Bundles".


    Write a Command to Compile Dart to JavaScript

    The Google Dart Bundle is great for adding syntax support for Dart, so when you create a file ending in .dart you get colored syntax and code folding and that sort of thing. But it doesn’t include any snippets or commands. The most useful command (indeed, the first thing I thought of) is a command to compile your current Dart script with frogc for you. We’ll add one in this step.

    In TextMate, open the Bundle Editor (press Command-Option-Control-B, or go to Bundles > Bundle Editor > Show Bundle Editor)

    Click on the Dart entry in the left-hand list.

    With the “+” button at the bottom left, choose “New Command”.

    You should see a new “untitled” command appear under the Dart bundle. Rename it to “Compile with frogc”

    In the large text area on the right (labeled “Command(s)”), enter the following:

    
    
    frogc "$TM_FILEPATH"
    

    Above the text area, you have the option of having the command save the file before running. This might appeal to you (it does to me; one less keystroke!). If it does, then change the “Save” option from “Nothing” to either “Current File” or “All Files in Project”.

    Underneath the text area, where it says “Input,” set it to “None”.

    Below that, where it says “Output,” set it to “Show as Tooltip”. This lets any output from the command show up in a tooltip near the cursor, which means if you have errors while running frogc you can see them. They’re not terribly pretty but it’s better than nothing.

    Below that, where it says “Activation,”, make sure it’s set to “Key Equivalent” then put your cursor in the text field to the right. Type Command-B; this sets this command to trigger when you type that keyboard shortcut. Command-B is the TextMate idiom for a Build command if bundles have one.

    Below that, where it says “Scope Selector,” type “source.dart#8221;.

    Your command window should look something like this:

    The completed frogc command

    Close the Bundle editor window.


    Step 1: Tell TextMate Where frogc is

    We’ve set up Terminal so that it knows where frogc is, but unfortunately TextMate does not share that information. The easiest solution is to add the path that we added to the .bash_profile file to your Textmate preferences.

    Open up TextMate’s Preferences (press Command-, or go to TextMate > Preferences…).

    Click on the Advanced button at the top, then click on the Shell Variables tab.

    The Shell Variables preference

    If you don’t already have a PATH variable, click the “+” button and, in the first column, type PATH.

    In the second column of the row that begins PATH, type the path you added to the .bash_profile (just the path, not the part that says EXPORT PATH=@PATH:). Be sure to leave the existing value intact — add a colon at the end of what’s there already, and then copy in the new path.

    Close the Preferences window, and you’re ready to try it out. If you need a Dart file, you can either create a Hello World file by creating a new project with Dart Editor, or you can dig up the examples from the Dart Editor download, in the “samples” folder. Open up a Dart file in TextMate and hit Command-B; if all goes well you should have a JavaScript file next to the Dart file after a few seconds.


    Step 2: Get Feedback From frogc

    If you’d like to get a little fancier, you can change the code of your compile command to this:

    
    
    result=`frogc "$TM_FILEPATH"`
    if ["$result" == ""]; then
        echo "Compile completed"
    else
        echo $result
    fi
    

    This will give you a “Compile completed” tooltip once frogc is done running, if it runs successfully. If you have errors, they’ll still show up as they did before.

    One other option: if you liked the idea of automatically saving files when running the command, you might like the idea of replacing the Save command with a Save-and-Compile command. This is as simple as changing Command-B to Command-S, and making sure you’re saving the “Current File” in the command. This overrides the regular Command-S, which simply saves the current document, with the execution of this command, which saves and then compiles.

    For completeness, you can create a duplicate command that saves “All Files” and has an activation key of Command-Option-S. This shortcut will override the regular Command-Option-S in TextMate, which normally saves all files in a project. Note that because you’ve set a Scope Selector, this override will only happen in Dart files, not every time you save any file.


    Start Building Snippets

    There are potentially many useful snippets to be added to a Dart bundle. Generally I find myself incrementally adding them as I get to know a language and discover that the existing .tmbundle doesn’t already include one. Let me get you started by adding a snippet that creates a new method.

    In the Bundle Editor, make sure the Dart bundle (or an item in the Dart bundle) is selected, then choose “New Snippet” from the “+” button. Name it “method”.

    In the large text area, select all the existing text and delete it. Now enter (or paste) the following:

    
    
    ${1:void} ${2:methodName}(${3:arguments}) {
        $0${1/void|(.+)/(?1:\n\treturn null;)/}
    }
    

    Under “Activation,” set the pop-up to “Tab Trigger” and enter method in the text field (feel free to change this).

    Under “Scope Selector,” type in source.dart.

    Your snippet should look like this:

    The completed method snippet

    Close the Bundle Editor.

    Try it out. In a Dart file, type “method” (or whatever you specified, if you forged your own tab trigger), press Tab, and watch it expand. You can tab through the various stops, starting at the return type, then to the method name, and finally in between the parentheses if you want to add arguments. The last tab will drop you at the first line of the method.

    The cool part is that if you change the return type from void, you get an automatic return null statement at the end of your method body. Naturally you’ll want to adapt this to your needs, but hopefully it’s a feature that saves a little typing. The magic happens in the unwieldy second line of the snippet; if you’ve never seen this before, then it’s somewhat difficult to explain concisely, but it looks at the contents of the first tab stop (the return type) and if it’s anything other than “void”, it adds the return null. It might make sense if you’ve ever used Regular Expressions, particularly with the substitution syntax of /pattern/substitute/ found in Perl.

    Given that Google provides no snippets with their .tmbundle, the field’s wide open for the creation of time-saving Dart snippets. Feel free to post your snippets in the comments. We’ll compile them and see if we can get Google to incorporate them into their official bundle.


    That is All

    Thanks for reading! I hope you are as excited about Dart as I am. And the Dart Editor is neat and everything, but I’m a fool for TextMate. Combining Dart with my text editor of choice is something that just had to be shared.


  5. Kah Shiu Chong says:
    April 17, 2012 at 2:49 am

    In this tutorial, I’ll follow the approach suggested by Richard Davey (Thanks, Richard!), and used by him and others, in detecting collisions between bitmaps with a subtle modification. I’ll also compare performance between various approaches of bitmap collision detection using Grant Skinner’s PerformanceTest harness.


    Step 1: Overview

    I describe this alternative approach in short here.

    1. Check whether there’s any overlap between the two bitmaps.
    2. If there is, proceed to #3. Otherwise, drop out.
    3. Check whether the overlap area has any opaque pixels overlapping.
    4. If so, the bitmaps overlap. Otherwise, drop out.

    Step 2: Bounding Boxes

    First, we check whether the two bitmaps’ bounding boxes are overlapping using Rectangle. The scripts are as below. First, the variables.

    
    
    private var enemy1:Bitmap, myShip:Bitmap;
    private var myShipSp:Sprite;
    private var rec_e:Rectangle, rec_m:Rectangle;
    private var intersec:Rectangle;
    
    
    
    enemy1 = new E1 as Bitmap;	addChild(enemy1);
    myShip = new My as Bitmap;
    myShipSp = new Sprite; addChild(myShipSp);
    myShipSp.addChild(myShip);
    
    enemy1.x = stage.stageWidth >> 1;	myShipSp.x = stage.stageWidth >> 1;
    enemy1.y = stage.stageHeight * 0.2;	myShipSp.y = stage.stageHeight * 0.8;
    
    //drawing boxes around the sprite
    draw(enemy1.getBounds(stage), this, 0);
    draw(myShipSp.getBounds(stage), this, 0);
    

    Here we check for any overlapping area between the boxes. Check out DetectVisible.as in the source download for the full script

    
    
    private function refresh(e:Event):void
    {
        //determining the bounding box of intersection area
        rec_e = enemy1.getBounds(stage);
        rec_m = myShipSp.getBounds(stage);
        intersec = rec_e.intersection(rec_m);
    
        //redraw the bounding box of both sprites
        this.graphics.clear();
        draw(enemy1.getBounds(stage), this, 0);
        draw(myShipSp.getBounds(stage), this, 0);
    
        //only draw bounding box of intersection area if there's one
        if (!intersec.isEmpty()){
            lines.graphics.clear();
            draw(intersec, lines);
    
            t.text ="Intersection area by red rectangle."
        }
        else {
            t.text ="No intersection area."
        }
    }
    

    Here’s a demo. Drag the smaller spaceship around.

    (Don’t worry about the red box that gets “left behind” when the ship is dragged out of the other’s bounding box.)


    Step 3: Drawing into the Intersection Area

    So if there’s an intersecting box area, we proceed to check whether there are overlapping pixels in the area. However, let’s first try to draw bitmap into this intersection area. The full script’s in DetectVisible2.as

    
    
    private function refresh(e:Event):void
    {
        //determining the bounding box of intersection area
        rec_e = enemy1.getBounds(stage);
        rec_m = myShipSp.getBounds(stage);
        intersec = rec_e.intersection(rec_m);
    
        //redraw the bounding box of both sprites
        this.graphics.clear();
        draw(enemy1.getBounds(stage), this, 0);
        draw(myShipSp.getBounds(stage), this, 0);
    
        //only draw bounding box of intersection area if there's one
        if (!intersec.isEmpty()){
            lines.graphics.clear();
            draw(intersec, lines);
    
            //to draw the intersection area and checking for overlapping of colored area
            var eM:Matrix = enemy1.transform.matrix;
            var myM:Matrix = myShipSp.transform.matrix;
    
            bdt_intersec = new BitmapData(intersec.width, intersec.height, false, 0)
            eM.tx -= intersec.x;	eM.ty -= intersec.y
            myM.tx -= intersec.x;	myM.ty -= intersec.y
    
            bdt_intersec.draw(enemy1, eM);
            bdt_intersec.draw(myShip, myM);
    
            bm_intersec.bitmapData = bdt_intersec;
            bm_intersec.x = 10
            bm_intersec.y = stage.stageHeight * 0.8 - bm_intersec.height;
    
            t.text = "Intersection area by red rectangle.\n"
        }
        else {
            t.text ="No intersection area."
        }
    }
    

    Note that since we are drawing the area by the use of a matrix, any scaling, skewing and other transformations on both bitmaps are taken into account. Here’s a demo; check out the box in the bottom left corner.


    Step 4: Check for Color in the Intersection Area

    So how do we check for the right pixel? Well first of all, we give the color of this intersection box a shade of black (Red = 0, Green = 0, Blue = 0). Then, the shade of the smaller spaceship will painted into this dark box as green, with the blend mode of ADD. Similarly, the shade of the bigger stationary alien spaceship will be painted red.

    So now, there will be areas of red and green for the spaceships, and black if there are no overlapping area. However, if there are pixels from these two bitmaps that overlap, these will be drawn in yellow (Red = 255, Green = 255, Blue = 0). We use the method Bitmapdata.getColorBoundsRect to check for the existance of this area.

    Here’s the snippet in Main.as

    
    
    //to draw the intersection area and checking for overlapping of colored area
    var eM:Matrix = enemy1.transform.matrix;
    var myM:Matrix = myShipSp.transform.matrix;
    
    bdt_intersec = new BitmapData(intersec.width, intersec.height, false, 0)
    eM.tx -= intersec.x;	eM.ty -= intersec.y
    myM.tx -= intersec.x;	myM.ty -= intersec.y
    
    //tweak color
    bdt_intersec.draw(enemy1, eM, new ColorTransform(1,1,1,1,255,-255,-255), BlendMode.ADD);
    bdt_intersec.draw(myShip, myM, new ColorTransform(1,1,1,1,-255,255,-255), BlendMode.ADD);
    
    bm_intersec.bitmapData = bdt_intersec;
    bm_intersec.x = 10
    bm_intersec.y = stage.stageHeight * 0.8 - bm_intersec.height;
    
    t.text = "Intersection area by red rectangle.\n"
    
    //check for the existance of the right color
    intersec_color = bdt_intersec.getColorBoundsRect(0xffffff, 0xffff00);
    if (!intersec_color.isEmpty())	t.appendText("And there are interesecting pixels in the area.");
    

    Note that we suppress the Red and Blue components in line 113 to max out Green for the small spaceship. On line 112 we do the same with the alien spaceship with the Blue and Green components.


    Comparing Approaches

    So after receiving comments on performance issues regarding collision detection, I decided to do some quick and dirty tests on these approaches. I created 20 enemy spaceships and one player spaceship and checked collision detection between that one player ship against the other 20. These sprites are packed into the same vicinity to force collision detection for all approaches to have a complete run.

    The first approach is the simplest. BitmapData is captured on initiation and for each frame, collision detection is checked using BitmapData.hitTest(). For the second approach, BitmapData is updated each frame and collision detection is done based upon those BitmapData captured. The third one refers to the approach suggested by this tutorial.

    So the result for one of the tests I’ve done is as below.

    
    
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    bitmapdata fixed (1000 iterations)
    Player version: WIN 11,1,102,55 (debug)
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    method...................................................ttl ms...avg ms
    bitmapdata fixed                                            168     0.17
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    bitmapdata updates (1000 iterations)
    Player version: WIN 11,1,102,55 (debug)
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    method...................................................ttl ms...avg ms
    bitmapdata updates                                         5003     5.00
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    custom method (1000 iterations)
    Player version: WIN 11,1,102,55 (debug)
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    method...................................................ttl ms...avg ms
    custom method                                              4408     4.41
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    

    The PerformanceTest gives different results whenever I run test. So I ran it a few times and derived an average time. Conclusion: the fastest method is the first, followed by the third and then the second approach.

    So storing away BitmapData for bitmaps when they are first introduced into stage and checking for hitTest every frame after is actually efficient, provided these sprites don’t perform any transformations other than translation (such as rotation, skewing and scaling) across time. Otherwise, you will be forced to adopt either the second or third approach, and the third one is more efficient as indicated by the image above.

    You may check out Collisions.as and Results.as for the full script.


    Searching for Expensive Methods

    I embarked thereafter to search for the specific lines of code that took up more computation time. The second and third approach took more time, so I derived several functions from them, each breaking at different points. Check out one of the results below.

    
    
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    default hitTest (1000 iterations)
    Player version: WIN 11,1,102,55 (debug)
    include bounds
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    method...................................................ttl ms...avg ms
    default hitTest                                             189     0.19
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    default hitTest (1000 iterations)
    Player version: WIN 11,1,102,55 (debug)
    include transform
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    method...................................................ttl ms...avg ms
    default hitTest                                             357     0.36
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    default hitTest (1000 iterations)
    Player version: WIN 11,1,102,55 (debug)
    include hittest
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    method...................................................ttl ms...avg ms
    default hitTest                                            4427     4.43
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    custom method (1000 iterations)
    Player version: WIN 11,1,102,55 (debug)
    inlcude bounds and transform
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    method...................................................ttl ms...avg ms
    custom method                                               411     0.41
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    custom method (1000 iterations)
    Player version: WIN 11,1,102,55 (debug)
    include draw and bounds
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    method...................................................ttl ms...avg ms
    custom method                                              3320     3.32
    ––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––––
    

    The first, second, and third times refer to the second approach at different breakpoints, and the fourth and fifth times refer to the third approach. Looking at the third and the fifth time results, BitmapData.draw seems to take a lot of computation time. And the time taken for drawing with the second approach seems to be more expensive in computation time, which leads me to think that the sizes for BitmapData.draw to operate on does matter. You may check out Collisions2.as and Results2.as for the full scripts.

    One thing I find a little disturbing is the inconsistency of these tests – I always don’t get the same time results, although they almost follow the same ranking at all times. So, its good enough to do some simple comparison between functions.

    Conclusion

    Well, thanks for your time looking reading this little tip. Hope it has been useful. Do leave comments if you don’t agree with anything in this tutorial. I’d love to respond to feedback!


  6. James Tyner says:
    April 17, 2012 at 3:27 am

    In this tutorial I will show you how to access the same saved data in separate Flash and JavaScript apps, by storing it in HTML5 LocalStorage and using ExternalInterface to reach it with AS3. We will create the same app in both JavaScript and Flash to demonstrate that it is platform agnostic.


    Step 1: Examining Local Storage

    Local Storage is an exciting part of HTML5 that allows you to do browser side storage that is persistent, meaning it lasts between browser sessions. It only disappears when the user clears their browser cache.

    It is a very easy API to use, using simple key-value pairs to store data, and can be used in a few different ways. One way is to use localStorage.setItem('key','value'), and localStorage.getItem('key'). Another way is to use Object Notation: localStorage[key] = value to set a value, and theValue = localStorage[key] to retrieve it. And, if that wasn’t enough, there is yet a third way – Dot Notation: localStorage.key = value to set it, and theValue = localStorage.key to retrieve it.

    I am opting for the third way in this tutorial, but if you prefer one of the other ways you can modify the code and it should work just fine. Local Storage does have a few other methods, but these are the only two methods we need: one to set a value and one to retrieve that value.


    Step 2: Setting Up the JavaScript Project

    We will create the JavaScript app first. You should develop both this and the Flash project on a live server, otherwise you will run into problems. I am using WAMP on my machine as a local hosting environment.

    Create a folder to store your project in. Within this folder create two new folders. Name one of them “js” and the other “styles”.

    Within the “styles” folder create a new file and name it “style.css”, and within the “js” folder create a new file and name it “externalinterface.js”. Finally, at the root of your project folder create a new file and name it “index.html”.


    Step 3: The Index HTML Page

    Enter the following code within the “index.html” file you created in the step above.

    
    
    <!DOCTYPE html>
    <html>
    <head>
    <title>Local Storage with external Interface</title>
    <link rel="stylesheet" type="text/css" href="styles/style.css" />
    <script type="text/javascript" src="js/externalinterface.js"></script>
    </head>
    <body>
    <div id="wrapper">
    <div id="scorewrapper">
    <p id="scorediv"></p>
    
    <p id="randomscorediv">Random Score is: </p>
    
    <button type="button" id="scorebtn">Generate Score</button>
    </div>
    
    </div>
    
    </body>
    </html>
    

    Here we set up the structure of our “index.html” file. We include the “style.css” and the “externalinterface.js” we created in the step above. The scorediv will be updated when we achieve a new high score, and the randomscorediv will be updated each time we generate a new score (click on the button to generate a random score).


    Step 4: style.css

    Enter the following within the “style.css” you created in the step above.

    
    
    #wrapper{
    	width:400px;
    	height:400px;
    	margin: 0 auto;
    
    }
    #scorewrapper{
    	width:400px;
    	height:200px;
    	background-color:#FFFFFF;
    }
    
    #randomscorediv{
    	visibility: hidden;
    }
    
    body{
    	background: #f2f2f2;
    	text-align: center;
    	padding: 20px;
    }
    

    Here we set the app to be centered in the page, set the background-color of the scorewrapper div, and set the randomscorediv to initially be hidden (invisible). When we click on the button we will set the randomscorediv to visible.


    Step 5: window.onload

    Enter the following code within the “externalinterface.js” you created in the step above.

    
    
    window.onload = function(){
    alert("Window Loaded");
    }
    

    Whenever you need to tie into elements on your web page you should make sure the window has loaded first. Since we need to tie into the button, we use the window.onload function provided by JavaScript. Here we are just popping up an alert with the words “Window Loaded”. If you test the page you should see it is working.


    Step 6: setScore()

    In this step we will code the setScore() function that initially sets the score to 0. Enter the following code within the “externalinterface.js”.

    
    
    window.onload = function(){
    	function setScore(){
    		if(!localStorage.score){
    			localStorage.score = 0;
    		}
    	}
    }
    

    Here we check whether the localStorage.score exists, and if it doesn’t we initialise its value to 0. When the user first runs the app, or after they clear their cache, this value would not exist – so we need to create it.

    Now call this function immediately after you create it, and test by putting it in an alert.

    
    
    window.onload = function(){
    	function setScore(){
    		if(!localStorage.score){
    			localStorage.score = 0;
    		}
    	}
    	setScore();
    	alert(localStorage.score);
    }
    

    Step 7: getScore()

    We have a way to set our score, now we need a way to retrieve it. That is what the getScore() function will accomplish. Enter the following beneath the setScore() function you created in the step above.

    
    
    function getScore(){
    	if(localStorage.score){
    		return(localStorage.score);
    	}
    }
    

    Here we check that localStorage.score does exist, and if it does we simply return the value it holds. Remove the alert from the previous step, and add the following alert beneath the getScore() function.

    
    
    function getScore(){
    	if(localStorage.score){
    		return(localStorage.score);
    	}
    }
    alert(getScore());
    }
    

    If you test now you should see the alert again showing the score of “0″.


    Step 8: updateScore()

    Now that we have a way to set and get our score, we need a way to update it. That is exactly what the updateScore() function achieves. Add the following beneath the getScore() function you created in the step above.

    
    
    function updateScore(newScore){
    	if(localStorage.score){
    		localStorage.score = newScore
    	}
    }
    

    Here we pass as a parameter a newScore; we then set the localStorage.score equal to this value. Remove the alert from the previous step, then add the following beneath the updateScore() function you just created.

    
    
    function updateScore(newScore){
    	if(localStorage.score){
    		localStorage.score = newScore;
    	}
    }
    updateScore(10);
    alert(getScore());
    

    If you test now, you should see “10″ being alerted, since on line 6 we updated the score to 10.


    Step 9: showScore()

    Now that we have all our methods in place to manipulate the score, let’s get it showing. Enter the following beneath the updateScore() function you created in the step above.

    
    
    function showScore(){
        var scoreText = document.getElementById('scorediv');
        scoreText.innerHTML = "Current High Score is "+getScore();
    }
    

    Here we get a reference to the scorediv, and alter its innerHTML property to show the current score.

    Call this function immediately after you create it.

    
    
    function showScore(){
        var scoreText = document.getElementById('scorediv');
        scoreText.innerHTML = "Current High Score is "+getScore();
    }
    showScore();
    

    If you test the page now you should see the words “Current High Score is 10″.


    Step 10: Getting a Reference to the Button

    We want to run a function when the user click on the button. Add the following beneath the showScore() button you created in the step above.

    
    
    var scoreBtn = document.getElementById('scorebtn');
    scoreBtn.addEventListener('click',getRandomScore,false);
    

    Here we get a reference to the button which we gave the ID scorebtn. We then add an EventListener of type click, which calls the getRandomScore() function that we will create in the next step.


    Step 11: getRandomScore()

    The getRandomScore() function is where the logic of this application takes place. Add the following beneath the line scoreBtn.addEventListener('click',getRandomScore,false); you entered in the step above.

    
    
    function getRandomScore(){
    	var randScoreText = document.getElementById('randomscorediv');
    	randScoreText.style.visibility='visible';
    	var randScore = Math.floor(Math.random()* 200000);
    	var currentScore = Number(getScore());
    	randScoreText.innerHTML = "Random Score is "+randScore;
    	if(randScore > currentScore){
    		alert("New High Score!!");
    		updateScore(randScore);
    		showScore();
    	}
    }
    

    Here, we first get a reference to the randomscorediv and set it to be visible. We then generate a random score by calling Math.floor(Math.random()* 200000), which creates a number between 0 and 200,000. We use our getScore() function to set the variable currentScore (making sure we cast it to a Number), and set the innerHTML of the randScoreText to the randScore.

    Lastly we check whether randScore is greater than currentScore, and if it is we show an alert("New High Score!!") and then update the localStorage.score by calling our updateScore() method and passing in the randomScore. We then use showScore() to show the new score.

    This concludes the JavaScript application – you can test it here. In the next step we will start devloping the Flash version.


    Step 12: Setting Up the Flash Project

    In this step we will set up the Flash project.

    Create a folder to store your project files in. Now inside this folder create a folder named “js”, and within this folder create a file and name it “externalinterface.js”. At the root of your project folder create a file named “index.html”. Lastly, create a new Flash Project and save it in the root folder, making sure you name it “externalInterface.fla”. Give it a white background, and set the size to 400x200px.


    Step 13: Setting Up the Index Page

    Add the following to the “index.html” file you created in the step above.

    
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&gt;
    <html xmlns="http://www.w3.org/1999/xhtml"&gt;
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Tuts+ Premium: Demo</title>
    <style>
    	body {background: #f2f2f2;  text-align: center; padding: 20px}
    </style>
    <script type="text/javascript" src="js/externalinterface.js"></script>
    </head>
    <body>
    <object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,0,0&quot; width="400" height="200" id="externalInterface" align="middle">
      <param name="allowScriptAccess" value="sameDomain" />
      <param name="allowFullScreen" value="false" />
      <param name="movie" value="externalInterface.swf" />
      <param name="quality" value="high" />
      <param name="bgcolor" value="#ffffff" />
      <embed src="externalInterface.swf" quality="high" bgcolor="#ffffff" width="400" height="200" name="externalInterface" align="middle" allowScriptAccess="sameDomain" allowFullScreen="false" type="application/x-shockwave-flash" pluginspage="http://www.macromedia.com/go/getflashplayer&quot; />
    </object>
    </body>
    </html>
    

    Here we set up our “index.html” file. We include the “externalinterface.js” we created in the step above, and embed the SWF file inside the object tag. If you decided to name your FLA something different, it is important to place the correct value for the SWF where applicable.


    Step 14: Setting Up externalinterface.js

    Add the following to the “externalinterface.js” you created in the step above.

    
    
    function setScore(){
        if(!localStorage.score){
    		localStorage.score = 0;
    	}
    }
    
    function getScore(){
    	if(localStorage.score){
    	 return localStorage.score;
    
    	}
    }
    
    function updateScore(newScore){
    		localStorage.score = newScore
    	}
    

    These are the same functions we used in the JavaScript application, so I will not be explaining them here. It is important to note that I removed the window.onload, however.


    Step 15: Setting Up the FLA

    In this step we will setup the UI for the FLA you created in the steps above.

    Select the Text tool and make sure the following properties are set under the “Character” panel.

    • Color: black
    • Size: 16pt

    Now drag a TextField out to the stage and give it the following properties.

    • X: 102.00
    • Y: 14.00
    • W: 210.00
    • H: 25.25

    Give it the instance name “currentScore_txt” and make sure the type is set to “Classic Text” and “Dynamic Text” respectively.

    Now, drag another TextField onto the stage and give it the following properties.

    • X: 102.00
    • Y: 49.00
    • W: 210.00
    • H: 25.25

    Give it the instance name “randomScore_text”.

    Go to the Components panel, and drag a button onto the stage. (You can get to the Components panel by going to Window > Components or just by pressing CTRL + F7.)

    Give the button the following properties.

    • X: 150.00
    • Y: 110.00
    • W: 100.00
    • H: 22.00

    Give it the instance name “scorebtn”.

    Within the “Components Parameters” panel, change the label to “Generate Score”.

    Select the Rectangle tool and give it a fill color of “#CCCCCC” and no stroke.

    Now, drag a rectangle out on the stage. Click to select the rectangle and give it the following properties.

    • X: 118.00
    • Y: 50.00
    • W: 173.00
    • H: 82.00

    Now, right-click on the rectangle and choose “Convert To Symbol”; give it the name “alertBox”.

    Double click on the rectangle to go into editing mode. Open the Components panel and drag a Button into this MovieClip. Give the Button the following properties.

    • X: 37.00
    • Y: 52.00
    • W: 100.00
    • H: 22.00

    Give it the instance name “alertBox_btn”, and change the label to read “OK”.

    Drag a TextField into the MovieClip and give it the following properties.

    • X: 29.00
    • Y: 10.00
    • W: 131.00
    • H: 22.00

    Type the words “New High Score!!” into the TextField, then close this MovieClip.


    Step 16: Main.as

    Create a new ActionScript file and save it as “Main.as”. Then, back in your FLA, set Main to be the Document Class.


    Step 17: Package and Imports

    Add the following within the “Main.as” file you created in the step above.

    
    
    package  {
    	import flash.display.Sprite;
    	import flash.events.*;
    	import flash.external.ExternalInterface;
    
    	public class Main extends Sprite {
    
    		public function Main() {
    
    		}
    	}
    }
    

    Here we import the classes we will need, and code our constructor function.


    Step 18: ADDED_TO_STAGE

    Add the following within Main().

    
    
    public function Main() {
    	addEventListener(Event.ADDED_TO_STAGE,setup);
    }
    

    The ADDED_TO_STAGE event runs when the movie has fully loaded. Here it calls a setup function, which we will create next.


    Step 19: The setup() Function

    Add the following beneath the Main() constructor function.

    
    
    private function setup(e:Event):void{
    	trace("MOVIE READY");
    }
    

    If you test now you’ll see that “MOVIE READY” is traced in the output panel.


    Step 20: Hiding the Alert Box

    You may have noticed the Alert Box we created was showing when the movie first starts; let’s hide it. Enter the following within the setup() function.

    
    
    private function setup(e:Event):void{
    	alertBox.visible = false;
    }
    

    Here we set the alertbox to not be visible. Go ahead and test the movie.


    Step 21: setScore()

    In this step we will use the External Interface class to call our setScore() function that we set up in the JavaScript code. Enter the following beneath the setup() function you created in the steps above.

    
    
    private function setScore():void {
        ExternalInterface.call("setScore");
    }
    

    Here we use the call() method of the ExternalInterface class to run the setScore() function that is in our JavaScript code. The call() method takes as a parameter the name of the JavaScript function to run (as a string). If we had parameters in our setScore() function, we would have included them here too.

    We want this function to run when the movie first starts, so add it to the setup() function.

    
    
    private function setup(e:Event):void{
    	alertBox.visible = false;
    	setScore();
    }
    

    Step 22: getScore()

    In this step we will get the score to show in our Flash movie. The JavaScript will be sending the score to Flash, and to do this we will use the External Interface method addCallback() to make the function accessible to it.

    Add the following within the setup() function.

    
    
    private function setup(e:Event):void{
    	alertBox.visible = false;
    	setScore();
    	ExternalInterface.addCallback("getScore", getScore);
    }
    

    The addCallback takes two parameters: the name of a function that you want to make accessible via JavaScript (as a string), and an AS3 function that this call will be linked to (as an AS3 function callback). Here we want to make the AS3 getScore() function available to our JavaScript code first; for simplicity’s sake we give it the name getScore() when accessed via JavaScript as well.

    We will now code this getScore() AS3 function. Add the following beneath the setScore() function you created in the step above.

    
    
    private function getScore(score:String):int{
    	var theScore:int = int(score);
    	return theScore;
    }
    

    Here we set up our getScore() function. Since we will be receiving a string back from the JavaScript, we set the parameter as a string, and we return an integer. Inside this function we set up a variable named theScore and cast it to an int; we then return theScore.


    Step 23: showScore()

    In this step we make the current score display in the Flash movie. Enter the following beneath the getScore() function you created in the step above.

    
    
    private function showScore():void{
    	currentScore_txt.text = "Current High Score is: "+ExternalInterface.call("getScore");
    }
    

    Here we set the currentScore_txt.text to display the current score. We use ExternalInterface.call("getScore") to call the getScore function in the JavaScript code, which in turn triggers the getScore() function in our ActionScript code. Remember, this returns the score.

    Now add the following within the setup() function.

    
    
    private function setup(e:Event):void{
    	alertBox.visible = false;
    	ExternalInterface.addCallback("getScore", getScore);
    	setScore();
    	showScore();
    }
    

    If you test the movie now, you should see the score being shown.


    Step 24: addButtonListeners()

    We need a way to add some listeners to our buttons, so that when the user clicks on them they do something. Add the following beneath the showScore() method you created in the step above.

    
    
    private function addButtonListeners():void{
    	scorebtn.addEventListener(MouseEvent.CLICK,getRandomScore);
    	alertBox.alertBox_btn.addEventListener(MouseEvent.CLICK,hideAlertBox);
    }
    

    Add the following highlighted line within the setup() function.

    
    
    private function setup(e:Event):void{
    	alertBox.visible = false;
    	setScore();
    	ExternalInterface.addCallback("getScore", getScore);
    	showScore();
    	addButtonListeners();
    }
    

    Here we set up our scorebtn to call an AS3 function named getRandomScore(), and we set up the alertBox_btn that is within the alertBox to call an AS3 function named hideAlertBox(). Next we will add these functions.

    Add the following beneath the addButtonListeners() function you just created.

    
    
    private function getRandomScore(e:MouseEvent):void{
    
    }
    
    private function hideAlertBox(e:Event):void{
    	alertBox.visible = false;
    }
    

    We will finish the getRandomScore() function in the next step. All we do in the hideAlertBox() function is set the alertBox to not be visible. We will be making it visible when the user gets a new high score.


    Step 25: getRandomScore()

    In this step we will code the getRandomScore() function, where – just like in the JavaScript application we made – all the app’s logic takes place. Add the following within the getRandomScore() body you created in the step above.

    
    
    private function getRandomScore(e:MouseEvent):void{
    	var randScore:int = Math.floor(Math.random()* 200000);
    	var currentScore:int = ExternalInterface.call("getScore");
    	randomScore_text.text = "Random Score is: "+ randScore.toString();
    		if(randScore > currentScore){
    		alertBox.visible = true;
    		ExternalInterface.call("updateScore",randScore);
    		showScore();
    	}
    }
    

    This works in a very similar way to the JavaScript version. We first generate a number between 0 and 200,000. Then we get the current score by using ExternalInterface.call("getScore"). We set randomScore_text to read out the random score. Lastly we check whether randScore is greater than currentScore, and if it is we show the Alert Box, update the score in Local Storage by using ExternalInterface.call("updateScore",randScore), and call our showScore() method to show the new score.

    Check out the demo.


    Conclusion

    We have used External Interface to access the Local Storage API from HTML5. I hope you have found this tutorial useful and thanks for reading!


  7. Ashish Bogawat says:
    April 17, 2012 at 4:25 am

    In a different approach to our series of critiques, this week Ashish Bogawat compares two similar apps for saving notes and keeping to-do lists: Checkvist and Workflowy. Vote in the poll to let us know which app’s design you prefer.


    Introduction

    I’m a big fan of information management techniques and anything that helps me organize my thoughts, notes, tasks and everything else. Although I’ve been using Remember the Milk for task management and mind mapping apps like Freemind for organizing information for a few years now, I recently stumbled across a way to combine the two. It was too good a prospect to let be, so I decided to dig into the two web-based applications that seem to the best way to do it – Checkvist and Workflowy.

    At their cores, both applications are outline-based information management apps with varying degrees of task management layers thrown in for good measure. The idea is simple: you simply start typing and jot down anything and everything in hierarchical lists, establishing relationships and grouping common things on the way. Applied to task management, this means you simply note everything you want to do without having to in a multi-level list. Finish all sub-tasks and the parent task is obviously complete.

    Now, both Checkvist and Workflowy provide the basic tools to do this, so I decided to give both apps some time and compare how they stack up against each other. Here are the results.


    Quick and Dirty Knowledge Capture

    Since the focus here is to enable quick and easy jotting down of thoughts, it is incredibly important for the apps to provide a distrction-free framework. And they do a pretty decent job with it – Workflowy more so with its sleek, clean and minimalistic interface. Once you on the canvas to start typing, pretty much everything is done through the keyboard: hit Enter to add a new item, Tab to create child elements, Shift-Tab to go back one step, and other keyboard shortcuts to move items up or down.

    The basic difference as far as this core functionality goes is in how Checkvist lets you create multiple lists and then edit each one at a time while Workflowy gives you just one big list with the ability to zoom in and out of any part of it to focus on that section at a time. What’s better entirely depends on your preferred workflow. Personally, I like the ability to segregate different contexts into lists with Checkvist for task management, but the ability to zoom into a third or fourth level in Workflowy makes a lot of sense when I’m simply jotting down my thoughts and need to focus.


    Extra Features

    As far as features beyond the outline creation go, Workflowy remains pretty barebones. You can add a note to an item, mark it as done and that’s about it. You can share any part of the list with others and decide whether you want them to be able to only view the list or edit it. Changes made by other collaborators reflect in your list almost instantly, which is pretty nice. The one thing I love about Workflowy’s collaboration is that it does not rely on getting people to sign up for the service. You simply share a link with everyone and they can simply open up the page and start editing. That’s some serious non-intrusive collaboration workflow I wish more web apps provided.

    Checkvist, on the other hand, crams in so many features into the simple looking package, it is a full-fledged GTD system in itself. For starters, every item you add is considered a task, irrespective of its position in the hierarchy. Mark all child elements complete and the parent gets struck off automatically. Now since everything is a todo, due dates can’t be far behind. You can use natural language for due dates – like “tomorrow”, “next Monday”, “weekdays” – and Checkvist will figure out what you mean. You can add multiple notes to an item, add tags, or invalidate it so that it doesn’t count as a task. The dashboard shows a list of all your tasks along with a visual representation of how many tasks you have completed – a simple but excellent progress indicator for each of your projects. And then there’s search and the ability to share a list with others for collaborative editing.

    Unfortunately, more features also mean a more cluttered interface and a higher learning curve. Thankfully, I like the way Checkvist’s keyboard shortcuts work, making the interface pretty much invisible. You can do pretty much everything without ever touching the mouse. Unlike other apps with single key or Shift/Ctrl + key shortcuts, you use two key sequences in Checkvist: so it’s ‘hh’ for the home page, ‘dd’ to add due dates, ‘aa’ for the actions menu on an item, and so forth. It feels weird at first, but once you get used to it, things begin to feel very natural.


    Which Should You Choose?

    Where Workflowy scores with its simplicity and clean interface, Checkvist caters really well to the power user who might find regular task managers too limiting and time consuming. Here’s my take on who should choose what:

    • If your objective is to jot down things quick and easy, and you don’t really care for task and project management features, Workflowy is for you. It is simple, clean and best suited for information management.
    • If you are looking for a different take on todo lists and getting things done, Checkvist probably has more than what you will need. It’s feature set and overall focus on productivity makes it ideal for task management.

    I’ll reiterate that both apps bring a boatload of interaction design goodness and are worth a try at the very least. Go ahead, check them out and let us know your own personal take on them in the comments.


    Your Turn

    Based on this analysis, which app’s approach do you prefer? Vote in the poll to let us know:


    And if you’ve got a browser app or game that you’d like the Activetuts+ community to do a critique on, submit it here. We’re looking forward to seeing what you’ve built.


  8. Dru Kepple says:
    April 17, 2012 at 4:44 am

    In this tutorial, I’ll introduce you to Google’s new web programming language, Dart, and explain why you should like it and what you need to know about it. Learn about this new language and form some opinions about it – will it really replace JavaScript?


    What Is Dart?

    Straight from the horse’s mouth (which is located here):

    Dart is a class-based, single-inheritance, pure object-oriented programming language. Dart is optionally typed … and supports reified generics and interfaces.
    Dart programs may be statically checked. The static checker will report
    some violations of the type rules, but such violations do not abort compilation
    or preclude execution.

    If that’s a great, steaming pile of mumbo-jumbo to you, allow me to paraphrase the above.

    Class-based: Dart expects you to use classes. While JavaScript is kinda-sorta class-based, you can’t write Dart without writing classes.

    Single-inheritance: Classes can extend other classes, but only one at a time. This is a common structure in Object-Oriented Programming. A rare few languages support multiple-inheritance, but the general consensus is that that causes more problems than it solves, so most OOP languages go for single-inheritance.

    Object-oriented programming language: This statement is a bit redundant, given the “class-based” bit from before. But it is significant: Dart is OOP through and through. In fact, it’s rather reminiscent of Java (not JavaScript; Java), which is also class-based.

    Optionally typed: Most languages are either typed or not. JavaScript, for example, is not. When you define a variable, it will be untyped. You can set it to a String, then to a Number, and nobody will complain (well, I will). Moreover, you can call Array methods on that variable, and you won’t have any problems until you actually run that line of code. In contrast, Java is typed. Every variable must be declared with a type, such as String or int. And when a variable is typed, you can’t put a different type of value into it. And if you tried calling a method that doesn’t exist on that type, the compiler will raise an error, letting you know your mistake before you run your code. C and its variants are other typed languages, while Ruby and Python are other untyped languages.

    Optionally typed means, as you may now guess, that you have the option declaring a type for variables. It’s as simple as this: you can leave the type off, and the compiler won’t do any extra checking. If you supply a type, then the compiler will help you out with errors. ActionScript is an example of another optionally typed language.

    This is rather clever move, one that is probably intended to help speed adoption. Dart programmers will most likely be JavaScript programmers making the leap. Providing support for an untyped language gives JavaScripters an easier learning curve, while providing a feature that many programmers insist is essential to serious programming, which can be picked up at a later time.

    Reified generics: Generics are a language feature that allow you to type the elements of a collection. For example, an Array in JavaScript cannot guarantee that the objects it contains are any specific type (aside from the fact that JavaScript isn’t a typed language). However, generics allow you to specify that every item in an Array – or any other collection type – must be of a certain type, perhaps a String. Thus if you try to insert a Number into the Array, you can get warnings. Reified generics go an extra step and allow this type safety past the compiler. Type integrity at runtime is preserved.

    Interfaces: An interface is a handy Object-Oriented technique. It defines a type without defining functionality. It’s uses are hard to sum up in a sentence or ten, suffice it to say that they are integral to advanced (and clean) Object-Oriented Programming techniques (namely design patterns). Once you grok interfaces, you’ll lament the lack of them in other languages.

    Statically checked: This goes back to the typing thing. When typing is in use, a variable with a type is considered “statically typed,” and as such the type can’t be changed once it’s been declared. This allows the compiler (or “static checker”) to make assumptions about your intentions with your code; that is, if you declare a variable as a String, then you shouldn’t try calling changeTimeZone on it. If you did (maybe you typed in what you thought was that variable holding the Date object), then the compiler can alert you to the error without having to run the code.


    So What Is Dart Really?

    Yes, I had to provide the “official” explanation of Dart. But that may or may not satisfy you. Here’s what Dart is, with the typical web developer in mind.

    Dart is Google’s replacement for JavaScript. It was announced in October of 2011, and the general intention is to provide the same tools that JavaScript does, only as power tools. As explained in the previous step, it has a lot of Object-Oriented features that the typical JavaScript programmer will be unfamiliar. But most programmers who are familiar with these features will readily advocate them as being essential for serious development.

    In short, Google feels that JavaScript isn’t up for the task of being a “real” programming language, given the amount of heavy lifting it’s been doing since the iPhone shot a hole in Flash’s zeppelin.

    Google’s hope is that Dart will be supported natively by all major browsers in the long term. That obviously isn’t happening right now, given that Dart itself is still in early-release mode, and we can’t expect Apple, Microsoft, or Mozilla to be jumping on board yet. However, what makes Dart worth looking at now is that Google has provided a compiler to convert Dart code into regular JavaScript.

    Personally, this is something I’ve been longing for since turning to JavaScript, coming from ActionScript. Say what you will about Flash (and rest assured, I’ve probably said it, too), but ActionScript 3 is a good language. Going from advanced AS3 to JavaScript has been proven to cause swearing. I have actually been toying with my own JavaScript compiler (I did not get very far and am quite happy to abandon the project), and I wanted something that could at least let me develop with some niceties, like real classes, interfaces, and types, even if it compiled into untyped, prototypical JavaScript. The compile process can catch some errors before you run the project, which is a huge time saver.

    So, whether or not Google gets its way, it is certainly possible to write Dart projects for the web today, and we’ll do just that by the end of this tutorial. Hopefully, along the way, I’ll convince you that Dart is actually pretty promising.


    What’s Wrong With Dart?

    Now that you’re all excited over “JavaScript 2.0,” now for the hard truth. Only Google has plans to incorporate support for Dart into its browser. No one else has expressed interest in that. In fact, quite a few statements have been made against Dart, or at least the idea of supporting Dart natively, from the browser makers themselves.

    And while Dart projects can compile into JavaScript, there’s necessarily some overhead in this process. The Dart compiler isn’t quite like the CoffeeScript compiler, which is more of a one-to-one process. Dart is its own language, with libraries and such, and when compiling into JavaScript it consequently compiles extra library code into the resulting JavaScript. This currently weighs more than a library such as jQuery. This is bound to change as the Dart team finesses the compilation, but basically it’s a jQuery-ish library that is required to normalize browser differences, and also some extra “sugar” to help make JavaScript a little more honest about its dynamic nature. This kind of extra weight isn’t terrible, but if you’re building a simple UI widget for a mobile site, then maybe Dart isn’t the best choice. It will shine more with larger-scale web applications that rely on significant JavaScript.

    Lastly, Dart is currently in development. That’s exciting, and it’s not something “wrong” with Dart per se, but if you start developing in Dart now, there’s a certain chance that the API will change, or that things won’t be documented fully or correctly, and the amount of information on the web is less than, say, the amount you can find about jQuery. It’s bleeding edge, and that may not be for you, or for a given project.


    What’s Awesome About Dart?

    At the same time, it’s bleeding edge, and that’s awesome. Investing in a little time now to learn Dart could put in a nice place once Dart is more stable. If you get involved now, you even have the opportunity to help shape the language. The Dart mailing list on Google Groups often has some back-and-forth between people suggesting ideas and Google engineers responding to that idea. Often user-contributed ideas are considered and it’s not uncommon to see them incorporated.

    I’ve already discussed the advantages of a typed, object-oriented language, and it probably goes without saying that those traits are also awesome.

    Dart is also rather reminiscent of Java, and that should be awesome if you’re more of a Java (or C) programmer looking to get into web development. Dart should provide a better transition into that world than JavaScript would.

    Lastly, Dart is not just about a JavaScript competitor. It’s a whole new language, and it’s poised to run anywhere. It’s being used on the server side, a la Node.js, and promises to be another widespread tool, like Ruby or Python.


    Should You Care?

    This is, of course, a loaded question, and I’d be inviting a comment-based Inquisition no matter how I answer. But answer I shall.

    You’ll probably already care, or not care, depending on how much you’re bothered by Dart’s problems, or excited by Dart’s advantages. The previous two steps give you plenty of information the lead you to your own conclusion.

    If you’re the type of person whose idea of programming JavaScript is to search the web for jQuery plugins and install them on your HTML page, then Dart probably isn’t going to offer you much excitement. Feel free to not care. And let me save you a bunch of time and reveal that I base the rest of this tutorial on the assumption that the reader cares, at least enough to explore.

    However, if you’re the type of front-end developer that actually believes in object-oriented JavaScript, tries to build error-catching into your scripts, and tends to write more JavaScript than HTML, then you may be a redneck someone who cares about good programming, regardless of the language or platform, in which case you might care about Dart. You at least owe it to yourself to try it out.

    In my opinion, Dart shows a lot of promise and has a place in web programming. But I don’t think it’s going to replace or even really offset JavaScript’s dominion over scripting on HTML pages. I think it will find a place with the more serious web applications, but unless the browser makers (other than Google) agree to build in a Dart VM – which I don’t think likely – Dart’s destiny is relegated to Chrome experiments and large-scale web apps. Simple scripting tasks, like a dash of interactivity on an otherwise static page, will not greatly benefit from Dart.

    Having said that, I feel it’s always worth learning about new stuff. You may learn that the new thing isn’t worth your time, but you should formulate that opinion on your own, through experience. We’ll provide some experience in this tutorial, so if you’re feeling adventurous, get ready for Dart.


    Conclusion

    This brief discussion of Dart has hopefully sparked your interest in this new language that may or may not take the web by storm. If you’d like to try it out, take a look at my Facebook-exclusive tutorial that will get your hands dirty with a simple Dart project.

    (If you’re not on Facebook, don’t worry. The tutorial will be on the main Activetuts+ site eventually, and we have plenty more Dart content lined up in the mean time.)

    Thanks for reading! Share your opinions about Dart in the comments.


  9. Kah Shiu Chong says:
    April 17, 2012 at 5:15 am

    In my previous tutorial about collision detection between a circle and a line, I covered projection on a line using the dot product of a vector. In this tutorial, we shall look at the perpendicular dot product and use it to predict the point of intersection for two lines.


    Final Result Preview

    Let’s take a look at the final result we will be working towards. Use the left and right arrow keys to steer the ship (triangle), and press up to boost the speed temporarily. If the projected future collision point is on the wall (the line), a red dot will be painted on it. For a collision that “already” happened (i.e. would have happened in the past, based on the current direction), a red dot will still be painted but slightly transparent.

    You can also mouse click and drag the black dots to move the wall. Note that we don’t just predict the location of the collision, but also the time.


    Step 1: Revision

    Before getting into the topic, let’s do some revision. Here’s the dot product equation (previously covered here):

    dot product formula

    And here’s the perpendicular dot product definition as extracted from Wolfram:

    perp dot product formula

    Step 2: Perpendicular Dot Product

    Now to help us form a mental picture, I’ve prepared the image below. I’m confident at this point you are able to derive the vertical and horizontal components of a vector, so the components involving sine and cosine shouldn’t be a challenge.

    a mental picture for two formula

    Let’s substitute both components with their equivalent. I have used A with a hat to represent the unit vector of A (that is, a vector that points in the same direction as A, but has a magnitude of exactly 1). Another detail is that the perpendicular of B is actually the right normal of B – more on normals next step.

    second mental picture for two formula

    From the diagram above we can see that the projection of B on A will produce |B|*cos (theta). But why would the projection of B’s normal produce |B|*sin (theta)?

    To better understand this, I’ve included a Flash demo below. Click and drag the black arrowhead. As you gently move it about, you will notice that its perpendicular axis follows as well. As they turn, the bold red lines will be also be animated. Note that these two lengths are the same – hence the equation of perpendicular dot product.


    Step 3: Normals

    Normals, by definition, lie on a perpendicular line intersecting your line of interest. Let’s try to imagine these lines on a geometric plane.

    normals of a vector

    The Cartesian coordinate system is used in the diagram above. B is the left normal and C is the right normal. We see that the x-component of B is negative (because it’s pointing to the left) and the y-component of C is negative (because it’s pointing down).

    But check out the similarities between B and C. Their x and y components are the same as A’s, except swizzled. The difference is only the position of the sign. So we reach a conclusion by the image below.

    second mental picture for two formula

    Note that we are referring specifically to the Cartesian coordinate system in this example. The y-axis of Flash’s coordinate space is a reflection of the one in Cartesian, resulting in a swap between the left and right normal.


    Step 4: Projecting the Point of Collision

    To figure out the point of collision of vector k on plane A, we shall link up the tail of k with an arbitrary point on plane A first. For the case below, vector j is the linking vector; then we get the perpendicular projection of k and j on plane A.

    using perp dot product of vectors

    The red dot on image below is the collision point. And I hope you can see the similar triangle in diagram below.

    similar triangles
    • |k|, magnitude of vector k
    • Length of j on perpendicular of plane A
    • Length of k on perpendicular of plane A

    So given the three components above, we can use the concept of ratio to deduce the length between the red and blue points. Finally, we set the magnitude of vector k to the said length and we have our collision point!


    Step 5: ActionScript Implementation

    So here comes the ActionScript implementation. I’ve included a demo below. Try to move the arrowheads so that both lines intersect. A little black dot will mark the intersection point of the lines. Note that these segments are not necessarily intersecting, but the infinite lines they represent are.

    Here’s the script that does the calculations. Check out Basic.as in the source download for the full script.

    
    
    private function recalculation ():void {
        reorient();
    
    /*Explain:
         * v1 & v2 are vectors to represent both line segments
         * v1 joins set1b(tail) to set1a (head) - analogous to vector k in diagram
         * v2 joins set2b(tail) to set2a (head)
         * toV2b is vector analogous to that of vector j in diagram
         */
        var perp1:Number = v1.perpProduct(v2.normalise());
        var toV2b:Vector2D = new Vector2D (set2b.x - set1b.x, set2b.y - set1b.y);
        var perp2:Number = toV2b.perpProduct(v2.normalise());
    
    /*Explain:
         * length is calculated from the similar triangles ratio
         * it is later used as magnitude for a vector
         * that points in v1's direction
         */
        var length:Number = perp2 / perp1 * v1.getMagnitude();
        var length_v1:Vector2D = v1.clone();
        length_v1.setMagnitude(length);
    
    /*Explain
         * extend to locate the exact location of collision point
         */
        intersec.x = set1b.x + length_v1.x;
        intersec.y = set1b.y + length_v1.y;
    }
    

    Step 6: Line Equations

    So I hope the first approach I’ve presented was easily understood. I understand performance in getting the intersection point is important, so next I’ll provide alternative approaches, although that will require some math revisions. Bear with me!

    First, let’s talk about line equations. There are several forms of line equation but we’ll just touch two of them in this tutorial:

    • General form
    • Parametric form

    I’ve included the image below to help you recall. Those interested in this can refer to this entry on Wikipedia.

    different forms of line equation

    Step 7: Deriving Standard Form

    Before we perform any manipulations on two line equations, we must derive these line equations first. Let’s consider the scenario where we are given coordinates of two points p1 (a, b). and p2 (c, d). We can form a line equation connecting these two points from the gradients:

    derive constants

    Then, using this equation, we can derive the constants A, B, and C for the standard form:

    derive constants

    Next, we can proceed to solving simultaneous line equations.


    Step 8: Solving Simultaneous Equations

    Now that we can form line equations, lets proceed to take two line equations and solve them simutaneously. Given these two line equations:

    • Ex + Fy = G
    • Px + Qy = R

    I’ll table these coefficients according to the general form Ax + By = C.

    A B C
    E F G
    P Q R

    In order to obtain value of y, we do the following:

    1. Multiply reciprocal coefficients of x for whole equation.
    2. Perform subtraction operation (from above) on both equations.
    3. Rearrange obtained equation in terms of y.
    A B C Multiply by
    E F G P
    P Q R E

    And we arrive at the following table.

    A B C
    EP FP GP
    PE QE RE

    After we subtract two equations out, we arrive at:

    • y (FP – QE) = (GP – RE), which rearranges to:
    • y = (GP – RE) / (FP – QE)

    Moving on to obtain x:

    A B C Multiply by
    E F G Q
    P Q R F

    We arrive at the following table

    A B C
    EQ FQ GQ
    PF QF RF

    After we subtract the two equations out, we arrive at:

    • x (EQ – PF) = (GQ – RF), which rearranges to:
    • x = (GQ – RF) / (EQ – PF)

    Let’s further rearrange y.

    • y = (GP – RE) / (FP – QE)
    • y = (GP – RE) / -(QE – FP)
    • y = (RE – GP) / (QE – FP)

    So we arrive at the intersection point of x and y. We notice they share the same denominator.

    • x = (GQ – RF) / (EQ – PF)
    • y = (RE – GP) / (QE – FP)

    Now that we have worked out the math operations and gotten the result, just pluck values in and we have the intersection point.


    Step 9: Applying Onto Actionscript

    Here’s the Actionscript implementation. So all vector operations are reduced to simple arithmetic but it’ll require some algebra workings initially.

    
    
    private function recalculation ():void {
        reorient();
    
        var E:Number = set1b.y - set1a.y;
        var F:Number = set1a.x - set1b.x;
        var G:Number = set1a.x * set1b.y - set1a.y * set1b.x;
    
        var P:Number = set2b.y - set2a.y;
        var Q:Number = set2a.x - set2b.x;
        var R:Number = set2a.x * set2b.y - set2a.y * set2b.x;
    
        var denominator:Number = (E * Q - P * F);
        intersec.x = (G * Q - R * F) / denominator;
        intersec.y = (R * E - G * P) / denominator;
    }
    

    Of course it’s the same result as previous demo, just with less maths involved, and with no use of the Vector2D class.


    Step 10: Matrix Alternative

    Another alternative to solving this problem is by the use of matrix math. Again, I invite interested readers to dive into Prof. Wildberger’s lecture on equations of lines. Here, we’ll just breeze through the concept quickly.

    According to Prof Wildberger, there are two frameworks we can adopt:

    • The Cartesian framework
    • The parameterised vector framework

    Let’s go through the Cartesian one first. Check out the image below.

    Cartesian matrix operation

    Note that matrix T and S contain constant values. What’s left unknown is A. So rearranging the matrix equation in terms of A will give us the result. However, we must get the inverse matrix of T.


    Step 11: Implementing With AS3

    Here’s the implementation of the above with ActionScript:

    
    
    private function recalculation ():void {
        reorient();
    
        var E:Number = set1b.y - set1a.y;
        var F:Number = set1a.x - set1b.x;
        var G:Number = set1a.x * set1b.y - set1a.y * set1b.x;
    
        var P:Number = set2b.y - set2a.y;
        var Q:Number = set2a.x - set2b.x;
        var R:Number = set2a.x * set2b.y - set2a.y * set2b.x;
    
        var T:Matrix = new Matrix(E, P, F, Q);
        T.invert();
        var S:Matrix = new Matrix();
        S.a = G; S.b = R;
    
        S.concat(T);	//multiplying the matrix
        intersec.x = S.a;
        intersec.y = S.b;
    }
    

    Step 12: Parametric Form

    Lastly, there’s the parametric form of the line equation, and we shall attempt to solve it through matrix math again.

    deriving matrix from parametric equations

    We’d like to get the intersection point. Given all information except for u and v which we attempt to find, we shall rewrite both the equations into matrix form and solve them.


    Step 13: Matrix Manipulation

    So again, we perform matrix manipulations to arrive at our result.

    arriving at result

    Step 14: Implementing With AS3

    So here’s the implementation of the matrix form:

    
    
    rivate function recalculation ():void {
        reorient();
    
    /*Explain:
         * r, s are actually referring to components of v2 normalised
         * p, q are actually referring to components of v1 normalised
         */
        var norm_v2:Vector2D = v2.normalise();
        var norm_v1:Vector2D = v1.normalise();
    
        var a_c:Number = set1b.x - set2b.x;
        var b_d:Number = set1b.y - set2b.y;
    
        var R:Matrix = new Matrix;
        R.a = norm_v2.x; R.c = norm_v1.x;
        R.b = norm_v2.y; R.d = norm_v1.y;
        R.invert();
    
        var L:Matrix = new Matrix;
        L.a = a_c;
        L.b = b_d;
        L.concat(R);
    
        intersec.x = set2b.x + L.a * norm_v2.x;
        intersec.y = set2b.y + L.a * norm_v2.y;
    }
    

    Step 15: Performance

    We’ve covered four approaches to solve this little problem. So what about performance? Well I think I’ll just leave this issue to readers to judge, although I believe the difference is negligible. Feel free to utilise this performance testing harness from Grant Skinner.

    So now that we’ve gotten this understanding, what’s next? Apply it!


    Step 16: Predicting Collision Time

    Assume a particle is moving in a path bound to collide with a wall. We can calculate the time to impact by the simple equation of:

    Velocity = Displacement / Time

    concept of tunneling

    Imagine you are inside of this orange round particle and for each passing frame and announcement is made on the time to collide with wall. You’ll hear:

    "Time to impact: 1.5 frames" – Frame 1

    "Time to impact: 0.5 frames" – Frame 2

    "Time to impact: -0.5 frames" – Frame 3

    When we reach frame 3, collision with line has already happened (as indicated by the negative sign). You have to rewind time in order to reach the point of collision. Obviously collision should happen some time between Frames 2 and 3, but Flash moves in single frame increments. So if collision happened halfway between frames, a flip of the sign to negative will indicate that the collision has already happened.


    Step 17: Negative Time

    getting negative displacement

    In order to get negative time, we’ll use the vector dot product. We know that when we have two vectors and the direction of one is not within 90 degrees either side of the other, they will produce a negative dot product. Also, the dot product is a measure of how parallel two vectors are. So when collision has already happened, the velocity and direction of a vector to a point on wall will be negative – and vice versa.


    Step 18: ActionScript Implementation

    So here’s the script (included in CollisionTime.as). I’ve also added collision detection within line segment here. For those who find it unfamiliar, do refer to my tutorial on collision detection between a circle and a line segment, Step 6. And for help on steering ships, here’s another reference.

    
    
    //deciding if within wall segment
    var w2_collision:Vector2D = new Vector2D(collision.x - w2.x, collision.y - w2.y);
    collision.alpha = 0;
    
    //when ship is heading to left of wall
    if (w2_collision.dotProduct(v1) < 0) {
        t.text = "Ship is heading to left of wall";
    }
    else {
        //when ship is heading to right of wall
        if (w2_collision.getMagnitude() > v1.getMagnitude()) {
            t.text = "Ship is heading to right of wall"
        }
        //when ship is heading to wall segment
        else {
            var ship_collision:Vector2D = new Vector2D(collision.x - ship.x, collision.y - ship.y);
            var displacement:Number = ship_collision.getMagnitude();
            if (ship_collision.dotProduct (velo) < 0) displacement *= -1;
    
            //showing text
            var time:Number = displacement / velo.getMagnitude();
            t.text = "Frames to impact: " + time.toPrecision(3) + " frames.\n";
            time /= stage.frameRate;
            t.appendText("Time to impact: " + time.toPrecision(3) + " seconds.\n");
    
            //drop down alpha if collision had happened
            if (displacement > 0) collision.alpha = 1;
            else {
                collision.alpha = 0.5;
                t.appendText("Collision had already happened.")
            }
        }
    }
    

    Step 19: Final Result

    So here’s a demo of what you will arrive at. Use the left and right arrow keys to steer the ship (triangle), and press Up to boost speed temporarily. If the predicted future collision point is on the wall (the line), a red dot will be painted on it. For collision that has “already” happened, a red dot will still be painted but slightly transparent. You may also drag the black dots either side of the wall to move it.

    Conclusion

    So I hope this tutorial has been informative. Do share if you’ve actually applied this idea elsewhere from the one I’ve mentioned. I’m planning a brief write-up on applying it to paint laser targets – what do you think? Thanks for reading and let me know if there are any errors.


  10. Christer Kaitila says:
    April 17, 2012 at 6:13 am
    This entry is part 5 of 5 in the series Build a Stage3D Shoot-’Em-Up

    In this tutorial series (part free, part Premium) we’re creating a high-performance 2D shoot-em-up using Flash’s new hardware-accelerated Stage3D rendering engine. In this Premium part, we’re adding gameplay elements such as health, score, and lives, the GUI elements to display them, and game logic transitions to deal with dying, game overs, level changes, and the final credits screen.


    Premium Preview

    Let’s take a look at the result we will be working towards:

    We can now boast a detailed game world filled with things that can actually destroy the player, plus all sorts of fancy GUI elements like the high score and a health meter to give it a true arcade feel. We give the player a lot more in-game feedback now, whether in the form of “LEVEL COMPLETE” messages, sparks flying from our ship when we are about to die, or a period of innulnerability after we get hit so we have a chance to recover before being bombarded by the next wave of deadly enemies. Our game is now quite challenging.


    Read the Full Tutorial

    Premium members can access the full tutorial right away!

    If you’re not yet a Premium member, you can still read the first few steps for free. Plus, the first three parts of this series are free, in case you haven’t read them yet.


    Tuts+ Premium Membership

    We run a Premium membership system which periodically gives members access to extra tutorials, like this one, from across the whole Tuts+ network. If you’re a Premium member, you can log in and read the tutorial. If you’re not a member, you can of course join today!

    Also, don’t forget to follow @envatoactive on twitter, circle us on Google+, like us on Facebook, and grab the Activetuts+ RSS Feed to stay up to date with the latest tutorials and articles.


Leave a Reply

Click here to cancel reply.

search search search search search
Find an Article
Categories
  • Flash Video Training
  • Hints and Tips
  • Recommended
Please Support Our Sponsors
Recent Posts
  • Tuts+ Community Meetup in New York!
  • HTML5 Canvas Optimization: A Practical Example
  • Recreate the Cover Flow Effect Using Flash and AS3
  • Drawing Activetuts+ to a Close
  • Intro to Dart: Creating a Marquee
Tag Cloud
2011 ActionScript Active Activetuts+ Adobe animation Basic Basix Best Build Button Character Code Create Creating Critique Custom design Effect Effects Files Flash from Game Guide HTML5 Introduction Macromedia Motion Muzzle part Player Premium Professional Quick Silverlight Simple Text Tool Tutorial Tuts+ Using Video website Workshop
About Our Site:

Hey there and welcome to "Flash Video Training Source", a resource for anybody interested in learning more about Adobe's great tool. We feature educational videos, which will help you master Adobe Flash and help you get to know all of its features. We at "Flash Video Training Source" believe that video training and video... more

Why don't you follow us on Twitter and get the latest video tutorials twitted to your account. Just click on the floating twitter bar to your right!

Go Back In Time
May 2013
M T W T F S S
« Jul    
 12345
6789101112
13141516171819
20212223242526
2728293031  
Pretty Blank Box
top

Blogroll

  • Development Blog
  • Documentation
  • Plugins
  • Suggest Ideas
  • Support Forum
  • Themes
  • WordPress Planet

Meta

  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org

Archives

  • July 2012
  • June 2012
  • May 2012
  • April 2012
  • March 2012
  • February 2012
  • January 2012
  • December 2011
  • November 2011
  • October 2011
  • September 2011
  • August 2011
  • July 2011
  • June 2011
  • May 2011
  • April 2011
  • March 2011
  • February 2011
  • January 2011
  • December 2010
  • November 2010
  • October 2010
  • September 2010
  • August 2010
  • July 2010
  • June 2010
  • May 2010
  • April 2010
Powered by WordPress  |  Designed by Elegant Themes  |  Lightning Fast Hosting by Site 5 Hosting