Create an Epic War Game in Flash: Part 2


In the first lesson we created an engine to build the environment for our game; a camera that can follow active player objects; and one player object, the tank. But the game is still far from finished. During this tutorial we'll fix all of the problems and turn it into an actual game. Let's get started!

Also available in this series:

  1. Create an Epic War Game in Flash: Part 1
  2. Create an Epic War Game in Flash: Part 2

Step 1: Initial Code for Tank Shell Class

At the beginning we'll create a shell for player tank. Open up FlashDevelop and in the root directory of your project create a folder called "shells". Right-click it and add new class. Call it Shell.

Here's the initial code for it:

The code should look familiar because we already used similar construction in the first lesson.

Step 2: Tank Shell MovieClip

Open up WarGame.fla and go to Insert > New Symbol. Choose Movie Clip and attach it to

This shouldn't give you any errors, so if it does, check everything once again.

Now choose PolyStar Tool from the instruments palette:

Go to tool's options and set Number of Sides to 3, so you could draw a triangle

For Stroke Color choose none and for Fill > Linear Gradient

In the Color palette transform the gradient fill like this

Now draw a triangle with the top at the registration point

This can be done pretty easily. Just type in these parameters:

To make it look more like a real shell, choose Convert Anchor Point Tool

Click the topmost point of the shell and simply drag the handlers to the right or left to make it look like this, and save it:

Step 3: Explosion MovieClip

Go to Insert menu once again and insert another MovieClip symbol. Call it Explosion and click OK. You don't have to export it for ActionScript because it will be used only as a nested MovieClip inside our Shell.
Pick up Brush Tool, set its width to whatever value you like (the bigger the better). Choose "none" for Stroke Color and yellow for Fill.

Draw some irregularly shaped spot like this:

This shape will represent the explosion. Red and yellow parts are for the fire (as you could guess) and black ones are for smoke. Exit the Symbol Editing Mode and open Shell symbol again.

Step 4: Assembling the Shell

Now right-click the second frame of the Shell timeline and choose Insert Bank Keyframe

Drag Explosion MovieClip from the library to this frame, give it an instance name of mExplosion and change its width and height to 4

Select mExplosion (by clicking it once) and add Blur filter to it

Set Blur X and Blur Y to 12 and Quality to High

Now select the 6th frame and hit F6 to insert another Keyframe. Select this frame

...and change mExplosion's width and height to 40

Right-click any frame between 1st and 6th and choose Create Classic Tween

The last thing you need to do now is to stop the shell's animation from playing. To do this, select the first frame and hit F9. When the actions panel pops up, type in stop(); and close it.

Step 5: First Variables for the Shell

In order to move the shell, we need to define its speed, direction (which is equal to the rotation parameter passed to the constructor), initial position (not this.x and this.y, because these values will change during the shell's flight but we'll need constant values), destination point, and the Boolean variable to check if the shell's hit something.

So, first thing to do now is add these variable to Shell class:

Initialize them in the constructor. Your class should look this way now:

Step 6: Where To Fly and For How Long?

To move the shell we need either an ENTER_FRAME or TIMER event listener. We'll use ENTER_FRAME here. Go to the constructor method and add Event.ENTER_FRAME listener to it.

then create an event handler for moveShell().

We're close to moving the shell now, but what distance should it travel? It's obvious that the destination point is the mouse's position at the moment the shell is created, so we only need to calculate the distance between the destination point and the shell's initial position.

Luckily, Actionscript can do it for us, we only have to pass two points that we need to know the distance between to the Point class's distance() function. Let's do it.
Add one more variable:

and initialize it in the constructor:

That's it. Now the program knows the distance that the shell should travel when it's created.

Step 7: Choice Between Movement and Shooting

You probably want to test the shell already, but we can't do it until we create some means to launch it. The idea is: when you click somewhere the tank moves to the pointer's position, but if you press Control key and then click, it shoots at the pointer. To do this, open up in FlashDevelop and find where you declared these variables:

Add one more variable right below them

Go to buttonControls() method and add the next conditional statement (that will set _controlPressed variable to true) to the top of it.

Since we have code that sets variable to true when Control key is pressed, we need another one that'll set it back to false when the key is released. Go to releaseKey() method and add one more case to its switch statement:

Now we need to delimit when out tank should move and when shoot. Find moveObjects() method. That's how it looks now:

And modify it like this:

If you test it now, it will give you a #1069 Error because Ptank class does not implement shoot() method yet.

Step 8: Shoot() Method Test

Open up in FlashDevelop and create public function shoot() somewhere (I'd do it right after the constructor).

And now, if you test it again, it will trace "shoot" string when you click the stage with Control key pressed.
Remove the trace statement and paste the next line instead:

It's not the correct version of the method, but at least we can see if the tank can create a shell.
Test it now, and if you see a new shell right over the center of the turret every time you Control-Click somewhere > everything is fine.

Note: if you're working in FlashDevelop it will do all the imports for you, but if not, don't forget to import the necessary classes. In the case with shell it'll be:

Step 9: Major Modifications to shoot() method

It's time to pass real parameters to Shell.

Test it again and you'll see shells appear in the correct positions

Step 10: Major Modifications to

Go to moveShell() method and update it like so:

Step 11: What Objects Can the Shell Hit?

That's a reasonable questions. Normally it should hit enemies or some static stuff like trees or houses. And since we don't have any enemies yet, let's make it hit some statics.
Open up and add one more variable to it

Find where you added trees to the camera

And add a line that will push it into the hittableStuff array right after it

The idea here is that BitmapData has a perfect collision detection method. We can now test whether some object collides with its transparent pixels or not.

Step 12: Shell HitTesting

In find moveShell() method and add the next "for in" loop right before the "if statement"

If you shoot now, it will trace "hitting bitmap" which means that the shell can touch trees or houses. So, now we need to find out what kind of pixel it touches, transparent or not.
Remove trace statement and put the next "if" statement instead of it:

Step 13: Removing the Shell After Explosion

If you test the game at this point, you'll see how the shell explodes if it touches a tree or a house but it is not removed after it and it doesn't look too good.
You probably think now, where do I remove the shell?

That's a reasonable question. If you remove the shell right after the collision you won't see an explosion animation and thus you can't check if it can kill some character or not. So the best place to remove shell is the last frame of explosion animation.

Open up Shell Movie Clip in Flash (by double-clicking its icon in the library) and add one more layer above the animation layer, call it AS3.

Now convert the last frame and the one before it to empty key frames. Select the last one and hit F9 to open Actions Panel and place the code that will remove shell to it:

If you test the game now, you'll see how the shell is removed after explosion. Easy isn't it?

As to frame before the last one, will come back to it later, after we create the enemy tank.

Step 14: Enemy Tank Coloring

Basically, enemy tank should be almost just like the player's one, except for coloring. Open tankBody.png in Photoshop and hit Control + U to open Hue / Saturation panel and adjust colors to whatever values you like. I chose these parameters:

Save it for Web And Devices as enemyTankBody.png. Do the same to the turret graphics. If you don't have Photoshop you can use my graphics from the Source download above.

Drag the graphics to Flash library and assemble enemy tank Movie Clip just like we did in the first part of the tut with the player's tank.

Step 15: Tank Explosion Animation

Close turret and body layers and add a new layer above them. Then select 6th frame and hit F6 to add a blank key frame. You should get this:

Select the last frame and simply drag it to the second position to get this:

Drag Explosion Movie Clip from the library to the second frame of explosion layer. Add Blur filter to it.

  • Blur X: 13
  • Blur Y: 13
  • Quality: High

Select the last frame and hit F6 to convert it to Key Frame. Then scale Explosion Movie Clip to get something similar to this:

Basically it's the same as with shell explosion animation, so you should also create a Classic Tween . And don't forget to add stop(); method to the first frame of the explosion layer as well.

Do the same to the Tank (player tank) Movie Clip.

Step 16: Enemy Tank Class

Enemy tank is supposed to be just like player tank, except for one thing, it will be controlled by Artificial Intelligence. But for now just duplicate Ptank class and call it Etank, and also change the class and constructor names inside:

Attach this class to EnemyTank MovieClip like you did before with player tank:

Step 17: Two More Custom Events

Open up and add two more "events" (constants) to it:

Both of these events will be dispatched by a shell. The first one is needed to inform active objects that they have been damaged and the second one will be used to let tank shoot again only when the previous shell is removed. Actually tank could shoot without it but it would cause 2 big problems.
1)If it shoots too many times, flash player might just freeze because it wouldn't be able to process an enormous number of shells at a time.
2)If enemy tank spotted you, it would just give you no chance to survive by shooting a series shells at you.

