Articles

Playing with webcam / face tracking / Box2D in Flash

I’ve spent part of a day playing with webcam + face tracking + box2d in Flash. The source for the face detection and tracking is from Quasimondo. I used QuickBox2D to get a simple Box2D setup. And the rest is just good old actionscript. It’s the beginnings of a game, and I’ll keep updating the post as I go.

First up is the initial experiment. Keep the ball in the air!

Next up I’ve changed the paddle to move in the opposite direction to the webcam image, which ends up being the same direction that you’re actually moving. I’d initially though that the way it was in the first experiment added to the difficulty but it was counter intuitive and everyone I’d watched give it a go mentioned it. I also smoothed out the rotation movement compared to the first example, and the paddle becomes shorter as you progress. So here’s the second version.

So another day spent on this webcam game and I’ve made a bunch of additions / improvements. I’ve added a calibration section which helps with translating the webcam tracking to the paddle control, as I’d found it was quite difficult to get the paddle to far edges of the screen. Seems to be better control of the paddle now. Also added some of the standard game screens (home, instructions, start, game over, etc). Everything’s just simple placeholders as the moment as I’ll be skinning the UI when all the functionality is done. So try phase three of the game in progress. And if you give it a go leave a comment, I’d like as much feedback as possible.

Articles

Loading an external swf with parameters in the URL

Recently I needed to load an external swf with parameters in the URL (eg. file.swf?foo=bar). The reason was to prevent loading of any cached version of the swf by adding a random number to the url. Seems like a simple enough task, right? Well it kind of is. But not as simple as just using the Loader class which throws a 2035: URL Not Found error. And using URLLoader also doesn’t work, as that throws a 2032: Stream Error. The only solution I could work out was to change the dataFormat property of the URLLoader to “binary” (it is set to “text” by default) load the swf, and then use a Loader’s loadBytes method to load the URLLoader’s data. See my ParamSWFLoader class below as well a simple usage example. Feel free to use it in your own projects. Note it makes us of Robert Penner’s AS3 Signals which you’ll need to download.

/**
 *
 * ParamSWFLoader class
 *
 * Copyright (c) 2010 Ben Kanizay
 * This software is released under the MIT License
 *
 */
package beekay.core.utils {
	import org.osflash.signals.Signal;

	import flash.display.DisplayObject;
	import flash.display.Loader;
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.IOErrorEvent;
	import flash.events.ProgressEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;

	/**
	 * @author ben.kanizay
	 */
	public class ParamSWFLoader {

		protected var request:URLRequest;
		protected var urlLoader:URLLoader;
		protected var bytesLoader:Loader;

		protected var _loadedContent:DisplayObject;
		public function get loadedContent() : DisplayObject {
			return _loadedContent;
		}

		public var signalError:Signal;
		public var signalProgress:Signal;
		public var signalComplete:Signal;

		public function ParamSWFLoader() {
			this.signalError = new Signal(String);
			this.signalProgress = new Signal(int);
			this.signalComplete = new Signal();
		}

		public function load(url:String) : void {
			this.urlLoader = new URLLoader();
			this.urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
			this.request = new URLRequest(url);

			this.urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.onLoaderSecurityError, false, 0, true);
			this.urlLoader.addEventListener(IOErrorEvent.IO_ERROR, this.onLoaderIOError, false, 0, true);
			this.urlLoader.addEventListener(ErrorEvent.ERROR, this.onLoaderError, false, 0, true);
			this.urlLoader.addEventListener(ProgressEvent.PROGRESS, this.onURLLoaderProgress, false, 0, true);
			this.urlLoader.addEventListener(Event.COMPLETE, this.onURLLoaderComplete, false, 0, true);

			try {
				this.urlLoader.load(this.request);
			}
			catch (error:Error) {
				this.signalError.dispatch(error.message);
				this.removeURLLoaderListeners();
			}
		}

		protected function onLoaderError(event : ErrorEvent) : void {
			this.signalError.dispatch(event.text);
			this.removeURLLoaderListeners();
		}

		protected function onLoaderSecurityError(event : SecurityErrorEvent) : void {
			this.signalError.dispatch(event.text);
			this.removeURLLoaderListeners();
		}

		protected function onLoaderIOError(event : IOErrorEvent) : void {
			this.signalError.dispatch(event.text);
			this.removeURLLoaderListeners();
		}

		protected function onURLLoaderProgress(event : ProgressEvent) : void {
			var percentLoaded:int = Math.round((event.bytesLoaded/event.bytesTotal)*100);
			this.signalProgress.dispatch(percentLoaded);
		}

		public function removeURLLoaderListeners() : void {
			this.urlLoader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, this.onLoaderSecurityError);
			this.urlLoader.removeEventListener(IOErrorEvent.IO_ERROR, this.onLoaderIOError);
			this.urlLoader.removeEventListener(ErrorEvent.ERROR, this.onLoaderError);
			this.urlLoader.removeEventListener(ProgressEvent.PROGRESS, this.onURLLoaderProgress);
			this.urlLoader.removeEventListener(Event.COMPLETE, this.onURLLoaderComplete);
		}

		protected function onURLLoaderComplete(event : Event) : void {
			this.removeURLLoaderListeners();

			this.bytesLoader = new Loader();
			this.bytesLoader.contentLoaderInfo.addEventListener(ErrorEvent.ERROR, this.onLoaderError, false, 0, true);
			this.bytesLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.onBytesLoaderComplete, false, 0, true);

			try {
				this.bytesLoader.loadBytes(this.urlLoader.data);
			}
			catch (error:Error) {
				this.signalError.dispatch(error.message);
				this.removeBytesLoaderListeners();
			}
		}

		protected function onBytesLoaderComplete(event : Event) : void {
			this.removeBytesLoaderListeners();
			this._loadedContent = this.bytesLoader.content;
			this.signalComplete.dispatch();
		}

		public function removeBytesLoaderListeners() : void {
			this.bytesLoader.contentLoaderInfo.removeEventListener(ErrorEvent.ERROR, this.onLoaderError);
			this.bytesLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, this.onBytesLoaderComplete);
		}		

	}
}

