Tutorial: Creating a geography game using Flash ActionScript



Tutorial: Creating a geography game using Flash ActionScript

This game was partially inspired by the Lufthansa Virtual Pilot game: and a general interest in geography.

For other geography games, see my State Capital game with the other Flash games:

Also, see my JavaScript HTML page at for map games using JavaScript, the Google Map API, and data in xml files. This does not have the flying object. It does have the benefit of being entirely data-driven: that is, a new game requires no changes in the JavaScript code, just gathering information such as latitude and longitude coordinates and producing an xml file of a specified format.

Go to the Processing examples for a very basic start of a find game. This one provides a way to have sets of answers:

Scaling up and working in phases is an important part of application development. I describe first a basic game in which questions are selected at random from an array of information. A specific place can be asked more than once. This game does not end. The mileage 'off' for each answer is displayed, but there is no other scoring. Then, I describe a second version of the game in which there is an array defining rounds. Each round has its own map movie clip instance specified, along with the maximum point for each answer, the number of milliseconds allowed for each turn and the number of turns. This game ends with the final score displayed. The third version of the game adds a new type of place: a region as opposed to a single location. This type of place was added to provide a way to test the player on counties or townships.

There are no fixed rules for how to divide implementation into phases. I did what comes naturally to me. The basic game requires implementation of the flying object, the timing, handling clicks on the map, and calculating the miles between the right position and the one the player clicked. The second version handles the encoding of rounds, the scoring using the miles way to be subtracted from the maximum point value, the coding to prevent a place from being asked more than one time per round, and, most critically, the creation of a second map with the dots indicating towns removed. The third version adds the feature of testing against areas such as counties or towns as opposed to single x,y points. This requires creating additional movie clip symbols, specifically one for each region. Doing all of this at once is just too much. Actually, each of the 3 major phases had sub-phases. For example, the red dot and red line from the correct place to the player's spot came after the rest of the basic game was completed.

As always, these examples are meant to instruct in general and specific concepts in programming. Please correct, enhance, and improve and send comments.

Basic requirements: Maps and Geographic features, event handling and calculations

The geography game requires

• generation or acquisition of a map,

• a way of encoding places, including the location of the place and the text used in posing the question

• setting up the event handler for the player clicking on the map

• calculating the distance from the right answer location to the location indicated by the player

The fundamental requirement of a geography game is the map. I obtained maps from and then modified the images in Corel Paint Shop Pro. The site provides more features than I actually used, and I urge you to explore its capabilities.

I determined the pixel coordinates of the places and recorded them along with text in an array. I use one array of places for all (two) maps. For the third version of the game, I created movie clip symbols that represented regions by duplicating the whole map and then erasing all the material except the region I wanted.

I used the standard event handling of Flash ActionScript to detect the player's mouse clicks and to animate the flying object.

Map making is a complex business. For this application, I used pixel coordinates to determine a distance and multiplied by a factor I calculated to get to miles. This is not as accurate as the spherical system of cosines method using latitude and longitude used in my other application, but I didn't have latitude and longitude, only pixels. One way to conceptualize this is that this application assumes the world is flat over the region of the map. Given that my example was a reasonably compact congressional district and not all of Europe, this is good enough. My other game, using Google Maps, presents a projection that is flat, but does calculations in terms of latitude and longitude.

Implementation

The Flash game is all contained in the .html and .swf files produced by the Flash programming environment. The map or maps are movie clip symbols in the Library as is the flyer object.

The dynamic construction of the circle and line giving feedback to the player on the accuracy of the move requires the use of the Vector datatype and graphics methods.

The animation showing the flyer flying is done using a Timer. The direction of flight is calculated using the current position of the mouse. The following diagram indicates how the calculating works: at each time interval, consider a vector drawn from the current position of the flyer to the current position of the mouse. The flyer is moved along that vector interval/timeleft of the way, where interval is the length of the timing interval (20 milliseconds) and timeleft is the amount of time left in the time allotted for the move. This is held in the variable duration. Keep in mind that the position of the mouse can change so the flyer does not necessarily move in a straight line. When the player clicks on the screen, the code re-positions the flyer immediately at the location clicked.

[pic]

Basic game

The screen shot shows the opening display. The picture is the Eastern Blue-bird, the state bird of New York State.

[pic]

After clicking on the Next Place? button, the screen will look something like this:

[pic]

The blue bird, the movie clip instance I named flyer, gradually shrinks in size. The screen shot above was taken after a few seconds.

