Written by: Billy T | Tableau Flash tutes
Time: 1 hour
Difficulty Level: Beginner
Requirements: Flash MX
Topics Covered: Listeners, attachmovie, duplicatemovieclip,attachSound,hitTest
Assumed knowledge: Very little.

Source File

This tutorial will cover some of the essential actionscript techniques that are required to make games in Flash. Issue 40 (2002) of Computer Arts Magazine is entirely devoted to games in Flash so if this is something you want to learn about - buy that mag.

OK, the topics covered in this tute will include:

  • watching the mouse position
  • watching the keyboard
  • duplicating and attaching movieclips
  • animating movieclips with actionscript
  • collision detection
  • outputting text to a dynamic text box
  • playing sound via actionscript

OK let's get started. Load Flash MX and set the stage size of the blank document to 600X400 (click on the Size button in the Property Inspector to open the Document Properties window). Also, set the frame rate to 31.

In the one and only layer in the timeline, click on frame 1 and enter these actions into the actions panel (make sure the actions panel is in "Expert" mode).

watchMouse = new Object();
watchMouse.onMouseMove = function() {
        trace("The mouse X position is "+_xmouse);
        trace("The mouse Y position is "+_ymouse);
};
Mouse.addListener(watchMouse);

Save your movie as "games.fla" and then choose Control/Test Movie. Move the mouse around and you will see messages appear in the Output window telling you the x and y position of the mouse. How does it work? Well first we create and empty object - watchMouse - which is basically an empty box that you can store actions, properties, variables etc in. We then put something in that box - an onMouseMove action. However, unlike a MovieClip, an Object doesn't automatically keep track of mouse events so we need to tell it to "watch the mouse" with the addListener method. The trace function that gets called when the mouse moves is a handy one to remember. It allows you to output strings (such as "The mouse X position is ") and properties (such as _xmouse) and just about anything else you can think of, making it a very useful debugging tool.

OK so now we have an object that can keep track of where the mouse is at any time. At the moment we are just trace the mouse position which isn't very useful but we will do a little more with it shortly.

For now though, let's do something similar but this time we will watch the computer keyboard to see if any buttons are pressed. Underneath the watchMouse code, paste this

watchKeyBoard = new Object();
watchKeyBoard.onKeyDown = function() {
        trace("a key is pressed. the key that is pressed is "+Key.getAscii());
};
Key.addListener(watchKeyBoard);

It should be pretty obvious what is going on here. Again we create a blank object, assign it an action (onKeyDown) and this time tell it to watch the keyboard. Test your movie again and hit a few keys. You will see some messages appear in the output window telling you that "a key is pressed. the key that is pressed is " followed by a number. Every letter and number on your keyboard has an ASCII value. When you use the getAscii() method like we have above, a number is returned that can be used to identify the key that was pressed. Again, we will do something a little more interesting than the trace with this in a bit.

OK lets move on. name the one and only layer "Actions" and the make a new layer and name it "box". On frame 1 of the box layer draw a square that is about 50x50px. Double click on the square with the black arrow tool to select it all and then hit F8 to convert it to a symbol. Make sure you are creating a movieclip with its registration point in the middle - name it box_mc. Click OK and give the square on the stage an instance name of "box_mc". It's a good idea to get in the habit of ending your movieclip instance names with _mc because when you are typing in the actions panel, Flash will see that you are adding actions for a movieclip and will give you a list of appropraite actions (buttons should _btn and textfields should _txt).

Make another layer and name it "ball". Draw a little ball and convert it to a movieclip named ball_mc. Click OK and give the ball on the stage an instance name of "ball_mc". Now paste these actions underneath the watchKeyBoard code -

function moveStuff() {
        if (Key.isDown(Key.LEFT)) {
                this._x -= 5;
        }
        if (Key.isDown(Key.RIGHT)) {
                this._x += 5;
        }
        if (Key.isDown(Key.UP)) {
                this._y -= 5;
        }
        if (Key.isDown(Key.DOWN)) {
                this._y += 5;
        }
}
box_mc.onEnterFrame = moveStuff;
ball_mc.onEnterFrame = moveStuff;

Test your movie and press the arrow keys on your keyboard. What's happening there is we have a function (moveStuff) that says "if the left arrow key is pressed, move the object that is calling this function 5 pixels to the left" and likewise for the other 3 arrow keys. We then tell our 2 movieclips (affectionately known as box_mc and ball_mc) to call this function every frame. It's important to understand that even though our timeline only has 1 frame, the Flash player is playing that frame 31 times per second. In fact, even if we had a stop action in that frame, the enterFrame event would still occur 31 times per second.