And a simple usage example:

package {

import beekay.core.utils.ParamSWFLoader;

import flash.display.Sprite;

/**
* @author ben.kanizay
*/
public class Main extends Sprite {

protected var swfLoader:ParamSWFLoader;

public function Main() {
this.swfLoader = new ParamSWFLoader();
this.swfLoader.signalError.addOnce(this.onSwfLoaderError);
this.swfLoader.signalProgress.add(this.onSwfLoaderProgress);
this.swfLoader.signalComplete.addOnce(this.onSwfLoaderComplete);

this.swfLoader.load("http://path/to/content.swf?cacheBlocker=" + parseInt(String(Math.random()*1000)));
}

protected function onSwfLoaderError(error:String) : void {
trace("ERROR: " + error);
}

protected function onSwfLoaderProgress(percent:int) : void {
trace(percent + "% LOADED");
}

protected function onSwfLoaderComplete() : void {
trace("DONE!");
this.swfLoader.signalProgress.remove(this.onSwfLoaderProgress);
this.addChild(this.swfLoader.loadedContent);
}

}
}
12
Apr 2010
POSTED BY
POSTED IN

Articles

DISCUSSION 4 Comments
Articles

AS3 Drawing Utility Class

Trying to work as much in pure AS3 as I can on projects, I’m often dynamically drawing simple shapes like squares, rectangles and circles. Rather than re-writing the drawing methods each time I have a few stored in a DrawingUtil class, which I just include in my Libraries folder – along with a bunch of other utilities I reuse over and over (I’ll post more of these utils in time). Anyhow I thought I’d share this DrawingUtil class with the world in the hope it may make some of your lives easier. If you want to just scroll straight down to the script (download link is at the bottom) go for it. Else I’ll explain a little some of it first…

There’s 3 main methods in the class drawRectSprite which will return a square/rectangle shaped Sprite (with optional rounded corners via the ellipse parameter), drawCircSprite which will return a circle shaped Sprite and drawTriSprite which will return a simple triangle shaped Sprite. With all 3 methods there are optional parameters for stroke and fill properties as Objects.

For the fill Object (null by default), you can create a gradient fill by setting fill.colour as an Array of two hex values (I’ve only written it to be a simple gradient between 2 colours). If the fill.colour value is an Array with only one item or fill.colour itself is a hex value the shape will be filled with that one solid colour. Note that if fill or fill.colour is null the default fill colour of 0x00FFFF will be used. All the options that are accepted for the fill Object are:

  • colour - A single hex value (eg 0xFFFFFF) or an Array containing one hex value item for a solid fill. Or an Array containing 2 hex values for a gradient fill.
  • alpha – For solid fills this can be a Number value, or an Array value where alpha[0] is a Number. For gradient fills, an Array of two Number items will match alpha[0] to colour[0] and alpha[1] to colour[1]. If the fill is a gradient and only one alpha is set, either as a Number of Array item that single value will be used for both gradient colours. Default value is 1.

The following additional properties are available in the fill Object for gradients:

  • gradientType - A String value of “linear” or “radial” (See GradientType). Default value is GradientType.LINEAR
  • spreadMethod - A String value of “pad”, “repeat” or “reflect” (See SpreadMethod). Default value is SpreadMethod.PAD
  • ratios – An Array with 2 Number items containing the gradient fill ratios. Default is [0, 255]
  • rotation – A Number value of the gradient fill rotation. Default value is 0

For the stroke Object (null by default), you can leave it as null to not draw a stroke or you can set stroke.weight and stroke.colour (hex value). Note that if the stroke Object is not null and stroke.colour is not set, a black stroke will be drawn. The default values noted below are set if the stroke paramater is not null.

  • weight – A Number value of the stroke weight. Default is 1
  • colour - A single hex (uint) value for the colour of the stroke. Default is black (ox000000)
  • pixelHinting - A Boolean value for whether to use pixel hinting or not. Default is true
  • scaleMode – A String value for the stroke scale mode (See LineScaleMode). Default is LineScaleMode.NONE
  • alpha - A Number value for the stroke alpha. Default is 1
