So I find myself writing a whole bunch of AS1 recently (upgrading another old game). In this case, the project was originally done in a single ginormous FLA file. No separate assets file, no external .as files. We’ve broken lots of timeline code out into source files, but have yet to break any of the assets out. That’s my current project.

I’m actually splitting assets up into several small files and then loading them up into an invisible container clip.

When I started writing the loading code, I forgot that AS1 doesn’t have the MovieClipLoader class. That’s a Flash 7 invention. So, I got to do things the really old fashioned way, by listening to bytes loaded and all that. I figured since it is potentially a common problem (for those who wind up having to go back into the stone ages, that is), I may as well share the code with folks.

Please note that this code is a skeleton. It does not do a whole lot as it is. It just gives you a starting framework to edit to your individual application.


FakeMCL = function( parent, depth ) {
	if( !parent )
		parent = _root;
	this.parent = parent;	// parent clip of container
	this.loadQueue = [];	// queue of clips to be loaded
	this.container = null;	// clip into which all children are loaded
	this.init( depth );
};// end: "constructor"

FakeMCL.prototype.init = function( depth ) {
	if( !depth )
		depth = this.parent.getNextHighestDepth();
	// create an invisible container clip at parent._fakemcl
	this.container = this.parent.createEmptyMovieClip( "_fakemcl", depth );
	this.container._visible = false;
	trace("<fakemcl> container "+this.container+" is at depth "+depth);
}// end: init()

FakeMCL.prototype.loadMovie = function( url ) {
	this.loadQueue.push( url );
	if( !this.loadInterval ) {
		trace("<fakemcl> starting interval");
		// try to tick once every 25ms (that's once per frame at 40fps), we're
		// probably not running that fast, so it will just execute once per frame
		// NOTE: you can (and might want to) throttle this back
		this.loadInterval = setInterval( this.onInterval, 25, this );
	}// end: start interval
}// end: loadMovie()

FakeMCL.prototype.onInterval = function( mcl ) {
	// stop iterating if we're completely out of crafts to load
	if( !mcl.loadQueue.length && !mcl.loadingClip ) {
		trace("<fakemcl> queue is empty");
		clearInterval( mcl.loadInterval );
		// NOTE: you might want to do something here now that the whole queue is loaded
		return;
	}// end: queue is empty
	// start loading a clip if we aren't already
	if( !mcl.loadingClip ) {
		var clipURL = mcl.loadQueue.shift();
		var instanceName = "_"+clipURL.substr( 0, clipURL.indexOf(".") );
		trace("<fakemcl> loading "+clipURL+" ("+instanceName+")");
		mcl.loadingClip = mcl.container.createEmptyMovieClip( instanceName, mcl.container.getNextHighestDepth() );
		mcl.loadingClip.loadMovie( clipURL );
		// NOTE: clip init goes here
	}// end: not loading anything
	else {
		// check status of currently loading movie
		var percentLoaded = mcl.loadingClip.getBytesLoaded() / mcl.loadingClip.getBytesTotal();
		trace("<fakemcl> loaded "+mcl.loadingClip.getBytesLoaded()+"/"+mcl.loadingClip.getBytesTotal());
		if( percentLoaded >= 1.0 && mcl.loadingClip.getBytesTotal() ) {
			// we're done loading
			trace("<fakemcl> done, "+mcl.loadingClip);
			mcl.loadingClip = null;
			// NOTE: callback for individual clip loading goes here
		}
	}// end: currently loading
}// end: onInterval()

Again, this is skeleton code and isn’t terribly useful as is. I’ve put some notes in the code where you might want to do more interesting things to adapt the durned thing to your application. Also, feel free to yoink the trace statements, they can get awful spammy ;)

This skeleton supports queuing up a whole list of swf’s for loading, but in actual use, it is probably going to be more useful for you to just load one swf at a time. Shrug.

example

Put the following code on a frame of your timeline:


#include "FakeMCL.as"

var mcl = new FakeMCL( this, 10 );
mcl.loadMovie("fish.swf");
mcl.loadMovie("bird.swf");

And hopefully, you’ll get some output like this:


<fakemcl> container _level0._fakemcl is at depth 10
<fakemcl> starting interval
<fakemcl> loading fish.swf (_fish)
<fakemcl> loaded 0/-1
<fakemcl> loaded 1465744/1465744
<fakemcl> done, _level0._fakemcl._fish
<fakemcl> loading bird.swf (_bird)
<fakemcl> loaded 1427856/1427856
<fakemcl> done, _level0._fakemcl._bird
<fakemcl> queue is empty

When all is done running, you’ve got those clips at _fakemcl._bird and _fakemcl._fish that you can toy with however you need. Chances are, you probably want to know when the loading is actually done, however. That’ll take some editing of this code. Put a stop() on the frame you call FakeMCL.loadMovie() from and then put “mcl.parent.play()” in the load complete block, etc…