OK the problem with our moveStuff function at the moment is that its not very flexible. What if we want our objects to move at different speeds? What if we don't want them to disappear off the stage like they do? Let's fix these problems. Replace the code just above with this -

StageWidth = 600;
StageHeight = 400;
function moveStuff() {
        if (Key.isDown(Key.LEFT)) {
                this._x -= this.speed;
        }
        if (Key.isDown(Key.RIGHT)) {
                this._x += this.speed;
        }
        if (Key.isDown(Key.UP)) {
                this._y -= this.speed;
        }
        if (Key.isDown(Key.DOWN)) {
                this._y += this.speed;
        }
        if (this._x>StageWidth+(this._width/2)) {
                this._x = 0-(this._width/2);
        }
        if (this._x<0-(this._width/2)) {
                this._x = StageWidth+(this._width/2);
        }
        if (this._y>StageHeight+(this._height/2)) {
                this._y = 0-(this._height/2);
        }
        if (this._y<0-(this._height/2)) {
                this._y = StageHeight+(this._height/2);
        }
}
box_mc.speed = 5;
ball_mc.speed = 3;
box_mc.onEnterFrame = moveStuff;
ball_mc.onEnterFrame = moveStuff;

OK that's quite a bit more code but is much more effective as you will soon see. Test your movie again and press the arrow keys. You will see that our shapes now move at different speeds and if they move off the edge of the stage then they will appear on the other side. Let's look at what has changed. First we create a couple of variables (StageWidth and StageHeight) that we use to store the size of the stage. Then, down the bottom of the moveStuff function we check to see if our shape is off the stage and if it is, move it to the opposite side. It's important to note that when we check the _x of our shapes, the value returned is the _x position of the crosshair in the middle of our shape. This is why we use something like +(this._width/2) which will give use the x position of the edge of our shape (which is what we want).

if (this._x>StageWidth+(this._width/2)) {
        this._x = 0-(this._width/2);
}


So as you can see, if the x position of our shape is greater than the width of the stage plus half the width of the shape, then it gets moved left edge of the stage minus half the width of the shape.

Note - the top left corner of stage has xy coordinates of 0,0

OK so that's how we get the looping effect, but what about the speed?

Well, instead of having

this._x -= 5;

we now have

this._x -= this.speed;

What this does is check the variable "speed" inside the object that is calling the function and moves the object that many pixels. You can see here

box_mc.speed = 5;
ball_mc.speed = 3;

that we have set the speed variable in our 2 movieclips to different amounts, and they will therefore move a different number of pixels each frame.

Let's move on.

The next action I want to show you is the movieclip method hitTest(). hitTest is used to detect whether or not 2 objects are touching each other. Paste these actions underneath all the others

this.onEnterFrame = function() {
        if (box_mc.hitTest(ball_mc._x, ball_mc._y)) {
                trace("hit");
        }
};

Test the movie and move the shapes around until they are touching. You will see that "hit" is displayed in the output window. This time the enterFrame action is being applied to _root (which is refered to as "this" because we are placing the action on a frame in _root). _root is just like any regular movieclip, and has has all the movieclip methods available to it (eg attachmovie - which we will get to soon). So, every frame flash checks to see if the 2 shapes are touching and if they are it displays our message in the output window. Now lets duplicate the ball_mc whenever the 2 shapes touch. To do this we will use the duplicatemovieclip() method. Replace the actions just above with this -

i=0;
this.onEnterFrame = function() {
        if (box_mc.hitTest(ball_mc._x, ball_mc._y)) {
                newBall=ball_mc.duplicatemovieclip("ballCopy"+i,i++);
                newBall._x=ball_mc._x;
                newBall._y=ball_mc._y;
        }
};

Test your movie and move the shapes around until they touch. You will see that a copy of the ball_mc gets created and placed on the stage where the collision occured. Let's go through that code. First we initialise a variable (i) and set it to 0. Next we start our enterFrame action and run the hitTest. If the hitTest returns true, then we

  • duplicate the ball_mc
  • give the copy an instance name of "ballCopy" plus whatever i is equal to at the time (eg ballCopy0, ballCopy1 etc)
 
  • place the copy at a depth of whatever i is equal to and then increase the value of i by 1 (i++)