/**
 *
 * DrawingUtil class
 *
 * Copyright (c) 2009 Ben Kanizay
 * This software is released under the MIT License
 *
 */

 package beekay.core.utils {
	import flash.display.LineScaleMode;
	import flash.display.GradientType;
	import flash.display.Graphics;
	import flash.display.SpreadMethod;
	import flash.display.Sprite;
	import flash.geom.Matrix;

	/**
	 * @author ben.kanizay
	 */
	public class DrawingUtil {

		/*
		 * Returns a rectangular sprite.
		 *
		 * @param width:Number  the width of the rectangle
		 * @param height:Number  the height of the rectangle
		 * @param fill:Object (optional) - An object containing fill properties - default is a solid 0x00FFFF fill and alpha of 1
		 * @param stroke:Object (optional) - An object containing stroke properties - default is no stroke
		 * @param ellipse:int (optional) the size of the rounded corners - default is 0
		 *
		 * @return Sprite
		 *
		 */

		public static function drawRectSprite(width:Number, height:Number, fill:Object=null, stroke:Object=null, ellipse:int=0):Sprite {

			var sprite:Sprite = new Sprite();
			var graphics:Graphics = sprite.graphics;

			// check if we're drawing a stroke
			if (stroke != null) {
				var strokeObj:Object = getStroke(stroke);
				graphics.lineStyle(strokeObj.weight, strokeObj.colour, strokeObj.alpha, strokeObj.pixelHinting, strokeObj.scaleMode);
			}

			// determine the fill type and properties
			if (fill != null && fill.colour != null && fill.colour is Array && fill.colour.length > 1) {
				// gradient
				var gradProps:Object = getGradient(fill);
				var gradMatrix:Matrix = new Matrix();
				gradMatrix.createGradientBox(width, height, gradProps.rotation, 0, 0);
				graphics.beginGradientFill(gradProps.type, gradProps.colours, gradProps.alphas, gradProps.ratios, gradMatrix, gradProps.Spread);
			}
			else {
				// solid
				var fillProps:Object = getSolid(fill);
				graphics.beginFill(fillProps.colour, fillProps.alpha);
			}

			// rounded corners or not?
			if (ellipse > 0) {
				graphics.drawRoundRect(0, 0, width, height, ellipse);
			}
			else {
				graphics.drawRect(0, 0, width, height);
			}

			graphics.endFill();
			return sprite;
		}

		/*
		 * Returns a circular sprite.
		 *
		 * @param radius:int  the radius of the circle
		 * @param fill:Object (optional) - An object containing fill properties
		 * @param stroke:Object (optional) - An object containing stroke properties
		 *
		 * @return Sprite
		 *
		 */

		public static function drawCircSprite(radius:int, fill:Object=null, stroke:Object=null):Sprite {
			var sprite:Sprite = new Sprite();
			var graphics:Graphics = sprite.graphics;

			// check if we're drawing a stroke
			if (stroke != null) {
				var strokeObj:Object = getStroke(stroke);
				graphics.lineStyle(strokeObj.weight, strokeObj.colour, strokeObj.alpha, strokeObj.pixelHinting, strokeObj.scaleMode);
			}

			// determine the fill type and properties
			if (fill != null && fill.colour != null && fill.colour is Array && fill.colour.length > 1) {
				// gradient
				var gradProps:Object = getGradient(fill);
				var gradMatrix:Matrix = new Matrix();
				gradMatrix.createGradientBox(radius, radius, gradProps.rotation, 0, 0);
				graphics.beginGradientFill(gradProps.type, gradProps.colours, gradProps.alphas, gradProps.ratios, gradMatrix, gradProps.Spread);
			}
			else {
				// solid
				var fillProps:Object = getSolid(fill);
				graphics.beginFill(fillProps.colour, fillProps.alpha);
			}

			graphics.drawCircle(0, 0, radius);
			graphics.endFill();
			return sprite;
		}

		/*
		 * Returns a simple triangle shaped Sprite with equal sides.
		 *
		 * @param base:int  the length of the triangle base (the other sides will be the same)
		 * @param fill:Object (optional) - An object containing fill properties
		 * @param stroke:Object (optional) - An object containing stroke properties
		 *
		 * @return Sprite
		 *
		 */

		public static function drawTriSprite(base:int, fill:Object=null, stroke:Object=null) : Sprite {
			var height:Number = Math.sqrt((base*base)-((base/2)*(base/2)));
			var sprite:Sprite = new Sprite();
			var graphics:Graphics = sprite.graphics;

			// check if we're drawing a stroke
			if (stroke != null) {
				var strokeObj:Object = getStroke(stroke);
				graphics.lineStyle(strokeObj.weight, strokeObj.colour, strokeObj.alpha, strokeObj.pixelHinting, strokeObj.scaleMode);
			}

			// determine the fill type and properties
			if (fill != null && fill.colour != null && fill.colour is Array && fill.colour.length > 1) {
				// gradient
				var gradProps:Object = getGradient(fill);
				var gradMatrix:Matrix = new Matrix();
				gradMatrix.createGradientBox(base, height, gradProps.rotation, 0, 0);
				graphics.beginGradientFill(gradProps.type, gradProps.colours, gradProps.alphas, gradProps.ratios, gradMatrix, gradProps.Spread);
			}
			else {
				// solid
				var fillProps:Object = getSolid(fill);
				graphics.beginFill(fillProps.colour, fillProps.alpha);
			}

			graphics.moveTo(0, height);
			graphics.lineTo(base/2, 0);
			graphics.lineTo(base, height);
			graphics.lineTo(0, height);
			graphics.endFill();

			return sprite;
		}

////////////////////////////////////////////////////////////////
//		HELPER METHODS
////////////////////////////////////////////////////////////////		

		/*
		 * Creates the stroke properties, using defaults for any null values
		 */
		private static function getStroke(props:Object) : Object {
			var stroke:Object = {};

			var strokeWeight:Number = (props.weight != null) ? props.weight : 1;
			var strokeColour:uint = (props.colour != null) ? props.colour : 0x000000;
			var pixelHinting:Boolean = (props.pixelHinting != null) ? props.pixelHinting : true;
			var scaleMode:String = (props.scaleMode != null) ? props.scaleMode : LineScaleMode.NONE;
			var strokeAlpha:Number = (props.alpha != null) ? props.alpha : 1;

			stroke.weight = strokeWeight;
			stroke.colour = strokeColour;
			stroke.pixelHinting = pixelHinting;
			stroke.scaleMode = scaleMode;
			stroke.alpha = strokeAlpha;

			return stroke;
		}

		/*
		 * Creates the gradient fill properties, using defaults for any null values
		 */
		private static function getGradient(fill:Object) : Object {
			var gradient:Object = {};

			var gradColours:Array = [parseInt(fill.colour[0]), parseInt(fill.colour[1])];
			var gradAlphas:Array = (fill.alpha is Array) ? (fill.alpha.length > 1) ? [fill.alpha[0], fill.alpha[1]] : [fill.alpha[0], fill.alpha[0]] : (fill.alpha != null) ? [fill.alpha, fill.alpha] : [1, 1];
			var gradType:String = (fill.gradientType != null) ? fill.gradientType : GradientType.LINEAR;
			var gradSpread:String = (fill.spreadMethod != null) ? fill.spreadMethod : SpreadMethod.PAD;
			var gradRatios:Array = (fill.ratios is Array && fill.ratios.length > 1) ? [fill.ratios[0], fill.ratios[1]] : [0, 255];
			var gradRotation:Number = (fill.rotation != null) ? fill.rotation : 0;

			gradient.colours = gradColours;
			gradient.alphas = gradAlphas;
			gradient.type = gradType;
			gradient.spread = gradSpread;
			gradient.ratios = gradRatios;
			gradient.rotation = gradRotation;

			return gradient;
		}

		/*
		 * Creates the solid fill properties, using defaults for any null values
		 */
		private static function getSolid(fill:Object) : Object {
			var solid:Object = {};

			var fillAlpha:Number = (fill.alpha == null) ? 1 : (fill.alpha is Array) ? fill.alpha[0] : fill.alpha;
			var fillColour:uint = (fill.colour != null) ? (fill.colour is Array) ? parseInt(fill.colour[0]) : parseInt(fill.colour) : 0x00FFFF;

			solid.alpha = fillAlpha;
			solid.colour = fillColour;

			return solid;
		}

	}
}