Step 18: How To Add Flexibility to GameStructure Class?

By now, we have manually added player tank to the display list. This system is far not the most flexible because we can't add active characters for a particular mission without recompiling the whole project. So, the best way to work around this problem now is to add mission characters right to the Assets.xml. Open it up in FlashDevelop and add one node for player tanks and one for enemies. Here's the entire code for Assets.xml.

I've also added the position and rotation attributes.

Step 19: Adding Player Tanks Dynamically

Open and find where you added player tank:

and add this code instead:

Check if you have the same GameStructure code as below, and test the game.

Step 20: Object Selection

You've noticed that your tank is added to the display list but you can't control it anymore. But if you click the tank you'll get the next text in the output panel:
[object Ptank]

This means that everything works.
Now we just need to push our tank to playerObjects array. But first, add one more variable:

and just like before, update releaseKey() method to process shift release:

and buttonControls():

You also need to modify moveObjects() method:

And finally, a little modification to selectObject() method:

Thus, when you press Shift key you can select some of the objects you have in current mission.

Step 21: Adding Enemy Character

Since enemy tank is already added to Assets.xml, find the loops that adds playerTanks, and add one for enemy tanks right below it.

Compile the game and you'll see enemy tank in the top left corner. There is a tank but there is no means to destruct it and we need to fix it.

Step 22: Enemy Tank Destruction Preparations

It might sound a bit complicated at first but actually it's a pretty easy task to accomplish. We've already created custom event that should be dispatched to the character that's about to be destroyed, so all we need to do now is add event listener for this type of event to each character.

Open up in FlashDevelop and go to its constructor method. Add one line to it:

Create event handler:

Alright, the tank can now receive this event and play explosion animation. The only problems is that after the animation is finished, it will remain on battlefield safe and sound, so we need to remove it right after the animation is complete. For this purpose, open EnemyTank Movie Clip in Flash, select the last frame of explosion layer and hit F9 to open Actions panel. Add the next code to it:

Step 23: Dispatch DAMAGE Event

It's obvious that this event should be dispatched by some object that collides with the tank. First of all let's make our shell do it. Open Shell Movie Clip in Flash and go to that blank key frame before the last one. Hit F9 and paste the next code into the Actions panel:

By the way, after I tested it, I didn't like that the tank explodes too fast, so I added a few frames to its explosion animation (do the same to player's tank)

Step 24: Fixing A Little Problem With Player Tank

You've noticed that when you select a tank, it does not start rotation its turret until you click somewhere. It happens because there's no ENTER_FRAME listener added to it after the selection. This problem can be fixed by creating another public method that'll add it.

Open up and create this new method somewhere:

Now you need to call this method from Find selectObject() method and update it like this:

It could have been so simple but it will make all of your tank instances "look" at the pointer even though only one is supposed to be active. We need to make sure this doesn't happen. Open up Ptank again and add this code to the top of moveMe() method:

The code is self-explanatory, so you can understand what it does.

Step 25: Player Tanks Destruction

Just like you did with enemy tank, add DAMAGE event listener to Ptank, and create the same event handler for it. Then, also add this code to the last frame of player tank's explosion animation:

You can test how your tank can now destroy itself by losing in on the trees and shooting at them. The explosion of a shell will also touch your tank and destroy it.

Step 26: Enemy Detector

There's a lot of books written on artificial intelligence and of course it's impossible to explain it all in a single tutorial. But anyway, we need some enemy reaction to our actions. That's why we need some basic AI for it.
First of all, let's create a "radar" for it. Open up and declare a new variable:

then add a new private function that will create a radar:

It simply draws something similar to a radar wave. Call this method from the constructor:

And you'll see this:

If you compile the game now, enemy tank will turn its turret to the pointer's position. It's nothing, we'll fix it later.

Step 27: Trying To Detect Player

The idea of player detection is based on hitTestObject method. Radar tests for collision every frame and if some player object touches it, the tanks starts to follow this object and shoot at that. Let's add this check to the very top of Etank's moveMe() method

If you touch the radar area you should get "I see target" string in the Output panel

Step 28: Removing Reaction to the Pointer