We then place the new copy at the same x and y position as the original ball_mc. For more information on duplicatemovieclip go here and to learn the difference between levels, layers and "depth" go here. It's important to note that only one object can exist in a depth at a time. We can use this to limit the number of copies of the ball that can be shown on the stage at once. Change the code just above to this -

i=0;
this.onEnterFrame = function() {
        if (box_mc.hitTest(ball_mc._x, ball_mc._y)) {
                newBall=ball_mc.duplicatemovieclip("ballCopy"+i,i++);
                newBall._x=ball_mc._x;
                newBall._y=ball_mc._y;
                if(i>10){
                        i=0;
                }
        }
};

Test your movie and you will see that once more than 12 copies of the ball have been made then the original copies start getting replaced. This is because when i becomes greater than 10 we are setting it back to 0

if(i>10){
        i=0;
}

which means that the next copy will be placed at a depth of 0 and will therefore replace the first copy that was made.

OK that's duplicatemovieclip(). Now let's look attachmovie which is similat except it pulls a symbol from the library and places it on the stage. Open your library and click on the little blue + sign down the bottom left. This will create a new Symbol which should be a movieclip named "arrow_mc". Click OK and you should be automatically taken inside the new mc (movieclip). Use the pen tool to draw an arrow point up. When you are finished, select the arrow and use the align panel to move it to the center of the Symbol (which is identified by the crosshair). Now click once on the arrow_mc in the library and from the drop down menu up the top right of the library select "linkage". In the window that opens, click on the "Export for Actionscript" box. Leave the Identifer as "arrow_mc" and leave the "Export in first frame" box checked. Click OK. What this does is ensures that our arrow is included in in the swf when we publish our movie (by default, Flash removes any items from the library that are not manually placed on the stage - exporting for actionscript stops our symbol being removed so that we can place it on the stage using actionscript).

OK lets place the arrow on the stage using the attachmovie method. Change the moveStuff function to this -

function moveStuff() {
        arrowDepth=50;
        arrowX=550;
        arrowY=350;
        if (Key.isDown(Key.LEFT)) {
                this._x -= this.speed;
                _root.attachmovie("arrow_mc","arrowMC",arrowDepth);
                arrowMC._x=arrowX;
                arrowMC._y=arrowY;
                arrowMC._rotation=-90;
        }
        if (Key.isDown(Key.RIGHT)) {
                this._x += this.speed;
                _root.attachmovie("arrow_mc","arrowMC",arrowDepth);
                arrowMC._x=arrowX;
                arrowMC._y=arrowY;
                arrowMC._rotation=90;
        }
        if (Key.isDown(Key.UP)) {
                this._y -= this.speed;
                _root.attachmovie("arrow_mc","arrowMC",arrowDepth);
                arrowMC._x=arrowX;
                arrowMC._y=arrowY;
                arrowMC._rotation=0;
        }
        if (Key.isDown(Key.DOWN)) {
                this._y += this.speed;
                _root.attachmovie("arrow_mc","arrowMC",arrowDepth);
                arrowMC._x=arrowX;
                arrowMC._y=arrowY;
                arrowMC._rotation=180;
        }
        if (this._x>StageWidth+(this._width/2)) {
                this._x = 0-(this._width/2);
        }
        if (this._x<0-(this._width/2)) {
                this._x = StageWidth+(this._width/2);
        }
        if (this._y>StageHeight+(this._height/2)) {
                this._y = 0-(this._height/2);
        }
        if (this._y<0-(this._height/2)) {
                this._y = StageHeight+(this._height/2);
        }
}

OK most of that is the same as before but you will see that at the top we declare a few variables

arrowDepth=50;
arrowX=550;
arrowY=350;

which store the x and y positions of where we want to place the arrow and also the depth in which the arrow will be placed into. A little further down you will see

_root.attachmovie("arrow_mc","arrowMC",arrowDepth);
arrowMC._x=arrowX;
arrowMC._y=arrowY;
arrowMC._rotation=-90;

which attaches a copy of the arrow_mc symbol (remember, arrow_mc is the linkage name of our arrow symbol) to _root, gives it an instance name of arrowMC and places it in a depth of whatever our arrowDepth variable is set to (in this case, 50). We then position the arrowMC on the stage and rotate it to reflect which arrow key has been pressed. Each time another arrow is attached, it replaces the previous one as it is being placed in the same depth. Test your movie and you will see that it is working fine except the arrow stays there even when no arrow button is being pressed. What we need to do is remove the arrow when a keyUp event occurs - lets do that now.