Some basic usage examples

// a 200x200 black square:
var box1:Sprite = DrawingUtil.drawRectSprite(200, 200, {colour:0x000000});

// a 200x200 square with a linear gradient from grey to black:
var box2:Sprite = DrawingUtil.drawRectSprite(200, 200, {colour:[0xCCCCCC, 0x000000]});

// and 200x200 black square with a grey 2px grey stroke
var box3:Sprite = DrawingUtil.drawRectSprite(200, 200, {colour:0x000000}, {weight:2, colour:0xCCCCCC});

// the same as above but with rounded corners set to 20
var box4:Sprite = DrawingUtil.drawRectSprite(200, 200, {colour:0x000000}, {weight:2, colour:0xCCCCCC}, 20);

// and the black square with rounded corners but no stroke
var box5:Sprite = DrawingUtil.drawRectSprite(200, 200, {colour:0x000000}, null, 20);

And similarly on it goes with drawTriSprite and drawCircSprite. Best way I find is to have a play, so download the DrawingUtil class and go nuts.
As always I appreciate your feedback am happy to improve if you have any suggestions.

13
Nov 2009
POSTED BY
POSTED IN

Articles

DISCUSSION 4 Comments
Articles

AS3 Bandwidth Checker

A while back I worked on an FLV player where it gave the user a choice between viewing the high or low quality version of each video. But because the video automatically started when the player loaded (much like YouTube) I wanted to determine which video to show initially based on each visitors bandwidth. And so, this BandwidthChecker class came about. Basically it loads an image you specify and based on the time it takes to load, determines whether it’s high or low. I’ve only used public and protected namespaces so you can easily extend the class if you want to enhance it. Not only for video players, this could also be used for an image gallery to determine whether to show larger, higher quality images or smaller, more compressed ones. Simple I guess, but has come in handy for me at least. Here it is:

/**
 *
 * BandwidthChecker utiltiy
 *
 * Copyright (c) 2009 Ben Kanizay
 * This software is released under the MIT License
 *
 */

