Debugging in local enviroment [basic]

In previous post I've described some very basic debugging methods for already released products - while we are on topic of debugging, lets have a look at some popular and easy methods for fixing errors in local environment. Of course everything comes down to what we want to do with our application, but there will be always room for those universal tricks:

1. Execution speed time.
Skipping past the fact you should build your application also considering slower computers, speed measuring is a great way to test algorithms, especially if we know how much time they need beforehand. For more prominent results it's usually a good idea to add a loop:

public static function measureProcessTime(fun:Function, repeat:int = 10):Number {
	var time:Number = getTimer();
	for(var i:int = 0; i < repeat; i++) {
		fun();
	}
	return getTimer() - time;
}
Note that local functions are especially useful here as Flash allows their creation pretty much everywhere, allowing us to test any part of the code by simply enclosing it in the following way:
var someVar:Number = 0;
someVar = 1;
function debug() {
	var otherVar:Number = 10;
	someVar += otherVar;
}
trace(measureProcessTime(debug));
}

2. Loop traversing.
This is another neat neat way to test algorithms. Basically the idea here is to display one single point in a loop, but with ability to move that point around. Example implementation:

private static var DEBUG_COUNT:int = 0;
private static var DEBUG_STEP:int = 0;
public static function nextStep():void {
	DEBUG_STEP++;
}
public static function prevStep():void {
	DEBUG_STEP--;
	if(DEBUG_STEP<0) DEBUG_STEP=0;
}
public static function resetDebug():void {
	DEBUG_COUNT = 0;
}
public static function countDebug():Boolean {
	DEBUG_COUNT++;
	return  DEBUG_COUNT == DEBUG_STEP;
}
Functions nextStep and prevStep are meant to be connect to the arrow keys, allowing us to traverse the loop. The resetDebug function should be called before the loop we want to test. Finally the countDebug function which will return true when our loop reaches the selected step. Although it doesn't seem like much, loop traversing can be incomparable debugging tool. Good example of this is a line drawing algorithm:
(Use the up and down arrow keys to move around the loop)

3. Graphics overlay.
I think no one will disagree it's easier to see data in graphic representation rather than list of variables, which is why every application should always have some kind of way to draw (using Graphics class) information above every other object. This can be easily done by holding the main application in a "container" MovieClip, while having a single Sprite one step above it, which will be empty but accessible from anywhere in the application:
public static debug:Sprite;
public function DocumentClass() {
	var main:MyApplication = new MyApplication();
	addChild(main);

	var debug:Sprite = new Sprite();
	addChild(debug);
}
Now we just need to write DocumentClass.debug.graphics to start drawing over everything else. Personally I like to use it as a way to check hit-boxes of objects on the screen:

Debugging on user side [basic]

{EAV:22fbf37c62ace31e} Debugging is a popular term used to describe a process of removing errors (or any kind of unwanted behavior) from a source code. Developing in Flash Professional or FlashDevelop we already have an access to pretty good debugging tool, which no doubt helped all of us fix at least few problems. Unfortunately debugger can only used by the developers themselves and they can not always foreseeing every possible scenario users might find themselves in. Which is why it is always a good idea to implement a mechanism for catching any errors from the user side - lets have a look at the most popular ones.

1. Main Loop.
This, what might seem to be a trivial thing, should be a part of every self-respected applications and not only because it makes debugging a much easier process, but also helps avoid memory leaks and makes whole code easier to read. But what exactly is a "main loop"? In Flash it basically comes down to having only ONE "ENTER_FRAME" event in whole application, which means every other function will executed directly (or indirectly) from the main loop. Such approach will make catching errors a trivial task, as it only requires a simple try and catch around loop's code, ensuring we always stay in control. Here is an example of a main loop:

public function render(e:Event):void {
	try {
		processPlayer();
		processEnemy();
		processMap();
	} catch(e:Error) {
		trace("Error!");
	}
}

