Undocumented XMLUtil class in Adobe Flash CS5?

I tried to recompile an older Flash CS 4 project with Flash CS 5 but got this compile time error 1061: Call to a possibly undefined method reuseNodes through a reference with static type Class caused by the usage of one of my utility classes:

This didn’t work

import de.superclass.util.XMLUtil;
...
XMLUtil.someMethod();

This still works

...de.superclass.util.XMLUtil.someMethod();

So there was no error in my class because it’s still usable when I use the full class package but it didn’t work when I just import it with the import statement.

After some research I found an undocumented XMLUtil class which avoids the correct access to my de.superclass.util.XMLUtil class.

This class doesn’t exist in Flash CS 4 but is available in Flash CS 5. Simply create a new FLA in Flash CS 5 an run this code on timeline

trace( describeType( XMLUtil ) );

and you’ll get this result:

<type name="XMLUtil" base="Class" isDynamic="true" isFinal="true" isStatic="true">
  <extendsClass type="Class"/>
  <extendsClass type="Object"/>
  <constant name="kAttrYMin" type="String"/>
  <constant name="kAttrXMax" type="String"/>
  <constant name="kAttrYMax" type="String"/>
  <constant name="kAttrObj0" type="String"/>
  <constant name="kAttrObj1" type="String"/>
  <constant name="kAttrX0" type="String"/>
  <constant name="kAttrY0" type="String"/>
  <constant name="kAttrX1" type="String"/>
  <variable name="kTagGravity" type="String"/>
  <constant name="kAttrY1" type="String"/>
  <variable name="kTagCollideSpace" type="String"/>
  <constant name="kAttrType" type="String"/>
  <variable name="kTagRigidBody" type="String"/>
  <constant name="kAttrPin" type="String"/>
  <variable name="kTagRect" type="String"/>
  <constant name="kAttrSlider" type="String"/>
  <variable name="kTagJoint" type="String"/>
  <constant name="kAttrLength" type="String"/>
  <variable name="kTagSpring" type="String"/>
  <constant name="kAttrStrength" type="String"/>
  <variable name="kTagFrames" type="String"/>
  <constant name="kAttrDamping" type="String"/>
  <variable name="kTagFrame" type="String"/>
  <constant name="kAttrSimSpeed" type="String"/>
  <variable name="kTagFluid" type="String"/>
  <constant name="kAttrAxis" type="String"/>
  <variable name="kTagEmitter" type="String"/>
  <constant name="kAttrAsset" type="String"/>
  <variable name="kTagOrigin" type="String"/>
  <constant name="kAttrSpring" type="String"/>
  <variable name="kTagKeyframes" type="String"/>
  <constant name="kAttrSpringStrength" type="String"/>
  <variable name="kTagKey" type="String"/>
  <constant name="kAttrSpringDamping" type="String"/>
  <variable name="kTagView" type="String"/>
  <variable name="kTagGeom" type="String"/>
  <constant name="kAttrIndex" type="String"/>
  <constant name="kAttrCollideType" type="String"/>
  <constant name="kAttrCellSize" type="String"/>
  <constant name="kAttrSpringValue" type="String"/>
  <constant name="kAttrCollideSpace" type="String"/>
  <constant name="kAttrRowDimen" type="String"/>
  <constant name="kAttrDensity" type="String"/>
  <constant name="kAttrColDimen" type="String"/>
  <constant name="kAttrPosX" type="String"/>
  <constant name="kAttrTimeStep" type="String"/>
  <constant name="kAttrPosY" type="String"/>
  <constant name="kAttrTime" type="String"/>
  <constant name="kAttrAngle" type="String"/>
  <constant name="kAttrColor" type="String"/>
  <constant name="kAttrVelX" type="String"/>
  <constant name="kAttrStrokeColor" type="String"/>
  <constant name="kAttrVelY" type="String"/>
  <constant name="kAttrAlpha" type="String"/>
  <constant name="kAttrVelA" type="String"/>
  <constant name="kAttrFPS" type="String"/>
  <constant name="kAttrCOR" type="String"/>
  <constant name="kAttrSprings" type="String"/>
  <constant name="kAttrCOF" type="String"/>
  <constant name="kAttrLimits" type="String"/>
  <constant name="kAttrFixed" type="String"/>
  <constant name="kAttrMinAngle" type="String"/>
  <constant name="kAttrID" type="String"/>
  <constant name="kAttrDBlend" type="String"/>
  <constant name="kAttrPoseStrength" type="String"/>
  <constant name="kAttrPoseDamping" type="String"/>
  <constant name="kAttrBodyDamping" type="String"/>
  <constant name="kAttrX" type="String"/>
  <constant name="kAttrY" type="String"/>
  <constant name="kAttrXMin" type="String"/>
  <accessor name="prototype" access="readonly" type="*" declaredBy="Class"/>
  <method name="nodeHasAttribute" declaredBy="XMLUtil" returnType="Boolean">
    <parameter index="1" type="XML" optional="false"/>
    <parameter index="2" type="String" optional="false"/>
  </method>
  <method name="getIntAttribute" declaredBy="XMLUtil" returnType="int">
    <parameter index="1" type="XML" optional="false"/>
    <parameter index="2" type="String" optional="false"/>
    <parameter index="3" type="int" optional="true"/>
  </method>
  <method name="getNumberAttribute" declaredBy="XMLUtil" returnType="Number">
    <parameter index="1" type="XML" optional="false"/>
    <parameter index="2" type="String" optional="false"/>
    <parameter index="3" type="Number" optional="true"/>
  </method>
  <method name="getBoolAttribute" declaredBy="XMLUtil" returnType="Boolean">
    <parameter index="1" type="XML" optional="false"/>
    <parameter index="2" type="String" optional="false"/>
    <parameter index="3" type="Boolean" optional="true"/>
  </method>
  <method name="getStringAttribute" declaredBy="XMLUtil" returnType="String">
    <parameter index="1" type="XML" optional="false"/>
    <parameter index="2" type="String" optional="false"/>
    <parameter index="3" type="String" optional="true"/>
  </method>
  <factory type="XMLUtil">
    <extendsClass type="Object"/>
  </factory>
