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

2009/12/21

Event Listener Info

Listener Repetition Prevention

The same event type and listener combo will only be added once:
For the following example code, when the mouse clicks on the stage, clickHnd() is only called once. And since there is only one listener for MouseEvent.CLICK, only that listener is removed in clickHnd().

protected function setup():void
{
	stage.addEventListener(MouseEvent.CLICK, clickHnd, false, 1, true);
	stage.addEventListener(MouseEvent.CLICK, clickHnd2, false, 2, true);
	stage.addEventListener(MouseEvent.CLICK, clickHnd, false, 3, true);  // duplicate except for higher priority
}

protected function clickHnd2(e:MouseEvent):void
{
	trace("TestEventCount.clickHnd2");
}

protected function clickHnd(e:MouseEvent):void
{
	e.currentTarget.removeEventListener(e.type, clickHnd);
	trace("TestEventCount.clickHnd");
}

I used e.currentTarget rather than e.target to remove the listener because the listener is on the stage, not on the clicked object. This is not always needed, and often currentTarget will give the wrong results; so know what you are calling, and why, or test it!
See Adobe’s capture-bubble explanation for more details on target vs. currentTarget, and events in general.

So you can repeat adding events to an object, and it won’t add any processing time except for the check to see if it already exists.

Reference Type

Note that the above event listeners use weak reference. This means that if the object that they are attached to gets deleted, then these listeners get deleted by the garbage collector.  Above, they are attached to the ’stage’ object; in use, though, often times they will be attached to some other foreign object. In order to make these listeners NOT get garbage-collected, attach them with strong reference:

	stage.addEventListener(MouseEvent.CLICK, clickHnd, false, 1, false); // 5th parameter is set to 'false', not 'true'.

Priorities

Higher priorities (larger numbers) will get fired first. Simple! In the above code, clickHnd2() will be processed before clickHnd().  (There is a priority 3 event listener, but it is a duplicate, so doesn’t actually exist).


Etc.

If anything interesting (to me!) comes up about events, I will add on to this post.

2009/12/01

TextField JSFL fix

Filed under: Adobe, Flash, JSFL — delfeld @ 5:51 pm
Tags: , , , , ,

Font embedding has been a problem in the past with Flash. I believe now most of the issues are fixed, though it is useful to set up TextFields using best practices.

On the deleteaso blog, some JSFL code was posted that fixed various font embedding issues. This was a response to Seb Lee-Delisle’s idea of fixing fonts using AS3. Though using JSFL is an excellent idea, and the deleteaso code does work, it is pretty bad code:

  • an extra while loop (j-loop), which slows everything down. Fix: removed the loop.
  • a mis-placed if-statement; the code cannot get to the “static” if-statement via the code provided. Fix: break apart the if-statement.
  • should combine all matrix manips into one. Fix: None. I did not change this, since I do not want to debug/test it.
  • should scan all objects in the library that can contain other objects (it only scans MovieClips). Fix: add check for other DisplayObject types. Since I don’t use “button” item type, I only have added/tested the “graphic” item type. The other possible types are: “undefined”, “component”, “movie clip”, “graphic”, “button”, “folder”, “font”, “sound”, “bitmap”, “compiled clip”, “screen”, or “video”.
  • it is not ideal to switch static TFs to dynamic as a default condition. Fix: added a flag and continue-statement to control that feature.
  • very limited feedback for the user, and few comments within the file. Fix: obvious.
  • not sure why a while-loop was used instead of a for-loop. Fix: none . . . not a big deal

Further, I wanted to add an improvement that allowed setting TextField defaults in all open FLA documents, not just one. This is the loop at the top of the code.

If you use this code, note that it only affects TextFields found within the library. This will miss any TFs put directly on the stage – at this point, I have not changed that functionality.

// This code modified by Delfeld, copyright 2009.
// Modified code found at: http://delfeld.wordpress.com/2009/12/01/jsfl-textfield-fix/
// Original code found at: http://deleteaso.com/fix-textfields-jsfl/

fl.outputPanel.clear();

//scanLibrary(fl.getDocumentDOM().library);

var doChangeStaticToDynamic = false;
var outStr = "Fixing textfields for proper output.\nNOTE: This will only affect objects held in the library (or instances of them).\n\n";
var testDoc;

// cycle through all open documents:
for (doc in fl.documents)
{
	outStr += "Document: " + fl.documents[doc].name + "\n";

	// set TextField defaults in this doc:
	scanLibrary(fl.documents[doc].library);
}

fl.trace( outStr );

// -------------------------------------------------------

// auxillary function:
function getIsWholeInt(n)
{
	var s = String(n);
	if(s.indexOf(".") != -1)
	{
		return false;
	}
	else
	{
		return true;
	}
}

// auxillary function:
function round(n)
{
	var s = String(n);
	var a = s.split(".");
	var num = parseInt(a[0]);
	var dec = parseInt(a[1].substr(0, 1));
	if(dec >= 5)
	{
		num++;
	}
	return num;
}

/**
* Scans the supplied flash library for linked classes and makes sure the textfields are set up properly
* @param 	library		A flash library to scan.
*/
function scanLibrary(library)
{
	// outStr += "Scanning textfields";

	// loop variables:
	var item, timeline, layerCnt, frameCnt, elems, elemCnt, matX, tx, matY, ty;

	var items = library.items;
	var replaceCount = 0;

	// for each item in the library:
	for( var i = 0; i < items.length; i++ )
	{
		item = items[i];

		//outStr += "item: " + item.itemType + "\n"

		// dig into any MCs:
		if (item.itemType == 'movie clip' || item.itemType == 'graphic' )
		{
			// store current timeline:
			timeline = item.timeline;

			// store total number of layers:
			layerCnt = timeline.layerCount;

			// set up editing mode:
			library.selectItem(item);
			library.editItem();

			// cycle through all layers within this timeline:
			while(layerCnt--)
			{
				// store total frames
				frameCnt = timeline.layers[layerCnt].frameCount;

				// cycle through all frames within this layer:
				while(frameCnt--)
				{
					// store elements array:
					elems = timeline.layers[layerCnt].frames[frameCnt].elements;

					// store total number of elements in this frame:
					elemCnt = elems.length;

					// cycle through all elements within this frame:
					while(elemCnt--)
					{
						// process any text type element:
						if( elems[elemCnt].elementType == "text" )
						{
							outStr += "\t Found a textfield: "
							+ ((elems[elemCnt].name == "") ? "{no name}" : elems[elemCnt].name);

							//Change the static TextFields to dynamic
							if( elems[elemCnt].textType == "static" )
							{
								if( !doChangeStaticToDynamic )
								{
									outStr += ".  Ignoring this static TextField.\n";
									//skip this iteration (element)
									continue;
								}
								else
								{
									outStr += ". (Switching this static TextField type to dynamic.)\n";
									// change text type to dynamic:
									elems[elemCnt].textType = "dynamic";
								}
							}

							outStr += "\t Text type: " + elems[elemCnt].textType + "\n";

							if(elems[elemCnt].textType == "dynamic")
							{
								//Remove the ability to be selectable
								elems[elemCnt].selectable = false;
							}

							// This section applies to all dynamic or input TextFields

							// Remove the auto kern attribute
							elems[elemCnt].setTextAttr('autoKern', false);

							// Embed fonts UpperCase, LowerCase, Numerals, Punctuation
							// This can be fine tuned to get a smaller font set.
							elems[elemCnt].embedRanges = "1|2|3|4";

							// Pop onto whole pixel
							// not sure that two assignments are needed:
							matX = elems[elemCnt].matrix;
							tx = matX.tx;
							if(!getIsWholeInt(tx))
							{
								matX.tx = round(tx);
								elems[elemCnt].matrix = matX;
							}
							// Can this be combined with the 'x' section?
							matY = elems[elemCnt].matrix;
							ty = matY.ty;
							if(!getIsWholeInt(ty))
							{
								matY.ty = round(ty);
								elems[elemCnt].matrix = matY;
							}
						}
					}
				}
			}
		}
	}
}

2009/07/28

Arbitrary MovieClip rotation – by Tim Whitlock

Filed under: AS3.0, Adobe, Flash — delfeld @ 3:32 pm
Tags: , , , , , , ,

I am keeping this on my blog only to be able to not lose it.

The explanation of this class is at: http://web.2point1.com/2008/04/13/scale-rotate-around-an-arbitrary-centre, and any usage must be credited to that blog’s owner, Tim Whitlock.

/**
 * File containing class example.VirtualCentreSprite
 * @author Tim Whitlock
 */
