logo

 

     
 
Home
Site Map
Search
 
:: Bitwise Courses ::
 
Bitwise Dusty Archives
 
 
 

rss

 
 

ruby in steel

learn aikido in north devon

Learn Aikido in North Devon

 


Section :: ActionScript
- Format For Printing...

Adventures in ActionScript - part one

Learn ActionScript and Flex the fun way!
Saturday 28 February 2009.
 

It is a truth universally acknowledged that the acme of the programmer’s art is the Text Adventure! Well, ok, so maybe not universally acknowledged but, anyhow, in my highly biased view, writing a text based adventure game is not only a considerable programming challenge but, as an added bonus it’s a heck of a lot of fun...

Prelude To An Adventure

A text adventure was the first really big project I undertook when I was learning to program, way back in a far distant time known as the 1980s. One week I was writing “Hello world” the next week I was exploring a deserted city containing an uncooperative wombat, a gigantic termite that wobbled menacingly at me as I tried (and failed) to enter a forbidden courtyard and, naturally, the obligatory Nameless Horror.

And thus it was that I discovered the noble art of adventure game writing. My game (‘The Golden Wombat Of Destiny’) was written in Turbo Pascal. The quality of mu coding was, I am pretty certain, pretty poor when I started writing that game. By the time I’d finished it (about a year later) it was a good deal better. Even to this day, whenever I learn a new programming language the first task I set myself is to write a simple adventure game.

An adventure game forces you to get to grips with almost every area of a language. You have to learn about its data structures (to create maps containing rooms and rooms containing treasures); you have to master strings (to interact with the user), disk IO (to save and restore games), visual design features (for the user interface) and error checking and recovery (game players are great at doing all the things they are not supposed to). And if you happen to be writing in an Object Orientated language, then you will need to get to find out how to create your own custom class hierarchies. After all, a Treasure may be a type of Thing, a Troll may be a type of Actor and so on....

It was only about a year ago that I first began to learn ActionScript. That was at around the time my company was first began planning Amethyst, our Visual Studio IDE for Adobe’s Flex framework. Flex uses ActionScript as its programming language and Flash graphics for rendering user interfaces. Naturally, the first task I set myself was to write a game. Being just an exercise for my own benefit, that game was very simple: half a dozen rooms, a few treasures and a player that walks from one room to the next taking and dropping things.

Recently we released the third public beta of Amethyst and so I thought it might be fun to go back to my simple ActionScript adventure and extend it to create a proper game - one that other programmers could us as a way to learn ActionScript and Flex. If that sounds like fun to you, you’ve come to the right place...

In this series I will be using the Amethyst IDE but, if you prefer, you can use Adobe’s Flex Builder. The beta of Amethyst is freely available and my company will be releasing a free personal edition of Amethyst later in the year.

Debugging in Amethyst

- Download Page For Amethyst
- More Information on Amethyst

Download The Source Code of the Adventure Game project: adventure1.zip
To load this project into Amethyst, unzip the archive then, in Amethyst, select File, Open, Project/Solution, browse to the solution file, Adventure1.sln and click the open button.

Room To Manoeuvre

The first essential building block of an adventure game is the Room. A Room is an object with a name (such as “Treasure Room”) and one or more exits (typically the four directions: North, South, East and West). In ActionScript, the template of a Room can be defined as a class, like this:

public class Room {               
        private var _n:String;
        private var _s:String;
        private var _w:String;
        private var _e:String;
       
        public function Room(aN:String,aS:String,aW:String,aE:String){                       
                _n = aN;
                _s = aS;
                _w = aW;
                _e = aE;
        }       
}

An ActionScript class definition begins with the keyword class followed by the name of the class. The keyword public makes the class available to other code throughout the project. Its variables _n, _s, _w, and _e are all preceded by the keyword var and these are private so cannot be accessed by code outside the boundaries of the Room class. Keeping variables private is generally good practice since it means that any changes to them must be made through clearly defined interface functions which are under the control of the class’s author.

The public function Room is the class constructor. It is called when you create a new class and, in this case, it takes four string arguments which initialize the values of the four private variables. Now, in order to allow the values of variables to be accessed or changed from outside the class we need to defining ‘getter’ and ‘setter’ functions like this:

public function get n():String{
        return _n;
}
               
public function set n(aN:String):void{
        _n = aN;
}

If you want to make a specific variable ‘read only’, give it a getter (to return its value) but no setter (to modify it). In most cases the exits from a room remain unchanged throughout the game (so require only getters). However, you may decide to create a puzzle in which a door can be locked or unlocked or a time machine may lead to different locations at different times - which is why all my exists have both getters and setters.

A Class Act

In ActionScript a class usually occupies a file with the same name as the class itself and ‘as’ as its extension - so the file containing the Room class is called Room.as. ActionScript is case sensitive and the case of the file name must match that of the class name. Generally class names begin with an uppercase letter followed by lowercase or mixed case letters. Multiple classes can be grouped together inside ‘packages’ which have the same names as the folders in which they are kept. I’ve created a folder called Classes (it lives beneath the default \src folder containing the source of my project). The code of my class is then placed inside a pair of curly brackets preceded by the package name:

package Classes
{
        public class Room
        {
                // etc.
        }
}

Now all I have to do is create a few room objects. I’ve done that in the file Wombat.mxml. In Flex, an MXML file may contain a mix of XML formatting code to define the application and its visual controls plus some ActionScript programming code. Here, for example, is the MXML definition of my ‘move north’ button:

<mx:Button x="198" height="22" width="40" y="300" label="N" click="moveTo('n')"></mx:Button>

Notice that in addition to its size and position this definition also includes the name of a function moveTo() including the string argument ‘n’. This is associated with the button’s click event which means that moveTo(‘n’) will be called when the button is clicked.

This and a few other functions are found in the ActionScript code in the same file. When you write ActionScript inside an mxml file, the code must be contained within pairs if ‘script’ and ‘CDATA’ tags:

<mx:Script >
        <![CDATA[
               
        ]]>
</mx:Script>

The first thing I’ve had to do in order to be able to access the Room class is to import it. The import statement is found right at the top of my code and takes the form of the ‘package’ name, Classes followed by the class name, Room:

import Classes.Room; The code that actually creates Room objects is found in the init() function. This function is called automatically after my application has initialized due to the fact that it has been designated as the event-handling method for the Application’s applicationComplete event. This has been done inside the Application tag on line 2 of the file:

<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" applicationComplete="init()">

A new object is created by preceding the class name with the new keyword and passing to the class’s constructor all the required arguments:

new Room(NOEXIT,CRYSTALDOME,TREASUREROOM,DRAGONSLAIR)

Here, the arguments are string constants which have been defined earlier like this:

private const NOEXIT:String = "No Exit";
private const CAVE:String = "Cave";

On The Map

To understand why I’ve used string constants to indicate the exists, consider how we might make a ‘map’ of all the rooms in a game. A map can be thought of as a collection of room objects. One of the simplest types of collection is an array. You could simply put rooms into an array like this (assuming that the three named Room objects have been created previously):

var arrayMap:Array = [room1,room2,room3];

When you subsequently need to move into a new room you will have to obtain a room object at a specific array index, like this:

newroom = arrayMap[1];

The above code refers to the room at index 1 which, here, happens to bee room2. The trouble is that the index, 1, is not very descriptive; it’s just a number. In order to see that it refers to room2, you would have to look at the code of your array definition. If this is a long array containing tens or hundreds of rooms you will eventually end up accessing rooms at high indexes such as arrayMap[89] and you may find yourself having to waste time by counting through all the items in the array just to figure out which room is at that index!

In my code, I’ve decided to make it easier to see which room is which by using an ‘associative array’ (that is, a ‘hash’ or ‘dictionary’) as my ‘map’. Each item in an associative array takes the form of a key-value pair. The key is a string identifying the room such as “Cave” and the value is a Room object:

"Cave":  new Room(NOEXIT, CRYSTALDOME, TREASUREROOM, DRAGONSLAIR)

For more on Associative Arrays see HERE

The player’s position is saved in the variable, here, which contains a string matching the key (e.g. “Cave”) of the Room representing the player’s current location. When the player tries to go in a certain direction - let’s say to the South - the value of the Room’s private variable _s (which is accessed using its s ‘getter’ function) indicates the room that lies in that direction. You can see this in the moveTo() method. Let’s assume that the player is currently in the first room (the “Cave”) and clicks the ‘S’ button. This button’s click event calls moveTo(‘s’). A ‘switch’ statement now executes and when it matches ‘s’ this code is run:

newroom = Room(map[here]).s;

This uses the string variable, here to index into the map. So if here is “Cave”, this will obtain the Room associated with the key, ”Cave” - namely, the Room object which we created earlier with these exits:

(NOEXIT, CRYSTALDOME, TREASUREROOM, DRAGONSLAIR)

The .s getter function returns the _s variable of this room object. In this case this happens to be the string defined by the CRYSTALDOME constant. The local variable, newroom is assigned this value. If newroom equals NOEXIT then a messages is displayed and the player’s position is left unchanged:

if (newroom == NOEXIT){
        ta.text += "No Exit in that direction!\n";
}

However, if (as in the present case) there is an exit (here it leads to the “Crystal Dome”)), the variable here is assigned the string value of that Room (the constant, CRYSTALDOME) so that, when the player next tries to move, the moveTo() function will look for exists from the “Crystal Dome” Room.

This months simple game lets you explore half a dozen rooms

So, already in just a few lines of code we have the makings of a simple exploring game which allows the user to move around a map from one location to another. Moreover, by making use of an associative array to store the Room objects I have been able to identify each room object by a descriptive name rather than by a non-descriptive array index. If I had used a simple array the Rooms would have identified their exits with numbers like this (here -1 is used to indicate No Exit):

new Room(0,-1,2,4)

By using an associative array and descriptive constants, the code becomes much easier to understand:

new Room(CAVE, NOEXIT, TROLLROOM, SMALLHOUSE)

But using strings and string constants is really only one step up from using numbers. I still have to ‘look up’ rooms by indexing values in a list. Wouldn’t it be so much nicer if, instead of having strings to ‘represent’ the rooms in each direction, I just had the rooms themselves? In other words, why can’t I create room objects that really do ‘lead into’ adjoining rooms?

It turns out that this can be done quite easily in ActionScript. I’ll explain how in the next part of this series.

AddThis Social Bookmark Button

Source Code


313.6 kb


Home