This website uses cookies to improve user experience. By using our website you consent to all cookies in accordance with our Cookie Policy. X

Understanding the "Graphics" symbol

Anyone who already spent some quality time in ActionScript is no doubt fully aware what MovieClips do or how they work, however Flash also allows for creation of something very similar, the symbols called "Graphics". At first they might seem like a simplified versions of MovieClips, which is of course true but their existence relies on much simpler rule... during the exporting process all "Graphics" symbols are split into pieces, similar to how the "break apart" command (CLTR+B) does it, with only difference being that all properties and tweens are applied to objects within. From the ActionScript's perspective this gives us an interesting ways of optimization by removing excessively nested MovieClips.
For example lets take a scenario where we want two objects to be circling around each other and also at the same time both of them smoothly relocated to a new position:

So the Anim symbol contains the animation of two circling objects, while on the stage both of them are being moved to a new location. Nothing interesting here yet, but if we decide we want access to both of those objects from ActionScript instinctively the first step will be to switch Anim from "Graphics" into "MovieClip" and than followed by giving it an instance name so we can navigate through it to access the two objects we want. Which is exactly the nesting we can avoid by using the Graphics - of course if we know how they work.
I suggest experimenting with this by yourself, starting by creating two MovieClips on the stage with two different key-frames within them, then name them "mc1" and "mc2" (instance names, so you can access them from ActionScript) and finally enclose the whole thing inside a Graphics symbol. What will happen if we call the following script from the Stage's timeline?
Because the Graphics symbol will be broken apart with MovieClips "mc1" and "mc2" being thrown outside, the script will execute correctly.
A ready example for download:

Strict comparison to a class type without the use of 'is' or 'instanceof'

In ActionScript2 and 3 you can always check if an object belongs to a certain class - in AS2 the keyword for this is instanceof while in AS3 its is and both of them do a pretty fine job, however they also return 'true' when checking a base class against a subclasses. In other words MovieClip check against Sprite will get us 'true':
var mc:MovieClip = new MovieClip();
trace(mc is Sprite); //true
This raises a question: what if we want to check against a specific class and only that one class? Solutions are two:
1. The getQualifiedClassName function.
Which is the most straightforward answer - we extract the class name as a String and compare it against the name we are searching for.
var mc:MovieClip = new MovieClip();
trace(getQualifiedClassName(mc) == "MovieClip"); //true
trace(getQualifiedClassName(mc) == "Sprite"); //false
2. Comparing the constructors.
This is a bit more advanced topic that only works in AS3. It is not always clearly stated but each object in ActionScript3 has a 'constructor' field which always holds the reference to the constructor used in creation process of an object and because in ActionScript there can be only one constructor, it just a matter of comparing them:
var mc:MovieClip = new MovieClip();
var con:Object = MovieClip.prototype.constructor;
trace(mc.constructor == con); //true
However there are few things worth pointing out here:
- because 'prototype' and 'constructor' are created dynamically, their access time is slower in comparison to the getQualifiedClassName function.
- however with a reasonably long loop and local variable holding the constructor reference, execution time can be even as twice as fast.
- mc.constructor in FlashDevelop (but not Flash Professional) will most likely throw an error, which needs to worked around by casting mc to the Object class:
var con:Object = MovieClip.prototype.constructor;
trace((mc as Object).constructor == con); //true
Unfortunately this will decrease the execution time by around 10%.

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:

That is pretty much it, now the only thing left to do is explain how getTile works step by step:
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).
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.
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.