package beekay.core.utils {

	import flash.display.Loader;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLRequest;
	import flash.system.Capabilities;
	import flash.utils.getTimer;

	public class BandwidthChecker extends EventDispatcher {

		public static const DEFAULT_BANDWIDTH : String = "low";
		public static const DEFAULT_BANDWIDTH_SPEED : Number = 900;

		protected var bandwidthDetected : String;
		protected var bandwidthSpeedDetected : Number;

		protected var startTime : uint;
		protected var testFile : String;
		protected var loader : Loader;
		protected var request : URLRequest;

		public function BandwidthChecker() {
		}

/////////////////////////////////////////////////////////////////////////////
//		PUBLIC METHODS
/////////////////////////////////////////////////////////////////////////////

		public function check(testImage:String) : void {
			this.testFile = testImage;
			this.loader = new Loader();
			this.request = new URLRequest(this.testFile + this.getCacheBlocker());
			this.addLoaderListeners();
			this.loader.load(this.request);
		}

/////////////////////////////////////////////////////////////////////////////
//		EVENT HANDLERS
/////////////////////////////////////////////////////////////////////////////

		protected function onLoadStart(event:Event) : void {
			this.startTime = getTimer();
		}

		protected function onLoadError(event:Event) : void {
			this.bandwidthDetected = DEFAULT_BANDWIDTH;
			this.bandwidthSpeedDetected = DEFAULT_BANDWIDTH_SPEED;
			this.complete();
		}

		protected function onLoadComplete(event:Event) : void {
			var time : Number = (getTimer() - this.startTime) / 1000;
			this.bandwidthSpeedDetected = Math.round(( this.loader.contentLoaderInfo.bytesTotal / 1000 * 8 ) / time);
			this.bandwidthDetected = (this.bandwidthSpeedDetected > DEFAULT_BANDWIDTH_SPEED) ? "high" : "low";
			this.complete();
		}

/////////////////////////////////////////////////////////////////////////////
//		HELPERS
/////////////////////////////////////////////////////////////////////////////

		protected function getCacheBlocker() : String {
			if ((Capabilities.playerType == "External" || Capabilities.playerType == "StandAlone")) {
				return "";
			}
			else {
				var date : Date = new Date();
				var time : Number = date.getTime();
				return "?t=" + time.toString();
			}
		}

		protected function addLoaderListeners() : void {
			this.loader.contentLoaderInfo.addEventListener(Event.OPEN, this.onLoadStart, false, 0, true);
			this.loader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.onLoadComplete, false, 0, true);

			this.loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, this.onLoadError, false, 0, true);
			this.loader.contentLoaderInfo.addEventListener(IOErrorEvent.NETWORK_ERROR, this.onLoadError, false, 0, true);
			this.loader.contentLoaderInfo.addEventListener(IOErrorEvent.DISK_ERROR, this.onLoadError, false, 0, true);
			this.loader.contentLoaderInfo.addEventListener(IOErrorEvent.VERIFY_ERROR, this.onLoadError, false, 0, true);
			this.loader.contentLoaderInfo.addEventListener(SecurityErrorEvent.SECURITY_ERROR, this.onLoadError, false, 0, true);
		}

		protected function removeLoaderListeners() : void {
			this.loader.contentLoaderInfo.removeEventListener(Event.OPEN, this.onLoadStart);
			this.loader.contentLoaderInfo.removeEventListener(Event.COMPLETE, this.onLoadComplete);

			this.loader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, this.onLoadError);
			this.loader.contentLoaderInfo.removeEventListener(IOErrorEvent.NETWORK_ERROR, this.onLoadError);
			this.loader.contentLoaderInfo.removeEventListener(IOErrorEvent.DISK_ERROR, this.onLoadError);
			this.loader.contentLoaderInfo.removeEventListener(IOErrorEvent.VERIFY_ERROR, this.onLoadError);
			this.loader.contentLoaderInfo.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, this.onLoadError);
		}

		protected function complete() : void {
			this.removeLoaderListeners();
			this.loader = null;
			this.request = null;
			this.testFile = null;
			this.dispatchEvent(new Event(Event.COMPLETE));
		}

/////////////////////////////////////////////////////////////////////////////
//		GETTERS
/////////////////////////////////////////////////////////////////////////////

		public function getBandwidth() : String {
			return this.bandwidthDetected;
		}

		public function getBandwidthSpeed() : Number {
			return this.bandwidthSpeedDetected;
		}

	}
}

Simple usage example (you can use this as a document class):

package {
	import beekay.core.utils.BandwidthChecker;

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

	public class Example extends Sprite {

		private var bandwidthChecker : BandwidthChecker;
		private var testFile : String = "path/to/your/image.jpg";

		private var bandwidth:String;

		public function Example() {
			this.bandwidthChecker = new BandwidthChecker();
			this.bandwidthChecker.addEventListener(Event.COMPLETE, this.onBandwidthCheckComplete, false, 0, true);
			trace("Checking bandwidth...");
			this.bandwidthChecker.check(this.testFile);
		}

		private function onBandwidthCheckComplete(event : Event) : void {
			this.bandwidthChecker.removeEventListener(Event.COMPLETE, this.onBandwidthCheckComplete);
			this.bandwidth = this.bandwidthChecker.getBandwidth();
			trace("Detected bandwidth: " + this.bandwidth);
		}
	}
}

Because my usage of it was fairly basic, if it detects errors BandwidthChecker still just dispatches a Event.COMPLETE event and applies the default bandwidth as set by the constants. Because it actually listens for errors, this can easily be changed by extending the class and creating your own error event handlers.

Also, it’s probably a good idea not to use too large or too small an image to load for the test. If too small, the calculation may not be so accurate and if too large the user could be waiting a while (if they’re on a slow connection). I usually test with an image around 30 – 40kb.

As internet speeds get faster this may not be as useful as it once was. But then again you may want to do adjust it and check whether a user is on just a fast, or blow-your-mind, snap-your-head-back-in-your-chair connection speed.

Click here to download the BackwidthChecker and Example classes.

Enjoy.

12
Nov 2009
POSTED BY
POSTED IN

Articles

DISCUSSION 5 Comments
Articles

AS3 version of PHP’s empty() function

When working in PHP I often find the empty() function handy for checking whether a variable is empty or has a zero value. It’s great because I can use it to check different types of variables: boolean, string, array and any variable that is null, or declared but without a value in the class. So I decided to write a similar function in AS3 for my Flash projects. Here it is:

public static function empty(variable:*) : Boolean {

	var isEmpty:Boolean;

	if (variable == null || variable == undefined) {
		isEmpty = true;
	}
	else if (variable is String) {
		var str:String = variable.replace(/^\s+|\s+$/g, '');
		isEmpty = (str.replace(/\s+/g, ' ') == "" || variable == "" || variable == "0" || variable == "false");
	}
	else if (variable is Number || variable is int || variable is uint) {
		isEmpty = (variable == 0);
	}
	else if (variable is Array) {
		isEmpty = (variable.length == 0);
	}
	else if (variable is Boolean) {
		isEmpty = (variable == false);
	}

	return isEmpty;

}

