banner laser_dragon_banner

Creating a Point-and-Click Adventure Game in Actionscript 3.0

By Quinn Stephens

This tutorial will cover, in parts, the steps to creating a simple point-and-click adventure game in the style of Monkey Island, Space Quest, and many other such classics from the adventure game renaissance of the 1990s. Once I've completed a basic engine I plan to provide the source files for anyone who's interested in building adventure games. This is a learning process for me as well, so I encourage anyone to provide feedback and their own improvements to the code as they work through it. Questions and comments can be addressed to .

We'll be getting into some heavy-duting coding here before too long, so if you're not already familiar with Actionscript (and AS 3.0 in particular) I'd highly recommend the tutorials at Foundation Flash as a good place to get started. If you don't own Flash CS3 or newer, there are open source options available (more info here) although I can't vouch for them personally and they may not be fully compatible with this tutorial. Fortunately, Adobe offers a free 30-day trial for Flash.

*Note: Rather than finish this tutorial I decided to go ahead and make an open-source Flash adventure game engine instead. It's called ALPACA and the full source code and documentation is available for free at http://alpacaengine.com. Enjoy!

Part 1

Making your Character Walk

We'll start simple by creating an interface that moves the character (just a red circle for now) to any point that the user clicks, seen below.

You do not have the proper version of Flash installed. Click here to download the plugin.

Looks simple, right? Well, it is, relatively speaking. Let's set up our Flash files. Later on it would be a good idea to set up a Flash project, which will keep your files organized and allow you to automatically back up all your changes to a server, but for now we'll keep things simple. Open Flash and create a new Flash File (Actionscript 3.0). Save this file as simpleWalk.fla.

