Make a tanks clone: Part 1 - The tanks

10/04/2008

I'm a big fan of tanks. Previously, I wrote about a tank clone written in ActionScript 2.0. This tutorial will show you how to make tanks using OOP in ActionScript 3.0. Therefore, I am assuming that everyone is using Adobe® Flash® CS3. This is what we will be making today:

(Either JavaScript is not active or you are using an old version of Adobe Flash Player. Please install the newest Flash Player.)

Up and Down arrow keys to rotate turret. Space to fire, pgUp and pgDown to adjust power.

Getting Started

To start off with, it is good if we make a new folder somewhere, called TanksClone. We'll stick all our files in here and it will be much more organised. Using Flash, create and save the following four files into the folder TanksClone.

  • tanks.fla (make sure it is an ActionScript 3.0 document.
  • Main.as
  • Tank.as
  • Bullet.as

These are the only four files that we will be using today.

Note: To create new .as files, press ctrl+N in Flash, select ActionScript File and press OK.

Setting up the graphics

This would be a good place to continue; setting up the graphics. We know that we will need graphics for at least two things - the Tank and the Bullet. Also, we will need a graphic for the Tanks' turret. This is so that we can rotate the turret seperate to the tank later on. OK, this is how we go about it.  Draw your tank in Flash using whatever drawing tools you like. Select the tank and press F8 to convert this symbol to a MovieClip. Enter tank_mc into the Name: field but don't press OK yet. Switch to the advanced mode by pressing the Advanced button. Check the box that says Export For ActionScript. Enter Tank into the Class: field.

Creating the Tank graphic

Repeat this twice by drawing the bullet and the turret, entering into the Class: field Bullet and Turret  respectively.

Note: It is convential to name classes with a capital letter at the start of each word eg. MovieClip. Also, on a side note, it is convential to name variables with a lowercase letter at the start and a capital letter for each word thereafter eg. enemiesHealthBarValue. Although these conventions do not always have to be followed; they just make your code neater.

Here's what I drew for each part that we needed:

Our Tank

Tank

Our Bullet

Bullet

Our Turret

Turret

Let's make the Tank class!

Alright folks, here we go. Open up Tank.as. This is the code we will be writing in this file:

Actionscript:
  1. package {
  2.  
  3. import flash.display.MovieClip;
  4. import flash.display.Stage;
  5. import flash.events.KeyboardEvent;
  6. import flash.events.Event;
  7. import flash.ui.Keyboard;
  8.  
  9. public class Tank extends MovieClip {
  10.  
  11. public var theStage:Stage;
  12. public var up:Boolean = false, down:Boolean = false;
  13. public var pgUp:Boolean,pgDown:Boolean;
  14. public var power:Number = 3;
  15. public var angle:Number = 0;
  16. public var turret:Turret;
  17. public var playerName:String;
  18. public var health:Number;
  19. public var canMove:Boolean;
  20. public var alive:Boolean = true;
  21.  
  22. public function Tank (n:String,s:Stage) {
  23. theStage = s;
  24. playerName = n;
  25. theStage.addEventListener (KeyboardEvent.KEY_DOWN,keyIsDown);
  26. theStage.addEventListener (KeyboardEvent.KEY_UP,keyIsUp);
  27. addEventListener (Event.ENTER_FRAME,manageMovement);
  28. turret = new Turret();
  29. addChild (turret);
  30. }
  31. public function keyIsDown (e:KeyboardEvent) {
  32. if (canMove) {
  33. if (e.keyCode == Keyboard.UP) {
  34. up = true;
  35. }
  36. if (e.keyCode == Keyboard.DOWN) {
  37. down = true;
  38. }
  39. if (e.keyCode == 33) {//pgUP
  40. pgUp = true;
  41. }
  42. if (e.keyCode == 34) {//pgDOWN
  43. pgDown = true;
  44. }if (e.keyCode == Keyboard.SPACE) {
  45. fire ();
  46. }
  47. }
  48. }
  49. public function keyIsUp (e:KeyboardEvent) {
  50. if (canMove) {
  51. if (e.keyCode == Keyboard.UP) {
  52. up = false;
  53. }
  54. if (e.keyCode == Keyboard.DOWN) {
  55. down = false;
  56. }
  57. if (e.keyCode == 33) {
  58. pgUp = false;
  59. }
  60. if (e.keyCode == 34) {
  61. pgDown = false;
  62. }
  63. }
  64. }
  65. public function manageMovement (e:Event) {
  66. if (canMove) {
  67. if (up) {
  68. angle+=.02;
  69. }
  70. if (down) {
  71. angle-=.02;
  72. }
  73. if (pgUp) {
  74. power += .02;
  75. }
  76. if (pgDown) {
  77. power -= .02;
  78. }
  79. } else {
  80. up = false;
  81. down = false;
  82. }
  83. turret.rotation = angle * 180/Math.PI;
  84. }
  85. public function fire () {
  86. var vx:Number = power*Math.cos(angle);
  87. var vy:Number = power*Math.sin(angle);
  88. var bullet:Bullet = new Bullet(x,y,vx,vy,angle);
  89. parent.addChild (bullet);
  90. canMove = false;
  91. }
  92. }
  93. }

Explanations:

Actionscript:
  1. package {
  2.  
  3. import flash.display.MovieClip;
  4. import flash.display.Stage;
  5. import flash.events.KeyboardEvent;
  6. import flash.events.Event;
  7. import flash.ui.Keyboard;
  8.  
  9. public class Tank extends MovieClip {
  10.  
  11. public var theStage:Stage;
  12. public var up:Boolean = false, down:Boolean = false;
  13. public var pgUp:Boolean,pgDown:Boolean;
  14. public var power:Number = 3;
  15. public var angle:Number = 0;
  16. public var turret:Turret;
  17. public var playerName:String;
  18. public var health:Number;
  19. public var canMove:Boolean;
  20. public var alive:Boolean = true;

Here we are simply setting up this class; importing all the classes we will need and declaring all of our variables.

Actionscript:
  1.         public function Tank (n:String,s:Stage) {
  2. theStage = s;
  3. playerName = n;
  4. theStage.addEventListener (KeyboardEvent.KEY_DOWN,keyIsDown);
  5. theStage.addEventListener (KeyboardEvent.KEY_UP,keyIsUp);
  6. addEventListener (Event.ENTER_FRAME,manageMovement);
  7. turret = new Turret();
  8. addChild (turret);
  9. }

This is the constructor function. It is run when we first create our Tank object. The two paremeters we recieve are our tank's name and the stage; we use this to communicate with the main movie. We add three listeners here: KEY_DOWN, KEY_UP and ENTER_FRAME. Instead of writing Event.ENTER_FRAME we could have easily written "enterFrame", but this way is neater and causes less headaches if we have any errors in our code.

In the constructor we also create a turret movie clip and add it as a child. All we need this for is to display where our tank is aiming.

Actionscript:
  1.         public function keyIsDown (e:KeyboardEvent) {
  2. if (canMove) {
  3. if (e.keyCode == Keyboard.UP) {
  4. up = true;
  5. }
  6. if (e.keyCode == Keyboard.DOWN) {
  7. down = true;
  8. }
  9. if (e.keyCode == 33) {//pgUP
  10. pgUp = true;
  11. }
  12. if (e.keyCode == 34) {//pgDOWN
  13. pgDown = true;
  14. }if (e.keyCode == Keyboard.SPACE) {
  15. fire ();
  16. }
  17. }
  18. }
  19. public function keyIsUp (e:KeyboardEvent) {
  20. if (canMove) {
  21. if (e.keyCode == Keyboard.UP) {
  22. up = false;
  23. }
  24. if (e.keyCode == Keyboard.DOWN) {
  25. down = false;
  26. }
  27. if (e.keyCode == 33) {
  28. pgUp = false;
  29. }
  30. if (e.keyCode == 34) {
  31. pgDown = false;
  32. }
  33. }
  34. }

These two functions each listen for an event: keyIsDown() listens for KeyboardEvent.KEY_DOWN and keyIsDown() listens for KeyboardEvent.KEY_UP. These simply change the value of booleans up, down, pgUp and pgDown so that we can either tell if one of those keys are being pressed or not. We then go on, in manageMovement() to adjust power and angle every frame if one of these are down. I like to handle keys this way because it gives a more fluid result.

keyIsDown() also calls the function fire() if the space bar is pressed.

canMove is simply a boolean - It is changed to false after the tank fires and will then be later changed to true after it is this tank's turn again.

Actionscript:
  1.         public function manageMovement (e:Event) {
  2. if (canMove) {
  3. if (up) {
  4. angle+=.02;
  5. }
  6. if (down) {
  7. angle-=.02;
  8. }
  9. if (pgUp) {
  10. power += .02;
  11. }
  12. if (pgDown) {
  13. power -= .02;
  14. }
  15. } else {
  16. up = false;
  17. down = false;
  18. }
  19. turret.rotation = angle * 180/Math.PI;
  20. }

Simply adjusts power/angle variables and the turret's rotation.

Actionscript:
  1.         public function fire () {
  2. var vx:Number = power*Math.cos(angle);
  3. var vy:Number = power*Math.sin(angle);
  4. var bullet:Bullet = new Bullet(x,y,vx,vy,angle);
  5. parent.addChild (bullet);
  6. canMove = false;
  7. }

When this function is called the tank fires a bullet using its current angle and power to work out velocities for the bullet. We then add the bullet to our parent MovieClip and set canMove to false (we have now finished our turn.)

The Bullet Class

Open Bullet.as and plug in this code:

Actionscript:
  1. package {
  2.  
  3. import flash.display.Sprite;
  4. import flash.display.MovieClip;
  5. import flash.display.Stage;
  6. import flash.events.Event;
  7.  
  8. public class Bullet extends Sprite {
  9.  
  10. public var vx:Number = 1;
  11. public var vy:Number = 0;
  12. public var ay:Number = 0.05;
  13.  
  14. public function Bullet (_x:Number,_y:Number,_vx:Number,_vy:Number,_angle:Number) {
  15. vx *= _vx;
  16. vy =_vy;
  17. x = _x;
  18. y = _y;
  19. rotation = _angle * 180/Math.PI;
  20. addEventListener (Event.ENTER_FRAME,manageMovement);
  21. }
  22. public function manageMovement (e:Event) {
  23. //Move the bullet
  24. vy += ay;
  25. y += vy;
  26. x += vx;
  27. //Work out its rotation
  28. var _angle = Math.atan2(vy,vx);
  29. rotation = _angle * 180/Math.PI;
  30. if (x <0 || x> 600 || y <0 || y> 400) {
  31. dispose ();
  32. }
  33. }
  34. public function dispose () {
  35. removeEventListener (Event.ENTER_FRAME,manageMovement);
  36. MovieClip(parent).increaseTurn ();
  37. parent.removeChild (this);
  38. }
  39. }
  40. }

This code will manage the bullet's when they are on the stage. All we do here is have a listener listening for the "enterFrame" event so that the bullet adjusts its' position and rotation every frame. If the bullet is outside the stage, dispose of the bullet (remove listener and remove it from its' parent.)

The Document Class

Now that we have written the classes for both the tank and bullet, we now have to write a controlling class, which will add the tanks to the stage and manage turns etc.

Open tanks.fla and head towards the Properties panel. In the text box labeled Document class enter "Main" (without the quotations.)

This means that Main.as is linked to this flash document. Whenever we run the .swf, Main.as is instantiated.

Open Main.as

Plug in this code:

Actionscript:
  1. package {
  2.  
  3. import flash.display.MovieClip;
  4. import flash.events.KeyboardEvent;
  5. import flash.text.TextField;
  6.  
  7. public class Main extends MovieClip {
  8.  
  9. public var tanks:Array = new Array();
  10. public var turn:int = 0;
  11. public var displayName:TextField;
  12.  
  13. public function Main () {
  14. tanks.push (new Tank("Player One",stage));
  15. tanks.push (new Tank("Player Two",stage));
  16. tanks.push (new Tank("Player Three",stage));
  17. for (var i in tanks) {
  18. tanks[i].x = Math.random()*580+10;
  19. tanks[i].y = 350;
  20. addChild (tanks[i]);
  21. }
  22. tanks[0].canMove = true;
  23.  
  24. displayName = new TextField();
  25. displayName.selectable = false;
  26. addChild (displayName);
  27. displayName.text = tanks[0].playerName + "'s turn";
  28. }
  29. public function increaseTurn () {
  30. turn++;
  31. if (turn>= tanks.length) {
  32. turn = 0;
  33. }
  34. while (!tanks[turn].alive) {
  35. turn++;
  36. if (turn>= tanks.length) {
  37. turn = 0;
  38. }
  39. }
  40. tanks[turn].canMove = true;
  41. displayName.text = tanks[turn].playerName + "'s turn";
  42. }
  43. }
  44. }

When run for the first time, this code adds three tanks (Player one, Player two and Player three) to the array tanks. We then go on to add these three tanks to the stage, at random positions. We then proceed to make our first tank movable by setting canMove to true.

The next part is simply setting up a text box that will let us know whose turn it is.

increaseTurn() does simply that, it increases the number that relates to whose turn it is (bypassing the dead tanks) and makes that tank able to move. The final little bit of this updates the text field with new information about whose turn it is.

Using these classes that we have created today, you should be able to compile the movie and it should run like the one shown above.

If you have had any problems at all, please don't hesitate to leave me a comment and I'll get back to you!

In the next part to this tutorial we will learn how to kill the tanks and we will also create destructible terrain!

Thanks for reading!

No comments yet.

Write a comment: