ActionScript tutorial

a.k.a. How AkiRoss does Flash on linux.

Well, let's start saying that I'll use only FLOSS software, since this will suffice. And let's continue saying that often you don't need a complete environment to do a Flash animation. And let's end saying that - since I'm a proud programmer - I prefer to script and write instead of using mouse (oh well, i don't dislike it, but coding is just funnier).
So, here is my objective - i.e. what we'll go through this tutorial: using OpenSource linux-aviable scripting tools to create animated and dynamical SWF content.
Last but not least: this is not a programming tutorial: I'll talk about methods, functions, object, and so on. Here I won't explain these concepts, just use them.

Warning I am not a Flash/ming/php/ActionScript experienced programmer. This content is made for my own personal interest and use. I publish it because I want comments and opinion from experienced users. So if you're a noob that want some helps, just read this paper, and ask someone else and search more on google. Here you find everything I know about this topic, so if you can't find an answer here, don't ask me. I can't reply.

Tools

To begin, it's a good idea to say which tool we are using: Actually, I'm using only php+ming to generate SWF, just because it's faster to put the ActionScript (or AS) in a single php page (modity-refresh) instead of using mtasc which need a separate compilation of the ActionScript source. (modify-compile-refresh).
If you like to create your tool that do anything for you, that use javascript/ajax to read your code directly from the web page and that update your swf in realtime, feel free to do it. It would be nice :) And don't forget to tell me what have you done, so I can link it here.
In dispite of this, I'll show you both the options, and then we'll focus just on the ActionScript 2.0. I'll proceed with the php, but you can do as you wish. It may also happen that we use both ;)

Framework

Before proceeding, I'll show you what I'm using to work. Of course the most important thing is google (or is it the editor? ;). With it I found the official Macromedia ActionScript reference. I'm not linking it since the docs change at each release of the Flash suite. Just go on livedocs.macromedia.com and search for ActionScript reference.

PHP framework

Using php in this way is a bit faster (ehm, i'm talking about production, not code speed) than using mtasc. I use the following php code, in which i insert the actionscript needed for my tests. I think that is obvious, that you need php with ming support to use that :D

<?
    /* Movie parameters */
    ming_useswfversion(7);
    $movie = new SWFMovie();
    $movie->setDimension($movieW, $movieH);
    $movie->setRate(30);

    /* Script section */

    $script = "// Here goes the ActionScript";

    $movie->add(new SWFAction($script));

    /* Return data */
    header('Content-type: application/x-shockwave-flash');
    $movie->output(0);
?>

Using mtasc

If instead you prefer to use mtasc (which is an ActionScript 2.0 compiler), you have to install it (see the website for instructions). Ow... there's nothing special to say, anything you need to know is in the homepage of mtasc. There is a short tutorial that show you the (fiew) options that this compiler have. It's quite simple to use. Note that's a class-oriented environment.
The only advice is about the class path. I'm on gentoo, and the path is /usr/share/ocaml/mtasc/std/. So when I compile using mtask i use:

mtasc -cp /usr/share/ocaml/mtasc/std/ -swf output.swf -main -header width:height:framerate input.as
You can also use a classpath from a macromedia flash installation, just note that you've to copy some files from the std to your classpath. See the mtasc mailing list for further information.

Let's start

Good, we're ready to start. If you know ActionScript you can go alone: bye dude!
Or... you may prefer to stay here with me and learn something (e.g. how little skilled I'm :). Just for your information: I used flash when there was the version 4 and 5 was in relasing process. I don't know how many time is passed, but my ActionScript competances was fiew in that period. Now I've forgot almost everything about that, so i'm starting from scratch... What? Are you saying that I'm crazy? Yes, but I've a brain on my side, so I learn quickly.

The first objective I want to archive is having a clear idea on how thing works. As probabily you know, in flash we have a timeline, it is composed of frames. A rapid sequence of consecutive frames does an animation. Of course. It's logic. Yeah. Next, you may know that in flash there are also objects. It's an Object Oriented event-driven environment. Just like visual basic if you know it :D The mechanism is simple: you have an object, which has properties, methods and events. You can read/write the propreties of the object to change it's aspect, you can call its methods to make it to do something, and you can say that when an even occur, something should happen.
The way seems to be less cloudy, doesn't it? Well, before coding I want to have visibility A++ (which is B, ahahah), so let's go further.
The movie itself is the parent of everything, it's the root element, a movie clip object, referred in ActionScript as _root. When you add something to the scene, you add something to this element. Each movie clip object has it's timeline. It's obvious that the overall timeline is the _root's one.
Oh, maybe i'm running to much. I think that getting in mind how everything is the most difficult part. Once you've mastered it it's clear how we should build our application. Well, as I said there are timelines for each movie clip object. So, if you want to animate something on the screen, you can draw each frame in the root's timeline, and going with it. Else, you may prefer to add another object which have it's own timeline, and base your animation on it.

In Flash, ActionScript allow you to control the timeline, the objects and to write programs that handle everything. So, if you want different objects to compute each something different, you just attach a script to that object, and it's done.
For example: as you know in flash we have buttons. Well, since we do eveything with ActionScript, we need a script in the root that create that button, then we need a script attached to that button to do what you want on the desidered event.
Isn't it so difficult, is it?

Since we have not Flash, AS is used in a different way: there is no timeline, and we have just one frame. To draw an animation, we have to draw the first frame of our animation, then clear the frame, draw the second and so on, or use ActionScript to move and transform the objects on the scene.

I guess that to start, we should try to do something which can help understanding. So, the first and really simple example just draw something static on the screen. Then we'll try to create a simple animation, and finally we'll try to handle it. Since we're not using the Flash drag-n-drop, this is a central part: we are doing something with ActionScript instead of the mouse.
It's futile to say that to master this approach, you need a good ActionScript reference. I'm using the FlashMX 2004 guide, not linked above.

Just something static

First of all, what want we draw? Come on, think about something... Yes? You there, you raised your hand "err, mr Ross, what about a square?", "pfff the same-old-square? I vote... NO! What about a sphere?" The class agree with interest... I'm a great teacher with these non-existing classrooms.

A square is just some lines. A sphere is some circles and some fills. So we need to start with a bit advanced topic: the basic classes in ActionScript don't allow us to draw a circle: the basic shapes are curves! ANYONE can use curves, but I want a circle. So, I search in the reference for the word "circle" finding... No matches :| Damn! I knew I had to study history at college... Action script won't help us with hi-level functions. So let's take our reference and see what the MovieClip class have for us. Looking in the Drawing method summary I see interesting things: all we need for our sphere, and even more (e.g. for the square)

Mmh for our sphere we're interested in *To() and *Fill() methods: we want to draw a filled circle. And the fill must be circular as well.

Since I'm curious, I peek the next pages of the book I'm copying for this tutorial without copyright licensing (I don't need to tell you when I'm joking, right?) and I take a look at the beginGradientFill() method. I read the first line and I already like it: fillType Either the string "linear" or the string "radial". Good, of course we want the radial one.
OUCH! The teacher hit me with his wand: "No peeking, akiross!"... Hey, wasn't me the teacher?!? Anyway, let's proceed with order. First, let's draw that circle... Hey! Nice :D The reference have a circle as example code of the curveTo() method. [I noticed that with ming (I'm using 0.3) the type of the variables gives syntax errors, while with mtasc it doesn't. So I remove all the data types from the following codes since I'm using ming, and actually we don't need strong typing].

_root.lineStyle(0, 0x000000);
drawCircle(_root, 10, 10, 100);

function drawCircle(mc, x, y, r)
{
	var pi8 = Math.PI/8;
	var pi4 = Math.PI/4;

	mc.moveTo(x+r, y);
	mc.curveTo(               r+x,  Math.tan(pi8)*r+y,  Math.sin(pi4)*r+x,  Math.sin(pi4)*r+y);
	mc.curveTo( Math.tan(pi8)*r+x,                r+y,                  x,                r+y);
	mc.curveTo(-Math.tan(pi8)*r+x,                r+y, -Math.sin(pi4)*r+x,  Math.sin(pi4)*r+y);
	mc.curveTo(              -r+x,  Math.tan(pi8)*r+y,               -r+x,                  y);
	mc.curveTo(              -r+x, -Math.tan(pi8)*r+y, -Math.sin(pi4)*r+x, -Math.sin(pi4)*r+y);
	mc.curveTo(-Math.tan(pi8)*r+x,               -r+y,                  x,               -r+y);
	mc.curveTo( Math.tan(pi8)*r+x,               -r+y,  Math.sin(pi4)*r+x, -Math.sin(pi4)*r+y);
	mc.curveTo(               r+x, -Math.tan(pi8)*r+y,                r+x,                  y);
}

Copy it in the template I gave you before, and try it. What? Doesn't it work? Did you set the dimensions of the movie or did you leaved it unchanged? ;) Well I did :| Anyway, this code works and the circle is fine. Thanks to the good reference library, you can read this code and discover many interesting things. Like the the math class and how the curveTo() is used.

Now that we can draw a circle in a more higher level than just curveTo(), let's draw our sphere: we create a fill and draw the sphere.

_root.lineStyle(0, 0x000000);
_root.beginGradientFill("radial",
		[0xff0000, 0x660000],
		[100, 100],
		[0, 255],
		{matrixType:"box", x:30, y:30, w:100, h:100, r:0});
drawCircle(_root, 100, 100, 50);
_root.endFill();

Looking at the beginGradientFill() page you can understand the whole thing. In fiew words it does draw our circle with a radial gradient, with 0xff0000 as light color (inner color), and 0x660000 as dark (outer) color, full alphas, uniform gradient (0 to 255), and a trasformation matrix like: x and y to 30, starting at the same point we used for the circle (that is the origin), with sizes of 100 and no rotation of the gradient.
Well, looks nice, doesn't it? :)

Giving a soul to the object: animating

The drawing task is a simple matter: once you know how to master lines and gradients it's just a mechanical thing (err, at least it's simple to me). Animation need a bit more understanding, also because now we are handling objects.
It's here that we find a difference between the normal "Flash way" and the "ActionScript way", infact in flash we have a timeline which is composed by multiple frames. In each physical frame you can draw objects, put scripts and so on.
Since we're in just a single frame, AS will be just like a normal computer program: we have one frame where we draw, and the animation is done on that frame, changing the drawing at each refresh. Keep in mind that anyway flash animation are frame-based. So to compute an animation we need to create a script that, at each frame, draw what we want. The question is: how we handle the frames? With events, of course. There is an interesting event, that is actived each time a frame is entered. Using it, we can compute a piece of code that draw what we need.
To begin, let's move our sphere in this way: at each frame entered, we draw the sphere again in a different position. We'll put the sphere code in a function, so the sphere drawing is coordinate-indipendent.

function drawSphere(mc, x, y, r)
{
	var grad_center_x = x - 3 * r / 2;
	var grad_center_y = y - 3 * r / 2;

	mc.lineStyle(0, 0x000000);
	mc.beginGradientFill("radial",
		[0xff0000, 0x660000],
		[100, 100],
		[0, 255],
		{matrixType:"box", x:grad_center_x, y:grad_center_y, w:r*2, h:r*2, r:0});
	drawCircle(mc, x, y, r);
	mc.endFill();
}

And now let's move that sphere, attaching the movement code to the enter-frame event.

var x = 10;
var y = 100;

drawSphere(_root, 0, 0, 10);
_root.onEnterFrame = function()
{
	if (x < 190)
	{
		_root.clear(); // We have to clear the frame!
		drawSphere(_root, x++, y, 10);
	}
	else
		x = 10;
};

Note that source: we use 2 variables to keep track of the sphere position, then at each frame we clear the screen and redraw the sphere at a different location. Well, it does the work, and it's clear how it works, but we can do better: instead of drawing all on the root, we create a new object "sphere". We draw once the sphere in that object, then we'll move it. In this way we don't need to clear the frame every refresh, but we just use the object-oriented capabilities of flash.

// We're going to move this clip, not the sphere itself.
this.createEmptyMovieClip("sphere", 2);

// To keep things complicated, the sphere is drawn in a square of 20x20
drawSphere(sphere, 10, 10, 10);

// Then we move the clip to our starting position
sphere._x = 0;
sphere._y = 90;

sphere.onEnterFrame = function()
{
	if (this._x < 180) // Note
		this._x++;
	else
		this._x = 0;
};

Well it's done. As you see, a _root.sphere object is created (but since we're in the root we refer it just as sphere. Then we draw the sphere in it. To help you understand that we're drawing things in a different context, drawSphere() draws the sphere at position (10, 10), relatively the movie clip just created. This means that the center of the sphere, is the point (10, 10) of the movie clip. So, keep in mind that we're moving a square of 20x20, with begins in the top-left corner. Thus it's positioned at (0, 90) instead of (10, 100) like the previous example. And the enter frame function, check that's far away 20 from the right edge (180 instead of 190).

Well, that's easy. What we should look at more carefully is the handling of the frames. Just for the sake of the learning i've added an istruction that stops the animation, when the sphere reach the end of the path:

sphere.onEnterFrame = function()
{
	if (this._x < 180) // Note
		this._x++;
	else
	{
		this._x = 0;
		_root.stop();
	}
};

Well, the ball should stop when it reach the right side of the movie, right? But it doesn't stop! Why? The MovieClip.stop() method should (from the reference) stops the movie clip currently playing. This means that the frames of the timeline won't proceed anymore. The fact is that we're not using the timeline, but just a single frame. Shouldn't the onEnterFrame event stop to be generated, after the calling of stop()? The reference say: invoked repeatedly at the frame rate of the SWF file. This means that anywhere, anytime, the onEnterFrame is called on the current movie framerate. No matter if stop() is called.

Now it seems obvious that to stop an ActionScript animation, we should put a flag somewhere that tell us if the ActionScript should proceed or not, or just remove the function related to the onEnterFrame event. In thi way, the event will continue to occur, but the function to execute will not exist.

sphere.onEnterFrame = function()
{
	if (this._x < 180) // Note
		this._x++;
	else
	{
		this._x = 0;
		delete sphere.onEnterFrame;
	}
};

I got an idea! Did you see the properties of the MovieClip? There are some really interesting, and I'm curious to see their values during an animation. I'm talking about these:

Let's see these values :) In the animation above, we'll add a textfield containing these values. That's not so difficult... you can also use a textfield for debugging your applications. As you know, trace() doesn't work with ming and mtasc (since trace() is made for the Flash IDE). There are some libraries and addons that works just like trace(), but i think that it's easier to add a text field :D

// Add the text
_root.createTextField("text", 3, 10, 10, 180, 50);
_root.text.background = true;
_root.text.backgroundColor = 0xeeeeee;
_root.text.text = _root._currentframe + ", " + _root._totalframes;

The result doesn't surprise too much, but it's interesting: both current and total frames are 1. Immediately it comes in my mind that this is because there is no timeline in our animation. Thus, every function like start() stop() and gotoAndPlay() won't work, since we have just one frame.

Good. Thing looks like more clear. Now let's try something more complicated :) A nice thing of flash animation is that you can create a movie clip, which is animated by itself, and if you put that clip in the scene, it will continue to be animated even if the scene is stopped.
So, we want to archive an animation into an animation, to see how we can control 2 different entities. Let's draw a circle into a circle. We'll make it rotate on itself, and then we'll move the whole around.

_root.createEmptyMovieClip("circle", 2);
drawDoubleCircle(_root.circle, 0, 0, 10); // The circle goes in the middle of the clip

// Let's make that circle rotating
_root.circle.onEnterFrame = function()
{
	this._rotation += 2;
};

// Now let's move arount the circle
this.circle._x = 10;
this.circle._y = 100;
var count = 100;

var left2rightFunction = function()
{
//	_root.text.text = "Left to right";
	if (this.circle._x < 190)
		this.circle._x++;
	else
		_root.onEnterFrame = right2leftFunction;
};

var right2leftFunction = function()
{
//	_root.text.text = "Right to left";
	if (this.circle._x > 10)
		this.circle._x--;
	else
		_root.onEnterFrame = left2rightFunction;
};

_root.onEnterFrame = left2rightFunction;

As you see, there are different handlers for different object. In this way we can handle the enter frame event as we like.

For now that's all, i'll continue with buttons and co. later. For any error or comment, please contact me.