Next, create a new Actionscript File. Save this file as Walk.as (make sure it's capitalized) in the same folder as your .fla file.

We'll set up the .fla file first because that's the least amount of work. First off, click on the stage. Notice the Properties tab at the bottom of the screen will now show an input box called "Document class." Type "Walk" into this field and hit Enter.

This tells the .fla file to automatically load your external Walk.as file when exporting the .swf, so we can store (almost) all of our code in that file. This might seem like a headache, but trust me, it makes your workflow a lot smoother.

You'll note that I also set the background to a light gray and the framerate to 30, so the .swf would look a little nicer on a web page. This is entirely optional.

Now, hit Ctrl+F8 to insert a new symbol. Make it a movie clip and call it "circle." Then draw a simple circle in the middle of your new movie clip.

See how that little crosshair is right in the middle of the circle? This is important - that's the circle's registration point, and that's where Flash considers the circle's coordinates to be. You'll see this in action in a minute.

But first, now that the MovieClip "circle" is in your library, right click on it and select "Linkage." In the dialog box that comes up, check the box for "Export for Actionscript."

This will automatically fill in the "Class" and "Base class" fields, so you can go ahead and click OK. Now this MovieClip can be dynamically placed on the stage using Actionscript. This is an easy step to forget, but your code will not work without it!

And with that, we're done with the .fla file. All the rest of our work will be done in the Walk.as file. Keep your .fla file open and head over to Walk.as.

The first code you're going to type is:

package {
import flash.display.MovieClip;
import flash.events.*;

public class Walk extends MovieClip{

Don't worry too much about what this means; we're just setting up the .as file so that Flash will be able to actually read it. We're importing a couple of basic Flash features in order to use them in the file. Note that the public class Walk is capitalized, exactly matching the file name. This is important, so make sure it all matches.

Next, we're going to declare some public variables:

public var player:MovieClip = new MovieClip();
public var walkRate = 10;
public var targetBuffer = 10;

Public variables can be accessed by any and all functions, and they need to be declared right at the top of the class like so. In this case we're creating a public variable for the player (just the circle in our case), and variables that we'll use in the math when making the player move. Keeping them public will make it easier to change them later on if need be.

Now we create the actual Walk function itself. This has to match the class and the .as file, and it's case-sensitive:

public function Walk(){
player = new circle;
addChild(player);
player.x = 200;
player.y = 200;

stage.addEventListener(MouseEvent.CLICK, goToPoint);
} // end Walk function

The first four lines of code in the Walk function are adding the player to the stage (remember how we exported our MovieClip to Actionscript with the name "circle"? Now that we've done that, we can call it up from the library simply with "new circle". Pretty easy, huh?), and then setting the player's x and y coordinates (based on the registration point, as you'll recall) so that it's close to the middle. Otherwise, Flash will default to 0,0 and the circle will be partly out of the frame.

The last line of code up there is setting up the stage to respond to a mouse click from the user. Once the user clicks on the stage, the function goToPoint will be called. So our next step will be to create that function:

public function goToPoint(e:MouseEvent):void{
player.targetx = mouseX;
player.targety = mouseY;
player.addEventListener(Event.ENTER_FRAME, movePlayer);
}

This function is really a setup for the function that will do the actual work of moving the player. It assigns the x and y value of the point the user has clicked to variables within player. We could also use public variables for this, but I find this keeps things a little less cluttered. Since player is, itself, a public variable, we can attach values like this to it as much as we want and any function will be able to read them.

Once those values are added, the function calls the movePlayer function, which runs continually at the framerate of the project (that's what ENTER_FRAME means). In our case, that function will run 30 times per second. So what's the next step? Writing the function, of course:

public function movePlayer(e:Event):void{
var increments:Array = new Array();
increments = getIncrements(player.targetx, player.targety);

So what's all this? Well, in order to move the player across the screen in a straight line, we need to move it by increments along the x and y axes. These increments need to be calculated so that the player moves at a consistent speed, no matter how far or near the distance traveled. Since this requires a bit of math, we'll put all those calculations in a separate function. That function will be called getIncrements and we'll get to it in a moment. For now, just know that all we're doing is getting a simple two-element array that contains the distance in x and y, respectively, that the player needs to move on each frame.

Once we have this array, we'll move its values over to some new variables that are easier to write:

var xinc = increments[0];
var yinc = increments[1];

And then we set the player's x and y value to increase by these increments with every frame:

player.x += xinc;
player.y += yinc;

That's all well and good, but we need to make sure the player actually stops moving once it reaches the point clicked. This isn't quite as simple as telling the function to stop once player.x and player.y are equal to player.targetx and player.targety, however. The player's coordinates will never match the target coordinates exactly, down to fractions of a pixel (at least not at the level of math I'm prepared to do), and it will jitter in place forever. So, we'll estimate instead. This is where our public variable targetBuffer comes in. We'll just set up a simple conditional statement that will stop the function if the player's coordinates are within that number of pixels away from the target point.

// Stop the player once it's within targetBuffer pixels of the point clicked
if ((player.x > (player.targetx-targetBuffer) && player.x < (player.targetx+targetBuffer))
&& (player.y > (player.targety-targetBuffer) && player.y < (player.targety+targetBuffer))){
player.removeEventListener(Event.ENTER_FRAME, movePlayer);
}
} // end movePlayer

Messy, but it works. I find that 10 pixels is a good targetBuffer value for the speed we're working with.

All right, now for our last bit of code, the getIncrements function:

public function getIncrements(thisX, thisY):Array{
var increments:Array = new Array();
var xdiff = (thisX - player.x);
var ydiff = (thisY - player.y);
var diff = Math.sqrt(Math.pow(xdiff, 2) + Math.pow(ydiff, 2));
var fraction = 10/diff;
var xinc = fraction*xdiff;
var yinc = fraction*ydiff;

increments.push(xinc, yinc, diff);

return increments;
}

This whole function is basically just applying the Pythagorean theorem to the player's movement. It returns an array to the movePlayer function with the necessary increments, and now our code is functional! So just add a few more brackets to close up the code:

} // end Walk class
} // end package

And we're done! Go ahead and preview and you should be able to move your little circle-character around. Not much of a game yet - we've got a long way to go - but this is a big first step.

Download source files (ZIP)

Coming up next: nothing! Just use ALPACA, my open source Flash adventure game engine. Full source code and documentation available here.

Click here for some more tutorials.