</type>

So I had to rename my class to de.superclass.util.XMLUtils.

You might run into the same problems when using mx.utils.XMLUtil (Flex Framework) or com.adobe.utils.XMLUtil (AS3CoreLib) but I didn’t test it!

UPDATE:
I found XMLUtil classes in
Adobe Flash CS5/Common/Configuration/ActionScript 3.0/libs/ik.swc and
Adobe Flash CS5/Common/Configuration/ActionScript 3.0/libs/PffLib.swc

Removing the $(AppConfig)/ActionScript 3.0/libs directory within the ActionScript export settings avoids the problem. But I have no time to test if this directory is necessary. If you run into problems please leave me a comment!

Aligning Flash TextField instances visually correct

I was looking for an easy way to align TextField instances with different font sizes visually correct. I really thought this wouldn’t take much time but it turned out to become really frustrating.

So think about a simple scenario: Try to align two TextField instances with different font sizes side by side. If you just set the TextField.y property to the same value you will recognise that different font sizes have different top margins. Well, you might fix that by hand if you have a static layout but what if you don’t know what kind of Text will be displayed? In my case I needed to align text always on top with the same margin regardless of the headline font size.

I tried hard to get this done by using different methods provided by the TextField class. Then I tried to work with the TextLineMetrics class as well. Nothing worked properly for me. I needed to work with CSS formatted HTML text so I was looking for a margin-top style or something similar. Not available. Hm.

Then I remembered my solution about finding the alpha bounds within external loaded PNG files and used that. Well, the main downside is that I need to create a BitmapData instance to determine the exact text bounds but otherwise it worked out very well.

It all comes down to two simple methods that will slip into my library. At the end you will receive a rectangle representing the bounds on the TextField instance.

If you know a different way to obtain the same exact result please leave me a comment!

Here’s my solution:

function getDisplayObjectAlphaBounds( displayObject : DisplayObject, smoothing : Boolean = true ) : Rectangle
{
	var bitmapData : BitmapData = new BitmapData( displayObject.width, displayObject.height, true, 0 );
		bitmapData.draw( displayObject, null, null, null, null, smoothing );
 
	var alphaBounds : Rectangle = bitmapData.getColorBoundsRect( 0x01000000, 0x01000000 );
 
	bitmapData.dispose();
 
	return alphaBounds;
}
 
function getTextFieldTextBounds( textField : TextField, smoothing : Boolean = true ) : Rectangle
{
	var background : Boolean = textField.background;
	var backgroundColor : uint = textField.backgroundColor;
	var border : Boolean = textField.border;
	var borderColor : uint = textField.borderColor;
	var opaqueBackground : Object = textField.opaqueBackground;
 
	textField.background = false;
	textField.border = false;
	opaqueBackground = null;
 
	var textBounds : Rectangle = getDisplayObjectAlphaBounds( textField, smoothing );
 
	textField.background = background;
	textField.backgroundColor = backgroundColor;
	textField.border = border;
	textField.borderColor = borderColor;
	textField.opaqueBackground = opaqueBackground;
 
	return textBounds;
}
 
var textFormat : TextFormat = new TextFormat();
	textFormat.align = TextFormatAlign.CENTER;
	textFormat.color = 0x0000FF;
	textFormat.font = "Arial";
	textFormat.size = 30;
 
var textField : TextField = new TextField();
	textField.border = true;
	textField.borderColor = 0x0000FF;
	textField.background = true;
	textField.backgroundColor = 0x000022;
	textField.defaultTextFormat = textFormat;
	textField.height = 260;
	textField.text = "\nThis text\ncan be\naligned\nproperly!";
	textField.width = 300;
	textField.x = 30;
	textField.y = 30;
 
addChild( textField );
 
// Get bounds
var textBounds : Rectangle = getTextFieldTextBounds( textField );
 
// Draw bounds
var shape : Shape = new Shape();
	shape.x = textField.x;
	shape.y = textField.y;
 
var g : Graphics = shape.graphics;
	g.lineStyle( 1, 0x00FFFF );
	g.drawRect( textBounds.x, textBounds.y, textBounds.width, textBounds.height );
 
addChild( shape );

FFK10 – Flashforum conference notes

Every year I spend two days sitting in the audience of the Flashforum conference (FFK) listening to great speakers and getting impressed by their experiments and thoughts. It’s also a good way to renew some of the basic knowledge in Flash development and of course to meet some friends in the Flash scene.

So this are most of my notes:

Flash / Flex development:

  • Jessee Freemans Flash Augmented Reality Debug Tool provides an easy way to start with augmented reality development.
  • Use stunning Hype-Framework effects.
  • Dive into dynamic sound generation with André Michelles Audio-Sandbox.
  • Have a look at the PushButton-Engine for component based development.
  • Remember that Flex 4 includes a lot of performance features! It’s highly recommended to switch as soon as possible. For example the Embed-tag reduces PNG file size and lots of improvements are made within the Flex components.
  • Watch the Adobe TV video preview: Flex for mobile devices
  • See Christian Cantrell’s blogpost One Application , Five Screens (Including the iPad)
  • Great tip: ByteArray.org – Why cacheAsBitmap is bad
  • Adobe’s Mobile Development Guide for the Flash platform (applies also to desktop applications) contains performance tips like:

    • DisplayObject.width / height should be divisible by 2 as often as possible
    • Try to avoid Events and / or stop propagation as soon as possible
    • Set DisplayObject.mouseEnabled=false if possible
    • Stop MovieClip animations when removed from stage
    • Use TextField.opaqueBackground=true if possible
    • Use final statement (final class ClassName {})
    • Use BitmapData.get / setVector()

HTML / JavaScript basics:

  • Use “Sprites” with HTML / CSS (load only one bitmap graphic containing all the asset images for your page and show only the particular relevant parts) and preload next page assets using AJAX while your current page is idle.
  • Since usually only 2 simultaneous domain http requests are supported by browsers it’s a good practice to distribute contents from various ip addresses (use asset-servers for example).
  • Load only visible parts of your site using JavaScript viewport events.

Useful workflow utils:

Embed fonts with Adobe Flash IDE

Almost every Flash project needs some embedded fonts and it is a good practice to compile the fonts into a separate SWF file. So here is a simple way to create a file that contains only embedded fonts. The SWF file will also show the exact internal font names:

  1. Copy and paste the ActionScript code below into the first keyframe of your timeline.
  2. Create new fonts in flash library and export them for ActionScript.
    A good tutorial can be found here.
  3. Run ONLY the code for step 2 to evalute and trace the exact font names.
  4. Assign the exact font names to the actual library symbols and their export class name.
  5. Run ONLY the code for step 4 to export the swf.
Security.allowDomain( "*" );
 