This step is truly the easiest in the whole project. Just go to moveMe() method and remove these lines of code:

Step 29: What To Use Instead of the Pointer?

Basically, we need to pass target's position to the tank instead of mouse's, and since the destination point is set in prepareToMove() method, we need to modify this method. But before doing it, change all methods' (except for constructor's) access modifiers from public to private, because you won't need to access any of them from outside this class. Now go to prepareToMove() method and find these two lines (they should be at the very top of it):

Step 30: Cleaning Unnecessary Lines

As enemy tank's code is a copy of player tank's, it also implements the code that controls camera movement. Of course we don't need it, so go to moveMe() method and remove all of these lines:

And remove this line:

...from both player and enemy tanks' prepareToMove() methods

Step 31: Attempting To Follow Target

It's time to try and follow the detected target. Go to "for in" loop that we created in Step 27 and modify it like so:

You probably want to ask why I added 90 to some angles. To say the truth, I didn't expect this bug, but it happened. When tank detected target, its turret and radar turned 90 degrees away from it (for some unknown reason), so I thought it was the easiest way to work around this problem, and it did the trick.

Test the game to see how the tank follows you.

Step 32: Fixing A Problem With Etank's Rotation Tween

You may have noticed that when you turn too much away from enemy tank, it can't turn directly to your tank while it moves, thus it moves a bit sideways. This happens because it creates new tween every frame, and so its animation just has no time to complete until you stop giving it new destinations.

I found pretty simple way to work around this issue. Firs of all, we'll need some "toggler" variable, which will not let the tank create new tween if is set to false. Let's create it:

Then put the tween into a conditional statement that will let/forbid it to play.

And finally, create toggleTrue() event handler:

Make sure you have the same Etank code as below, and test the game.

Step 33: Shooting Restriction

Of course you wouldn't want enemy tank to destroy your tank with a series of shots right after it spots you. So first, we need to restrict its ability to shoot. It will be able to shoot again only after the first shell is removed. Open Shell Movie Clip in Flash, select the last frame of the AS3 layer and paste this line to the end:

Now it can dispatch SHELL_HIT events. Since enemy tank cannot shoot yet, we'll check this system on Ptank. Open up in FlashDevelop and add a new variable:

This variable is supposed to be set to true right after the tank has shot, and set back to false after the shell receives SHELL_HIT event. While it's true, tank won't be able to create another shell.

Update shoot() method:

Do the same to enemy tank.

Step 34: Attack Player Or Not?

Since player tank shoots at pointer's position, enemy tank should not do the same. So, let's modify its shoot() method a little bit:

Now go down to moveMe() method and find this line:

and add this code right after it

Now go to detector() method and change its visibility from true to false

Compile the game and try to get in front of enemy tank. If you don't like its shooting tempo, experiment with random values. Try changing 990 to something smaller until you're happy with the results

Step 35: Responses To Collisions

At this point, we need to check if the tank collide with something. Open and go to its moveMe() method. Find the lines that move it:

Add this "for in" loop after them:

Do the very same thing to enemy tank, and test the game

Step 36: Fixing Little Problem with a Hit Tank

Everything seems to be working great, except for one thing. After enemy hits your tank it destroys itself but still remains in the hittableStuff array, so enemy keeps on shooting at the position where your tank was. Lets fix it now.

Open Tank Movie Clip in flash and go to the last frame of Explosion layer. Open Actions panel and find the line where tank removes itself:

Add this code right before it:

Do the same to EnemyTank

Step 37: Water Detection

When some objects moves over a painted area, it can detect what color it is over by using bitwise operators. Our tanks move over Bitmap map, so they need to access its color. To do this, open, find where you declared _bmdContainer variable and change its access modifier from public to public static:

It can now be accessed from outside this class. So, we only need to determine what is water and what is not. Get back to Ptank class, and in its moveMe() method find these lines:

Add this code right after them:

Step 38: Drowning Animation

Our tank can drown now but there's now animation for this action. Open Tank MovieClip in Flash, click tank's body to select it, and give it an instance name of mBody (like you did with mTurret earlier). This step is necessary because the tank's body must also be accessible from outside

Select the second frame of explosion layer, then select explosion Movie Clip and also give it an instance name of mExplosion (you should do the same for the last frame too).

Create new layer and call it drowning. Lock all other layers and draw several white circles like this:

This circles are supposed to represent air bubbles when tank drowns. Select all circles and hit F8 to convert them to movie clip. Give it an instance name of mBubbles.

Then open this Movie Clip and create 10 or more layers. Draw more white circles in each frame chaotically so that they form a bubbling animation.

Quit symbol editing mode, select the first frame of explosion layer open Actions panel and add one line to it: mBubbles.visible = false;

And, of course, do all of this with the enemy tank too

Step 39: Making Tank Drown

We already have a method that destroys tank. Basically, drowning should be exactly the same but we'll set mBody, mExplosion and mTurret visibility to false, and mBubble to true.
Find where you traced this (in Ptank):

And add this code instead of the trace statement:

Test the game

Step 40: Winning Or Losing Setup

The game is almost ready, there's only a few things left to be done. First of all, let's create two events for Winning and Losing, and one for checking whether you lose or not. Open up and add three more constants:

Now we need another way to initialize game. Right-click the project file in FlashDevelop and add a new class. Call it Initializer. Here's the code for it:

Notice that I've passed "stage" as a parameter to GameStructure. If you test it now, it'll give you an error because GameStructure does not receive any parameters at the moment. Let's fix it.

Open GameStructure class in FlashDevelop, click Ctrl + H to find and replace text, and replace all stage. words to _stage. (don't forget the dot at the end, it's important!)

Add a new variable:

And modify the constructor

To determine whether you lose or win, you'll need two arrays. One to contain all of the player objects and one for enemies. After a tank is killed it will splice itself from the array; the program will then check if some array is empty and, if so, dispatch YOU_WIN or GAME_OVER event depending on which array is empty. So add these two arrays to GameStructure:

Now find where you created player tanks and enemy tanks and add a line that will push them into respective arrays:

And, of course you'll need an event handler for it:

Step 41: Splicing Killed Objects From Arrays

Splicing tanks from enemy and player arrays is a very easy task. Open up Ptank and find destroyTank() method. Add these lines to the top of it:

Do the same with Etank (only changing the word player to enemy)

Step 42: Check For Winner

Now we have checkWinner() function that dispatches GAME_OVER or YOU_WIN events, but there are no listeners for these event, and, of course, no event handlers. Let's create them now. Open up and add two event listeners to gameStructure instance:

Step 43: Some Graphics to Indicate Game State

Well, maybe graphical representation of "Game Over" or "You Win" phrases is not very important, but I think the game wouldn't look good without them. Open up Flash and insert new Movie Clip symbol (Ctrl + F8). Select Text Tool (T), choose some Fill color, Tahoma font, font size 31, bold, and type "Game Over". Align it on stage:

Press Ctrl + B twice, to turn the font into vector shape. Insert new key frame and type "You Win" on it. Also turn it into vector shape.

Add the third frame with phrase "Next Mission". It will be shown if the mission that you won wasn't the last.

Now you need to export GameState Movie Clip for Actionscript

Step 44: Mission Number

If you remember from the previous lesson, the number of the current mission is set right inside MapBuilder class. We need another way of setting that. So open up and go to its constructor method. Add only one line to it:

Since there's no "currentMission" variable in the Initializer class so far, let's create it.

From now on, the current mission number depends on this variable's value

Step 45: Major Modifications to the Initializer Class

The idea of these modifications is that when losing() method is triggered, the current mission remains the same and you need to click the phrase "Game Over" to play the mission again, and when winning() is triggered, you click "Next Mission" to play the next mission or "You Win" to start the game over.

To do it all, we need to make a lot of modifications to Initializer, both tanks' classes, and the GameStructure class. First of all, we'll need to load Assets.xml from Initializer to know how many missions are there in the game, then add some cleaner method to GameStructure, to remove all of the present environment before creating another mission. Here's the modifications to Initializer (follow the comments):

Step 46: Major Modifications to the Initializer Class

In the Initializer class we call two methods that are supposed to be part of GameStructure but they aren't yet. Let's fix this problem. Open up and add these two methods:

Step 47: Tiny Modification to Etank

Finally, open and add one public method to it:

The only thing left to do now is to create more missions. I've already created three missions, you can use them to test the game or feel free to create your own.

Well, that's it. It's been a lot of work, but I hope you liked my tutorial, and it helped you learn some techniques that you didn't know before :)