2. Log.
Even though catching errors is very important, they might useless without information about circumstances that surround them, which is why a log that details all key functions used by the user is second most important element in every application. Writing down user status (Flash version, user's operating system etc.), button they press or windows they opened, or in case of games, what map they are on, what is their progress and events they already completed will help us recreate the scenario in which user encountered the problem.
Log class is always the easiest one to implement:

public class Log {
	private static var log:String = "";
	public function Log() {}
	public static function add(s:String):void {
		log += s+"n";
	}
	public static function toClipboard():void {
		System.setClipboard(log);
	}
}

3. Console.
Most likely there is no PC gamer didn't ever use a console, even if it only was to enable the "god mode". By console I mean a (usually hidden) applications commend line, which allows every user to fire a predefined functions at any given moment. Implementation of a good console requires a good deal of work, but luckily there is already a nice selection of ready solutions on the Internet, like for example: Flash Console. Everyone should try out creating a console for their application - one time is enough to fall in love for them.

Finally, when our application is ready, it is a good idea to replace the trace function in the main loop with something that will help us receive any possible error's information, by for example sending an e-mail.

Happy Easter!

Today I only have some updates regarding the blog:
First of all, the hosting company has been upgrading their servers make this blog almost completely inaccessible throughout the whole passing week, but it should be alright now.
As for why I don't have any updates this time around, it is because I've been working on something special - a mobile version of the blog. In other words lighter and (hopefully) faster version of the whole site dedicated specifically to users browsing on mobile devices. For anyone interested the lighter version is available here: 4as.pl/mobile but all mobile users are automatically redirected there upon entering the blog. Also, just a quick note, it still needs some polish.

Basics of tile-based engine part 3: line of sight [advanced]

So we already have isometric view and path finding topic behind us, now it is time for the third and final part: field of view. Basically it usually comes down to checking whether there is a direct connection between two points (for example a player character and an enemy), without anything blocking the line. More often than not, it is usually quite a complicated issue, but luckily not in tile-based engines -we just need a line drawing algorithm. Why is that? Line drawing algorithms start at one point and move toward another one pixel by pixel, until they form a line. Now if we were to replace the drawing function with one checking the tiles, the result would be an algorithm that step by step checks a line in between two points for some kind of obstacles.
That said, we first need an algorithm - personally I recommend one from Wikipedia: Bresenham Algorithm. Implemented in ActionScript it might look like this:
function isInSight(tiles:Vector.<Vector.<int>>,x1:Number, y1:Number, x2:Number, y2:Number):Boolean {
	var dx:Number;
	var dy:Number;
	if (x1>x2) {
		dy = y1;
		y1 = y2;
		y2 = dy;
		dx = x1;
		x1 = x2;
		x2 = dx;
	}
	dx = x2-x1;
	dy = y2 - y1;
			
	var y:Number;
	var x:Number;
	var m:Number;
	if (Math.abs(dx)>Math.abs(dy)) {
		m = dy/dx;
		y = y1;
		for (x=x1; x<x2; x++) {
			if(tiles[x][y] == 1) return false;
			y = y+m;
		}
	} else {
		m = dx/dy;
		x = x1;
		if (y1<y2) {
			for (y = y1; y < y2; y++) {
				if(tiles[x][y] == 1) return false;
				x = x+m;
			}
		} else {
			for (y = y1; y > y2; y--) {
				if(tiles[x][y] == 1) return false;
				x = x-m;
			}
		}
	}
	
	if(tiles[x2][y2] == 1) return false;
	return true;
}
The isInSight function will return false when on the way from point 1 to point 2 there will be a tile marked as 1 in two-dimensional array tiles.
Easy. And the best part is that most line drawing algorithms are quite fast making this solution viable even in really big maps.

Basics of tile-based engine part 2: path finding [advanced]

In the previous part I've talking a little about isometric view and how to achieve it a simplest way possible. This time around, just as promised, we will have a look at path finding in tile-based engine using one of the easiest methods available (but not the fastest). I am not really sure if the algorithm presented here has any specific name, but without a doubt it is the most direct way out of all possible solutions.
For all of you that are not interested in details I've prepared a class built around the whole algorithm, plus also a FLA file showing how to work with it: PathFind.zip
Running it will look like this:
(Pick a starting and an ending point but clicking any empty field. SPACE to create different map)

The idea behind the algorithm is quite simple and can be divided into two phases:
1. Target search. Starting for on of the chosen points we check, step by step, every tile in the neighborhood in search of the second specified point, while constantly saving current iteration step into the grid. These numbers are needed in the next phase.
Here is a code snippet from the example:

// ...
var vWrite:Vector.<Vector.<int>> = recreateTiles(tiles);
vWrite[startx][starty] = 1;
vWrite[endx][endy] = -1;
var vCheck:Vector.<Point>;
var vRead.<Point> = new Vector.<Point>();
			
var tile:Point;
var nStep:int = 2;
vRead.push( new Point(startx,starty) );
while(vRead.length != 0) {
				
	vCheck = vRead;
	vRead = new Vector.<Point>();
	for each(tile in vCheck) if(lookupTile(tile.x, tile.y, nStep, vRead,vWrite,vTile)) {
		return retracePath(tile.x,tile.y,nStep,vWrite);
	}
	nStep ++;
}
return null;
// ...
The lookupTile function looks for a next tile to be processed. If the end destination is anywhere in the 4 directions from the current tile, the loop ends and a found path is returned. In other cases vWrite is updated to hold new information, while any found tile is written into vRead to be processed in next iteration.
2. Path backtracking. Once we find our destination it is time to backtrack along the numbers saved in vWrite ,one by one to retrieve our path. Lets say the iteration stopped at 20 step - now we have start searching for the tile containing number 19, then 18, 17, and so on. Untill we reach number 1 which is the starting point (0 is used for tiles that were not checked).

Just a small note to finish this off: the algorithm can be speed up by executing it simultaneously on both starting and ending point, which means the path will be found when both of them meet each other half way through. This method was used in the attached example.
Next time we will have a look at the idea of "field of vision" in tile-based engines.

Basics of tile-based engine part 1: isometric view [basic]

In this three-part article I will try to explain some of basic problems one might stumble upon when working with tile-based engines. Since they are quite old one might think that the topic was already exhausted, however many people still have problems with it, often complicating trivial issues.
One of those issues is isometric view (sometimes might be called "diagonal view") which a specific type of 3D transformation without the perspective and was often used in older strategy games. Although the definition mentions 3D transformation it is actually not required at all and can be replaced with something much simpler, which unfortunately not many people try to do, resulting in many articles like this one focusing on basics of 3D - unnecessarily.
Basically you could sum everything to this simple function:
function getTile(nTargetX:Number,nTargetY:Number,nWidth:Number,nHeight:Number):Point {
	var nScale:Number = nWidth/nHeight;
	var nTransY:Number = (nScale*nTargetY-nTargetX)/2;
	var nTransX:Number = nTargetX+nTransY;
	var nTmpY:Number = Math.round( nTransY/(nHeight*nScale) );
	var nTmpX:Number = Math.round( nTransX/nWidth );	
	var nTileX:Number = (nTmpX-nTmpY)*nWidth;
	var nTileY:Number = (nTmpX+nTmpY)*nHeight;
	return new Point(nTileX,nTileY);
}
Which results in this:
(Green circle is the Point returned by the getTile function)
Try it yourself: tile_example.zip

That is pretty much it, now the only thing left to do is explain how getTile works step by step:
1.
var nScale:Number = nWidth/nHeight;
Scale will be used during the transformation to realign the difference between height and width (as the algorithm works only in 1:1 scale).
2.
var nTransY:Number = (nScale*nTargetY-nTargetX)/2;
var nTransX:Number = nTargetX+nTransY;

Here is where a point on the screen gets transformed into a point in the isometric view.
Usually when we are talking about an isometric view, we are thinking about a space that is rotated by 45 degrees in such way that movement to the right is not a simple +X, but rather some value of X plus partly also some Y. The example will probably make more sense:

As we can see the movement of the mouse cursor to the right is transformed into a point that is only partially moved to the right, because partially it is also moved upward. Armed with this knowledge we can try and write a general equation - since movement to the right (+X) gives a bit of upward movement (-Y) we can say that Y gets smaller along with X getting bigger, or in other words: Y = -nTargetX. However we have to remember it is not a straightforward transformation x=>y (otherwise it would result in 90 degrees turn), which is why have to divide the equation by 2 (as 90 degrees divided by 2 gives us 45... in a great simplification). Now that we know Y, getting X is quite trivial as we only need to subtract the Y value from target X (so the distance remains the same as the one before the transformation) and since Y is already negative we need to use the addition Y = nTargetX+nTransY;. By the way, you can switch plus and minus signs between each other to achieve a -45 degrees rotation.
3.
var nTmpY:Number = Math.round( nTransY/(nHeight*nScale) );
var nTmpX:Number = Math.round( nTransX/nWidth );
var nTileX:Number = (nTmpX-nTmpY)*nWidth;
var nTileY:Number = (nTmpX+nTmpY)*nHeight;

Here we are translating given point into a tile, by "shrinking" the whole space using width and height values, then discarding any numbers after the comma and finally bringing everything back to "normal size". Because I love examples here is another one: if tile #0 is 20 pixels width, then every pixel between 0 and 20 will belong to it. So now the question is: how to check which tile owns the seventh pixel? Lets take that 7 and divide it by 20, resulting in 0.35. Now we discard everything after the comma and get our tile with id #0. Lets re-try that for pixel 27; 27/20 = 1.35; drop the comma stuff and we get #1, which is correct because as we already know 27 doesn't belong to #0.

Phew, that is it for today. In two weeks I will write about a very simple path finding algorithm you could implement in a tile-based engine.

Preloaders in Flash [basic]

"Preloading" is simple idea that dates back to the beginnings of the Internet - perhaps even further than that. It is a process of moving all of the applications data or multimedia into a computer's memory, so it can be accessed smoothly without breaks for additional reading. Same goes for Flash, with preloading we will be certain our animations and whatnot won't turn into a slide-show.
It sound simple enough, but implementation of a preloader into Flash can sometimes be quite tricky. Generally speaking, it all comes down to 3 solutions depending on what we need:

1. For animations.
Movies done in Flash that use almost no ActionScript are the easiest target to include a preloader (since there will be nothing to interfere with it), just copy and paste a ready example.
One of the simplest requires a single MovieClip in the very first frame of the main timeline with instance name set to "preloader" (in properties tab) along with this code pasted in to the same frame:

import flash.events.Event;
import flash.events.ProgressEvent;

stop();
loaderInfo.addEventListener(ProgressEvent.PROGRESS,onProgress);
loaderInfo.addEventListener(Event.COMPLETE,onLoaded);

function onProgress(e:ProgressEvent):void {
	var percent:Number = loaderInfo.bytesLoaded/loaderInfo.bytesTotal;
	preloader.gotoAndStop( int(percent*preloader.totalFrames)+1);
}
function onLoaded(e:Event):void {
	loaderInfo.removeEventListener(ProgressEvent.PROGRESS,onProgress);
	loaderInfo.removeEventListener(Event.COMPLETE,onLoaded);
	//moves to a designated frame in the animation after the loading process
	gotoAndPlay(2);
}
The "preloader" MovieClip will have its animation stop on frame number that is equal to percent amount of content loaded, so if the "preloader" has total of 10 frames and the move itself is loaded to, for example 20%, the frame displayed will be 2 (10*20% = 2).
Ready to use example: preloaderSimple.zip

2. For applications created in Flash Professional.
Here it gets a bit tougher. When creating project that heavily use ActionScript people don't usually think about preloaders first and foremost, which actually isn't a bad thing but might bring some complications, especially when exporting to the first frame.
Speaking of exporting to the first frame, lets take a moment to look at this function. When exporting graphics to ActionScript we have an option to "export in 1 frame", which does exactly what it says and puts all of the graphics in the first frame of main timeline - the trouble is, this leaves us with no room to later insert a preloader. In Flash Professional this can be dealt in two ways:
- by changing a designated frame in ActionScript's properties

- or by simply putting the graphics manually on the stage in proper frame. Lets, for example, take a class that makes use of a exported graphic on frame 3 in the main timeline, allowing us to put them on frame 2 (even outside of the screen if needed) while having the "export to 1 frame" opetion turned off.

3. For applications created in FlashDevelop.
… or any other development environment that loads SWC files. I'm referring here to external ActionScript libraries that may or may not come packed with graphics or other multimedia. Those multimedia elements are loaded as soon as there is something referencing the classes they are linked with, which basically means any kind of import from SWC will instantly be blocked till the graphics gets loaded into the memory. On the other hands omitting those imports simply means no SWC class will be compiled into the application. This begs a question: how can something be included along the application without actually importing it? There is only one answer and it was found by Keith Peters at: www.bit-101.com. Long story short, this brilliant trick can be summed down to a single line: [Frame(factoryClass="Preloader")] ("Preloader" is the name of the class that will server as a preloader), which has to be put right before documents class definition (but after all the imports) - called, for example: "Main". Main will get loaded but not initialized because this role will be taken over by the Preloader.
Unfortunately there is still a matter of instancing Main after the loading process, since we still can use imports - we import Main, Main imports something else, which in turn imports something else and sooner or later there will come along a class that imports straight from the SWC file, blocking our application the graphics gets loaded. Fear not however as flash.utils.getDefinitionByName comes to our aid being used in the following way:

var mainClass:Class = getDefinitionByName("Main") as Class;
if(mainClass) {
	addChild(new mainClass() as DisplayObject); 
}
WARNING! The getDefinitionByName() function may from time to time return the NULL value, meaning ENTER_FRAME Event might be good idea to allow for further retries in later times.
As per usual, here is an example code: preloader.zip
And in action it can be viewed here: Preloader.html

Communication between AS2 and AS3[basic]

As soon as ActionScript3 saw the light of the day Adobe instantly pointed out that the virtual machine behind it is completely different than the one used in AS2, which in turn means there is no direct way of communicating between those two languages. Right now someone might think that if that is the case, then there is no point in trying, topic closed. But wait a second, no direct connection doesn't equal to no possible connection at all. Let me put it this way: lets take MySQL for example, Flash doesn't come packed with any means to connect to it directly, yet it doesn't stop us anyway, thanks to PHP (or other methods). Which brings us to the heart of the matter: indirect connection. In case of AS3 and AS2 it is even simpler than using PHP, because both of them include a class for communication between local SWF files - LocalConnection is its name.
Main purpose of LocalConnection is to communicate between two Flash applications running on the same computer, however since its implementation did not change at all in AS3, we can also use it to establish a connection between AS2 and AS3.
Creating a LocalConnection is very easy and almost identical for both languages. For ActionScript2 it will be:
var connectionAS2:LocalConnection = new LocalConnection();
connectionAS2.connect("LocalConnectionTestAS2");
And for ActionScript3:
var connectionAS3:LocalConnection = new LocalConnection();
connectionAS3.client = this;
connectionAS3.connect("LocalConnectionTestAS3");
connectionAS3.addEventListener(StatusEvent.STATUS,onStatus);
function onStatus(e:Event):void { }
AS2 version is quite trivial, however AS3 one requires some explanation. First of all, the line that says connectionAS3.client = this; is very important as it points to object that will handle the incoming function calls, without it the application will simply not work. StatusEvent is added to the LocalConnection only to stop Flash from throwing any errors before the connection is established.
Oh and texts put into the connect() functions are different to allow for bidirectional communication.
Okay, now that we have AS2 and AS3 ready to exchange commands, it is time to create function that will send them. Also, to get a better feedback on the whole process we will include TextFields to display the information. For ActionScript3 it will be:
var tf:TextField = new TextField();
tf.text = "No Messages";
addChild(tf);

function messageFromAS2(msg:String,nx:Number,ny:Number):void {
	tf.text = msg+" "+nx+" "+ny;
	tf.x = nx;
	tf.y = ny;
}
While for ActionScript2 it will be:
this.createTextField("textMsg",this.getNextHighestDepth(),0,0,200,25);
var tf:TextField = this.textMsg;
tf.text = "No Messages";

connectionAS2.messageFromAS3 = function(msg:String,nx:Number,ny:Number):Void {
	tf.text = msg+" "+nx+" "+ny;
	tf._x = nx;
	tf._y = ny;
}
One last thing remaining now is to fire up those functions - Mouse Events will do the trick. For ActionScript3:
stage.addEventListener(MouseEvent.MOUSE_MOVE,onMove);
function onMove(e:MouseEvent):void {
	try {
		connectionAS3.send("LocalConnectionTestAS2","messageFromAS3","AS3 MouseEvent",e.stageX,e.stageY);
	} catch(e:Error) {
		return;
	}
}
For ActionScript2:
this.onMouseMove = function():Void {
	try {
		connectionAS2.send("LocalConnectionTestAS3","messageFromAS2","AS2 Mouse Event",_root._xmouse,_root._ymouse);
	} catch(e) {
		return;
	}
}
In both cases try-catch is used to stop Flash from throwing errors before the connection is established.
In action, the whole thing will look like this:
Source code: localcon.zip

By the way, if you ever find yourself in a situation where you need to access an AS2 file that does not support LocalConnections and can't be modified, I suggest creating a separate SWF file in ActionScript2, then loading (loadMovie() function) the target file to read all the necessary information and send them further down the line wherever you like using LocalConnection.

Top 5 things that kill Flash's framerate.

Without a doubt most of us heard that Flash might be slow at times and even thought with each version it gets faster and faster, there are still games out there that might bring even most powerful PC to its knees. Surprisingly enough it might not necessarily be the programmers fault, but actually those inconspicuous graphic designers doing - that's right, ActionScript (especially 3) is already so fast, its contribution to the slowdowns might be marginal compered to some graphically intensive designs. That is why it is time to give those programmers a break and check if we accidentally aren't using any of the 5 most demanding graphics elements:
1. Filters.
Undisputed winner in Flash slow downs, one unfortunately placed filter can turn a smooth animation into a slide show. Not to get into too much details, any MovieClip that has a filter applied to it stops being a vector graphic and is instead turned into a bitmap - with bitmap itself not being a problem but rather the process of creating one. Which is why you need to remember those two things when working with bitmaps:
- avoid bulky graphics (no bigger than average website banner)
- avoid filtered animations (for reasons stated below in: "cache as bitmap")
2. Cache as bitmap.
I must say bitmaps in Flash are quite intriguing; for example lets take simple a red rectangle drawn in vector graphic and in a bitmap - in this case Flash will display the vector one much faster than the bitmap one. However, start filling that rectangle with decorations, patterns or gradients and soon enough it will turn out that the bitmap solution is the faster one. Where lies the boundary? Well, that is something everyone should find out on their own, but generally just adding a simple gradient makes for a good reason to go with the "cache as bitmap" option. Unfortunately, every rose has its thorn... bitmaps can bring great speedups but also massive slowdowns, as even a simple transformation (changing colors, scaling, rotation) might bring devastating results. So when working with bitmaps always remember:
- cache as bitmap is completely pointless on animated MovieClips.
- bitmaps and cached MovieClips are very slow with any kind of transformations.
- if you must, it is much better to transform a real bitmap rather than a MovieClip with "cache as bitmap".
3. Alpha (transparency).
Yeah I know, "give up on transparency? Forget it!" Even so there is a good reason why it takes a third place on this list - this silent assassin doubles the amount amount of time needed to draw a pixel on the screen for each transparent bit. So whenever possible, instead of yet again making another semi-transparent shape to serve as a shadow, actually take a moment to really shade the background graphics.
4. Excessive amount of MovieClips.
It is very easy to get accustom to the possibilities that MovieClips provide, since they are so easy to swap around, edit without affecting outside elements and most importantly clone to bring infinite details into scenes. Of course those features don't come without a price and we have to pay with performance. Each MovieClip on a scene added not only new graphics but also ton of parameters: position, scale, filters, colors, name, visibility, caching etc. and even thought you might not be using them at all, all of them are processed on each frame of animation. Whenever possible try to replaces MovieClips with Graphic objects (or better yet just Shapes).
5. Hidden objects.
In other words everything that we don't, but it is still there. The worst case example of this is masking, as everything under them is still rendered on the screen but afterwards only cut down to fit the designed boundaries . The exact same thing happens with Flash's display area, which also doesn't remove anything, which means every object on the scene impacts the performance the exact same way, no matter on screen or not.

Seek Bar in Flash [basic]

Happy New Year 2012!

Often on the Internet I see people asking how to make a seek bar in a Flash animation (similar to how other video players do it, such as the one used on YouTube) however in the end not many people go through with it - why is that? The answer lays in how Flash handles MovieClips, which is why before I go any further I will take a moment to write few words about them.
It might be hard to visualize it, but each MovieClip is a self contained movie with its own time-line (and settings) and is controlled completely independently of any other objects, which can be seen below (pick any moment, right click and uncheck "play"):

As one might expect the whole animation will be instantly stopped... except for the ball which is a MovieClip. No point in explaining why is that - it just is and will never change. Only thing left to do is to look for alternative solutions, and because stopping all the MovieClips will directly help us with creation of a seek bar it is best to look at two possible workarounds:
1. Remove all the graphics in your animation.
2. Manually stop all the MovieClips with ActionScript.
First solution is the best way to go, as long as you are just starting the creation of your movie, because it is always easier to prepare it beforehand than later convert all the MovieClips into Graphic objects. Afterwards moving around the animation will only require a single call to the gotoAndPlay function, which is exactly what we need for our seek bar. Second solution is only viable in animations with small amount of MovieClips, because seeking and stopping the MovieClips might be a bit too stressful for the processor, especially in the case of a seek bar since it requires a scan through the whole movie. Oh well, sometimes you just don't have a choice.
Code that stops all MovieClip is actually quite simple:
stopMovies(root as MovieClip);
function stopMovies(mc:MovieClip):void {
	for(var i:int = 0; i<mc.numChildren; i++) {
		var d:DisplayObject = mc.getChildAt(i);
		if(d is MovieClip) {
			(d as MovieClip).stop();
			stopMovies(d as MovieClip);
		}
	}
}
Now one might think that the only thing missing here is a gotoAndPlay function and we are set. Unfortunately it is not that simple, as each MovieClip has its own time-line, in will never react to changes in the main time-line (contrary to Graphic objects). Furthermore, replacing every stop function with gotoAndPlay is also pointless as having the main animation on 10th frame does not necessarily mean that each MovieClip is also on the 10th frame - it all comes down to the fact that MovieClip appearing on the, for example, 6th frame, on 10th one will be in 4th frame of its own time-line. It is best to see this in action - animation below has a MovieClip (with 20 frames) appearing on the 10th frame (all of them numbered):
While scrolling, the MovieClip appears to have a mind of his own.
Okay, but why not take the appearing position of the MovieClip and subtract it from the current position? Lets say we want to go to the frame #15 with the MovieClip appearing on the 10th one, so we just take 10 and subtract it from 15 resulting in gotoAndPlay(5) - main time-line stays on 15 while our MovieClip is re-winded to the 5th frame. This is a great idea and perfect solution, but unfortunately Flash has no way of telling us what is the MovieClip's appearing frame. Here is where the challenge for the processor I was talking about earlier comes in, as the next step would be to write down all the starting positions of each MovieClip; which isn't as easy as it sounds, because an animation that takes, for example, 1 minute to play out and is displayed at 20 frames per second, will have 1200 frames worth of scanning! Plus, lets say, 10 MovieClips per frame... Least to say, it is not a very useful solution, which is why for the rest of this article I will assume you are using only Graphic objects.
That is it for theory, time to create our seek bar - taking the bare minimum, we will need MovieClips: a bar representing the length of our whole animation and a marker showing the current position. To make this easier put both of them in a time-line of separate MovieClip (the whole seek bar will be in one place making it easier to, for example, move it between projects) and name them mcBar and mcMarker (instance name). Finally put this code into the time-line of the enclosing MovieClip (one holding the bar and the marker):
import flash.events.MouseEvent;
import flash.events.Event;
import flash.display.MovieClip;

var bSeek:Boolean = false;
var nLength:Number = mcBar.width-mcMarker.width;
mcMarker.mouseEnabled = false;
mcMarker.mouseChildren = false;

if( !mcBar.hasEventListener(MouseEvent.MOUSE_DOWN) ) {
	//to prevent unnecessary duplications on each loop
	mcBar.addEventListener(MouseEvent.MOUSE_DOWN,onSeekStart);
	mcBar.stage.addEventListener(MouseEvent.MOUSE_MOVE,onSeekDrag);
	mcBar.stage.addEventListener(MouseEvent.MOUSE_UP,onSeekStop);
	mcBar.stage.addEventListener(Event.ENTER_FRAME,onRender);
}

function onRender(e:Event):void {
	if(bSeek) return;
	mcMarker.x = ((root as MovieClip).currentFrame/(root as MovieClip).totalFrames)*nLength;
}
function onSeekStart(e:MouseEvent):void {
	bSeek = true;
	onSeekDrag(e);
}
function onSeekDrag(e:MouseEvent):void {
	if(!bSeek) return;
	
	mcMarker.x = mcBar.mouseX;
	if(mcMarker.x > nLength) {
		mcMarker.x = nLength;
	} else if(mcMarker.x<0) {
		mcMarker.x = 0;
	}
	
	var frame:int = int((mcMarker.x/nLength)*(root as MovieClip).totalFrames);
	
	(root as MovieClip).gotoAndStop(frame);
	mcMarker.x = (frame/(root as MovieClip).totalFrames)*nLength;
}
function onSeekStop(e:MouseEvent):void {
	bSeek = false;
	(root as MovieClip).play();
}
Nothing particularly complicated here - clicking on the seek bar moves the marker there and translates its X coordinate into the animation's position.
The whole code + an example how to use it can be download from here: seekbar.zip
And for anyone who wants to try the MovieClip scanning solution I prepared a special class and an example to go with it: seekbarEX.zip. In this case, to avoide conflicts with the animation itself, MovieClip which holds both the marker and the bar is exported for ActionScript with ForusSeekBar class.


Older posts>>>