I also want to show you the getAscii() method in action so let's do that at the same time. Change the watchKeyBoard code to this -

watchKeyBoard = new Object();
watchKeyBoard.onKeyDown = function() {
        if (Key.getAscii() == 32) {
                box_mc._xscale=box_mc._yscale-=5;
        }
};
watchKeyBoard.onKeyUp=function(){
        arrowMC.removeMovieClip();
}
Key.addListener(watchKeyBoard);

Test your movie and press the arrow keys and the spacebar a few times. You will see that our arrow gets removed whenever a keyUp event occurs. We do this with the removeMovieClip() method.

Note - removeMovieClip can only be used to remove clips that were placed on the dynamically (ie with attachmovie or duplicatemovieclip).

OK you will also see that our box gets smaller when you hit the spacebar. This part of the code takes care of that

if (Key.getAscii() == 32) {
        box_mc._xscale=box_mc._yscale-=5;
}

so when a key is pressed we check the ASCII value of the key. If the value is 32 (the ASCII value of the spacebar), then we reduce the xscale and yscale of the box_mc by 5%.

Ok that'll do for attachmovie. For more info on the attachmovie method go here.

Damn this is a long tute. Oh well...almost done.

Now, instead of outputting the xmouse and ymouse to the output window, let's display it in a dynamic text box. Make another layer and name it "text". Using the text tool, draw a text box that is about 200x50px. Make sure in the property inspector that the text box is set to dynamic and multiline. Also, set it to Verdana 12pt, then type "move your mouse" inside the text box. Give the text box an instance name of "displayMouse_txt".

Now, change the mouseWatch code up the top to -

watchMouse = new Object();
watchMouse.onMouseMove = function() {
        displayMouse_txt.text="The mouse X position is "+_xmouse+newline;
        displayMouse_txt.text+="The mouse Y position is "+_ymouse;
};
Mouse.addListener(watchMouse);

Test the movie and move the mouse around. It should be pretty obvious what is going on here. Everytime the mouse is moved, this line

displayMouse_txt.text="The mouse X position is "+_xmouse+newline;

sets the text in the textbox to say "The mouse X position is 523" and then starts a new line (+newline)

and then this line

displayMouse_txt.text+="The mouse Y position is "+_ymouse;

adds "The mouse Y position is 234" to the end of the textbox. The important thing to note here is in the line just above we use += which doesn't replace the text that is already in the textbox but rather adds a bit on to the end.

For Flash games, it is important to understand how to display dynamic data in textboxes so that you make scoreboards etc.

OK, the last thing I want to show you is how to play sounds via actionscript. For this example, I'm going to make a sound that is played whenever the "s" button on the keyboard is pressed.

Choose Window/Common Libraries/Sounds.fla to open the library of sounds that comes with Flash. Click on any one of them and drag it into the library or your movie. Click once on the sound in your library and choose "Linkage" from the library drop down menu. Click on the "Export for actionscript" button and give your sound an Identifier of "sSound". Click OK. Now change the watchKeyBoard code to this -

sKeySound=new Sound();
sKeySound.attachSound("sSound");
watchKeyBoard = new Object();
watchKeyBoard.onKeyDown = function() {
        trace(Key.getAscii());
        if (Key.getAscii() == 32) {
                box_mc._xscale=box_mc._yscale-=5;
        }
        if (Key.getAscii() == 115) {
                sKeySound.start();
        }
};
watchKeyBoard.onKeyUp=function(){
        arrowMC.removeMovieClip();
}
Key.addListener(watchKeyBoard);

Test your movie and hit the "s" key a few times - your sound should play. Look at the code and you will see that at the top we have created a new Sound object (called sKeySound) and the attached our sound in the library to this object (via its linkage name - sSound). Then in the onKeyDown code, we check to see if the "s" key has been pressed (if (Key.getAscii() == 115) {) and if it has, tell our sound object to start.

You can learn more about the attachSound method here.

That's about it. This has covered many of the comman actions needed to make games in Flash so I hope that you have found it useful.

If you are not a student of mine, please do not email me questions regarding this tute. Instead, post any questions you might have on the forums at www.actionscript.org

cheers