Tutorial: Shooter game using ActionScript 3faculty.purchase.edu/jeanine.meyer/as30/shooter.doc ·...

34
Tutorial: Shooter game using ActionScript 3.0 This is a tutorial describing a basic shooting game using ActionScript 3.0 with classes defined for the each of the component parts: bombs and antiaircraft (aa) as well as the gun, the ground and a health bar. The game opens with the following screen: After clicking on the start button, a new screen appears along with what is termed a health bar. This bar will grow or shrink depending on bombs hitting the ground or being hit by antiaircraft shot by the gun. NOTE: an early version of the game required the player to click on the gun to make it have "the focus" and receive events such as the arrow key strokes. The version I describe here sets up the proper

Transcript of Tutorial: Shooter game using ActionScript 3faculty.purchase.edu/jeanine.meyer/as30/shooter.doc ·...

Tutorial: Shooter game using ActionScript 3.0

This is a tutorial describing a basic shooting game using ActionScript 3.0 with classes defined for the each of the component parts: bombs and antiaircraft (aa) as well as the gun, the ground and a health bar. The game opens with the following screen:

After clicking on the start button, a new screen appears along with what is termed a health bar. This bar will grow or shrink depending on bombs hitting the ground or being hit by antiaircraft shot by the gun. NOTE: an early version of the game required the player to click on the gun to make it have "the focus" and receive events such as the arrow key strokes. The version I describe here sets up the proper focus by program control. However, I would call what I have done a 'hack' to get it working exactly as I wanted. Notice that bombs are already falling.

Clicking the space bar stops the game (but bombs in motion continue to fall) and produces a text field with a summary. Notice below that there is debris from bombs and that the health bar is smaller than it started.

The player ends the game by pressing the space bar. Bombs in motion continue to fall. A text field appears reporting a score.

This is intended to be a basis for a game. A relatavely minor enhancement is to develop a scoring scheme. As you can see from the last screen shot, all I do is report the number of times a bomb lands on the ground and the total shots. I also note if the health bar has gone to zero. I keep the game going even if the health bar is zero. You can make a more elaborate system for your game. Other enhancements could be to add objects on the ground and to differentiate if bombs hit these objects. Notice that in my game, no checks are made for a bomb hitting the gun. You also can add guns!

OverviewBefore getting to the implementation, here is an overview of this application in terms of events and [general] behavior: what happens when?

The events are: Clicking on the start button: this makes a new screen appear, starts bombs falling,

and enables the program to receive key strokes Clicking on the right and left arrow keys move the gun Clicking on the up arrow key causes firing of the gun New bombs appear on some sort of timed action Each bomb is animated, falls downward, by a timing event Each aa in animated, goes upwards, by a timing event

Clicking the space bar ends the game

Moving on, the next thing I thought about was: who does what. In many cases, this relates to the event handling of the events just listed. These very general ideas help me in the implementation. I will give detailed code later.

The event handler for the button sets off the chain of events for starting the game. This includes setting up timed events for dropping bombs.

An event handler—not clear where it is located—makes a probabilistic determination at regular time intervals on starting off a new bomb. The creation of a new bomb also sets up the event handling for a timed event.

The timer event handler associated with a bomb move the bomb down the screen and check if the ground has been reached.

The event handler for key strokes after detecting an left or right arrow, moves the gun. This same event handler after detecting the up arrow starts off an aa. The creation of a new aa sets up the event handling for a timed event. This event handler after detecting the pressing of the space bar initiates the ending of the game.

The timer event handler associated with an aa moves the aa up the screen AND checks if it collides (hits) any of the bombs. This means that somewhere there must be a list, that is, an array, of all the bombs.

With these general considerations in mind, I will now describe the implementation of the shooter using Flash and ActionScript 3.0. The critical feature of this application is the interaction between using the [traditional] Flash authoring system: creating symbols in the Library, placing them on the Stage in frames and writing frame code AND defining classes (also done 'in Flash' by opening a new ActionScript file) by writing ActionScript code in .as files. The symbols in the Library are each connected to the appropriate class using the Linkage panel in Flash CS3 and the Properties/Advanced panel in Flash CD4. The single instance of the bar and the multiple instances of the bombs and the aa's appear on the Stage as a result of coding. To put it another way, you do not move instances of the bar or the bomb or the aa to the Stage and give them instance names in the usual way. Instead, this is done all by code.