The next screen shot shows the resulting display after clicking near, but not on the dot representing Port Jervis:

[pic]

The feedback to the player consists of a red circle on the correct location and a line segment from the circle to the place where the player clicked.

The implementation includes ActionScript coding along with instances on the Stage. The screen shot shows the contents of the Stage and the Library:

[pic]

The Stage contains a button component named askquestbtn, a picture of the congressman in an instance named flyer, a text field named question and an instance showing the map named mapdots. There is only one frame and the code is shown below. To put it another way, you must

• create a movie clip symbol for the flyer and bring an instance onto the Stage and name it flyer

• create a map and bring an instance onto the Stage and name it however you will reference it in the code. I named it mapdots.

• use the Window/Components menu to bring a button to the Stage. Use Window/Component Inspector to change the label to Next Place?. Give it the name askquesbtn.

• use the T text tool to create a dynamic text field. Give it the name question.

There are 4 functions: nextquestion, advance, checkit and dist. The first three are invoked through the mechanisms of addEventListener calls; the dist function is invoked by the checkit function.

The most important global variable is the array called towns. It is an array of arrays. Each of the internal arrays, for example, ["Mt. Kisco",676,756], contains the name of the town followed by the x and then the y coordinate in pixels. This construction is more general than towns and can be used for buildings. The critical factor is that it is a x,y location. Use the Window/Info panel to determine the x, y pixel coordinates.

The other global variables are used to share data among the functions. For example, the started variable is used to determine whether there was a prior question. If so, then the circle and linea variables are references to the circle and line feedback, which are removed using removeChild. The flyer needs to be restored to the original width and height and placed back in the upper left.

var towns:Array = [

["Mt. Kisco",676,756],

["Peekskill",623,727],

["Stony Point",605,748],

["Beacon",610,649],

["Arlington",629,581],

["Port Jervis",418,697]

];

var choice;

var interval:Number=20;

var mtimer:Timer=new Timer(interval);

var duration:Number=5*1000;

var cmds:Vector.=new Vector.(2,true);

var pts:Vector.=new Vector.(4,true);

var timeleft;

var wdel;

var origw=flyer.width;

var origh=flyer.height;

var smallw=10;

var circle:Shape;

var linea:Shape;

var started=false;

mtimer.addEventListener(TimerEvent.TIMER,advance);

mapdots.addEventListener(MouseEvent.CLICK,checkit);

askquesbtn.addEventListener(MouseEvent.CLICK,nextquestion);

function nextquestion(event) {

choice=Math.floor(Math.random()*towns.length);

question.text="Click on "+towns[choice][0];

mtimer.start();

timeleft=duration;

wdel = (origw-smallw)/(duration/interval);

if (started) {

removeChild(circle);

removeChild(linea);

flyer.width=origw;

flyer.height=origh;

flyer.x=mapdots.x-40;

flyer.y=mapdots.y-40;

}

started=true;

}

function advance(event) {

var xp=stage.mouseX;

var yp=stage.mouseY;

var delta=interval/timeleft;

flyer.x +=(xp-flyer.x)*delta;

flyer.y +=(yp-flyer.y)*delta;

timeleft-=interval;

flyer.width-=wdel;

flyer.height = flyer.width * (origh/origw);

}

function checkit(event:MouseEvent) {

var gx=towns[choice][1];

var gy=towns[choice][2];

mtimer.stop();

flyer.width=smallw;

flyer.height = flyer.width * (origh/origw);

var xpos=event.stageX;

var ypos=event.stageY;

var dis=distance(gx,gy,xpos,ypos);

circle = new Shape();

linea = new Shape();

// Set circle

circle.graphics.lineStyle(1);

circle.graphics.beginFill(0xFF0000, 1);

circle.graphics.drawCircle(0, 0, .5*smallw);

circle.x=gx;

circle.y=gy;

addChild(circle);

//Set error line

linea.graphics.lineStyle(1,0xFF0000);

cmds[0]=1;

cmds[1]=2;

pts[0]=gx;

pts[1]=gy;

pts[2]=xpos;

pts[3]=ypos;

linea.graphics.drawPath(cmds,pts);

addChild(linea);

//need to convert to miles

dis=dis*.193719;

dis=Math.round(dis);

question.text=String(dis)+" mile(s) off ";

flyer.x=xpos;

flyer.y=ypos;

}

function distance(x1,y1,x2,y2):Number {

var dsqr =(x1-x2)*(x1-x2)+(y1-y2)*(y1-y2);

var d=Math.sqrt(dsqr);

return d;

}