package example { 

   import flash.display.Sprite;
   import flash.geom.Point;
   import flash.geom.Matrix; 

   /**
    * Simple sprite class that supports scaling and rotating around an arbitrary centre point
    */
   public class VirtualCentreSprite extends Sprite { 

      /**
       * Scale around an arbitrary centre point
       * @param Number local horizontal offset from 'real' registration point
       * @param Number local vertical offset from 'real' registration point
       * @param Number relative scaleX increase; e.g. 2 to double, 0.5 to half
       * @param Number relative scaleY increase
       */
      protected function scaleAround( offsetX:Number, offsetY:Number, absScaleX:Number, absScaleY:Number ):void {
         // scaling will be done relatively
         var relScaleX:Number = absScaleX / this.scaleX;
         var relScaleY:Number = absScaleY / this.scaleY;
         // map vector to centre point within parent scope
         var AC:Point = new Point( offsetX, offsetY );
         AC = this.localToGlobal( AC );
         AC = this.parent.globalToLocal( AC );
         // current registered postion AB
         var AB:Point = new Point( this.x, this.y );
         // CB = AB - AC, this vector that will scale as it runs from the centre
         var CB:Point = AB.subtract( AC );
         CB.x *= relScaleX;
         CB.y *= relScaleY;
         // recaulate AB, this will be the adjusted position for the clip
         AB = AC.add( CB );
         // set actual properties
         this.scaleX *= relScaleX;
         this.scaleY *= relScaleY;
         this.x = AB.x;
         this.y = AB.y;
      } 

      /**
       * Rotate around an arbitrary centre point
       * @param Number local horizontal offset from 'real' registration point
       * @param Number local vertical offset from 'real' registration point
       * @param Number absolute rotation in degrees
       */
      protected function rotateAround( offsetX:Number, offsetY:Number, toDegrees:Number ):void {
         var relDegrees:Number = toDegrees - ( this.rotation % 360 );
         var relRadians:Number = Math.PI * relDegrees / 180;
         var M:Matrix = new Matrix( 1, 0, 0, 1, 0, 0 );
         M.rotate( relRadians );
         // map vector to centre point within parent scope
         var AC:Point = new Point( offsetX, offsetY );
         AC = this.localToGlobal( AC );
         AC = this.parent.globalToLocal( AC );
         // current registered postion AB
         var AB:Point = new Point( this.x, this.y );
         // point to rotate, offset position from virtual centre
         var CB:Point = AB.subtract( AC );
         // rotate CB around imaginary centre
         // then get new AB = AC + CB
         CB = M.transformPoint( CB );
         AB = AC.add( CB );
         // set real values on clip
         this.rotation = toDegrees;
         this.x = AB.x;
         this.y = AB.y;
      }
   }
}

2009/07/07

Your very own Sound Event!

Filed under: AS3.0, Adobe, Flash — delfeld @ 3:55 pm
Tags: , , , , , , ,

Events are the core of actions in Flash. Often, though, the Events provided fail to provide the some information or command needed.

Say, for instance, a you have a number of sound files loading and playing, and want to manage them within another class. You want to listen for events that happen to the Sounds as well as the SoundChannels, the Event.SoundComplete event, and not have to have a complicated way of getting to the Sound in question. For that, you can set up a new Event to handle Sound events from various sources.

In the example below, I have taken a standard Event and added the ability to pass a Sound object with the event. I have also added a few more event types (EA_AUDIO_ADDED, etc.). I would have to set up where and when to fire the new event types; however, up to this point, I did not have access to some of these functions at all.

To use, set up a dispatchEvent within the sound loader class, the sound playing class, or wherever you are getting a sound from. For example, within a class called (for example) MyOwnSoundPlayer:

dispatchEvent(new EventAudio_test(someAudioObject, EventAudio_test.EA_SOUND_PLAY_COMPLETE, false, false));

Then you set up a listener attached to that class instance (this would be in another class, probably, though it doesn’t have to be – a class can listen for it’s own events, too), which references a function somewhere (usually this is in the same class, but an event listener can reference any accessible method or function):

var plyr:MyOwnSoundPlayer = new MyOwnSoundPlayer();
plyr.addEventListener(EventAudio_test.EA_SOUND_PLAY_COMPLETE, functionToRun, false, 0, true);

And the called function would look something like this:

protected function functionToRun(e:EventAudio_test):void
{
	var audioRef:Sound = e.audio;
	// Sound work here using audioRef . . .
}

So here is the full EventAudio_test class:

package AS3_0.classes.Events // Put your own package here.
{
	// Load whatever you need.  This is set up for sound files:
	import flash.media.Sound;
	import flash.events.Event; // Import the Event class.

	public class EventAudio_test extends Event // Extend Event to get it's functionality
	{
		// Create a bunch of global String constants for events that you need.
		// The constant's name should indicate the usage.
		// I also like to have identical text as the name, so I can't make a stupid mistake.
		static public const EA_AUDIO_ADDED:String = "EA_AUDIO_ADDED";
		static public const EA_SOUND_LOADED:String = "EA_SOUND_LOADED";
		static public const EA_ALL_CHANNELS_CLEARED:String = "EA_ALL_CHANNELS_CLEARED";
		static public const EA_SOUND_PLAY_COMPLETE:String = "EA_SOUND_PLAY_COMPLETE";

		// This is a new variable that holds whatever you want passed within the event through it's parameters.
		// An Event is only a function, so you can have as many variables as you wish, and can pass Functions, Arrays, Objects, etc.
		private var _audio:Sound;

		public function EventAudio(audio:Sound, type:String, bubbles:Boolean = false, cancelable:Boolean = false)
		{
			// Store the reference:
			this.audio = audio;
			// Finish the Event constructor:
			super (type, bubbles, cancelable);
		}

		public override function clone():Event
		{
			// Note that the variable needs to be added to the clone() method:
			return new EventAudio(audio, type, bubbles, cancelable);
		}

		public override function toString():String
		{
			// Note that the variable needs to be added to the toString() method:
			return formatToString("audio", "type", "bubbles", "cancelable", "eventPhase");
		}

		// Allow public access to the variable via getter/setter methods:
		public function get audio():Sound { return _audio; }
		public function set audio(value:Sound):void
		{
			// Not necessary, but is always good to reduce duplication, especially if you start using extensive objects:
			if (_audio !== value)
			{
				_audio = value;
			}
		}
	}
}

2009/06/24

What designers need to know to use ActionScript 3.0 . . .

  1. All things that exist on the stage (even if invisible or inactive) are Classes.
  2. All actions that happen are Events.
  3. All Events are Classes.
  4. All Classes inherit the Object class.

The only things that are different are:

Primitive data types:

  • Boolean
  • int
  • Null
  • Number
  • String
  • uint
  • void

These act quite a bit like classes in many use examples.

Complex data types:

  • Object
  • Array
  • Date
  • Error
  • Function
  • RegExp
  • XML
  • XMLList

Each of these have some specialized create, read, update, and delete (CRUD) techniques, so do not expect them to be consistent between each other. For example, compare the different ways to create an XML object and an Array object.

You can review this data type explanations at Adobe’s data type description page.

That’s it . . . though there may be more to come.

2009/06/04

Order of ops for ‘Record Pivot Rotation’ and ‘Parent in Place’ in Lightwave

Filed under: Lightwave 3D — delfeld @ 9:14 pm
Tags: , ,

This is such an important concept to what I am working on, I cannot let it get lost in the ether. I have not tested this in Lightwave’s lScript, but I suspect it is the problem I have been having with automatically adding bones. Ergo, here it is, lifted straight from Gary Creighton’s website http://www.angelfire.com/wizard/gandalf27/layout1.html:

“I continue, reparenting all other skelegon chains to ‘Deinon_Root’ with ‘Parent in Place turned on. I finish each bone’s reparenting by pressing ‘r’ to rethink that bone’s ‘Rest Positon’ before I move on to the next bone.

“You should always check to make sure your object looks correct. Since you won’t generally be reparenting more than about 5 or 6 bones, you might turn on visibility on the object after each bone to ensure that nothing is twisted, then save the scene with a new name because Layout has no undo (or one level of undo for limited operations).

“Then… turn visibility off on the object once more to reparent the next bone.

IMPORTANT – ‘Parent in Place’ ONLY WORKS IF YOU HAVE NEVER ROTATED THAT BONE BEFORE.

“Therefore, you must reparent bones BEFORE you record their rest positions. If you follow exactly what I said to do, and bones twist your object? Most likely you had already rotated those bones away from their original positions with ‘Record Pivot Rotation’, and thus ‘Parent in Place’ isn’t working.

“So, only ‘Record Pivot Rotation’ AFTER you’ve reparented bones (This is like building the house of cards, one-by-one. Beware that the house of cards is very senstive to the order in which you place those cards).”

2009/06/02

MovieClip Frame Life Cycle

Filed under: AS3.0, Adobe, Flash — delfeld @ 3:10 pm
Tags: , , , , , ,

I am taking no credit for this post. It is directly derived from Johannes Tacskovics blog post: http://blog.johannest.com/2009/05/22/the-movieclip-life-cycle-event-frame_constructed-and-event-exit_frame/
He also offers a more complete description of the events. I only need to make sure that I have the reference to the life cycle, so here it is:

  1. Event of event type Event.ENTER_FRAME dispatched.
  2. Constructor code of children MovieClips is executed.
  3. Event of event type Event.FRAME_CONSTRUCTED dispatched.
  4. Frame actions are executed.
  5. Frame actions of children MovieClips are executed.
  6. Event of event type Event.EXIT_FRAME dispatched.
  7. Event of event type Event.RENDER dispatched.

