Encouraging Stats for Unity3D as a Mobile Games Dev Platform but There’s Still a Reality Check for Non-Indie Developers

Interesting statistics from Game Developer magazine (May 2012), via Gamasutra:

…Game Developer magazine has released the results of its first-ever mobile and social developer technology survey in the May 2012 issue….

Unity topped the list of mobile game engines, with 53.1% of developers reporting using Unity compared to 39.8% using a custom engine., 17.7% using Cocos2D, 5.3% using Marmalade, and 5.3% using Corona (this question wasn’t exclusive, so the percentage count adds up to more than 100%).

I’m guessing most of the 53% are indie developers for now.

At the moment (Fri, Aug 24, 2012), I did a search for “Unity3D” on Indeed.com in all of the US, and got less than 100 job postings, many of them only mentioned Unity3D as a “nice to have”. Compared that with 8,828 jobs that mention HTML5, 12,567 for Flash (yes, still!), 2,087 for Actionscript (even after “Flash found dead in seedy San Jose motel room“) on the same date in all of the US.

I hope more Tech Directors & teams hear about Unity soon & pick it as their dev tool for mobile games & beyond. I like working in this game engine, so it’d be nice to have more job opportunities for it in more places.

Matrix Math Practice in AS3.0 with TweenMax & transformMatrix Plugin

package
{
	import flash.display.MovieClip;
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.geom.Matrix;
	import com.greensock.TweenMax;
	import com.greensock.plugins.*;
	
	[SWF (width="500", height="500", backgroundColor="#CCCCCC", frameRate="30")]		
	public class MatrixMathTesting extends Sprite
	{
		private var mc:MovieClip = new MovieClip();
		private var wishMatrix:Matrix = new Matrix;
		
		public function MatrixMathTesting()
		{
			TweenPlugin.activate([TransformMatrixPlugin]);
			
			mc.x = 250;
			mc.y = 250;
			addChild( mc );
			
			var sq:Shape = new Shape();
			sq.graphics.beginFill( 0x000000, .5 );
			sq.graphics.drawRect( 0, 0, 100, 100 );
			sq.graphics.endFill();			
			
			mc.addChild( sq );			
			
			var ar:Array = [ [2,   0], 		//[x-scale (a), y-skew (b)]
					  		 [0.5, 1],		//[ x-skew (c), y-scale (d) ]
					   		 [mc.x, mc.y] ]; //[x-position (tx), y-position (ty)] 
			
			transformMC( wishMatrix, ar );
			
			TweenMax.delayedCall( 2, removeTransform, [wishMatrix] );
			 
		}
	
		public function transformMC( m:Matrix, arr:Array ):void 
		{
			TweenMax.to(mc, 1, {transformMatrix:{ a:arr[0][0],  b:arr[0][1], 
										          c:arr[1][0],  d:arr[1][1], 
												  tx:arr[2][0], ty:arr[2][1] }});						
		}
		
		public function transformMCSimple( m:Matrix, arr:Array ):void 
		{			
			m.a  = arr[0][0];  //	 x-scale
			m.b  = arr[0][1];  //  	 y-skew
			m.c  = arr[1][0];  //	 x-skew 
			m.d  = arr[1][1];  //		 y-scale
			m.tx = arr[2][0]; //	 x-position 
			m.ty = arr[2][1]; //	 y-position 
			
			mc.transform.matrix = wishMatrix;
		}
		
		public function removeTransform( m:Matrix ):void
		{		
			//tween back to an identity matrix		
			TweenMax.to(mc, 1, {transformMatrix:{a:1, b:0, c:0, d:1, tx:mc.x, ty:mc.y }}); 
		}
		
		public function removeTransformSimple( m:Matrix ):void
		{		
			//no anmation, back to identity matrix (removes all previous transformations)
			m.a  = 1; m.b  = 0; 
			m.c  = 0; m.d  = 1;  
			m.tx = m.tx; m.ty = m.ty;			
			
			mc.transform.matrix = m;			
		}		
		
	}
}

Quick port of “Mac Dock Style Menu with AS3.0” into a single class

Here’s a Document Class version of an ActiveTuts+ tutorial called Create a Mac Dock Style Menu with AS3.

Here’s what I changed:

  • removed each button’s dependence on 3-level deep inheritance (OverButton > DockButton > DockItem)
  • removed each Library symbol’s linkage to OverButton.as
  • simplified the menu a bit for the purposes of this example, so that only the X axis moves. It’s easy enough to add in the Y axis & scale variables, if/when needed. You should be able to extend DockMenu.as to add the extra features, like Y axis movement & Scaling or add those vars directly.
