Advertisement
  1. Code
  2. Coding Fundamentals
  3. Game Development

Deploy a Tank on a Mission in an Isometric War Zone

Scroll to top
Read Time: 18 min

In this lesson we're going to create a little tank moving application. The core of this particular tutorial is to make a tank aim at the the mouse pointer and drive towards a mouse click.


Final Result Preview

Let's take a look at the final result we will be working towards:

Move the mouse to make the turret aim at it, and click anywhere to get the tank to drive to that point.

Step 1: Graphics Intro

Well, let's get started.. For this tutorial we'll need tank and turret graphics. I used Google SketchUp 7 to make mine.

First I created a tank as a whole. Then I turned the program's view mode to "parallel projection", positioning my camera at a 45° angle to the tank to make an illusion of isometrics in the game.

Please, don't judge me for horrible graphics, I really can do much better but this is just for the sake of explanation :)

Then I hid the turret leaving only the tank body visible and rotated the body by 45° eight times, rendering each position separately. So I achieved 8 different views of the body. I did the same thing to the turret while the tank was hidden. I saved all 16 pics as PNG 24 with alpha channel. You can see the final result below. (This is not actually enough for this kind of movement, it would be better to have twice as many views of the tank but it's enough for the purposes of this tutorial.)

.

You don't necessarily need to use Google SketchUp of course, you may wanna use 3ds max or Maya or whatever you like, you may also use my graphics to practice if you don't want to or cannot create your own graphics.

Step 2: New Flash Project

Now open up Flash (I use Adobe Flash CS4) and choose Create New Flash File (Actionscript 3.0). We will only need it to make a tank MovieClip and to connect the Game class to it later.

.

Step 3: Set the Document Properties

Right-click the stage and go to the Document Properties item. Make it 600 x 400 (or whatever you like) and 21 frames per second (this frame rate seems to be optimal to me so I almost always use it. It doesn't really matter, though).

Click OK. Go to File > Save as and save this file as tank.fla in some folder on your computer (I recommend you create a Tank folder and put the file inside).

Step 4: Import Your Tank Images to Library

Now go to File > Import > Import to library. Find the folder where you saved your eight tank images, select them all and import to library.

.

Step 5: New Symbol

Create a new empty symbol by clicking this little icon in the library:

Choose MovieClip type and call the symbol "tank". Put the registration point in the center (the registration point is that tiny black square in that grid of white squares, for those who don't know).

Step 6: Edit the Symbol

After the symbol is created it appears in the library (its name "tank" is just a cosmetic name which is used solely in the library so it doesn't matter what you call it). Open it by double-clicking its icon. Then take 1.png from the library and drag it onto the symbol's stage. Align the image to the symbol's registration point like in this screenshot:

You want the registration point to be in the center of the tank's turning circle.

Step 7: Create Seven Blank Key Frames

Go to the symbol's timeline. Rename the "Layer 1" to "Tank". Your tank's body image (1.png) should be on the first frame of this layer. Click the second frame and without releasing your mouse button drag the cursor to the 8th frame selecting frames 2 through 8. Then right-click this selected area and choose "Convert to blank keyframes".

Step 8: Drag All Images Onto the Stage

When 7 blank key frames are created, select the second frame and drag 2.png to it aligning the image with the registration point (just like you did with 1.png); imagining that the registration point is an axis that the tank will use to spin around when it's turning. Do the same to the rest of the frames. You don't have to align it very precisely, but the more precisely you align it the better the movement will look when the tank tries to follow the cursor as the game runs.

Step 9: Prevent the Timeline from Playing

After you dragged every image to the symbol's stage and aligned them select the first frame and push F9 to open the actions panel. Type a single method "stop();" in it and close the panel. This is an important step because we don't want our tank to automatically start spinning around as soon as it's added to the stage.

Step 10: Create a Turret MovieClip

Go to the library and create a new MovieClip symbol. Call it turret. Double-click the symbol's icon and do the same you did with the tank MovieClip (Steps 4. through 8.) to the turret but using turret images. Align it to the registration point this way, at the point around which the turret would naturally turn:

Notice that I've named the turret images like this: 1-.png, 2-.png and so on, so that they wouldn't replace the tank images when imported to library. You may want to use folders to put your images into. In this case your turret and tank images may have the same names.

Step 11: Assembling the Tank

After we've finished with turret symbol, open up the tank symbol again, create a new layer above the "Tank" layer and drag the turret movie clip from the library onto this layer. Align the turret with the tank body. The result should look like this:

Step 12: Give the Turret an Instance Name

Click once on the turret to select it (blue rectangle around it will indicate that it's selected):

...then go to properties and give it and instance name mTurret (for MovieClip turret):

Click "Scene 1" link to exit symbol editing mode.

Step 13: Export Your Tank for ActionScript

Right-click the tank symbol in the library, choose Properties (or Linkage if you're using Flash CS3) from the dropdown menu (make sure you're in an Advanced mode), check "Export for Actionscript", and for the class' name type in "Tank" (with capital T). This step will allow us to connect our Tank MovieClip to the instances created using our Tank class in future.

The graphics part is over for now; you may delete the tank from the stage. We'll add it programmatically later.

Step 14: Create a New ActionScript File

In your Flash Authoring tool go to File > New and create a new ActionScript file.

Step 15: Save Your File

Go to the folder where you saved your tank.fla file and create a new folder called "main" inside. Save your ActionScript file in this folder as TankMaker.as. The location of this file should therefore be: tank/main/TankMaker.as

Step 16: Let's Write Some Code

First of all, create a package and import the necessary classes into it:

1
2
3
package main {	
4
5
	import flash.display.*;
6
	import flash.events.*;
7
8
}

The word "main" here means the location of this file relative to our tank.fla

We will need "display" classes to show our assets on the screen and "events" to keep track of the mouse events.

Step 17: Public Class and Variables

Now we have to declare a public class TankMaker (according to the Actionscript file's name) and the necessary variables:

1
2
package main {
3
	
4
	import flash.display.*;
5
	import flash.events.*;
6
7
	public class TankMaker extends Sprite {
8
		
9
		// the instance of our Tank class (which we exported for actionscript before)

10
		private var tank:Tank;
11
12
		// the vars to keep the coordinates of the point where we want our tank to move

13
		private var moveTankToX:Number;
14
		private var moveTankToY:Number;
15
16
		// coordinates of the point at which the tank aims its turret

17
		private var turnTurretToX:Number;
18
		private var turnTurretToY:Number;
19
20
		// current position of the tank

21
		private var currentTankPositionX:Number;
22
		private var currentTankPositionY:Number;
23
		
24
		private var tankSpeed:Number = 2;
25
26
		// the point at which the tank moves after a mouse click

27
		private var clickPoint:Object;
28
		
29
		// the angle of tank and turret rotation in radians

30
		private var Atan2:Number;
31
		private var turretAtan2:Number;
32
	}
33
}

I declared all of the variables as "private" because I don't want them to be accessible from anywhere outside this class.

Step 18: Create a Constructor Method

Directly below the last variable create a public function and call it TankMaker. It's going to be the constructor of the class. Then add an event listener for Event.ADDED_TO_STAGE to it so that we can create our tank object only after the stage is. Then pass this event to the method called addStage.

1
2
public function TankMaker() {
3
	
4
	addEventListener(Event.ADDED_TO_STAGE, addStage);
5
6
}

Step 19: Create a Tank Instance

After the stage is created and the ADDED_TO_STAGE event has triggered the addStage() method it's about time to create a tank instance.

Declare a private function and call it addStage. The data type of the function will be: Event. And the return type will be "void" because we're not going to return anything from it.

1
2
private function addStage (e:Event):void {
3
	
4
	//create a new tank instance of the movie clip 

5
	// which you had exported for actionscript

6
	tank = new Tank();
7
	
8
	// now a little trick, I scaled my tank down

9
	// to the size of about one fourth of its original size

10
	// so that is wasn't so huge

11
	tank.scaleX = 0.25;
12
	tank.scaleY = 0.25;
13
	
14
	// set the initial position of the tank right

15
	// in the center of the stage

16
	tank.x = stage.stageWidth / 2;
17
	tank.y = stage.stageHeight / 2;
18
	// and add it to the display list

19
	addChild(tank);
20
	
21
	// now add event listeners for mouse down and mouse move

22
	// events to the stage

23
	stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownListener);
24
	stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveListener);
25
	
26
}

Step 20: Handling Mouse Movements

Create a private function mouseMoveListener() with a data type of mouse event to handle mouse movements across the stage. The event listener to trigger this function was created in the last Step.

1
2
private function mouseMoveListener(e:MouseEvent):void {
3
	
4
	// create a variable to keep the relative angle 

5
	// between mouse's current position in degrees

6
	// and the tank's current position

7
	var angle:Number;
8
9
	// the next 2 variables are gonna be 

10
	// equal to the the mouse's current position

11
	turnTurretToX = e.stageX;
12
	turnTurretToY = e.stageY;
13
	
14
	// calculate the relative angle in radians 

15
	// between the mouse's curent position 

16
	// and the tank's current position

17
	
18
	turretAtan2 = Math.atan2(turnTurretToY - currentTankPositionY, turnTurretToX - currentTankPositionX);
19
20
21
	// calculate the same angle in degrees.

22
	// to get degrees out of radians we have to

23
	// multiply radians by 180 and divide the result by PI

24
	// But here are some specifics of Flash

25
	// it calculates the angle not from 0 thru 360

26
	// but from 0 thru 180 and from - 180 thru 0

27
	
28
	// so let's add some conditional to get rid of this problem

29
	if (Math.round(turretAtan2 * 180 / Math.PI) < 0) {
30
		// if angle is between 0 and -180 add 360 to it

31
		angle = Math.round(turretAtan2 * 180 / Math.PI) + 360;
32
	} else {
33
		// if not, calculate it as usual

34
		angle = Math.round(turretAtan2 * 180 / Math.PI);
35
	}
36
	
37
38
	// having our angle value stored in a variable

39
	// let's rotate our turret towards the mouse pointer

40
	
41
	// I've done a lot of calculations trying to 

42
	// figure it all out, so you may try to undestand my logic

43
	
44
	// I'm just picking the right frame of the turret MC depending

45
	// on the angle of the mouse from the turret

46
	
47
	if (angle > 240 && angle < 300) {
48
		// go inside the tank MovieClip then inside mTurret

49
		// go to the frame 1 and stop  

50
		tank.mTurret.gotoAndStop(1);
51
	}
52
	if (angle > 300 && angle < 340) {
53
		tank.mTurret.gotoAndStop(2);
54
	}
55
	if ((angle >= 0 && angle < 20) || (angle > 340 && angle <= 360)) {
56
		tank.mTurret.gotoAndStop(3);
57
	}
58
	if (angle > 20 && angle < 60) {
59
		tank.mTurret.gotoAndStop(4);
60
	}
61
	if (angle > 60 && angle < 120) {
62
		tank.mTurret.gotoAndStop(5);
63
	}
64
	if (angle > 120 && angle < 160) {
65
		tank.mTurret.gotoAndStop(6);
66
	}
67
	if (angle > 160 && angle < 200) {
68
		tank.mTurret.gotoAndStop(7);;
69
	}
70
	if (angle > 200 && angle < 240) {
71
		tank.mTurret.gotoAndStop(8);
72
	} // end

73
	
74
}

Step 21: Mouse Click Handler

Below the end of our mouseMoveListener() function create a private function called mouseDownListener() with data type of MouseEvent and pass it to the moveTankOnEnterFrame method:

1
2
private function mouseDownListener(e:MouseEvent):void {
3
	
4
	// when click occurs assign the next variable 

5
	// the coordinates of the mouse pointer's current position

6
	moveTankToX = e.stageX;
7
	moveTankToY = e.stageY;
8
	// and add event listener to move the tank frame by frame

9
	addEventListener(Event.ENTER_FRAME, moveTankOnEnterFrame);
10
	
11
}

Step 22: Move Tank Frame By Frame

Create a new private function called moveTankOnEnterFrame() and inside it call a method moveToCoordinates() with 3 parameters:

1
2
private function moveTankOnEnterFrame(e:Event) {
3
	moveToCoordinates(tank, moveTankToX, moveTankToY);
4
}

This will, eventually, cause the tank to drive towards the clicked point, every frame.

Step 23: Teach the Tank To Move

Now we have called moveToCoordinates() method but we haven't created it yet. Let's do that now.

The tX and tY won't be used but we must create as many parameters as we called. This is how it looks:

1
2
private function moveToCoordinates(tank_mc:Tank, tX:Number, tY:Number) {
3
	
4
	// create a variable to keep the relative angle 

5
	// between mouse's current position in degrees

6
	// and the tank's current position

7
	// this variable exists only inside this function so 

8
	// you we can use the same name as we used before

9
	var angle:Number; 
10
	
11
	// calculate the angle (remember moveMouseListener?

12
	// we do the same here but with the tank itself)

13
	if (Math.round(Atan2 * 180 / Math.PI) < 0) {
14
		angle = Math.round(Atan2 * 180 / Math.PI) + 360;
15
	} else {
16
		angle = Math.round(Atan2 * 180 / Math.PI);
17
	}
18
19
	if (angle > 240 && angle < 300) {
20
		tank.gotoAndStop(1);
21
	}
22
	if (angle > 300 && angle < 340) {
23
		tank.gotoAndStop(2);
24
	}
25
	if ((angle >= 0 && angle < 20) || (angle > 340 && angle <= 360)) {
26
		tank.gotoAndStop(3);
27
	}
28
	if (angle > 20 && angle < 60) {
29
		tank.gotoAndStop(4);
30
	}
31
	if (angle > 60 && angle < 120) {
32
		tank.gotoAndStop(5);
33
	}
34
	if (angle > 120 && angle < 160) {
35
		tank.gotoAndStop(6);
36
	}
37
	if (angle > 160 && angle < 200) {
38
		tank.gotoAndStop(7);
39
	}
40
	if (angle > 200 && angle < 240) {
41
		tank.gotoAndStop(8);
42
	}
43
	
44
	// give the clickPoint a value of a current mouse position

45
	// when a click occurs

46
	clickPoint = {x:moveTankToX, y:moveTankToY};
47
	
48
	// calculate the angle in radians between the pointer's 

49
	// current position and tank

50
	Atan2 = Math.atan2(clickPoint.y - tank_mc.y, clickPoint.x - tank_mc.x);
51
52
	// now add a value of Atan2 cosine to the tank's X position

53
	// and sine to its Y position every frame

54
	tank_mc.x += Math.cos(Atan2) * tankSpeed;
55
	tank_mc.y += Math.sin(Atan2) * tankSpeed;
56
	
57
	
58
	// now give the values to currentTankPositionX and 

59
	// currentTankPositionY which we used in our mouseMoveListener

60
	currentTankPositionX = tank_mc.x;
61
	currentTankPositionY = tank_mc.y;
62
	
63
	// and finally a little trick. Since we don't want our tank to

64
	// start chaotically vibrating when it reaches its destination

65
	// we must calculate the distance between the tank's position and

66
	// the click point, and if it's less than 15, remove the ENTER_FRAME listener

67
	if (Math.abs(tank_mc.x - clickPoint.x) < 15 && Math.abs(tank_mc.y - clickPoint.y) < 15) {
68
		removeEventListener(Event.ENTER_FRAME,moveTankOnEnterFrame);
69
	}
70
}

Why use a distance of 15? Well, I found out that it never goes beyond 15 by simply tracing it like this:

1
2
trace(Math.abs(tank_mc.x - clickPoint.x));

...you can do the same if you want ;)

Step 24: Overviewing The Whole Class

As our class is ready, let's take a look at it as a whole:

1
2
package main {
3
	
4
	import flash.display.*;
5
	import flash.events.*;
6
7
	public class TankMaker extends Sprite {
8
9
		private var tank:Tank;
10
11
		private var moveTankToX:Number;
12
		private var moveTankToY:Number;
13
14
		private var turnTurretToX:Number;
15
		private var turnTurretToY:Number;
16
		private var currentTankPositionX:Number;
17
		private var currentTankPositionY:Number;
18
		
19
		private var tankSpeed:Number = 2;
20
21
		private var clickPoint:Object;
22
		
23
		private var Atan2:Number;
24
		private var turretAtan2:Number;
25
26
		public function TankMaker() {
27
			
28
			addEventListener(Event.ADDED_TO_STAGE, addStage);
29
30
		}
31
		
32
		private function addStage (e:Event):void {
33
			
34
			tank = new Tank();
35
			tank.scaleX = 0.25;
36
			tank.scaleY = 0.25;
37
			tank.x = stage.stageWidth / 2;
38
			tank.y = stage.stageHeight / 2;
39
			addChild(tank);
40
			
41
			stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDownListener);
42
			stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMoveListener);
43
			
44
		}
45
46
		private function mouseMoveListener(e:MouseEvent):void {
47
			
48
			var angle:Number;
49
50
			turnTurretToX = e.stageX;
51
			turnTurretToY = e.stageY;
52
			
53
			turretAtan2 = Math.atan2(turnTurretToY - currentTankPositionY, turnTurretToX - currentTankPositionX);
54
55
			
56
			if (Math.round(turretAtan2 * 180 / Math.PI) < 0) {
57
				angle = Math.round(turretAtan2 * 180 / Math.PI) + 360;
58
			} else {
59
				angle = Math.round(turretAtan2 * 180 / Math.PI);
60
			}
61
62
			
63
			if (angle > 240 && angle < 300) {
64
				tank.mTurret.gotoAndStop(1);
65
			}
66
			if (angle > 300 && angle < 340) {
67
				tank.mTurret.gotoAndStop(2);
68
			}
69
			if ((angle >= 0 && angle < 20) || (angle > 340 && angle <= 360)) {
70
				tank.mTurret.gotoAndStop(3);
71
			}
72
			if (angle > 20 && angle < 60) {
73
				tank.mTurret.gotoAndStop(4);
74
			}
75
			if (angle > 60 && angle < 120) {
76
				tank.mTurret.gotoAndStop(5);
77
			}
78
			if (angle > 120 && angle < 160) {
79
				tank.mTurret.gotoAndStop(6);
80
			}
81
			if (angle > 160 && angle < 200) {
82
				tank.mTurret.gotoAndStop(7);;
83
			}
84
			if (angle > 200 && angle < 240) {
85
				tank.mTurret.gotoAndStop(8);
86
			} 
87
			
88
		}
89
		
90
		
91
		private function mouseDownListener(e:MouseEvent):void {
92
			
93
			moveTankToX = e.stageX;
94
			moveTankToY = e.stageY;
95
			addEventListener(Event.ENTER_FRAME,moveTankOnEnterFrame);
96
			
97
		}
98
		
99
		private function moveTankOnEnterFrame(e:Event) {
100
			
101
			moveToCoordinates(tank, moveTankToX, moveTankToY);
102
		}
103
104
		private function moveToCoordinates(tank_mc:Tank, tX:Number, tY:Number) {
105
			
106
			var angle:Number; 
107
		
108
			if (Math.round(Atan2 * 180 / Math.PI) < 0) {
109
				angle = Math.round(Atan2 * 180 / Math.PI) + 360;
110
			} else {
111
				angle = Math.round(Atan2 * 180 / Math.PI);
112
			}
113
114
			if (angle > 240 && angle < 300) {
115
				tank.gotoAndStop(1);
116
			}
117
			if (angle > 300 && angle < 340) {
118
				tank.gotoAndStop(2);
119
			}
120
			if ((angle >= 0 && angle < 20) || (angle > 340 && angle <= 360)) {
121
				tank.gotoAndStop(3);
122
			}
123
			if (angle > 20 && angle < 60) {
124
				tank.gotoAndStop(4);
125
			}
126
			if (angle > 60 && angle < 120) {
127
				tank.gotoAndStop(5);
128
			}
129
			if (angle > 120 && angle < 160) {
130
				tank.gotoAndStop(6);
131
			}
132
			if (angle > 160 && angle < 200) {
133
				tank.gotoAndStop(7);
134
			}
135
			if (angle > 200 && angle < 240) {
136
				tank.gotoAndStop(8);
137
			}
138
139
			clickPoint = {x:moveTankToX, y:moveTankToY};
140
			Atan2 = Math.atan2(clickPoint.y - tank_mc.y, clickPoint.x - tank_mc.x);
141
142
			tank_mc.x += Math.cos(Atan2) * tankSpeed;
143
			tank_mc.y += Math.sin(Atan2) * tankSpeed;
144
			
145
			currentTankPositionX = tank_mc.x;
146
			currentTankPositionY = tank_mc.y;
147
			
148
			if (Math.abs(tank_mc.x - clickPoint.x) < 15 && Math.abs(tank_mc.y - clickPoint.y) < 15) {
149
150
				removeEventListener(Event.ENTER_FRAME,moveTankOnEnterFrame);
151
			}
152
		}
153
	}
154
}

Step 25: Final Strokes

Go to File > New, and create a new ActionScript file. Save it as "Game.as" in the same folder as your tank.fla file. This will be our document class.

Here's the code for Game.as class:

1
2
package {
3
	
4
	import flash.display.*;
5
    // import the contents of our main folder

6
	import main.*;
7
	
8
	public class Game extends MovieClip {
9
		
10
		private var newTank:TankMaker;
11
		
12
		public function Game () {
13
			
14
            // create a new instance of our tank maker class

15
			newTank = new TankMaker();
16
            // and add it to the stage of course

17
			addChild(newTank);
18
		}
19
	}
20
}

Save the file. All it does is create a tank and add it to the main stage, ready to control.

Step 26: Attach Game Class to the FLA

Open up tank.fla and go to the Properties panel. For Class type in "Game" and save the file.

Aha! There we go. You can now press CTRL + ENTER on PC (CMD + RETURN on Mac) and test the Game.

I hope you enjoyed this tutorial, thanks for reading :)

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.