This morning, Adobe announced that they are open sourcing Flex (under the MPL). Their FAQ is pretty informative.

Slashdot’s gallery of morons is lambasting the announcement as just a PR stunt, as a scared response to Microsoft’s recent announcement of WPF/E Silverlight, and likely as a joint conspiracy between Sony, the Bush administration, and Jack Thomspson to steal their mp3z. But you can’t make some idiots happy, no matter what you do.

I couldn’t be happier with this news. It’s a major win for not only Adobe (who gets fresh innovation and lots of good karma out of the deal) but also for the community in general. Flex is a tremendously powerful platform, it’s a serious platform. While Flash is just for pretty pictures, Flex is for content. It is fast to develop for, it is more reliable than AJAX (since it runs under a consistent VM that doesn’t change from browser to browser or OS to OS), and it looks better and runs faster than Java Swing (well, it doesn’t take much to look better than swing, or be faster than swing, but Flex does both, at the same time, and does it for less effort on the developer’s end).

Fact of the matter is, Adobe is open sourcing their compiler, their debugger, and their core language api. Really, the only thing they’re not opening up is the VM itself… but they’ve already done some of that. Adobe is also keeping control of the project, it’ll be hosted on their servers, etc… So between that, and their keeping the Flash 9 player closed source, the platform isn’t in danger of rapid death spiral type unreliability.

Ted Patrick has more details on his blog. Namely, the release schedule:

Starting Summer 2007 we will be posting daily builds of the Flex SDK and providing open access to a bug database online. The Flex community will have direct access to the same tools developers use internally to manage Flex quality and this will allow the Flex community to improve the quality of Flex directly.

In December of 2007 after the release of “Moxie” (aka Flex 3) we will be posting all software assets into a public Subversion repository for public access. During this transition period we will be clarifying governance on the Flex SDK and how contributions will be handled in phases.

Naturally, I’ve got more questions about this, but… I’m too excited to care at the moment.

I have the sinking suspicion that as one of the few groups on the planet to adopt Flex 2 as a development platform from its launch, a few of my comrades and I will be among the first to actually contribute code to the new Flex 3 project. We may none of us be compiler jocks… but I tell you what. We are going to fix a few broken/clunky UI classes and are gonna give the native data classes a good jolt of needed features.

I giggle at the thought of how much work DataGrid is going to see the first month ;)

Ok, this one’s kind of silly. I’m not entirely sure why I threw this together, really. But I got bored during lunch and here it is – a simple no-frills AS2.0 (Flash 7+) movie clip depth management class for times when you’re attaching and removing tons of clips, don’t really care what order they wind up in or what they’re called, and want to avoid running out of space.

MovieClip.getNextHighestDepth() works fine for most cases, but it’s also a wee bit dumb in that it won’t re-use depths that you skipped over. Now, this can be a desirable feature, and I might take advantage of it in an upgraded version of this class in the future… but for now, what it means is that it is very easy to programatically ratchet yourself out of available depths.

note

My version 0.1 of my SDM class (ie, the code provided here) has been tested only lightly and works fine under low stress environments. But it does seem to have issues when it starts getting hammered by lots of requests at once.

I’ve got a new version that should help remedy this problem in the works, but no schedule for when I’ll be able to release it.

the source

You can download the source here: simpledepthmanager-01.zip.


class org.simud.util.SimpleDepthManager extends MovieClip {
	///// private instance vars /////
	private var _depth:Number;
	private var _count:Number;
	private var _mcl:MovieClipLoader;

	private static var MAX_DEPTH:Number = 1048576; // 2^20

	///// public vars for listening to the mcl /////
	public var onLoadComplete:Function;
	public var onLoadError:Function;
	public var onLoadInit:Function;
	public var onLoadProgress:Function;
	public var onLoadStart:Function;

	///// constructor /////
	public function SimpleDepthManager() {
		// init our depth counter
		_depth = 1;
		// init our mc count, this is just for instance naming purposes
		_count = 0;
		// init our mcl
		_mcl = new MovieClipLoader();
		_mcl.addListener( this );
	}// end: constructor

	/**
	 * Creates and returns an empty movie clip
	 */
	public function createClip() : MovieClip {
		validateDepth();
		var mc:MovieClip = this.createEmptyMovieClip( getInstanceName(), _depth );
		return mc;
	}// end: createClip

	/**
	 * Attaches a symbol from the library and returns the new instance
	 */
	public function attachClip( id:String, init:Object ) : MovieClip {
		validateDepth();
		var mc:MovieClip = this.attachMovie( id, getInstanceName(), _depth, init );
		return mc;
	}// end: attachClip

