Flash for the masses – AS, JSFL, XUL, etc.

2009/01/24

Unsolvable? Fitting a raster image to a vector shape or raster alpha.

I have been on sabbatical from this blog because I am trying to track down a problem.

I have an image that I want to take a portion of (an interior, randomly shaped clip mask) and fit that clipping to another shape, and I need to automate it.

Sounds easy, right?

Ok, a more full description of the problem is:

I am trying to take a portion of an image from Photoshop and bring it to fit a UV map in LW. I am able to get an alpha cutout of the image easily, making the UV map in Lightwave is simple as you can get, the UV map comes into Photoshop or Illustrator (or any other program) simply enough, and it is simple to produce an alpha mask of it. The crux is that there is no relation between the shape of the image alpha map and the UV map, and neither one is consistently shaped from object to object or image to image.

The LW objects to be mapped are all one 1/2 of a 2-sided shell (that is, topologically similar to a half sphere). None of the objects have the same number of points or polys.

Those are the variables. . . the goal is to have the non-alpha portion of the image exactly fit the UV map of the objects. I am open to any options that involve morphs as well.

So the first thoughts were obvious: Use Photoshop! Didn’t work, so use Illustrator! Didn’t work, so use Inkscape, Gimp, Imagemagick, theoretical papers on shape matching for visually aware robots (I am not that far along yet. . .). I am considering the huge leap to program this in C++, but I want to avoid this for obvious reasons.

Adobe Illustrator has the “Envelope Distort”. These are the docs on that function’s options:

http://help.adobe.com/en_US/Illustrator/14.0/WS714a382cdf7d304e7e07d0100196cbc5f-6463a.html

But it doesn’t fit a clipping mask correctly . . . or maybe it does, but that feature is what I want it to do, as I am discussing in this forum:

http://www.layersmagazine.com/forum/showthread.php?t=6305

What the Envelope Distort does is fit the bounding box of the image to the shape. I get the same results no matter which envelope distort options I choose. It does not seem to respond to the transparency of a PNG image, nor to a clipping mask. In both of those situations, it defaults to the bounding box of the image again (this seems like a bug, but I have been told it may not be).

Photoshop has distortion tools, but they are manual editing only. That is, in order to fit a specific shape, you have to create a mesh that deforms the input image to the output shape; and you can save that mesh and apply it to other images. The problem is that none of the images I have are identical, nor are any of the shapes I am trying to fit identical. This kind of automation is not possible with Photoshop. Illustrator’s other distortion tools are the same – manual editing is ok, but programming it cannot work; or, because it is Illustrator, work fine and can be automated for vector objects, but not for raster. I have left a post on Adobe’s forum about this:

http://www.adobeforums.com/webx/.59b7a3db

I am attempting to find a solution through Lightwave 3D as well. I have not got a good response yet, but by looking at some of the features, there may be a solution. This is my forum post on Newtek’s forum site:

http://www.newtek.com/forums/showthread.php?t=94316