// Step 1: Create new fonts in flash library
//         and select "Export for ActionScript".
 
 
// Step 2: Run ONLY this code to evalute and trace the exact font names.
/**/
var embeddedFonts : Array = Font.enumerateFonts ( false );
var c : uint = embeddedFonts.length;
for ( var i : uint = 0; i < c; i++ )
{
	var font : Font = embeddedFonts[ i ] as Font;
	var className : String = font.fontName.split( " " ).join( "" );
 
	var lowerFontStyle : String = font.fontStyle.toLowerCase();
	if ( lowerFontStyle.indexOf( "bold" ) != -1 ) className += "Bold";
	if ( lowerFontStyle.indexOf( "italic" ) != -1 ) className += "Italic";
 
	trace( className );
}
/**/
 
// Step 3: Assign the exact font names to the actual
//         library symbols and their export class name.
 
 
// Step 4: Export the swf using ONLY this code. 
/**/
var posX : int = 10;
var posY : int = 10;
 
var embeddedFonts : Array = Font.enumerateFonts ( false );
	embeddedFonts.sortOn( "fontName", Array.CASEINSENSITIVE );
 
var c : uint = embeddedFonts.length;
for ( var i : uint = 0; i < c; i++ )
{
	var font : Font = embeddedFonts[ i ] as Font;
 
	var lowerFontStyle : String = font.fontStyle.toLowerCase();
 
	var textFormat : TextFormat = new TextFormat();
		textFormat.bold = ( lowerFontStyle.indexOf( "bold" ) != -1 );
		textFormat.italic = ( lowerFontStyle.indexOf( "italic" ) != -1 );
		textFormat.font = font.fontName;
		textFormat.size = 14;
 
	var className : String = font.fontName.split( " " ).join( "" );
	if ( textFormat.bold ) className += "Bold";
	if ( textFormat.italic ) className += "Italic";
 
	Font.registerFont( Class( getDefinitionByName( className ) ) );
 
	var boldItalic : String = "";
	if ( textFormat.bold && textFormat.italic )
	{
		boldItalic = "( needs to be BOLD and ITALIC ! )";
	}
	else if ( textFormat.bold )
	{
		boldItalic = "( needs to be BOLD ! )";
	}
	else if ( textFormat.italic )
	{
		boldItalic = "( needs to be ITALIC ! )";
	}
 
	var textField : TextField = new TextField();
		textField.autoSize = TextFieldAutoSize.LEFT;
		textField.defaultTextFormat = textFormat;
		textField.embedFonts = true;
		textField.text = font.fontName + "\t" + boldItalic;
		textField.x = posX;
		textField.y = posY;
 
	posY += textField.height + 5;
 
	addChild( textField );
}
/**/

DisplayObject.rotationX,Y,Z

Just a quick note about working with DisplayObject.rotationX,Y,Z:

Keep in mind that your DisplayObject rotates around it’s center point. This may result in weird optics if you forget to align your DisplayObject properly.

Update: Well, there is much more to know!

There’s need for an DisplayObjectContainer with an properly assigned PerspectiveProjection.

Copy & paste this code and you’ll see what I’m talking about:

stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.addEventListener( Event.RESIZE, onStageResize );
 
function onStageResize( event : Event ) : void
{
	draw();
}
 
function createRectSprite( w : int, h : int, rotationY : Number, container : Sprite, useProjection : Boolean ) : Sprite
{
	// 3D object
	var graphicSprite : Sprite = new Sprite();
		graphicSprite.name = "graphicSprite";
		graphicSprite.rotationY = rotationY;
		graphicSprite.buttonMode = true;
		graphicSprite.useHandCursor = true;
 
	// Make sure 3D objects content is centered
	var g : Graphics = graphicSprite.graphics;
		g.beginFill( 0, .5 );
		g.drawRect( w * -.5, h * -.5, w, h );
		g.endFill();
 
	// 3D Container
    var containerSprite : Sprite = new Sprite();
        containerSprite.addChild( graphicSprite );
 
	// Apply projection to 3D Container instead of 3D Object
	if ( useProjection )
	{
		var projection:PerspectiveProjection = new PerspectiveProjection();             
			projection.projectionCenter = new Point( 0, 0 );
 
		containerSprite.transform.perspectiveProjection = projection;
    }
 
	// Visualize center
	g = containerSprite.graphics;
	g.beginFill( 0xff0000 );
	g.drawCircle( 0, 0, 2 );
	g.endFill();
 
    container.addChild( containerSprite );    
    return containerSprite;
}
 