package 
{	
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.geom.Rectangle;
	import com.greensock.TweenMax;
	import flash.display.DisplayObjectContainer;
	
	public class DockMenu extends MovieClip
	{					
		protected var maxXDistance:Number = 50;
		protected var menuXes:Array = []; 
		protected var menuBtns:Array = []; 
		
		public function DockMenu():void 
		{
			addEventListener( Event.ADDED_TO_STAGE, init );			
		}					
	
		public function init(e:Event=null):void
		{			
			removeEventListener( Event.ADDED_TO_STAGE, init );						
			
			//uncheck "Export for Actionscript" for each of 
            //the button mc's in the Library			
			menuBtns = [star, config, apple, photo, shop, cont ]; 
			
			for( var f:uint=0; f < 6; f++ )
			{
				menuXes[f] = menuBtns[f].x; 
				menuBtns[f].buttonMode = true;
				menuBtns[f].mouseChildren = false; 
				var bounds:Rectangle = getBounds( menuBtns[f] );
				var hit:Sprite = new Sprite();
				hit.graphics.beginFill( 0,0 );
				hit.graphics.drawRect( bounds.x, bounds.y, bounds.width, bounds.height );		
				menuBtns[f].addChild( hit );
			}
			
			stage.addEventListener( MouseEvent.MOUSE_MOVE, mouseMove );
			stage.addEventListener( Event.MOUSE_LEAVE, mouseLeave );			
		}
		
		public function mouseMove(e:MouseEvent):void
		{								
			for( var g:uint=0; g < 6; g++ )
			{
				var xDistance:Number = mouseX - menuXes[g]; 
				xDistance = xDistance > maxXDistance ? maxXDistance : xDistance; 			
				xDistance = xDistance < -maxXDistance ? -maxXDistance : xDistance;
				var posX:Number = menuXes[g] - xDistance * 0.2; 
				TweenMax.to( menuBtns[g], .2, { x:posX } );		
			}		
		}
				
		public function mouseLeave(e:Event):void
		{			
			for( var h:uint=0; h < 6; h++ )
				TweenMax.to( menuBtns[h], .3, { x:menuXes[h] } );
		}
	}	
}

Flash Builder & Apparat example: BUILD FAILED “java.io.IOException: Cannot run program “…/mxmlc” (in directory “…/apparat-ant-example”): error=2, No such file or directory

Made sure my Flash Builder 4.5 on OSX 10.5 is set to use Java 1.6 (out of the box it came set to use JVM 1.5).

Followed the instructions on webdevotion.be and cultcreative.com.

When it came time to do “Run As > Ant Build” I kept getting this error:

Buildfile: /your_path_to_example_folder/apparat-ant-example/build/build.xml
clean:
compile:

BUILD FAILED
/your_path_to_example_folder/apparat-ant-example/build/build.xml:64:
Execute failed: java.io.IOException: Cannot run program "/Applications/Adobe%20Flash%20Builder%204.5/sdks/4.5.0/bin/mxmlc"
(in directory "/your_path_to_example_folder/apparat-ant-example"): error=2, No such file or directory

Total time: 860 milliseconds

After trying a bunch of things, it turned out it was a permissions problem.

To fix it, I swapped Flex SDK to one outside of /Applications/ folder. Inside the project’s build.properties I updated FLEX_HOME line to:

FLEX_HOME=/your_path_to_sdk/Flex_SDKs/4.1.0

instead of

FLEX_HOME=/Applications/Flash%20Builder%204.5/sdks/4.5.0

and it worked.

Robotlegs, Part 2: Adding ImageLoader & TweenMax

Expanding on the previous Robotlegs entry. The original source code is generously provided by hubflanger. I’m modifying her code to show images using two popular asset loading & animation libraries. To do this we have to modify the XML data file and image preloading, processing & display code in these 3 classes: LoaderMaxDataService, SectionVO & SectionContainer.

Updated LoaderMaxDataService:

package com.hubflanger.robotlegsdemo.service
{
	import com.hubflanger.robotlegsdemo.model.vo.SectionVO;
	import com.hubflanger.robotlegsdemo.model.SiteModel;
	import com.hubflanger.robotlegsdemo.events.SystemEvent;
	
	import com.greensock.events.LoaderEvent;
	import com.greensock.loading.ImageLoader;
	import com.greensock.loading.LoaderMax;
	import com.greensock.loading.XMLLoader;
	import com.greensock.loading.display.ContentDisplay;
	
	import flash.events.Event;
	import flash.net.*;
	
	import org.robotlegs.mvcs.Actor;
		
	/**
	 * A Service class that handles loading and parsing of the site's xml data.
	 */	
	public class LoaderMaxDataService extends Actor implements ISiteDataService
	{
		/**
		 * Inject the <code>SiteModel</code> Singleton.
		 */		
		[Inject]
		public var model				:SiteModel; 		
		private var queue				:LoaderMax; 		
		private var xml					:XMLLoader;

		public function LoaderMaxDataService()
		{ 			
			LoaderMax.activate([XMLLoader, ImageLoader]);				
		}
				
		public function loadData():void 
		{			
			var xml:XMLLoader = new XMLLoader("assets/data.xml", 
											  {name:"mainXML", 
											  onComplete:xmlComplete, 
											  estimatedBytes:4000});
			xml.load();						
		}
		
		/**
		 * Handler for the XMLLoader's onComplete.	
		 * <p>Preloads the images</p>
		 */			
		private function xmlComplete(e:LoaderEvent):void
		{
			xml = e.target as XMLLoader; 
			
			queue = new LoaderMax({name:"mainQueue", 
								   onProgress:qProgressHandler,
								   onComplete:qCompleteHandler,
								   onError:qErrorHandler});

			var sections:XMLList = xml.content.sections.section; 			
			for each (var section:XML in sections) 
			{
				queue.append(new ImageLoader(section.img.@src, 
												 {name:"image_" + section.@id,											
												  alpha:0,
												  visible:false,											  											
												  container:this}));					
			}						
			
			queue.load();	
		}

		private function qProgressHandler(e:LoaderEvent):void 
        { 
            trace("progress: " + e.target.progress); 
        }
		private function qErrorHandler(e:LoaderEvent):void
        { 
            trace("qErrorHandler(): error occured with " + e.target + ": " + e.text); 
        } 
		private function qCompleteHandler(e:LoaderEvent):void 
		{
			trace(e.target + "queue complete");							
			imagesCompleteHandler();						
		}			
		 		 
		/**
		 * Process content data after images are preloaded.	
		 * <p> Tell the rest of the application when data is ready.</p>
		 */				 
		private function imagesCompleteHandler():void 
		{			
			model.header = xml.content.header.text(); 
			var sections:XMLList = xml.content.sections.section; 
			
			for each (var section:XML in sections) 
			{
				var sectionVO:SectionVO = new SectionVO(section.@id,
														section.label.toString(),
														LoaderMax.getContent("image_" + section.@id), 
														section.content.toString());				
				
				model.sectionsList.push(sectionVO);
				model.sectionsHash[sectionVO.id] = sectionVO;
			}			
			
			model.defaultSection = model.sectionsList[0].id;
			
			dispatch(new SystemEvent(SystemEvent.INIT_VIEW, false));					
		}				
	}
}

A few minor changes to SectionVO, to accomodate the new image property:

package com.hubflanger.robotlegsdemo.model.vo
{
	import com.greensock.loading.display.ContentDisplay;
	
	public class SectionVO
	{
		private var _id:String;
		private var _label:String;
		private var _image:ContentDisplay;
		private var _content:String;
		
		/**
		 * A custom  object for storing Section node data
		 */
		public function SectionVO( id:String, label:String, img:ContentDisplay, content:String )
		{
			_id = id;
			_label = label;
			_image = img;
			_content = content;
		}

		public function get id():String
		{
			return _id;
		}
		
		public function get label():String
		{
			return _label;
		}

		public function get image():ContentDisplay
		{
			return _image;
		}
		
		public function get content():String
		{
			return _content;
		}
	}
}

And finally, the addition of a Sprite image container & image display code with TweenMax:

package com.hubflanger.robotlegsdemo.view.components
{
	import com.greensock.TweenMax;

	import com.hubflanger.robotlegsdemo.model.vo.SectionVO;
	
	import flash.display.*;
	import flash.text.*;
	import flash.utils.Dictionary;
	
	/**
	 * The display container for the section content.
	 */	
	public class SectionContainer extends Sprite
	{
		private var sectionsHash:Dictionary = new Dictionary();
		private var textField:TextField;
		private var img:Sprite;
		
		/**
		 * The constructor. 
		 * <p>
		 * Creates a Shape object as background. Creates a TextField 
		 * for the content copy and a Sprite to hold images.
		 */	
		public function SectionContainer()
		{
			var bg:Shape = new Shape();
			bg.graphics.beginFill(0xF9E5C2);
			bg.graphics.drawRect(0, 0, 550, 400);
			bg.graphics.endFill();
			addChild(bg); 
				
			var tf:TextFormat = new TextFormat();
			tf.font = "Helvetica";
			tf.color = 0x4B1E18;
			tf.size = 12;
			
			textField = new TextField();
			textField.x = 222;
			textField.y = 81;
			textField.width = 310;
			textField.height = 290;
			textField.multiline = true;
			textField.wordWrap = true;
			textField.defaultTextFormat = tf;
			addChild(textField);
			
			img = new Sprite();
			img.x = 12;
			img.y = 95;
			addChild(img);			
		}
		
		/**
		 * Assigns a Dictionary object to the <code>sectionsHash</code> property.
		 *  
		 * @param hash A Dictionary instance containing <code>SectionVO</code>
		 * objects with Section IDs as key.
		 */		
		public function init(hash:Dictionary):void
		{
			sectionsHash = hash;
		}
		
		/**
		 * Retrieves the <code>SectionVO</code> object associated with the 
		 * Section ID and and populates the <code>textField</code> with the 
		 * value of its <code>content</code> property. Populates <code>img</code>
		 * with <code>SectionVO</code>'s value of <code>image</code>. 
		 *
		 * @param str The String ID of the Section selected.
		 */
		public function update(id:String):void
		{
			var sectionVO:SectionVO = sectionsHash[id];
			textField.htmlText = sectionVO.content;			

			cleanImgHolder(img);			
			img.addChild(sectionVO.image);
			TweenMax.to(sectionVO.image, 0.66, {autoAlpha:1});											
		}
		
		private function cleanImgHolder(i:Sprite):void 
		{
			if(i.numChildren > 0) { 
				TweenMax.to(i.getChildAt(0), 0, {autoAlpha:0});		
				i.removeChildAt(0);
			}
		}
	}
}