Game with scoring, rounds

Here is the opening screen of the game with rounds:

[pic]

Here is the screen at the end of the first round and the start of the second:

[pic]

Notice that the map is different: there aren't any dots! I later added a third map, with no lines, for a third round:

[pic]

This is a screen shot at the end of the game:

[pic]

The Flash layout includes two additional dynamic text fields, roundtitle and roundtotal. The ActionScript, again just one frame, now includes a startgame function and a nextround function. The startgame function is called directly from the code. The nextround function is called from startgame and from checkit.

Added global variables are the askedthisround array and the rounds array. The askedthisround array is used to make sure the same town is not asked twice within a single round. This is done using a do { } while construct in the nextquestion function.

The rounds array contains the following information for each round: the instance name of the map, the time duration allowed for each move, the maximum point value, and the number of questions for each round.

Each map used in a round must be created as a movie clip symbol and then have an instance moved to the Stage and named. Note that only one of the map instances is made visible at a time.

Notice that the first entry in the towns array.

The code is

var towns:Array = [

["town where Mike Cindrich is mayor.",616,619],

["Peekskill.",563,589],

["Stony Point.",543,611],

["Beacon.",549,511],

["Arlington.",568,442],

["Port Jervis.",357,559]

];

var askedthisround = new Array(towns.length);

// movie clip instance, duration, points, number of turns

var rounds:Array = [

[mapdots,5*1000,1000,3],

[mapnodots,4*1000,2000,3],

[nolines,3*1000,3000,3]

];

var map:MovieClip;

var asked:int=0;

var rnd:int=0;

askquesbtn.addEventListener(MouseEvent.CLICK,nextquestion);

var choice;

var interval:Number=20;

var mtimer:Timer=new Timer(interval);

var duration:Number;

var pointmax:int;

var roundlimit:int;

var total:int=0;

var cmds:Vector.=new Vector.(2,true);

var pts:Vector.=new Vector.(4,true);

var timeleft;

var wdel;

var origw=flyer.width;

var origh=flyer.height;

var smallw=10;

var circle:Shape;

var linea:Shape;

mtimer.addEventListener(TimerEvent.TIMER,advance);

var started=false;

function nextquestion(event) {

asked++;

do {

choice=Math.floor(Math.random()*towns.length); }

while (askedthisround[choice]);

askedthisround[choice]=true;

question.text="Click on "+towns[choice][0];

mtimer.start();

timeleft=duration;

wdel = (origw-smallw)/(duration/interval);

if (started) {

removeChild(circle);

removeChild(linea);

flyer.width=origw;

flyer.height=origh;

flyer.x=mapdots.x-40;

flyer.y=mapdots.y-40;

}

started=true;

}

function advance(event) {

var xp=stage.mouseX;

var yp=stage.mouseY;

var delta=interval/timeleft;

flyer.x +=(xp-flyer.x)*delta;

flyer.y +=(yp-flyer.y)*delta;

timeleft-=interval;

flyer.width-=wdel;

flyer.height = flyer.width * (origh/origw);

}

function checkit(event:MouseEvent) {

var gx=towns[choice][1];

var gy=towns[choice][2];

mtimer.stop();

flyer.width=smallw;

flyer.height = flyer.width * (origh/origw);

var xpos=event.stageX;

var ypos=event.stageY;

var dis=distance(gx,gy,xpos,ypos);

circle = new Shape();

linea = new Shape();

// Set circle

circle.graphics.lineStyle(1);

circle.graphics.beginFill(0xFF0000, 1);

circle.graphics.drawCircle(0, 0, .5*smallw);

circle.x=gx;

circle.y=gy;

addChild(circle);

//Set error line

linea.graphics.lineStyle(1,0xFF0000);

cmds[0]=1;

cmds[1]=2;

pts[0]=gx;

pts[1]=gy;

pts[2]=xpos;

pts[3]=ypos;

linea.graphics.drawPath(cmds,pts);

addChild(linea);

//need to convert to miles

dis=dis*.193719;

dis=Math.round(dis);

total+=pointmax-dis;

question.text=String(dis)+" mile(s) off ";

flyer.x=xpos;

flyer.y=ypos;

if (asked>=roundlimit) {

roundtotal.text="Round "+String(rnd+1)+" done. Total is "+total;

asked=0;

rnd++;

if (rnd ................
................

In order to avoid copyright disputes, this page is only a partial summary.

Google Online Preview   Download