The application consists of the shooter1.fla file and 5 .as files: Gun.as, Aa.as, Bomb.as, Bar.as and Ground.as. The .fla file can be anywhere on the drive. The 5 .as files are contained in a folder named shooter, which was contained in a folder called as3 at the top of the drive. Each of the .as files contain opening and closing coding for the shooter package. An outline for each is:

package shooter { import statements class definition

}

To reinforce a point that was difficult for me to grasp: each of the class definitions is part of the shooter package.

The shooter1.fla file consists of 2 frames and 2 layers. The screen shot below shows the shooter1 .fla file, the first frame:

There are 4 objects placed on the stage: gun, ground, a button named startbtn made using a button in the Common Library, and a dynamic text field named instructions. Note that I edited the oval rust button to change the text to start. I also changed the font.

The second frame is similar to the first.. The text in the textfield has changed and the button is no longer there. The health bar is NOT on the Stage. It will be created by the program.

Concepts

The code in the .fla file and in all the .as files will be shown with explanation later. Here I will go over the critical concepts.

The use of classes and objects is best explained with concrete examples and the shooter application is a very good one. Briefly, an object puts together data and code. A class defines the data (variables) and the code (methods) for the objects of that class. New objects of a given class are created using what is termed the constructor function that is given as part of the class definition. For a new bomb, there would be a statement with

new Bomb();

A class method is for the whole class. Recall the Math.random and Math.floor methods and the Math.PI constant. For class definitions that you or I make, we indicate a class method or a class variable by the modifier static. A static variable can be changed. The static term means there is just one variable. Other modifiers specify what code can access the variable or method. A public method or variable is accessible by any code. Some object oriented computer scientists may dislike this characterization, but public static methods serve somewhat as global functions. Methods that are not static are for each object created in that class. The internal designation indicates that a variable or method is only used by code in the package. A private designation indicates that a variable or method is accessible only by code in the class. The default is internal. I have tried to go over and change this to private when appropriate. Similarly, I changed some public designations to internal to variables or methods used outside code in that particular class but in one of the other classes. Note that constructor methods must be public. Consider also that making designations overly broad does not produce any errors. What it does do is cause less checking to be done

during the translation (aka compiling) process. If you were working on a big project involving many people, this would be a disadvantage.

For the shooter application, I decided to write class definitions for the things that would appear on the screen: this is the single gun, the single ground and the single bar, and multiple bombs and aa's.

The code in this and other applications makes use of the built-in features of Flash. The good news is that there is considerable functionality to build on. The bad news is that you need to figure out what things are called and where they are found and insert the appropriate import statements. You should think of the .as files I/you write as extending the language. These also need to be imported into your program so the frame action code contains an import statement citing the package I wrote.

Each of the symbols: bar, gun, bomb, ground, aa have Linkage settings to point to the corresponding Class definition. For example, right click on the bomb symbol in the Library and then click on Linkage…in CS3 or Properties and then Advanced in CS4. You need to click on Export for ActionScript and Export in first frame. The Base class will be pre-set to flash.display.MovieClip. You will need to put in shooter.Bomb as the source file for the Class. This indicates the Bomb.as file in the shooter package (folder).

CS3 screen shot (after clicking on Linkage, then clicking Export for ActionScript and typing in shooter.Bomb)

CS4 screen shot (after clicking on Properties and then Advanced, clicking on Export for ActionScript and typing in shooter.Bomb):

Each of these class definitions are defined as extensions of the MovieClip class. This takes two operations (that can be done in any order).

Insert/New symbol and create a symbol in the usual way. Use the Linkage panel to connect the symbol to a class.

Create a class (that is, write the class definition). The header statement defining a class contains the phrase extends MovieClip This sets up the new class to inherit the variables and methods of the MovieClip class. Inheritance is one way to connect symbols to class objects.