var left : Sprite;
var right : Sprite;
 
function draw() : void
{
	while ( numChildren > 0 ) { removeChildAt( 0 ); }
 
	var container : Sprite = new Sprite()
		container.name = "container";
		container.x = stage.stageWidth * .5;
		container.y = stage.stageHeight * .5;
	addChild( container );
 
	var stepX : int = stage.stageWidth / 3;
 
	var w : int = 200;
	var h : int = 200;
 
	left = createRectSprite( w, h, -60, this, true );
	left.name = "left";
	left.x = stepX;
 
	right = createRectSprite( w, h, 60, this, true );
	right.name = "right";
	right.x = stepX * 2;
 
    left.y =
    right.y = stage.stageHeight * .5;
}
 
function onEventHitTest( event : Event ) : void
{
	var containerSprite : DisplayObjectContainer;
	var graphicSprite : DisplayObject;
 
	containerSprite = this.getChildByName( "left" ) as DisplayObjectContainer;
	graphicSprite = containerSprite.getChildByName( "graphicSprite" ) as DisplayObject;
 
	trace( "hit left containerSprite:", containerSprite.hitTestPoint( mouseX, mouseY, true ) );
	trace( "hit left graphicSprite:", graphicSprite.hitTestPoint( mouseX, mouseY, true ) );
 
	containerSprite = this.getChildByName( "right" ) as DisplayObjectContainer;
	graphicSprite = containerSprite.getChildByName( "graphicSprite" ) as DisplayObject;
 
	trace( "hit right containerSprite:", containerSprite.hitTestPoint( mouseX, mouseY, true ) );
	trace( "hit right graphicSprite:", graphicSprite.hitTestPoint( mouseX, mouseY, true ) );
	trace( "" );
}
 
stage.addEventListener( MouseEvent.MOUSE_DOWN, onEventHitTest );
 
draw();

Using Sprite.buttonMode and Sprite.mouseChildren

Today I found something I really did not understand very well. I had to debug a application because one button displayed the handCursor and another did not. Both use the same script and actually I didn’t find out why the behaviour of this buttons was like that but I fixed it through a workaround.

The first thing I understood today is, that it’s possible to receive all mouse events without using buttonMode! Simply add a listener to your objects and you can work with them.

But if you like to work with a button that contains additional sprites (which might be very useful) you definitely should take care about their behaviour. By default mouse events will be dispatched by the lowest sprite, exept you set mouseChildren to false…

Have a look at this little code example:

package
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
 
	public class ButtonModeTest extends Sprite
	{
		public function ButtonModeTest()
		{
			run();
		}
 
		private function run(): void
		{
			//-- Start listening
			addEventListener( MouseEvent.CLICK, onMouseEvent );
			addEventListener( MouseEvent.MOUSE_OVER, onMouseEvent );
			addEventListener( MouseEvent.MOUSE_OUT, onMouseEvent );
 
			//-- Button 1 on the main stage
			var button1: Sprite = createButton( 1 );
			addChild( button1 );
 
			//-- Button 2 within the container
			var button2: Sprite = createButton( 2 );
			var button2Container: Sprite = new Sprite();
			button2Container.addChild( button2 );
			button2Container.x = 150;
			button2Container.name = "button2Container";
			addChild( button2Container );
 
			// By default the mouse events will be dispatched by the buttons themself
			// (the lowest sprites)
 
			// Setting containers buttonMode to true doesn't force the container to
			// dispatch the mouse events ( so what the hell is button mode?)
			button2Container.buttonMode = true;
 
			// Setting containers useHandCursor to true only works when it's buttonMode
			// is also true. But the events will still be dispatched from the lowest
			// sprite (even its a button!?!)
			button2Container.useHandCursor = true;
 
			// Only setting mouseChildren to false forces the container to dispatch
			// the mouse events
			button2Container.mouseChildren = false;
		}
 
		private function createButton( index: uint ): Sprite
		{
			var button: Sprite = new Sprite();
			button.name = "button" + index; 
			button.graphics.beginFill( 0, 1 );
			button.graphics.drawRect( 0, 0, 100, 100 );
			button.graphics.endFill();
 
			return button;
		}
 
		private function onMouseEvent( event: Event ): void
		{
			trace( "Event (" + event.type + ") dispatched from : " + event.target.name );
		}
	}
}