Similar to the PHP version but with some extras, the following things are considered to be empty and will return false:

  • NULL
  • UNDEFINED
  • 0 (0 as a Number, int or uint)
  • “” (an empty String)
  • ” ” (a String containing only whitespace)
  • “0″ (0 as a String)
  • “false” (false as a String)
  • FALSE (Boolean)
  • Array() (an empty array or an array of zero length)
  • var myVar:*; (a variable declared, but without a value)

If you have any suggestions or improvements to this, let me know and I’ll update it.

11
Nov 2009
POSTED BY
POSTED IN

Articles

DISCUSSION 4 Comments
Articles

Create Dynamic XML With PHP’s DOMDocument Class and Use It In Your Flash App

I was reading up on PHP’s DOMDocument Class recently and it’s really handly if you want to dynamically create HTML or XML files from PHP.

One practical application would be to create a simple dynamic image gallery in Flash without the need to manually maintain an XML file or database. All you’d need to do is mange the image files/directory. So I combined DomDocument with some of php’s directory functions to come up with the script below.

// relative path to the directory we'll be reading
$path = "images/";

// use the opendir function (the @ symbol will supress error messages and warnings)
$dir_handle = @opendir($path) or die("Unable to open $path");

// an array of filetypes we'll include in the output
$filetypes = array("jpg", "png");

// create a new XML document
$doc = new DomDocument('1.0');
// nicely format the output with indentation and extra space
$doc->formatOutput = true;

// create the root node (I've called it IMAGES)
$root = $doc->createElement('IMAGES');
$root = $doc->appendChild($root);

// run a while loop to read through the directory contents
while ($file = readdir($dir_handle)) {

    // rawurlencode will replace any non-alphanumeric characters
    // except hyphen(-) and underscore (_)
    // with a percent (%) sign followed by two hex digits
    // eg. a space ( ) will be replaced with %20
    $file = rawurlencode($file);

    // split the filename string so we can get the file extension
    $split = explode(".", $file);
    $ext = strtolower($split[count($split)-1]);

    // if the extension matches a value in our $filetypes array we'll include it in the xml
    if (in_array($ext, $filetypes)) {

        // add a 'FILE' node
        $item = $doc->createElement("FILE");
        $item = $root->appendChild($item);

        // comment out the line below if you don't want to include the relative path
        $file = $path . $file;

        // create a 'path' attribute on this FILE node and give it the $file value
        $item->setAttribute('path', $file);

    }

}

// close the directory
closedir($dir_handle);

// dumps the internal XML tree back into a string
echo $doc->saveXML();

The output would be something like:

<?xml version="1.0"?>
<IMAGES>
    <FILE path="images/image1.jpg" />
    <FILE path="images/image2.jpg" />
    <FILE path="images/image3.jpg" />
    <FILE path="images/image4.jpg" />
    <FILE path="images/image5.jpg" />
</IMAGES>

You can then load this dynamic XML into Flash to create a gallery based on whatever images are in that directory.
The AS3 document class below simply loads the XML from the php script and then loads and display a single random image.
Assuming I’ve named the php script DirectoryXML.php and my directory structure looks something like:

  • path/to/your/gallery/
    • Main.swf
    • DirectoryXML.php
    • images/
      • image1.jpg
      • image2.jpg
      • image3.jpg
      • image4.jpg
      • image5.jpg
package {

    import flash.display.Bitmap;
    import flash.display.Loader;
    import flash.display.Sprite;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.IOErrorEvent;
    import flash.net.URLLoader;
    import flash.net.URLRequest;

    [SWF(width="800", height="600", frameRate="30", backgroundColor="#000000")]

    public class Main extends Sprite {

        private static const XML_SCRIPT:String = "DirectoryXML.php";

        private var xmlLoader : URLLoader;
        private var resultsXML : XML
        private var imageLoader : Loader;

        public var image : Sprite;

        public function Main() {
            this.xmlLoader = new URLLoader();
            this.xmlLoader.addEventListener(Event.COMPLETE, this.onXMLLoaded);
            this.xmlLoader.addEventListener(IOErrorEvent.IO_ERROR, this.onXMLLoadError);
            try {
                this.xmlLoader.load(new URLRequest(XML_SCRIPT));
            }
            catch (error:Error) {
                trace("Error: " + error.message);
            }
        }

        private function onXMLLoadError(event:IOErrorEvent) : void {
            trace("XML load IOError: " + event.text);
            this.xmlLoader.removeEventListener(Event.COMPLETE, this.onXMLLoaded);
            this.xmlLoader.removeEventListener(IOErrorEvent.IO_ERROR, this.onXMLLoadError);
        } 

        private function onXMLLoaded(event:Event) : void {
            this.xmlLoader.removeEventListener(Event.COMPLETE, this.onXMLLoaded);
            this.xmlLoader.removeEventListener(IOErrorEvent.IO_ERROR, this.onXMLLoadError);
            this.resultsXML = XML(this.xmlLoader.data.toString());
            this.loadRandomImage();
        }

        private function loadRandomImage() : void {
            var max:int = this.resultsXML.FILE.length() - 1;
            var rand:int = Math.ceil(Math.random() * (max - 0+1)) + 0 -1;
            var imageFile:String = this.resultsXML.FILE[rand].@path;
            this.imageLoader = new Loader();
            this.imageLoader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, this.onImageLoadError);
            this.imageLoader.contentLoaderInfo.addEventListener(Event.COMPLETE, this.onImageLoaded);
            try {
                this.imageLoader.load(new URLRequest(imageFile));
             }
            catch (error:Error) {
                trace("Error: " + error.message);
            }
       }

        private function onImageLoadError(event:IOErrorEvent) : void {
            trace("Image load IOError: " + event.text);
            this.imageLoader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, this.onImageLoadError);
            this.imageLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, this.onImageLoaded);
        }

        private function onImageLoaded(event:Event) : void {
            this.imageLoader.contentLoaderInfo.removeEventListener(IOErrorEvent.IO_ERROR, this.onImageLoadError);
            this.imageLoader.contentLoaderInfo.removeEventListener(Event.COMPLETE, this.onImageLoaded);
            this.image = new Sprite();
            var bmp:Bitmap = this.imageLoader.content as Bitmap;
            bmp.smoothing = true;
            this.image.addChild(bmp);
            this.addChild(this.image);
            this.scaleAndCenterImage(this.image);
        } 

        private function scaleAndCenterImage(img:Sprite) : void {
            var landscape:Boolean = (img.width >= img.height);
            var wider:Boolean = (img.width > this.stage.stageWidth);
            var taller:Boolean = (img.height > this.stage.stageHeight);

            // resize if needed
            if (landscape && wider) {
                // constrain size to width
                img.width = this.stage.stageWidth;
                img.height = (this.stage.stageWidth / img.width) * img.height;
            }
            else if (!landscape && taller) {
                // constrain size to height
                img.width = (this.stage.stageHeight / img.height) * img.width;
                img.height = this.stage.stageHeight;
            }

            // center vertically and horizontally
            img.y = Math.round(this.stage.stageHeight/2 - img.height/2);
            img.x = Math.round(this.stage.stageWidth/2 - img.width/2);

        }

    }

}