Don’t forget to update the XML file to contain an “img” element with an “src” attribute:

 <!-- ... -->
		<section id="home">
			<label>Home</label>
			<img src="images/1.jpg" />
			<content>
				<p>Lorem ipsum dolor sit amet...</p>
			</content>
		</section>
<!-- ... -->

AS3.0 Signals example juxtaposed with native version

AS3.0 Signals is an event handling framework from Robert Penner that’s gaining popularity lately. There’s a good video tutorial by John Lindquist and tutorials here.

Here’s a quick comparison of an old XML processing class written both ways for comparison. For this simple example, Signals was about 10 to 50 milliseconds faster than using regular AS3 events. The results varied a lot each time I compiled.

Using the Signals framework for events:

XML processing class:

package com.timshaya.net
{
	import org.osflash.signals.Signal;
	import org.osflash.signals.natives.NativeSignal;
	
	import flash.events.Event;
	import flash.net.URLLoader;
	import flash.net.URLRequest;	

	public class XmlGrabber 
	{		
		private var loadDone				:NativeSignal;									
		public var dataDone					:Signal;										
		public var cityItems				:Array = [];								

		private var xmlData					:XML;		
		private var loader					:URLLoader;
		private var reqst					:URLRequest;
		private var trgt					:URLLoader;
		
		public function XmlGrabber(pth:String):void 
		{
			loader = new URLLoader(); 
			reqst = new URLRequest(pth);
						
			dataDone = new Signal();	
			loadDone = new NativeSignal(loader, Event.COMPLETE, Event); 
			loadDone.addOnce(loadCompleted);					
			
			loader.load(reqst);	
		}	
		
		public function loadCompleted(e:Event):void 
		{
			var trgt:URLLoader = URLLoader(e.target);
			xmlData = new XML(trgt.data); 
			//trace(xmlData);
			
			if (trgt!=null) 
			{
				for each (var a:XML in xmlData.evnt) 
				{
					cityItems.push(a); 
					//trace("\n cityItems[a]: " + a.@city + "\n"); 
				} 													
			} else trace("error: check your data & datatype");
			
			dataReady();
		}
		
		public function dataReady():void 
		{	
			dataDone.dispatch(); 
		}
	}
}

Test class:

package 
{	
	import org.osflash.signals.Signal;	
	import org.osflash.signals.natives.NativeSignal;
	import com.timshaya.net.XmlGrabber; 	
	
	import flash.display.Sprite;	
	import flash.events.Event;	
	import flash.utils.getTimer;

	[SWF (width="600", height="400", backgroundColor="#FFFFFF", frameRate="30")]	
	public class Test extends Sprite 
	{	
		private var addToStage				:NativeSignal;
		private var xml						:XmlGrabber;		
		private var xmlPth                  :String = "xml/"; 
		private var xmlName  				:String = "events.xml"; 				
		private var timer					:Number;
		
		public function Test():void 		
		{										
			addToStage = new NativeSignal(this, Event.ADDED_TO_STAGE, Event); 
			addToStage.addOnce(initApp);			
		}

		public function initApp(e:Event):void 
		{
			timer = getTimer();
			
			xml = new XmlGrabber(xmlPth + xmlName);
			xml.dataDone.add(createPage); 									
		}

		public function createPage():void 
		{							
			trace("Test::createPage()");		
			trace("\t time elapsed: " + (getTimer() - timer));			
						
			trace("\t xml.cityItems[0].@city = " + xml.cityItems[0].@city); 
			trace("\t xml.cityItems[0].@state = " + xml.cityItems[0].@state);			
		} 				
	}
}

Same code using native AS3 events:

XML processing class:

package com.timshaya.net
{
	import flash.events.Event;
	import flash.net.URLLoader;
	import flash.net.URLRequest;	
	import flash.events.EventDispatcher;

	public class XmlGrabberOld extends EventDispatcher 
	{		
		public var cityItems				:Array = [];		

		private var xmlData					:XML;		
		private var loader					:URLLoader;
		private var reqst					:URLRequest;
		private var trgt					:URLLoader;

		public static var DATA_READY		:String	= "list_ready";
		
		public function XmlGrabberOld(pth:String):void 
		{
			loader = new URLLoader(); 
			reqst = new URLRequest(pth);
			loader.load(reqst);
			loader.addEventListener(Event.COMPLETE, listOnComplete, false, 0, true);	
		}
		
		private function listOnComplete(e:Event):void
		{			
			var trgt:URLLoader = URLLoader(e.target);
			xmlData = new XML(trgt.data);
			//trace(xmlData);
			
			if (trgt!=null) 
			{
				for each (var a:XML in xmlData.evnt) 
				{
					cityItems.push(a); 
					//trace("\n cityItems[a]: " + a.@city + "\n"); 
				} 					
			} else trace("error: check your data & datatype");			

			dataReady();
		}
		
		private function dataReady():void
		{
			dispatchEvent(new Event(XmlGrabberOld.DATA_READY));
		}
		
	}
}

Test class:

package 
{		
	import com.timshaya.net.XmlGrabberOld;

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

	[SWF (width="600", height="400", backgroundColor="#FFFFFF", frameRate="30")]	
	public class TestOld extends Sprite 
	{	
		private var xml						:XmlGrabberOld;		
		private var xmlPth                  :String = "xml/"; 
		private var xmlName  				:String = "events.xml"; 		
		private var timer					:Number;
		
		public function TestOld():void 		
		{								
			addEventListener(Event.ADDED_TO_STAGE, initApp);		
		}

		public function initApp(e:Event):void 
		{
			timer = getTimer();
		
			xml = new XmlGrabberOld(xmlPth + xmlName);
			xml.addEventListener(XmlGrabberOld.DATA_READY, createPage);											
		}

		public function createPage(e:Event):void 
		{						
			trace("TestOld::createPage()");		
			trace("\t time elapsed: " + (getTimer() - timer));		
			
			trace("\t xml.cityItems[0].@city = " + xml.cityItems[0].@city); 
			trace("\t xml.cityItems[0].@state = " + xml.cityItems[0].@state);						
		} 						
		
	}
}

Sample xml file, events.xml:

<?xml version="1.0" encoding="ISO-8859-1"?>
<evnts>		
	
	<evnt lnk="test.aspx?c=0" city="Atlantic City" state="New Jersey" />
	<evnt lnk="test.aspx?c=1" city="Clifton" state="New Jersey" />	
	<evnt lnk="test.aspx?c=2" city="Allentown" state="Pennsylvania" />
	<evnt lnk="test.aspx?c=3" city="Akron" state="Ohio" />
	<evnt lnk="test.aspx?c=4" city="Wichita Falls" state="Texas" />
	<evnt lnk="test.aspx?c=5" city="Austin" state="Texas" />
	<evnt lnk="test.aspx?c=6" city="Orlando" state="Florida" />
	<evnt lnk="test.aspx?c=7" city="Atlanta" state="Georgia" />
	<evnt lnk="test.aspx?c=8" city="San Francisco" state="California" />
	<evnt lnk="test.aspx?c=9" city="Los Angeles" state="California" />
	<evnt lnk="test.aspx?c=10" city="San Diego" state="California" />	
	
</evnts>

Instantiating SlideShoPro in an AS3.0 Class

A simple user class:

package
{
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;

	import com.slideshow.MySlideShowPro;

	public class Main extends Sprite
	{
		private var mssp:DisplayObjectContainer;

		public function Main()
		{
			mssp = new MySlideShowPro(500, 350); //(width, height)
			mssp.x = 20;
			mssp.y = 20;
			addChild(mssp);
		}

	}
}

A sample basic SlideShowPro class:

package com.slideshow
{
	import flash.display.Sprite;
	import net.slideshowpro.slideshowpro.*;

	public class MySlideShowPro extends Sprite
	{
		//http://wiki.slideshowpro.net/SSPplayer/API-API
		private var myssp:SlideShowPro;

		public function MySlideShowPro(w:int, h:int)
		{
			//make sure SlideShowPro is set for Export in your Library, or SWC/SWF asset files
			myssp = new SlideShowPro(this);

			myssp.setSize(w, h);

			myssp.xmlFilePath = "xml/slides.xml";
			myssp.xmlFileType = "Default";

			/*
			the commented out lines, like:
			//myssp.navButtonGlowAlpha = .25;
			give error "1119: access of possibly undefined property..."
			*/

			myssp.navAppearance = "Always Visible";
			myssp.navBackgroundAlpha = 1;
			myssp.navBackgroundColor = 0x121212;
			myssp.navButtonsAppearance = "All Visible";
			myssp.navGradientAlpha = .3;
			myssp.navGradientAppearance = "Glass Dark";
			myssp.navButtonColor = 0xEEEEEE;
			//myssp.navButtonGlowAlpha = .25; 				//error 1119
			//myssp.navButtonGradientAlpha = .6;			//error 1119
			myssp.navButtonInactiveAlpha = .4;
			//myssp.navButtonRolloverColor = 0xFFFFFF;		//error 1119...
			myssp.navButtonShadowAlpha = .6;
			//myssp.navButtonShadowStyle = "Under";
			//myssp.navLinkAnimate = "On";
			myssp.navLinkAppearance = "Numbers";
			//myssp.navLinkActiveColor = 0xEEEEEE;
			//myssp.navLinkInactiveColor = 0x999999;
			myssp.navLinkPreviewAppearance = "Visible";
			myssp.navLinkPreviewBackgroundAlpha = 1;
			myssp.navLinkPreviewBackgroundColor = 0xFFFFFF;
			myssp.navLinkPreviewScale = "Proportional";
			myssp.navLinkPreviewSize = [100,100];
			myssp.navLinkPreviewStrokeWeight = 1;
			myssp.navLinkRolloverColor = 0xFFFFFF;
			//myssp.navLinkShadowAlpha = .6;
			myssp.navLinksBackgroundAlpha = 1;
			myssp.navLinksBackgroundColor = 0x000000;
			//myssp.navLinksBackgroundShadowAlpha = .6;
			myssp.navLinkSpacing = 8;
			myssp.navNumberLinkSize = 9;
			myssp.navPosition = "Bottom";
			myssp.navThumbLinkInactiveAlpha = 1;
			myssp.navThumbLinkSize = [16,16];
			myssp.navThumbLinkStrokeWeight = 1;

			myssp.albumBackgroundAlpha = 1;
			myssp.albumBackgroundColor = 0x303030;
			myssp.albumDescColor = 0xCCCCCC;
			myssp.albumDescSize = 9;
			myssp.albumPadding = 8;
			myssp.albumPreviewScale = "Proportional";
			myssp.albumPreviewSize = [54,41];
			myssp.albumPreviewStrokeColor = 0xFFFFFF;
			myssp.albumPreviewStrokeWeight = 1;
			myssp.albumPreviewStyle = "Inline Left";
			myssp.albumRolloverColor = 0x262626;
			myssp.albumStrokeAppearance = "Visible";
			myssp.albumStrokeColor = 0x141414;
			myssp.albumTitleColor = 0xFFFFFF;
			myssp.albumTitleSize = 10;
			myssp.audioAutoStart = "On";
			myssp.audioLoop = "Off";
			myssp.audioPause = "Off";
			myssp.audioVolume = .8;
			myssp.autoFinishMode = "Switch";
			myssp.cacheContent = "None";
			myssp.captionAppearance = "Overlay on Rollover (if Available)";
			myssp.captionBackgroundAlpha = .6;
			myssp.captionBackgroundColor = 0xFFFFFF;
			myssp.captionElements = "Header and Caption";
			myssp.captionHeaderBackgroundAlpha = 0;
			//myssp.captionHeaderPadding = [6,6,2,6];
			myssp.captionHeaderText = "{imageTitle}";
			//myssp.captionHeaderTextColor = 0xEEEEEE;
			myssp.captionPadding = [2,6,6,6];
			myssp.captionPosition = "Top";
			myssp.captionTextAlignment = "Left";
			myssp.captionTextColor = 0xAAAAAA;
			//myssp.captionTextShadowAlpha = 0;
			myssp.captionTextSize = 9;
			myssp.contentAlign = "Center";
			myssp.contentAreaAction = "Launch Hyperlink";
			myssp.contentAreaBackgroundAlpha = 1;
			myssp.contentAreaBackgroundColor = 0x161616;
			myssp.contentAreaInteractivity = "Action Area Only";
			myssp.contentAreaStrokeAppearance = "Visible";
			myssp.contentAreaStrokeColor = 0x262626;
			myssp.contentFrameAlpha = 1;
			myssp.contentFrameColor = 0x262626;
			myssp.contentFramePadding = 0;
			myssp.contentFrameStrokeAppearance = "Hidden";
			myssp.contentFrameStrokeColor = 0x333333;
			myssp.contentOrder = "Sequential";
			myssp.contentScale = "Downscale Only";
			myssp.contentScalePercent = 1;
			myssp.contentWatermark = "";
			myssp.contentWatermarkAlign = "Bottom Right";
			myssp.directorLargePublishing = "On";
			myssp.directorLargeQuality = 80;
			myssp.directorLargeSharpening = 1;
			myssp.directorThumbQuality = 60;
			myssp.directorThumbSharpening = 1;

			myssp.displayMode = "Auto";
			myssp.feedbackBackgroundAlpha = .3;
			myssp.feedbackBackgroundColor = 0x000000;
			myssp.feedbackHighlightAlpha = .8;
			myssp.feedbackHighlightColor = 0xFFFFFF;
			myssp.feedbackPreloaderAlign = "Center";
			myssp.feedbackPreloaderAppearance = "Beam";
			myssp.feedbackPreloaderPosition = "Inside Content Area";
			myssp.feedbackPreloaderScale = 1;
			myssp.feedbackPreloaderTextSize = 12;
			myssp.feedbackTimerAlign = "Top Right";
			myssp.feedbackTimerAppearance = "Visible";
			myssp.feedbackTimerPosition = "Inside Content Area";
			myssp.feedbackTimerScale = 1;
			myssp.feedbackVideoButtonScale = 1;
			myssp.fullScreenReformat = "On";
			myssp.fullScreenTakeOver = "On";
			myssp.galleryAppearance = "Visible";
			myssp.galleryBackgroundAlpha = 1;
			myssp.galleryBackgroundColor = 0x1C1C1C;
			myssp.galleryColumns = 2;
			//myssp.galleryContentShadowAlpha = 0;
			myssp.galleryOrder = "Left to Right";
			myssp.galleryPadding = 10;
			myssp.galleryRows = 4;
			myssp.galleryNavActiveColor = 0x303030;
			myssp.galleryNavAppearance = "Visible";
			myssp.galleryNavInactiveColor = 0x000000;
			myssp.galleryNavRolloverColor = 0x262626;
			myssp.galleryNavStrokeAppearance = "Visible";
			myssp.galleryNavStrokeColor = 0x141414;
			myssp.galleryNavTextColor = 0xCCCCCC;
			myssp.galleryNavTextSize = 9;
			myssp.keyboardControl = "On";
			//myssp.loop = "Off";
			myssp.mediaPlayerAppearance = "Visible on Rollover";
			myssp.mediaPlayerBackgroundAlpha = .25;
			myssp.mediaPlayerBackgroundColor = 0x000000;
			myssp.mediaPlayerBufferColor = 0x000000;
			myssp.mediaPlayerButtonColor = 0xFFFFFF;
			myssp.mediaPlayerElapsedBackgroundColor = 0xFFFFFF;
			myssp.mediaPlayerElapsedTextColor = 0x000000;
			myssp.mediaPlayerPosition = "Bottom";
			myssp.mediaPlayerProgressColor = 0xCCCCCC;
			myssp.mediaPlayerScale = .8;
			myssp.mediaPlayerTextColor = 0x999999;
			myssp.mediaPlayerTextSize = 9;
			myssp.mediaPlayerVolumeBackgroundColor = 0x000000;
			myssp.mediaPlayerVolumeHighlightColor = 0xCCCCCC;
			myssp.panZoom = "Off";
			myssp.panZoomDirection = "Random";
			myssp.panZoomFinish = "Off";
			myssp.panZoomScale = [1,1.2];
			myssp.permalinks = "Off";
			myssp.smoothing = "On";
			//myssp.soundEffects = "";
			//myssp.soundEffectsVolume = .4;
			myssp.textStrings = ["Previous Screen","Next Screen","Screen","of","No caption","No title","Playing","Paused","Click play to start audio"];
			myssp.toolAppearanceContentArea = "Hidden";
			myssp.toolAppearanceNav = "Visible";
			myssp.toolColor = 0x222222;
			myssp.toolDelayContentArea = 0;
			myssp.toolDelayNav = .5;
			myssp.toolLabels = ["Gallery","Previous Group","Previous","Next","Next Group","Pause","Play","Full Screen","Normal Screen","Open Link"];
			myssp.toolTimeoutContentArea = 0;
			myssp.transitionLength = 2;
			myssp.transitionPause = 4;
			myssp.transitionDirection = "Left to Right";
			myssp.transitionStyle = "Cross Fade";
			myssp.typeface = "Lucida Grande,Lucida Sans Unicode,Verdana,Arial,_sans";
			//myssp.typefaceAntiAlias = "Advanced";
			myssp.typefaceEmbed = "Off";
			myssp.videoAutoStart = "On";
			myssp.videoBufferTime = 0.1;
		}
	}
}