[Note that this was not what was done in the jigsaw puzzle. In that program, a movie clip instance was placed on the Stage and named in the usual manner. This instance name was passed as a parameter to the Piece function. This is the constructor function for the Piece class. Each Piece object had a variable that held a reference to an instance already on the Stage.]

The class definition can specify variables and methods. These are in addition to the variables and methods for movie clips. The class definition can specify a constructor function. This is invoked implicitly for any instances of the symbol on the stage, for example, the gun or the ground. Each Piece object HAS-A jigsaw piece instance.

In general, think of inheritance as a way to re-use code. The subclass is an extension of the original class, called the superclass. In the situation here, I am building on MovieClip to form bomb, aa, etc. Some of the variables and methods will be used just the way there are in the original class (aka the superclass) but there probably will be new things! For example, any bomb object has a .x and a .y which will be used in the code. The .x and .y are variables in the MovieClip class.

[You can read about another use of inheritance in the Bouncing Things game. Balls (circles), rectangles and stars are created and bouncing around in a box. Inheritance is used to make some of the coding be shared.]

The gun responds to the left, right and up arrow by a call to addEventListener for the KeyboardEvent.KEY_DOWN event. The event handler will move the gun right or left for the right and left arrows. For the up arrow, the code creates a new aa. See comment below on the creation of bombs.

The clicking of the button is connected to a function in the usual way using addEventListener. One special thing that needs to be done is to call a class method called startup of the Bomb class. The call to startup includes sending the names of the ground and gun instances. This is necessary for the coding in the .as files to reference these instances. The startup method also sets the focus and does some manipulation (I call it a hack) to make sure the gun does not have a yellow box around it. The documentation indicates a way to avoid this but it did not work (see below).

Bombs are dropped based on a calculation involving Math.random. In terms of the implementation, dropping a bomb involves creating a new bomb object. When the player clicks on the up arrow, what I call an aa (for antiaircraft) is shot from the gun. The implementation for an aa is similar to that for the bomb: the program creates a new object. In both cases, for the new object to appear (to be displayed), it must be added to the display list. This is done by using the addChild method on something that already is being displayed.

Bombs move down and aa's move up using a Timer in the same way as a ball moved in bouncing ball and the cannonball moved in cannonball. In addition, the TIMER_COMPLETE event is used to remove the aa objects after they have gone up past the start of the Stage.

Bombs in motion are stopped on frame 1. When a bomb hits the ground, a gotoAndPlay command is issued to go to frame 2. Think of the target disintegrating in cannonball or the frame animation in rock paper scissors.

Implementation and Coding

Open up Flash and click on File/New to start a new Flash file. Rename the first layer board and add a new layer and name it actions. The board layer will hold the material and the actions layer will hold the [frame] code. Remember: most of the code of this application is in the .as files, not in the .fla file.

Create the symbols: gun, ground, aa, bomb, and bar. All except the bomb are 1 frame movies. The bomb has multiple frames, some of which can be keyframes and some regular frames. The frames from 2 on show an exploding bomb.

The first frame and the final frame each have a stop(); in the actions layer and the board layer as shown:

As described earlier, bring instances of the gun and the ground to the board layer and name them ground and gun1. There was no special reason to give the gun the name of gun1, except to suggest a possible extension with multiple gun instances. I also used a button in the Window/Common Library and changed the text to Start. I gave the button the name startbtn.

Write the code for frame 1. I move on to describe the code in the main movie time line and then go on to the .as files. The code in frame1 is

import shooter.*; Import the package that I create. The package (shooter folder) consists of 5 class definitions (5 .as files).

startbtn.addEventListener(MouseEvent.CLICK,startgame);

Set up the clicking on the button as an event with handler the function startgame.

function startgame(ev) { Start function definition. Note that you need to specify a parameter even

though the code doesn't use it.

Bomb.startup(ground, gun1); Call a class function of the Bomb class. Think of this as 'telling' the Bomb class how to reference the ground and the gun AND also kicking off the dropping of the bombs.

gotoAndPlay(2); Go the second frame } Close function definitionstop(); Stop at this frame.

Create a second frame by Insert/Timeline/Keyframe. The code in actions layer, frame 2 is simply:

stop();

Change the text in the text field to be "Click on space bar to stop".

You need to specify where to find the .as files. Putting this a more correct way, you need to specify where to find the shooter package that contains the class definitions. In this example, I created a package called shooter. I created a folder called shooter in a folder called as3 on my C drive. The shooter folder will contain files for each of the class definitions.

Click on File/Publish Settings and click on Flash

Click on Settings to the right of ActionScript 3.0. Click on the + sign and then the bull's eye to browse to the folder containing the package folder. In my case, this is C:\as3

A class definition is a specification of objects. It starts off with any import statements required and then includes specification of variables and methods. One method, the one with the name the same as the name of the class, is the constructor method. It is invoked to create a new object. As indicated earlier, each variable and each method can have the designation of static, meaning that it is NOT associated with a particular instance. Rather than continuing with general description, I describe how the features are used here.

By convention, a class name starts with a capital letter. So the files are Gun.as Ground.as Bomb.as Aa.as Bar.as

As I said earlier, there will be 5 files. I start with the file named Gun.as. Remember: it needs to be saved in the folder named shooter. I have specified that this folder is in c:\

as3. There is only one Gun object, the gun1 instance already on the Stage. Note each file is part of the shooter package and so starts with package shooter {

package shooter{ Specify package import flash.display.MovieClip; Import required built-in

features of Flash import flash.display.*; import flash.events.*; import flash.ui.Keyboard; import flash.text.*;

public class Gun extends MovieClip { Start class definition, specified as subclass of MovieClip

private static const UNIT = 10; Sets up the amount of horizontal movement triggered by click on an arrow key

public static var okay:Boolean = true; Set to false when health bar

goes to zero. public function Gun() { Start of constructor function

for the Gun. this.addEventListener(KeyboardEvent.KEY_DOWN,reportKeyDown);

Set up event handling for key presses

this.buttonMode = true; Necessary for accepting key

presses

} Close constructor function definition

private function reportKeyDown(ev) { Start of event handler for key presses. Note the ev parameter.

var newaa:Aa; This will hold the aa IF one is to be created.

The next statements check for what key actually is pressed.

if (ev.keyCode==Keyboard.RIGHT) { Is it the right arrow this.x += UNIT; … move the gun right } Close if if (ev.keyCode==Keyboard.LEFT) { Is it the left arrow this.x -= UNIT; … move the gun left } Close if if (ev.keyCode==Keyboard.UP) { Is it the up arrow

newaa = new Aa(); Create a new aa object newaa.x= this.x; Position it horizontally the

same position as the gun newaa.y = this.y - 50 ; Position it vertically slightly

above the gun parent.addChild(newaa); Add this aa to the display

list so it is visible. } Close the if if (ev.keyCode==Keyboard.SPACE) { Is it the space bar this.removeEventListener(KeyboardEvent.KEY_DOWN,reportKeyDown);

Stop event handling for key strokes

Bomb.timerb.stop(); Stop bombs var msg:TextField = new TextField(); Create a new text field var msgformat:TextFormat = new TextFormat();

Create a text format object

msgformat.font = "Tahoma"; Make the format specify the Tahoma font

msg.text = "Ground hits " + String(Bomb.groundhits)+ " Total shots "+ String(Aa.totalshots);

Put text into the text field

if (!okay) { If okay has been set to false msg.appendText(" ZERO BAR " ); Add the text about the bar } Close if msg.autoSize = TextFieldAutoSize.LEFT; Set up alignment to be left

justification msg.border = true; Set the text field to have a

border msg.setTextFormat(msgformat); Apply the formatting msg.x = 100; Position the msg

horizontally msg.y = 100; Position the msg vertically parent.addChild(msg); Make the text field visible } Close the if statement } Close the function } Close the class definition} Close the package

The Ground.as file essentially has not special content. It MAY be that I did not need to write anything, because when you specify a file in the Linkage panel, one is created automatically if none exists. I decided to create one because I thought I might add something to it and I wasn't that confidant that the default definition would do the correct thing. So the Ground.as file is the following:

package shooter {

import flash.display.MovieClip; public class Ground extends MovieClip { public function Ground() { } }}

The Bomb.as file contains the coding to make the determination when to drop a bomb, move the bombs down the screen and check for hitting the ground. There are no bomb instances placed manually on the Stage. They are each created by code.

In a class definition, a static method is not associated with a particular object of the class. There is just one copy of each static variable. For the Bomb, this means that the startup method is for the whole class, not any one object. This makes sense because it is called as a result of clicking on the start button before any bomb objects are created. The startup method sets up a timing event that is handled by the static method called maybemakebomb. The other methods defined in the Bomb class are for each bomb. This includes the constructor Bomb and intervalListener.

The startup method has coding that sets the gun to have the focus so that the key presses work. However, I had the problem that this put a yellow box around the gun. The documented way to remove the box did not work, so, after some experimenting, I came up with what is shown here: moving the gun off stage, do the code for the focus, and then moving it back.

package shooter{ Start package import flash.display.MovieClip; Import

statements import flash.utils.Timer; import flash.events.TimerEvent;

public class Bomb extends MovieClip { Start class definition

internal var timer:Timer; Each bomb will have a timer

internal static var groundhits:int = 0; Keep overall count of times a bomb hits the ground

internal static var healthbar:Bar; Used to hold the created Bar

internal static var timerb:Timer; The one timer for the dropping of bombs

private static const YD = 8; The unit movement of the bombs

private static const ENOUGH = 300; Limit for bomb moving down

internal static var bombs:Array = new Array();

Holds all the bombs

internal static var ground:Ground; One variable holding reference to the ground instance

internal static var groundlevel:int; One variable holding the vertical position of the ground instance

public function Bomb() { Constructor method

timer = new Timer(100, ENOUGH); Create the timer

timer.addEventListener(TimerEvent.TIMER, intervalListener);

Set up the event handler

timer.start(); Start the timer } Close method private function intervalListener(ev:TimerEvent):void {

Start definition of the method handling the timing event

this.y += YD; Move bomb down screen

if ((this.y+400)>Bomb.groundlevel) { Check if beyond the vertical level of the ground

this.y -= Math.random()*20; …re-position bomb slightly just for variety

this.gotoAndPlay(2); Set off bomb

to its second frame

timer.stop(); Stop the timer Bomb.groundhits++; Increment

static count of ground hits

Bomb.healthbar.decrease(30); Decrease the health bar

} Close if true clause

} Close method public static function startup(groundinst:Ground, guninst:Gun):void {

Start method definition, ground and gun instances are parameters

Bomb.ground = groundinst; Set the static ground variable to hold the ground instance

Bomb.healthbar = new Bar(); Create a new Bar object and set the healthbar variable to it.

guninst.x = -100; Move gun off-

stage Bomb.ground.addChild(Bomb.healthbar); Make the

health bar visible

Bomb.ground.stage.focus = guninst; Give the gun instance the focus

// next statement did not work by itself Bomb.ground.stage.stageFocusRect = false; Remove

yellow box? guninst.x = 200; Move gun

back on Stage Bomb.groundlevel = Bomb.ground.y; Set ground

level Bomb.timerb = new Timer(2000); Create new

timer for

creating bombs

Bomb.timerb.addEventListener(TimerEvent.TIMER, maybemakebomb);

Set up event handling for this timer

Bomb.timerb.start(); Start timer } Close method private static function maybemakebomb(ev) { Start method.

The parameter is not used.

var i = bombs.length; Set i to current size of array holding all the bombs

if (Math.random()>.5) { Bombs are created on average only ½ the time.

bombs.push(new Bomb); … create new bomb and push reference to bombs array

bombs[i].x =Math.floor(Math.random()*550)-Bomb.ground.x;

Position bomb randomly

bombs[i].y = 0-Bomb.groundlevel; Position bomb vertically above stage

Bomb.ground.addChild(bombs[i]); Make new bomb visible

} Close if true clause

} Close method } Close class

definition} Close package

The Aa.as file sets up the coding for the aa objects. Note that new aa objects are created by code in the Gun.as file (look back at the code in the reportKeyDown method). As is the case for bombs, there are no aa instances manually placed in the Stage. They are created by code.

package shooter { Start package definition

import flash.display.MovieClip; Import statements

import flash.utils.Timer;

import flash.events.TimerEvent; public class Aa extends MovieClip { Start class

definition private var timer:Timer; One timer per

object private static const YD = 20; Only one

constant value for vertical movement

private static const ENOUGH = 300; Specifies how much vertical movement (used to know when to delete aa's)

public static var totalshots:int = 0; Keep one count of aa's

public function Aa () { Constructor method

totalshots++; Increase count

timer = new Timer(100, ENOUGH); Create timer timer.addEventListener(TimerEvent.TIMER, intervalListener);

Set up event handler for timing interval

timer.start(); Start timer timer.addEventListener(TimerEvent.TIMER_COMPLETE, overListener);

Set up event handler for when ENOUGH intervals have occurred

} Close constructor method

private function intervalListener(ev:TimerEvent):void {

Start method

this.y -= YD; Move aa up screen

var i; Set variable i=0; Initialize while(i<Bomb.bombs.length) { Loop over all

the bombs if (Bomb.bombs[i].hitTestObject(this)) { Did this aa hit

the ith bomb? Bomb.bombs[i].visible = false; ….make

bomb invisible

Bomb.bombs[i].timer.stop(); Stop its timer Bomb.bombs.splice(i,1); Remove from

list of bombs Bomb.healthbar.increase(10); Add to health

bar } Close if true

clause else { Else i++; Increase i } Close else

clause } Close while

clause } Close method private function overListener(ev:TimerEvent): void {

Start method definition

timer.stop(); Stop the timer parent.removeChild(this); Remove this

aa from display list

} Close method } Close class

definition} Close

package

The Bar.as code handles the decreasing and increasing of the height of the health bar. The constructor method does not do anything, so creating a new bar just creates a new instance of the bar symbol in the Library.

package shooter{ import flash.display.*;

public class Bar extends MovieClip {

public function Bar():void {

} internal function decrease(amt:int):void { if (this.height <= amt) { Gun.okay = false; this.height = 0; } else { this.height -= amt; } } internal function increase(amt:int):void { this.height += amt; } }}

Recap

To make this game, you need to

Design the elements: gun, ground, bombs, anti-aircraft that I call aa, health bar. Set up the Linkage as described above for each symbol.

Create the start button. Create anything else you want on the Stage, drawing directly on the Stage or

using graphic or movie symbols. Place the gun and the ground and the start button on the Stage and give each an

instance name. Create the text field and write the text for the starting frame. Create a second frame and modify the text field to be the instructions 'during' the

game. Write the code for frame 1 and frame 2. Write the class definition code, that is, the 5 .as files and save in a folder named

shooter in a folder names as3. In Publish Settings in the .fla file, specify the classpath.

I often write tutorials in an iterative fashion to capture more how I program. You can decide whether you want to

Build the whole game: design the gun, ground, bar, bomb and aa symbols the usual way, and then put in the code in the frames and then in the 5 .as files.

Or

Proceed iteratively using what you have learned and consulting with the code. Here is one plan: first build the gun and make it move. You will need to create a

version of the Gun.as file. You might put in a trace statement in place of the code to create a new aa. Then write the code for starting the game. The first version of the event handler for the bomb could just make it fall down without any checks, then add checking for the ground. Next add the shooting of the aa's. Finally add the ending of the game.

ActionScript 2.0 to ActionScript 3.0

Only read this if you are familiar with previous versions of Flash and want to reflect on the differences.

In my AS 2.0 version of shooter, I created seed instances for bombs and aas and used duplicateMovieClip to produce the new ones. The ActionScript 3.0 version is cleaner. The creation of new objects is exactly what is called for and it makes the complexity of the Linkage process worthwhile.

The start up process required an event handler 'in' frame code and a static method in the Bomb class.

The setting of focus is done explicitly in this version of the shooter.

Recall that ActionScript 2.0 had a hitTest method that could be used two different ways. This is termed overloading. I like the approach in ActionScript 3.0 that has two different methods: hitTestObject and hitTestPoint.