Click here to see this basic sample in action

Click here to download a zip file containing the php script and as class

This is just one way to use the DomDocument class. For example, you could modify the php script to include other file types (swf, flv etc.) and do a range of things with them. I might post a more advanced script/example next but thought I’d get quick one up for now.

14
Oct 2009
POSTED BY
POSTED IN

Articles

DISCUSSION 3 Comments
Articles

Execute dynamic javascript from Flash with ExternalInterface

So I was playing around with the ExternalInterface class earlier today and wondered if I could call dynamic javascript with it, as opposed to the standard approach of calling an existing javascript function. And it appears that you can. I tried the following…

// Simple launch of the alert dialog
ExternalInterface.call("function() { alert('Testing'); }");

// Changed the height of a div
ExternalInterface.call("function() { document.getElementById('divID').style.height = '300px'; }");

// And with the jquery library included in the html head
// Call the toggle function
ExternalInterface.call("function() { $('#divID').toggle('fast'); }");

And what do you know! All of those work just fine.

Ideally you’d have a js file set up with functions for Flash to call. But I can certainly see how there might be some cases where you’d want/need to use the dynamic calls.

Feel free to download my test files for a closer look.

12
Aug 2009
POSTED BY
POSTED IN

Articles

DISCUSSION No Comments
Articles

75 Ways to Encourage Children

As parents there’s really no excuse for not encouraging and building up your children. Where else will they get it from? But if for some reason you’re stuck for what to say or you just need a simple reminder, this list is perfect. Now I don’t take credit for this list at all, it’s based on Thirty Ways to Say ‘Well Done!’ (Joan Waters, Lady Gowrie Resource Centre, Melbourne) and appears on a poster created by the Victorian Department of Education and Early Childhood Development. We were given a copy before our son was born and have it pinned up on our ensuite door as a constant reminder of the simple ways we can encourage our kids. I had intended on writing this post some time back but never got around to it. Now that our daughter has just been born it’s as good a time as any. Hope you find it helpful.

In the Family

  • Please
  • Thank you
  • How thoughtful
  • You’re the bees knees!
  • I love you
  • What a lovely smile
  • Thank you for sharing
  • You are a great help
  • Let’s make dinner
  • I’m very proud of you!
  • That was a kind thing to do
  • You are being very gentle
  • I like being with you
  • I like doing things together
  • Let’s take turns
  • You are playing so well with your brother/sister
  • You did a great job with your chores
  • How was your day?

With Friends

  • You are fun to be with
  • You’re being a wonderful friend
  • That’s excellent sharing!
  • Being together is great
  • Your friend is being very caring

Reading and Language

  • This is my favourite book too
  • Let’s read together
  • What an imagination!
  • What do you think about that?
  • Brilliant idea!
  • That was very polite
  • Well spoken!
  • You choose a book
  • Shall we read together?
  • I would love to read you a story
  • Let’s sing a song
  • What’s your favourite book?
  • What a funny joke
  • Can you tell me a story?

Well Done!

  • You’re on the right track
  • You can do it!
  • Good thinking!
  • You did that well
  • Way to go!
  • You seemed to really enjoy doing that
  • You must have been practising
  • Good trying
  • I reckon you could have a go at that
  • That’s the best you have ever done
  • That’s a real improvement
  • You remembered everything!
  • I’m proud of the way you worked!
  • Fantastic!
  • Good work!
  • Amazing – How did you do that?
  • That took effort
  • Now that’s what I call a fine job
  • You planned that well
  • Good on you mate
  • You’ve tried hard
  • You’re a star!
  • That’s wonderful!
  • You’re learning fast
  • What a good idea!
  • Well done – You should be proud of yourself!