AS2 to AS3.0 ports are fun

A quick AS3.0 port of Lee’s AS2 scrolling thumbnail panel.

//stroke = instance name of mc on stage
var b:Rectangle = new Rectangle(stroke.x, stroke.y, stroke.width, stroke.height);

//panel = instance name of mc on stage
panel.addEventListener(MouseEvent.MOUSE_OVER, panelOver, false, 0, true); 

function panelOver(e:MouseEvent):void 
{	
	panel.addEventListener(Event.ENTER_FRAME, scrollPanel, false, 0, true); 
	panel.removeEventListener(MouseEvent.MOUSE_OVER, panelOver);
}

function scrollPanel(e:Event):void 
{
	//if mouse is outisde bounding box, b, then stop all scroll action		
	if(mouseX<b.left || mouseX>b.right || mouseY<b.top || mouseY>b.bottom) 	
	{
		panel.addEventListener(MouseEvent.MOUSE_OVER, panelOver, false, 0, true);		
		panel.removeEventListener(Event.ENTER_FRAME, scrollPanel);		
	}
	
	if(panel.x >= 89) panel.x = 89; //left bound	
	if(panel.x <= -751) panel.x = -751; //right bound 			
	
	var xdist:Number = mouseX - 250; //250 == stage.stageWidth/2 (if panel is centered on stage)  
	
	panel.x += Math.round(-xdist / 7);
}