2009/05/06

XML Quick Reference

Filed under: AS3.0, Adobe, Flash — delfeld @ 5:27 pm
Tags: , , , ,

I will add to this list as I find a need, or as my memory plagues me:

  • xml.attributes()[idx] – Treating attributes, children, comments, descendants, elements, text, etc. as an array. The original output is an XMLList.
  • xml.attribute(“*”)[idx] – Treating attribute, child, comments, and others that use parameters as an array. The original output is an XMLList.
  • name() – Retrieving the name of the current XML object – the name of an attribute, element, or other XML non-data names.
  • xml.attributes()[idx].name() – Retrieving the name of an attribute.
  • xml.someElementName.(@id == marker).child(“*”) – Retrieve all children of all elements whose “id” attribute match the “marker” variable.
  • xml.elements(“someElementName”) – Retrieve an element by the element’s name. Returns a new XMLList of the element and it’s children.
  • Make an array out of XML.
  • length() – Get number of entries from some XML object (Array uses length (no parentheses), which is confusing at times)
  • A simple for loop looks like this:

    if ( xmlBase
    	&& xmlBase.someElement
    	&& xmlBase.someElement.multipleElementsArea
    	&& xmlBase.someElement.multipleElementsArea.length() > 0
    )
    {
    	trace("all elements = " + xmlBase.someElement.multipleElementsArea);
    
    	var val:int = xmlBase.someElement.multipleElementsArea.length();
    	for (var i:int = 0; i < val; i++)
    	{
    		trace( "some attribute value: " + xmlBase.someElement.multipleElementsArea[i].@attribute);
    		trace( "some element value: " + xmlBase.someElement.multipleElementsArea[i]);
    	}
    }
  • xml.entryItem.(@attribute1 == “someValue”).@attribute2[indexInt] – This returns data stored in an attribute contained within the same element as the searched attribute.
    Returns the value stored in an attribute named attribute2 within an XML element entryItem, where initially the list is sorted by attribute1. Useful in a for loop. This is similar to a horizontal lookup command in a spreadsheet.
  • var xml:XML = <elementName attr1={someVariable} attr2={someOtherVariable}>{variableForTheText}</elementName> – Using variables in implicit XML. Adds attributes and element text via variables. Note: Quotation marks are automatically added to attributes (around someVariable and someOtherVariable), but not added to element info (variableForTheText).

Converting XML to Array

Filed under: AS3.0, Adobe, Flash — delfeld @ 5:12 pm
Tags: , , , , ,

Sometimes, you have to have an Array rather than XML. There is no straightforward conversion, though the XML class does deliver a lot of Array-like content.

This is a quick and incomplete conversion of XML to an array. The main caveat you need to know is that all information is assumed to be in attributes, and not element text. Also, it ignores the first child of the XML file, processing only the children. Since there are many ways to set up XML “correctly”, this was a solution for a specific problem, and may not fit your needs.

But look at it anyway! There are many useful processes and setup that you may glean from this, and adding element text to the array is not a huge leap:

// This function is recursive.
protected function x2a(xd:XML):Array
{
	var arr:Array = [];
	var idx:int = 0;

	// attributes for this child
	var val2:int = xd.attributes().length();
	for (var j:int = 0; j < val2; j++)
	{
		xd.attributes()[j]
		arr[idx++] = xd.attributes()[j].toString();
	}

	// elements for this child
	var val:int = xd.elements().length();
	for (var i:int = 0; i < val; i++)
	{
		arr[idx++] = x2a(xd.elements()[i]);
	}

	return arr;
}

2009/04/30

Regular Expressions in AS3.0

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

These are the two main docs about RegEx class for AS3.0. If you already know re’s from some other language (e.g., Perl, Javascript, etc.), these will be famliar, and the only necessary reference:

Inner syntax: http://livedocs.adobe.com/flash/9.0/main/00000114.html#wp119166
Outer syntax: http://livedocs.adobe.com/flash/9.0/main/00000119.html#wp118940

A RegEx can be used in a string.replace() command or other locations.

RegEx in ActionScript, like XML, has two constructors: with quotes or without (examples from Adobe’s RegEx class reference):

Quotes:

var pattern1:RegExp = new RegExp("test-\\d", "i");

Without quotes:

var pattern2:RegExp = /test-\d/i;

Because these are constructors, they create a new RegEx invisibly, so can be used as function parameters (as in string.replace()) without further declaration:

var str:String = "test-12345";
str.replace("test-(\\d)", "replaced-\\1");
str.replace(/test-(\d)/i, "replaced-\\1");
Next Page »

Blog at WordPress.com.