At Play

  • Let’s play
  • You play well together
  • You are trying hard
  • You’ve worked it out!
  • That’s coming along well
  • Now you’ve got it!
  • You’re good at this game
  • What would you like to play?
  • That’s very creative
  • What a lovely painting!
  • Can we put that up on the wall for everyone to see?
  • Tell me about what you made
27
Jun 2009
POSTED BY
POSTED IN

Articles

DISCUSSION No Comments
Articles

10 Games in 10 Hours

Mark Cooke set himself a challenge to make 10 games in 10 hours. He actually completed the challenge in 9 hours and 27 minutes (Say what!). Mark presented the results at the 62nd Pecha Kucha Night in Tokyo which included iPhone, Flash and Unity games. The list of games with their platform and genre as posted on his blog are:

  1. “Can You Say Pecha Kucha?” – iPhone – Rhythm game
  2. “Shinjuku Shame” – Web (Unity) – Homeless staring FPS
  3. “Game Developer Life” – Web (Flash) – Game industry wage slave simulation
  4. “Scream Test” – iPhone – Screaming competition
  5. “Voice Invaders” – iPhone – Voice controlled vertical shooter
  6. “Surfing on Sine Waves” – Web (Flash) – Procedurally generated surfing game
  7. “Architris” – Web (Unity) – Blueprint Tetris
  8. “Para Para Punch” – iPhone – Hooligan punching game
  9. “Blind Masseuse” – Mac – Sound based massaging simulation
  10. “Basket Bomb” – Web (Unity) – Basketball terrorist physics game

Below is a screencast he recorded after the presentation

You can also watch the actual live presentation below, however as it was recorded on a mobile phone the quality isn’t great. Still a good watch nonetheless

07
Jun 2009
POSTED BY
POSTED IN

Articles

DISCUSSION No Comments
Articles

10 Text Generators For All Your Dummy Text Needs

Lorem Ipsum with Scrabble

For a long time I was using the Lorem Ipsum website to generate dummy text for my print and web design layouts. Nothing wrong with it at all but one day I became curious as to what other text generators were out there. So I’ve now put togther a list of 10 text generators in no particular order. Some of them are websites while others are downloadable tools (scripts, add-ons, etc). Why 10? I guess you might not like or want the same one(s) I do, and there’s plenty of options out there. So without further ado, here you go.

Dummy Lipsum Firefox Add-on

A nice and simple generator that sits in your Firefox (or Flock) browser Tools menu. It’s what I use most of the time now, and has a bunch of options letting you choose the number of paragraphs, words, bytes or lists.
https://addons.mozilla.org/en-US/firefox/addon/2064

HTML Lipsum

Although the Firefox add-on above includes a HTML tags option, if I need to generate dummy HTML then this ones my tool of choice. A really great site for various types of HTML dummy text. The text is already generated so you just need to click on the title of the type you want and it gets copied to your clipboard. Includes the following types of text: long, medium and single sentence paragraphs; definition list; ordered and unordered list with long and short items; unordered list navigation (with links); table; and kitchen sink which contains a bunch of different elements for a html page. The site also provides downloadable runtimes for Windows, Mac and Linux.
http://html-ipsum.com/

AppleScript Lorem Ipsum Generator

For Mac users. Run the script and enter the number of “Lorem ipsum…” paragraphs you require. They will be pasted at the current cursor position in the frontmost text editor document.

http://dougscripts.com/texedit/

Lorem Ipsum

AS3 Lorem Ipsum Generator Class

The LoremIpsumGenerator class uses the REST interface from Lipsum.com to retrieve “lorem ipsum” dummy text. This one is bundled in with some other AS3 utility classes that might come in handy for you, including a PapervisionCameraController class that provides keyboard control for a Papervision3D FreeCamera3D object. http://almiruncommonlib.riaforge.org/

Lorem2

As the name suggests, this site is an all-around better lorem experience. The text is already generated, so you can’t specify x paragraphs or x words but it does contain a mix of options such as long and short paragraphs as well as long and short list items.
http://lorem2.com/

Malevole Text Generator

If you’re sick of using Lorem Ipsum for you dummy text then go here for some humorous filler text such as:
Knight Rider, a shadowy flight into the dangerous world of a man who does not exist. Michael Knight, a young loner on a crusade to champion the cause of the innocent, the helpless in a world of criminals who operate above the law…
http://www.malevole.com/mv/misc/text/

Lorem Ipsum conversation

AdhesionText

This is a dynamic text generator that creates text using only the characters you input. You have options such as making all the text uppercase, sentence case or case insensitive (referring to the characters you input). It also has language options including English, French, German, Spanish, Portugese and Dutch.
http://www.adhesiontext.com/

Blind Text Generator

Another simple dummy text generator inlcuding more than just lorem ipsum. There’s other English paragraphs, Quick brown fox, and an option to display all characters and symbols (). Plus you can also the total number of words and/or paragraphs to generate.
http://www.blindtextgenerator.com/

Lorem Ipsum

Designers Toolbox Lorem Ipsum Generator

Includes a bunch of options for paragraphs, lists and starting text.
http://www.designerstoolbox.com/designresources/greek/

Lorem Ipsum

Correct me if I’m wrong but I think it was one of the first Lorem Ipsum text generators. Choose the number of paragraphs, words, bytes or lists and whether to start with ‘Lorem ipsum’.
http://www.lipsum.com/

23
May 2009
POSTED BY
POSTED IN

Articles

DISCUSSION No Comments