Inkscape’s (http://www.inkscape.org/) documentation is pretty lousy, even though it is good program overall. Actually, it is still pretty buggy in some places, but it is nice to see a good freeware program for illustration.

Gimp (http://gimp.org) seems somewhat promising, but no luck.
Imagemagick (http://www.imagemagick.org/script/index.php) is a little inscrutable for searching for specific functions, but I’ll keep digging at it.

I am thinking that Blender (http://www.blender.org/) may have some options – and I will be looking more at them – but the results need to be in Lightwave, ultimately. Still, it’s worth a shot.

So if you know how to do this – and it doesn’t need to be Adobe or Lightwave products, but must be programmable – let me know.

2009/01/18

Chaining a sequence of Tweens on button click

In Theory

The following code dynamically creates a button array, and sets up a tweening sequence between these buttons – one after the other, the buttons will be revealed.

This is my response to this Actionscript.org post:

http://www.actionscript.org/forums/showthread.php3?t=194501

The goal of this code is to automate button addition and tweening. It is not exactly useful as is, though, since it sort of assumes that you want to put them in a grid, and have the events fire in order. It can be modified quite easily – read the code for hints as to where to change a certain setting.

In Practice

To use this code, create an empty Flash file, and create one button in the library. In the Properties of that library button:

  • set the “Name” to “buttonTweenTest
  • check the “Export to Actionscript”
  • set the “Class” to the same as the “Name”: “buttonTweenTest
  • Next, set the root object’s class to “tweenTest” (see this post for help)

    Finally, put the following code in a file named “tweenTest.as” in the same directory as your FLA file.

    package
    {
    	import fl.motion.easing.Bounce;
    	import fl.transitions.Tween;
    	import fl.transitions.TweenEvent;
    	import flash.display.SimpleButton;
    	import flash.display.Sprite;
    	import flash.events.Event;
    	import flash.events.MouseEvent;
    	import flash.geom.Point;
    
    	/**
    	 * ...
    	 * @author Delfeld, copyright 2008
    	 */
    	public class tweenTest extends Sprite
    	{
    		private var newButton:SimpleButton;
    		private var buttonArr:Array;
    		private var twn:Tween;
    		private var totalButtonsInRow:Number;
    		private var totalButtonsInCol:Number;
    		private var curItemNum:int;
    		private var gap:Point;
    		private var totalButtons:int;
    		private var alphaStartVal:Number;
    		private var alphaEndVal:Number;
    		private var tweenInterval:Number;
    
    		public function tweenTest()
    		{
    			addEventListener(Event.ADDED_TO_STAGE, initHnd, false, 0, true);
    		}		
    
    		private function setDefaults():void
    		{
    			alphaStartVal = .1;
    			alphaEndVal = 1;
    			tweenInterval = .1;
    
    			gap = new Point(12, 9);
    
    			totalButtons = 20;
    			totalButtonsInRow = 0;
    			totalButtonsInCol = 0;
    
    			if (buttonArr !== null)
    			{
    				var valj:int = buttonArr.length;
    				for (var j:int = 0; j < valj; j++)
    				{
    					removeChild(buttonArr[j]);
    					buttonArr[j] = null;
    				}
    			}
    			buttonArr = new Array();
    		}
    
    		private function initHnd(e:Event):void
    		{
    			setDefaults();
    
    			for (var i:uint = 0; i < totalButtons; i++)
    			{
    				// create a new button, name
    				buttonArr[i] = new buttonTweenTest();
    				buttonArr[i].name = "button" + i;
    
    				// set button grid
    				if (i == 0 )
    				{
    					setGrid(buttonArr[i].width, buttonArr[i].height);
    				}
    
    				// listen for being added
    				buttonArr[i].addEventListener(Event.ADDED_TO_STAGE, initButtonHnd, false, 0, true);
    
    				// add button to the stage
    				addChildAt(buttonArr[i], i + 1	);
    			}
    		}
    
    		// set the dimensions for the button grid
    		private function setGrid(wid:int, ht:int):void
    		{
    			if (totalButtonsInRow == 0)
    			{
    				totalButtonsInRow = Math.abs(Math.floor((this.width / (wid + gap.x)) - 1));
    				totalButtonsInCol = Math.abs(Math.floor((this.height / (ht  + gap.y)) - 1));
    
    				//trace("tweenTest.setGrid: " + Math.abs(int((this.width + gap.x) / wid - 1)));
    				//trace("tweenTest.setGrid: " + int((this.width + gap.x) / wid - 1));
    				//trace("tweenTest.setGrid: " + ((this.width + gap.x) / wid - 1));
    				//trace("tweenTest.setGrid: " + this.width);
    				//trace("tweenTest.setGrid: " + gap.x);
    				//trace("tweenTest.setGrid: " + wid );
    
    				trace("tweenTest.initHnd: totalButtonsInRow: " + totalButtonsInRow + ", totalButtonsInCol: " + totalButtonsInCol);
    			}
    		}
    
    		private function initButtonHnd(e:Event):void
    		{
    			// remove the listener
    			e.target.removeEventListener(e.type, initButtonHnd);
    
    			// find this event's button
    			var btIdx:int = buttonArr.indexOf(e.target);
    			if (btIdx !== -1)
    			{
    				//trace("tweenTest.initButtonHnd: " + btIdx);
    
    				// for test: set alpha of everything not the first button to alphaStartVal
    				if (buttonArr[btIdx].name !== "button0")
    				{
    					buttonArr[btIdx].alpha = alphaStartVal;
    				}
    
    				// move this button to grid position
    				buttonArr[btIdx].x  = (buttonArr[btIdx].width + gap.x ) * Math.floor((btIdx % totalButtonsInRow) + 1);
    				buttonArr[btIdx].y  = (buttonArr[btIdx].height + gap.y ) * Math.floor((btIdx / totalButtonsInRow) + 1);
    				trace("tweenTest.initButtonHnd: " + buttonArr[btIdx].x);
    
    				// listen for click event
    				buttonArr[btIdx].addEventListener(MouseEvent.CLICK, buttonClickHnd, false, 0, true);
    			}
    			else
    			{
    				trace("tweenTest.initButtonHnd: button not found.");
    			}
    		}
    
    		private function buttonClickHnd(e:MouseEvent):void
    		{
    			// create a tween on click
    			createTween(SimpleButton(e.target));
    		}
    
    		// recursive, when used with with tweenFinishHnd
    		private function createTween(button:SimpleButton):void
    		{
    			// erase old tween, create a new one
    			// Note: for this example, there should only be one tween
    
    			// if tween does not exist, or tween's affected object is the current one
    			if (twn === null || twn.obj !== button)
    			{
    				// if tween exists. . .
    				if ( twn )
    				{
    					// change the focus object of the tween
    					twn.obj = button;
    
    					// listen for finish of this tween
    					twn.addEventListener(TweenEvent.MOTION_FINISH, tweenFinishHnd, false, 0, true);
    
    					// start the tween from 0 again
    					twn.start();
    				}
    				// if tween does not exist:
    				else
    				{
    					// create the tween
    					twn = new Tween(button, "alpha", Bounce.easeIn, alphaStartVal, alphaEndVal, tweenInterval, true );
    
    					// listen for finish of this tween
    					twn.addEventListener(TweenEvent.MOTION_FINISH, tweenFinishHnd, false, 0, true);
    				}
    			}
    		}
    
    		private function tweenFinishHnd(e:TweenEvent):void
    		{
    			// remove tween listener
    			e.target.removeEventListener(e.type, tweenFinishHnd);
    			//trace("tweenTest.tweenFinishHnd: ");
    
    			// find this event's button
    			var btIdx:int = buttonArr.indexOf(Tween(e.target).obj);
    			if (btIdx !== -1
    				&& (btIdx + 1 ) < buttonArr.length)
    			{
    				createTween(buttonArr[btIdx + 1]);
    			}
    			else
    			{
    				trace("tweenTest.tweenFinishHnd: Tweening complete.");
    				twn = null;
    			}
    		}
    	}
    }

    If you want to set up a trace() to test any areas, make sure to check :

    File | Publish Settings. . .

    “Flash” tab, “Advanced” section, “Permit Debugging”

    2009/01/17

    Speed of int, uint, arrays in for loops

    Filed under: AS3.0 — delfeld @ 6:04 am
    Tags: , , , , , ,

    So this was an interesting result of this post on Actionscript.org. I ran the suggested test for the speed of iterating over a for-loop, and it wasn’t accurate enough, so I developed the tests below.

    Basically, this shows the advantages of different variable types when used in various places in a for-loop. It also shows the relative advantages when used to seek/assign a value to an array. Since computers differ, it is best to just put this in an empty Flash file and run, and see what your results are:

    stop();
    
    var t1:Number;
    var t2:Number;
    var vali:int = 100000000;
    var valu:int = 100000000;
    var arr:Array = new Array();
    var rep:int;
    
    trace("int array init test:" );
        for(rep = 0; rep < 5; rep++){
            t1 = getTimer();
            for(var id:int = 0; id<10000000; id++){ arr[id] = 1;}
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("uint array init test:" );
        for(rep = 0; rep < 4; rep++){
            t1 = getTimer();
            for(var ic:uint = 0; ic<10000000; ic++){ arr[ic] = 1;}
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("int array assign test:" );
        for(rep = 0; rep < 4; rep++){
            t1 = getTimer();
            for(var ia:int = 0; ia<arr.length; ia++){ arr[ia] = 1;}
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("uint array assign test:" );
        for(rep = 0; rep < 4; rep++){
            t1 = getTimer();
            for(var ib:uint = 0; ib<arr.length; ib++){ arr[ib] = 1;}
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("int-int array seek test:" );
        for(rep = 0; rep < 4; rep++){
            vali = arr.length;
            t1 = getTimer();
            for(var i7:int = 0; i7<vali; i7++){ arr[i7] = 1; }
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("int-uint array seek test:" );
        for(rep = 0; rep < 4; rep++){
            valu = arr.length;
            t1 = getTimer();
            for(var i8:int = 0; i8<valu; i8++){ arr[i8] = 1; }
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("uint-int array seek test:" );
        for(rep = 0; rep < 4; rep++){
            vali = arr.length;
            t1 = getTimer();
            for(var i9:uint = 0; i9<vali; i9++){ arr[i9] = 1; }
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("uint-uint array seek test:" );
        for(rep = 0; rep < 4; rep++){
            valu = arr.length;
            t1 = getTimer();
            for(var i0:uint = 0; i0<valu; i0++){ arr[i0] = 1; }
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("int-const test:" );
        for(rep = 0; rep < 4; rep++){
            t1 = getTimer();
            for(var i1:int = 0; i1<100000000; i1++){}
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("uint-const test:" );
        for(rep = 0; rep < 4; rep++){
            t1 = getTimer();
            for(var i2:uint = 0; i2<100000000; i2++){}
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("int-int test:" );
        for(rep = 0; rep < 4; rep++){
            t1 = getTimer();
            for(var i3:int = 0; i3<vali; i3++){}
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("int-uint test:" );
        for(rep = 0; rep < 4; rep++){
            t1 = getTimer();
            for(var i4:int = 0; i4<valu; i4++){}
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("uint-int test:" );
        for(rep = 0; rep < 4; rep++){
            t1 = getTimer();
            for(var i5:uint = 0; i5<vali; i5++){}
            t2 = getTimer();
            trace(t2 - t1);
        }
    
    trace("uint-uint test:" );
        for(rep = 0; rep < 4; rep++){
            t1 = getTimer();
            for(var i6:uint = 0; i6<valu; i6++){}
            t2 = getTimer();
            trace(t2 - t1);
        }
    

    2009/01/15

    Making your own Easing for a Tween

    Filed under: AS3.0, Adobe, Flash — delfeld @ 11:38 pm
    Tags: , , , , , ,

    In Theory

    Take a look at the method TestEasing.easeTest. Here is a brief explanation of what is going on:

    TestEasing.easeTest is a simple method for placing a value within a range. In this example, the function is a sine, and its domain is (0,2*pi), or one sine cycle.

  • Since b is the start value, the results have to be added to b. What are the results?
  • c is the total change range (obtained from end-[b]egining=[c]hange, so starts at 0), so you multiply this by some percentage (values (0,1)) to obtain the current change value. How do you get the percentage?
  • The function’s range is the percentage. How does the function yield this range?
  • t/d gives the current time (or frame) that the tween is at. So multiply this by the domain of the function to get the current percentage results. In this case, the function is Math.sin(), which gives the result in the range (-1,1) (and that’s ok by me, but you will have to convert this into a range (0,1) to have a true percentage); and the function’s domain is (0,2*pi), so this is the final piece to the puzzle.
  • The result?
    return b + (c * Math.sin(t / d * (2 * Math.PI)));
  • In Practice

    Here is the basic class to test a Tween:

    package
    {
    	import fl.transitions.Tween;
    	import fl.transitions.TweenEvent;
    	import flash.display.MovieClip;
    	import flash.display.Sprite;
    	import flash.events.Event;
    
    	// Delfeld, copyright 2008.
    
    	public class TweenTestClass extends Sprite
    	{
    		private var moveBall:Sprite;
    		private var moveBallRad:uint;
    		private var twX:Tween;
    		private var twY:Tween;
    		public function TweenTestClass()
    		{
    			addEventListener(Event.ADDED_TO_STAGE, initHnd, false, 0, true);
    		}
    
    		private function initHnd(e:Event):void
    		{
    			e.target.removeEventListener(e.type, initHnd);
    
    			doTraceLine();
    			//showMatrixGradient();
    		}
    
    		private function doTraceLine():void
    		{
    			moveBall = new Sprite();
    			moveBallRad = 20;
    			moveBall.addEventListener(Event.ADDED_TO_STAGE, initBallHnd, false, 0, true);
    			moveBall.graphics.beginFill(0xddddff);
    			moveBall.graphics.lineStyle(2, 0x0000ff);
    			moveBall.graphics.drawCircle(0, 0, moveBallRad);
    			addChild(moveBall);
    		}
    
    		private function initBallHnd(e:Event):void
    		{
    			e.target.removeEventListener(e.type, initBallHnd);
    
    			//MovieClip(traceLine1).graphics.moveTo();
    			twX = new Tween(moveBall, "x", null, moveBallRad * 2, this.width - moveBallRad * 2, 3, true);
    			twY = new Tween(moveBall, "y", TestEasing.easeTest, this.height * .5, moveBallRad * 2, 3, true);
    
    			twX.addEventListener(TweenEvent.MOTION_CHANGE, motionChangeHnd, false, 0, true);
    			twX.addEventListener(TweenEvent.MOTION_STOP, endMotionHnd, false, 0, true);
    			twX.addEventListener(TweenEvent.MOTION_FINISH, endMotionHnd, false, 0, true);
    		}
    
    		private function motionChangeHnd(e:TweenEvent):void
    		{
    			twX.removeEventListener(TweenEvent.MOTION_CHANGE, motionChangeHnd);
    			trace("TweenTestClass.frameHnd: x: " + moveBall.x + ", y: " + moveBall.y);
    		}
    
    		private function endMotionHnd(e:TweenEvent):void
    		{
    			twX.stop();
    			twY.stop();
    			twX.removeEventListener(TweenEvent.MOTION_CHANGE, motionChangeHnd);
    			twX.removeEventListener(TweenEvent.MOTION_STOP, endMotionHnd);
    			twX.removeEventListener(TweenEvent.MOTION_FINISH, endMotionHnd);
    
    			trace("TweenTestClass.endMotionHnd: done.");
    		}
    	}
    }

    And here is the brand new Easing class:

    package
    {
    	/**
    	*  The TestEasing class defines one easing function to test
    	 *  motion with ActionScript animation.  (This is hacked-up code
    	 *  from Adobe's Bounce easing.)
    	 *
    	 * @playerversion Flash 9.0.28.0
    	 * @langversion 3.0
    	 * @keyword Ease, Copy Motion as ActionScript
    	 * @see ../../../motionXSD.html Motion XML Elements
    	 * @see fl.motion.FunctionEase
    	 *
    	 * Delfeld, copyright 2008.
    	*/
    	public class TestEasing
    	{
    		/**
    		 *  @param t Specifies the current time, between 0 and duration inclusive.
    		 *
    		 *  @param b Specifies the initial value of the animation property.
    		 *
    		 *  @param c Specifies the total change in the animation property.
    		 *
    		 *  @param d Specifies the duration of the motion.
    		 *
    		 *  @return The value of the interpolated property at the specified time.
    		 * @playerversion Flash 9.0.28.0
    		 * @langversion 3.0
    		 * @keyword Ease, Copy Motion as ActionScript
    		 * @see fl.motion.FunctionEase
    		*/
    		public static function easeTest (t:Number, b:Number, c:Number, d:Number) : Number
    		{
    			return b + (c * Math.sin(t / d * (2 * Math.PI)));
    		}
    	}
    }

    2009/01/14

    Handling object deletion correctly: Event.REMOVED_FROM_STAGE and Event.ADDED_TO_STAGE

    In Theory

    When a class object is removed from the stage, as in obj.removeChild(childObj), Flash keeps it active in memory, in case you add the same object again. Reactivating a removed object happens frequently and is good practice for controlling memory usage. Also, since class-based objects are always passed to methods by reference, you may be using the same object without knowing it at times (you shouldn’t not know it, but it can happen), and you may be using a deactivated object.

    The problem is that event listeners are also still active, and running! And that is a problem because this is memory/processor time you don’t need to waste – Flash is already bulky enough. For example, if you are creating a child object every time a frame event occurs, those child objects are still being created.

    The solution is simple: stop the listeners on deactivation. For all DisplayObject-based classes written, you should also write standard methods for adding/removing listeners: activate(e:Event) and deactivate(e:Event). In deactivate, listen for activation; similarly, in activation, listen for deactivation.

    Though I came on these ideas semi-independently, there are some really good explanations and examples out there. This is good explanation:

    http://wiki.mediaboxtraining.com/doku.php/flash:display_list_deactivation

    Though I couldn’t immediately find it, I think Senocular has talked about it, and here is his stage activation detection kit:

    http://www.senocular.com/flash/actionscript.php?file=ActionScript_3.0/com/senocular/events/StageDetection.as

    In Practice

    Here is a sample class setup with activation/deactivation detection. Please note that this is generic – that is, sometimes the activation listener should not be removed, or other listeners may require removal, but not activation (they are activated by some other method, by user interaaction, etc.).

    import flash.display.MovieClip;
    class TestClass extends MovieClip
    {
    	public function TestClass()
    	{
    		this.addEventListener(Event.ADDED_TO_STAGE, activate, false, 0, true);
    	}
    
    	protected function activate(e:Event):void
    	{
    		if (e !== null) { e.target.removeEventListener(e.type, activate); }
    
    		// ADD all event listeners here. . .
    
    		this.addEventListener(Event.REMOVED_FROM_STAGE, deactivate, false, 0, true);
    	}
    
    	protected function deactivate(e:Event):void
    	{
    		if (e !== null) { e.target.removeEventListener(e.type, deactivate); }
    
    		// REMOVE all event listeners here. . .
    
    		this.addEventListener(Event.ADDED_TO_STAGE, activate, false, 0, true);
    	}
    }
    

    2009/01/13

    Use FlashDevelop for Flex and AS3.0 editing

    Filed under: AS3.0, FlashDevelop — delfeld @ 3:05 pm
    Tags: , , , , ,

    FlashDevelop is a program that allows easy editing of AS3.0 and Flex content, similar to Eclipse. It also attaches to compilers easily (Flash, Flex, etc.) to allow creation of content without needing to be in the Adobe environment. FlashDevelop is now in release candidate 1 state, so is pretty stable:

    FlashDevelop

    There has been a lot of work put into this program, and it is my only AS3.0 editor. Using this as a code editor, it makes creation of Flash content free when you use the Flex 3 SDK:

    Flex 3 SDK

    This is not for people who just like to play around in the Flash IDE (that is, if you don’t know what an IDE is, you probably will have a steep learning curve for developing Flash content by this method). This is more for Java, Javascript, C++, .NET, etc. programmers. Fair warning!

    2009/01/10

    Loading XML from file, synchronously and asynchronously.

    In theory

    Actionscript 3.0’s XML model is based on this Ecma-357 (version 2) standard (here is the <a href="http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-357.pdf">PDF download). It's long and boring, which sucks because it is very educational in terms of what you can do in AS3. Adobe follows the standard pretty closely, so this can actually substitute for much of the Adobe documentation.

    Sometimes some examples help, and Adobe is very useful for finding usage samples. The XML demo code (and Adobe’s XML documentation in general) is found at AS3’s docs XML reference: http://livedocs.adobe.com/flash/9.0/ActionScriptLangRefV3/XML.html#includeExamplesSummary

    Also, don’t miss Kirupa’s prodigious efforts: Using XML in Flash CS3/AS3

    Of note

    • Non-intuitive in the Adobe example is that the first element in an XML assignment to a variable is that variable. So with this code:

    var refVar:XML = <test><setup><title>title</title></setup></test>;

    refVar takes the place of and the xml data is accessed by refVar.setup, and not refVar.test.setup.

    • Though you can loop using for-each syntax, I wanted an index number. (Eventually) I figured out that this works:

    //var val:uint = xmlData.questions.question.length;
    var val:uint = xmlData.questions.child("*").length();
    for (var i:uint = 0; i < val; i++)
    {
    trace("TriggerArray.quizXMLToDS: " + i + ": " + xmlData.questions.question[i].@id);
    }

    In Practice

    This example loads and prints an XML file. This is the basically same method that Kirupa uses. I like to dispatch an event after the XML is fully loaded, too, since I use this asynchronously, and need to wait until whatever post-load, pre-continuation data is done. So to use this example, you would listen for the XML_LOADED event:

    import flash.display.MovieClip;
    import flash.events.Event;
    import flash.net.URLLoader;
    import flash.net.URLRequest;
    
    public class Test extends MovieClip
    {
    	public static const XML_LOADED:String = "XML_LOADED";
    
    	private var xmlUrlReq:URLRequest;
    	private var xmlLoader:URLLoader;
    	private var xmlData:XML;
    
    	public var isOKToLoadSO:Boolean;  // This should really use a getter-setter. . .
    
    	private function loadXML():void
    	{
    		isXMLGood= false;
    		xmlLoader = new URLLoader();
    		xmlUrlReq = new URLRequest("file.xml");
    
    		// Listen for file load.
    		xmlLoader.addEventListener(Event.COMPLETE, xmlFileLoadedHnd, false, 0, true);
    
    		// After something has been done to the XML data, XML_LOADED event will fire, so listen for that event, too.
    		this.addEventListener(XML_LOADED, xmlIsCompleteHnd, false, 0, true);
    
    		xmlLoader.load(xmlUrlReq);
    	}
    
    	protected function xmlFileLoadedHnd(e:Event):void
    	{
    		// Reference the XML data from the target.
    		xmlData = new XML(e.target.data);
    
    		// Show the XML data.
    		trace(xmlData);
    
    		// Do something to the XML data
    		// and set a synchronous (linear) condition variable.
    		isXMLGood = doSomethingToXMLData(xmlData);
    
    		// Notify that everything is set up correctly
    		// by firing an asynchronous event.
    		dispatchEvent(new Event(XML_LOADED));
    	}
    
    	private function doSomethingToXMLData(xmlData:XML):Boolean
    	{
    		// Do something to the XML data here.
    		return true;
    	}
    
    	protected function xmlIsCompleteHnd(e:Event):void
    	{
    		// Continuation code goes here.
    	}
    }
    

    2009/01/09

    Flash root class inheritance, or subclassing the root.

    In order to make the root movieclip a subclass of some ActionScript 3.0 class you wrote, there are a few steps to follow.

  • The AS3 class must extend MC or Sprite class, because the root object of a Flash file is based on these:
    public class SomeClass extends MovieClip
  • The package name (the first line of the class file) must match the directory you are in — relative to the FLA file (not the SWF output file). So if your FLA file is kept in the directory:
    c:Flash

    and your AS3 class is kept in the file:

    c:FlashAS3SomeClassPackageSomeClass.as

    you must list the package in your class (on the first line of your file) as:
    package AS3.SomeClassPackage

  • A reference to the package and class must be in your root object’s Class box:
    Where to add superclass info on a Flash root object

    Where to add superclass info on a Flash root object

    You will be prompted if you do not enter it correctly. Just cut-and-paste the package listed in your class file, and then cut-and-paste the class name:
    AS3.SomeClassPackage.SomeClass

    Save it and test it before you do any serious coding. The trick I use is putting a trace in the class constructor, and I run a debug (“Debug | Debug Movie”) on the Flash file. If it is working, I will see the results in the “Output” window (“Window | Output”), and not if it is not:

    public function SomeClass()
    {
    trace("SomeClass - working!");
    }

    (If you do this, make sure that in “File | Publish Settings . . .” on the Flash tab, in the advanced settings, “Omit trace actions” is unchecked, and “Permit debugging” is checked.)

  • 2009/01/08

    Current URL of the SWF file – super easy, but super annoying.

    Filed under: AS3.0, Adobe, Flash — delfeld @ 8:57 pm
    Tags: , , , , , , ,

    It’s just hidden, that’s all. It would be better to go back to the days of root.url, but that’s too easy.

    Anyway, the super simple code is:

    		this.root.loaderInfo.url

    And just to make your life so much easier, here are a couple of methods to display this onscreen:

    		protected function slapOnText():void
    		{
    			var tf:TextField = new TextField();
    			tf.addEventListener(Event.ADDED_TO_STAGE, addTFHnd);
    			addChildAt(tf,this.numChildren-1);
    		}
    
    		protected function addTFHnd(e:Event):void
    		{
    			e.target.removeEventListener(e.type, addTFHnd);
    
    			var tf:TextField = TextField(e.target);
    
    			tf.autoSize = TextFieldAutoSize.LEFT;
    			tf.multiline = true;
    			//tf.wordWrap = true;
    			tf.textColor = 0xFFFFFF;
    			tf.scaleX = scaleY = 1.5;
    
    			// currently, this adds the URL info to the view, but it can be anything.
    			tf.text = "URL: " + this.root.loaderInfo.url;
    		}
    

    2009/01/06

    Loading and playing audio (as a class method).

    Filed under: AS3.0 — delfeld @ 5:02 pm
    Tags: , , , , ,

    This is the entirety of loading an audio file, set up for use in a class.

    Can be used to play an MP3 or any other acceptable audio format.

    Usage:

    loadAudio(“someSubDirectory/filename”, {Boolean});

    import flash.media.Sound;
    import flash.media.SoundChannel;
    import flash.net.URLRequest;
    import flash.events.IOErrorEvent;
    
    // This audioRootDir is a local file, but can be any URL that the Flash Player has access to.
    private var audioRootDir:String = "file:///drive:/folder/";
    
    private var hasAudio:Boolean;
    private var audioChannel:SoundChannel;
    private var sndStream:URLRequest;
    private var audio:Sound;
    
    public function loadAudio(audioFile:String = "", loopAudio:Boolean = true):void
    		{
    			try
    			{
    				if (audioFile !== "")
    				{
    					// setup file and sound
    					sndStream = new URLRequest(audioRootDir + audioFile);
    					audio = new Sound();
    					audio.addEventListener(IOErrorEvent.IO_ERROR, audioLoadErrorHandler, false, 0, true);
    
    					//use this for events to run when audio is fully loaded:
    					// audio.addEventListener(Event.COMPLETE, audioLoadedHandler, false, 0, true);
    
    					// load the audio file
    					audio.load(sndStream);
    
    					// Set to loop 1000 times, which avoids the need to add a listener for Event.SOUND_COMPLETE events;
    					// if there is a need for perpetual looping, however, use the event (not shown here).
    					// Start audio
    					// and loop the audio, if required.
    					audioChannel= audio.play(0, (loopAudio ? 1000 : 0));					
    
    					hasAudio = true;
    				}
    				else
    				{
    					hasAudio = false;
    				}
    			}
    			catch (err:Error)
    			{
    				hasAudio = false;
    				trace("Error: loadAudio: err msg: " + err + "\n" + err.getStackTrace() );
    			}
    			finally { }
    		}
    
    		private function audioLoadErrorHandler(e:IOErrorEvent):void
    		{
    			trace("Error: audioLoadErrorHandler: err msg: " + e + "\n" + e.toString());
    		}
    

    Blog at WordPress.com.