Implementing tracking pixels in AS3.0: Mediaplex

Embedding tracking pixels in a Flash app sounds like it should be simple. Just make a Request to the server from your .swf that calls up the 1×1 .gif, right? Somehow or other it ends up being a confusing process nonetheless.

In my humble experience, the oversight is usually on the side of the vendor whose technology a Developer is asked to implement. Simple requests for instructions on how to implement a 3rd party technology into an Actionscript 3.0 application are met with confused email responses by folks who don’t seem to understand that there’s more than one version of the Actionscript programming language and the occasional emailed PDF that describes things like “How to Add a clickTag” using screen shots from Flash 5 and Asctionscript 1.0. Since more often than not vendors can’t or won’t send clear instructions on how to implement their tags in the current version of Actionscript / Flash / Flex, what ends up happening is the Developer is asked to test and debug the vendor’s product for them, for free.

Going forward, it’d be nice if a company that sells technology which they say can be implemented in Flash could actually have someone in that company:

  1. create a modern Flash application
  2. implement the technology
  3. have the source files ready for clients to use for reference.

OK, enough venting.

Here’s one way to implement Mediaplex tracking pixels in AS3.0 (Flash / Flex)

Make sure you receive the Standard pixel path, i.e. one that starts with “<img” (not one that starts with “<iframe”). Here’s a basic test class that calls a Mediaplex tracking pixel:

package
{
	import flash.net.URLRequest;
	import flash.net.URLVariables;
	import flash.net.URLLoader;
	import flash.net.URLRequestMethod;
	import flash.display.Sprite;

	[SWF (width="500", height="750", backgroundColor="#000000", frameRate="31")]
	public class Main extends Sprite
	{
		private var yourMediaPlexURL:Array = ["http://sd.mediaplex.com/somepath/"];

		public function Main():void
		{
			Security.loadPolicyFile("http://www.your.com/crossdomain.xml");
			this.addEventListener(Event.ADDED_TO_STAGE, onAddedToStage);
		}

		public function  onAddedToStage(e:Event):void
		{
			//call the tracking code
			trackMediaPlex(yourMediaPlexURL[0]);
		}

		public function trackMediaPlex(yourURL:String):void
		{
			var myRequest:URLRequest = new URLRequest(yourURL);
			myRequest.method = URLRequestMethod.POST;

			var myLoader:URLLoader = new URLLoader();
			try{
				myLoader.load(myRequest);
				trace("\t trackMediaPlex(): myRequest.url = " + myRequest.url);
			} catch(e:Error) {
				trace(e.getStackTrace());
			}
		}
	}
}

Before activating the pixel, it’s a good idea to quickly test it in Firefox using Firebug. Under the Net tab[1] in in Firebug you should see three server hits:

  1. your original request
  2. a redirect
  3. a request for the 1×1 pixel file itself.

Once you see these firing in Firebug, activate the pixel and you should be good to go. For more detailed testing use Charles (Mac) and Fiddler (PC).

A basic crossdomain.xml policy file should allow all the potential Mediaplex urls that might be used, such as this one: http://somedomain.mediaplex.com. Be careful about how you specify urls in crossdomain.xml, the minuscule difference between “http://somedomain.mediaplex.com&#8221; and “http://somedomain.mediaplex.com/&#8221; can break your code.

Notes:
1. Make sure you Enable the Net tab first

Note to self: Structs in C++ are like Value Objects in Actionscript or Java

Wiser folks than I have accurately pointed out that no analogy is perfect. In this case, it’s plain to see that Structs in C++ are an official data type that’s part of the language itself, whereas Value Objects are more of a convention (former design pattern[1]) created by Java programmers (among others) over the years.

Structs are similar to Value Objects since both are essentially containers for a bunch of data. Both don’t have methods. Perhaps, Value Objects could have Getters & Setters, but more often there aren’t methods.

An example of a Struct in a C++ tutorial:

struct database {
  int id_number;
  int age;
  float salary;
};

int main()
{
  database employee;  
  employee.age = 22;
  employee.id_number = 1;
  employee.salary = 12000.21;
}

An example of a Value Object from an AS3 tutorial using PureMVC:

package com.flashtuts.model.vo
{
	public class DataVO
	{
		public var dataURL:String          = 'assets/xml/data.xml';
		public var urlsArray:Array	  = [ ];
		public var urlsDataArray:Array  = [ ];
	}
}

1. In Java the Value Object Pattern is now called the Transfer Object Pattern.