	/**
	 * Creates an empty movie clip and loads the specified clip into it.
	 */
	public function loadClip( url:String ) : MovieClip {
		var mc:MovieClip = this.createClip();
		_mcl.loadClip( url, mc );
		return mc;
	}// end: loadClip

	private function removeClipAt( d:Number ) : Boolean {
		var mc:MovieClip = this.getInstanceAtDepth( d );
		if( mc == undefined )
			return false;
		mc.removeMovieClip();
		// and save our new target depth
		if( d = MAX_DEPTH )
			throw new Error( "Unable to find a valid new depth." );
		trace( "[debug] _depth = "+_depth );
	}// end: validateDepth
}// end: class

the explanation

So, what does it do? Well, it extends MovieClip, first off. So in order to use it, you should either extend it further yourself or simply specify the class as the code behind your container MC.

The class provides the following simple methods for dealing with clips:

createClip()
Create and return an empty movie clip.
attachClip( id:String, [init:Object] )
Attach a symbol of the specified linkage id from the library, passing it an optional init object.
loadClip( url:String )
Use an internal MovieClipLoader to load an external swf or image into a new movie clip. If you want feedback from the loading process, simply set listener functions on your instance of the manager object as if it were the MCL itself (ie, onLoadComplete, onLoadProgress, etc…).
removeClip( mc:MovieClip )
And remove a movie clip.

You never have to look at instance id’s or depth numbers when using this. I intentionally used different method names (in stead of overriding the default behaviors of the existing Movie Clip management functions) in order to leave you the freedom to use the old methods if you ever need to.

I attempt to recycle old depths once you free them – the manager will always attempt to use the lowest depth that it knows about. If, for some reason, it fails to find a free depth into which to plunk something after 1000 attempts, the manager will fall back and call getNextHighestDepth().

the license

This class is made available under the Creative Commons Attribution 3.0 License. Basically, it means that you can do with it what you will, so long as you credit me for it.

Actionscript suffers like many other languages from poor string parsing commands. What would take one line of well crafted perl is… a bit more complex here.


public function hex2dec( hex:String ) : String {
	var bytes:Array = [];
	while( hex.length > 2 ) {
		var byte:String = hex.substr( -2 );
		hex = hex.substr(0, hex.length-2 );
		bytes.splice( 0, 0, int("0x"+byte) );
	}
	return bytes.join(" ");
}

private function d2h( d:int ) : String {
	var c:Array = [ '0', '1', '2', '3', '4', '5', '6', '7', '8',
			'9', 'A', 'B', 'C', 'D', 'E', 'F' ];
	if( d > 255 ) d = 255;
	var l:int = d / 16;
	var r:int = d % 16;
	return c[l]+c[r];
}

public function dec2hex( dec:String ) : String {
	var hex:String = "0x";
	var bytes:Array = dec.split(" ");
	for( var i:int = 0; i < bytes.length; i++ )
		hex += d2h( int(bytes[i]) );
	return hex;
}

What do these methods do? Well, hex2dec takes a hexadecimal string (and assumes you have a throwaway prefix of “0x” or “#”) and returns a space-separated list of decimal values. dec2hex does the same thing, but in reverse.

hex2dec("0xF00F04") returns “240 15 4″
dec2hex("240 15 4") returns “0xF00F04″

Also, the method doesn’t much care if you’ve got 6 hex digits or 60.

There are a few optimizations I could make on this code here, but it’s not getting called that frequently anyway. The performance gains in my application would be trivial.

And just by way of warning, this is AS3 code. It’d need a few tweaks to work reliably in AS2 or (shudder) AS1 environments. Actually, I should make a standard boilerplate for here since I’m only ever really going to present AS3.

Testing. Testing.

This post is a placeholder because WordPress is bugged and incapable of dealing with the notion of a brand new blog that has yet to receive any real posts. I’ll get around to writing something soon. Probably.

See… I’ve been writing software for a while now. I’ve even put my hand to the odd PHP/MySQL system before. And this sort of bug really annoys me. WordPress is a great application. I use it to host my personal web site. Yet… they’ve never gotten around to patching this issue. I understand that it is an edge case (any blog w/o posts is either going to have a post soon or has been abandoned), but that’s no excuse.

Pseudo-solution:


$query = "select * from wp_posts";
$result = mysql_query($query);
if( mysql_num_rows($result) > 0 ) {
    render_the_site_as_normal();
} else {
    // complain_and_throw_an_error();
    render_placeholder_because_there_are_no_posts_yet();
}

Notice my clever swapping of the “file not found” type error screen for a useful message? Yeah. It honestly is that simple.

 

July 2009
S M T W T F S
« May    
 1234
567891011
12131415161718
19202122232425
262728293031