Only being able to shoot in one direction is soooo 1978. Let’s add three more to choose from.
This tutorial is based on my avoider game tutorial, and follows on directly from Turn Your Avoider Game into a Shoot-’Em-Up. You’ll find this easier to understand if you went through both of those, but if you’ve got a decent grasp of AS3, don’t worry — just download this zip file, it’s got all the files you need, and they’re all set up ready for this post.
Move with the Keyboard, Shoot with… the Keyboard
Before we get on to using the mouse, I want to start with something simpler: move with the arrow keys, aim and fire with the WSAD keys. So pressing W will shoot a bullet upwards, A will shoot one left, and so on. (Apologies if you’re using a keyboard layout that happens to place these keys in weird positions!)
This means adding all the relevant Boolean variables, like wKeyIsBeingPressed. I know, I know, this is a pain. In a future tutorial I will explain a much simpler way of dealing with keyboard input. I’ll rush through these as we’ve done it all before.
AvoiderGame.as class-level variables:
28 29 30 31 | public var wKeyIsBeingPressed:Boolean; public var sKeyIsBeingPressed:Boolean; public var aKeyIsBeingPressed:Boolean; public var dKeyIsBeingPressed:Boolean; |
AvoiderGame() constructor function:
59 60 61 62 | wKeyIsBeingPressed = false; sKeyIsBeingPressed = false; aKeyIsBeingPressed = false; dKeyIsBeingPressed = false; |
onKeyPress() event listener:
else if ( keyboardEvent.keyCode == 87 ) { wKeyIsBeingPressed = true; } else if ( keyboardEvent.keyCode == 83 ) { sKeyIsBeingPressed = true; } else if ( keyboardEvent.keyCode == 65 ) { aKeyIsBeingPressed = true; } else if ( keyboardEvent.keyCode == 68 ) { dKeyIsBeingPressed = true; } |
(The onKeyRelease() event listener is almost the same, except true is changed to false. If you’re wondering where I got the numbers 87, 83, 65 and 68 from, the answer is this page. Don’t be put off that it says “ActionScript 2.0″ at the top — the key codes have stayed the same in AS3.)
Now for the actual shooting code. As a reminder, here’s our current, always-shoot-upwards code:
220 221 222 223 224 225 226 227 228 229 230 231 232 | if ( spaceBarIsBeingPressed ) { if ( ticksSinceLastShot >= 10 ) { var newBullet = new Bullet(); newBullet.x = avatar.x; newBullet.y = avatar.y; addChild( newBullet ); bullets.push( newBullet ); ticksSinceLastShot = 0; sfxSoundChannel = shotSound.play(); } } |
Well, it’s easy to change this to make the W key shoot upwards:
220 221 222 223 224 225 226 227 228 229 230 231 232 | if ( wKeyIsBeingPressed ) { if ( ticksSinceLastShot >= 10 ) { var newBullet = new Bullet(); newBullet.x = avatar.x; newBullet.y = avatar.y; addChild( newBullet ); bullets.push( newBullet ); ticksSinceLastShot = 0; sfxSoundChannel = shotSound.play(); } } |
What do we do with the other three shooting keys though? We could copy and paste this code, once for each key, and just change the direction somehow, but that’s messy and irritating.
First, let’s figure out how we’d shoot to the left (i.e. what should happen when we press A). Presumably, we’d start by changing the above code like so:
220 221 222 223 224 225 226 227 228 229 230 231 232 | if ( aKeyIsBeingPressed ) { if ( ticksSinceLastShot >= 10 ) { var newBullet = new Bullet(); newBullet.x = avatar.x; newBullet.y = avatar.y; addChild( newBullet ); bullets.push( newBullet ); ticksSinceLastShot = 0; sfxSoundChannel = shotSound.play(); } } |
Then what? There’s no code to say in which direction to fire the bullet. If you look further down in the onTick() function, you’ll remember this code:
234 235 236 237 238 239 240 241 242 243 244 245 246 | var j:int = bullets.length - 1; var bullet:Bullet; while ( j > -1 ) { bullet = bullets[j]; bullet.moveABit(); if ( bullet.y < -10 ) { removeChild( bullet ); bullets.splice( j, 1 ); } j = j - 1; } |
The movement logic for the bullets is completely contained in the bullet.moveABit() function. So let’s open Bullet.as and take a look:
4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public class Bullet extends MovieClip { public var speed:Number; //pixels moved per tick public function Bullet() { speed = 3; } public function moveABit():void { y = y - speed; } } |
Not much to it.
I’m going to adapt a technique from Mushyrulez’s Enemies tutorial for controlling the bullet’s direction. (Check out his post if you want to see a full explanation.)
4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public class Bullet extends MovieClip { public var speed:Number; //pixels moved in a direction per tick public var xDirection:Number; public var yDirection:Number; public function Bullet( startingXDirection:Number, startingYDirection:Number ) { xDirection = startingXDirection; yDirection = startingYDirection; speed = 3; } public function moveABit():void { x = x + ( speed * xDirection ); y = y + ( speed * yDirection ); } } |
See how that works? It’s actually very similar to the way we move both the enemies and the avatar. If we pass, say, startingXDirection = 1, startingYDirection = 0 then every tick, the x-coordinate of the bullet will increase by 3 pixels (because the speed is 3) — in other words, the bullet will move to the right.
Now head back to the code we were editing in AvoiderGame.as, and let’s get the A button shooting to the left:
220 221 222 223 224 225 226 227 228 229 230 231 232 | if ( aKeyIsBeingPressed ) { if ( ticksSinceLastShot >= 10 ) { var newBullet = new Bullet( -1, 0 ); newBullet.x = avatar.x; newBullet.y = avatar.y; addChild( newBullet ); bullets.push( newBullet ); ticksSinceLastShot = 0; sfxSoundChannel = shotSound.play(); } } |
If passing ( 1, 0 ) would make the bullet move to the right, it stands to reason that ( -1, 0 ) would move it to the left.
Try it out:
It works! OK, we need to make the bullet face the correct direction, but it’s a start. Now for the other three keys.
Shooting Up, Down and Right
This is the simplest way I can think of to include the W, S and D keys:
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 | else if ( wKeyIsBeingPressed ) { if ( ticksSinceLastShot >= 10 ) { var newBullet = new Bullet( 0, -1 ); newBullet.x = avatar.x; newBullet.y = avatar.y; addChild( newBullet ); bullets.push( newBullet ); ticksSinceLastShot = 0; sfxSoundChannel = shotSound.play(); } } else if ( sKeyIsBeingPressed ) { if ( ticksSinceLastShot >= 10 ) { var newBullet = new Bullet( 0, 1 ); newBullet.x = avatar.x; newBullet.y = avatar.y; addChild( newBullet ); bullets.push( newBullet ); ticksSinceLastShot = 0; sfxSoundChannel = shotSound.play(); } } else if ( aKeyIsBeingPressed ) { if ( ticksSinceLastShot >= 10 ) { var newBullet = new Bullet( -1, 0 ); newBullet.x = avatar.x; newBullet.y = avatar.y; addChild( newBullet ); bullets.push( newBullet ); ticksSinceLastShot = 0; sfxSoundChannel = shotSound.play(); } } else if ( dKeyIsBeingPressed ) { if ( ticksSinceLastShot >= 10 ) { var newBullet = new Bullet( 1, 0 ); newBullet.x = avatar.x; newBullet.y = avatar.y; addChild( newBullet ); bullets.push( newBullet ); ticksSinceLastShot = 0; sfxSoundChannel = shotSound.play(); } } |
Horrible, isn’t it? Look at all that duplicated code! Imagine if we ever wanted to change, say, the sound that played; we’d have to do it in four places.
Let’s simplify it. First, note that we can move the if ( ticksSinceLastShot >= 10 ) to be outside the other if statements:
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | if ( ticksSinceLastShot >= 10 ) { if ( wKeyIsBeingPressed ) { //bullet creation code goes here } else if ( sKeyIsBeingPressed ) { //bullet creation code goes here } else if ( aKeyIsBeingPressed ) { //bullet creation code goes here } else if ( dKeyIsBeingPressed ) { //bullet creation code goes here } } |
Now how about the code inside the lettered if statements?
Well, we can just stick this all in a new function, and use it as many times as we like
First, we take the bullet creation code, and we put it in a new function:
335 336 337 338 339 340 341 342 343 344 | public function createNewBullet():void { var newBullet = new Bullet( 0, -1 ); newBullet.x = avatar.x; newBullet.y = avatar.y; addChild( newBullet ); bullets.push( newBullet ); ticksSinceLastShot = 0; sfxSoundChannel = shotSound.play(); } |
(Remember to put this outside the onTick() function, but inside the AvoiderGame class — otherwise, you’ll get an error.)
Because all the variables we use here are either class-level variables (avatar, bullets, ticksSinceLastShot, sfxSoundChannel, shotSound) or created by the function (newBullet), we don’t run into any problems.
Now we can simply call this one function from the onTick() class, like so:
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | if ( ticksSinceLastShot >= 10 ) { if ( wKeyIsBeingPressed ) { createNewBullet(); } else if ( sKeyIsBeingPressed ) { createNewBullet(); } else if ( aKeyIsBeingPressed ) { createNewBullet(); } else if ( dKeyIsBeingPressed ) { createNewBullet(); } } |
Run it, to make sure it works:
It does, but of course it’s still only shooting in one direction, no matter which key you press. We have to pass the information about the key pressed to the new function, like so:
311 312 313 314 315 316 317 318 319 320 | public function createNewBullet( startingXDirection:Number, startingYDirection:Number ):void { var newBullet = new Bullet( startingXDirection, startingYDirection ); newBullet.x = avatar.x; newBullet.y = avatar.y; addChild( newBullet ); bullets.push( newBullet ); ticksSinceLastShot = 0; sfxSoundChannel = shotSound.play(); } |
220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 | if ( ticksSinceLastShot >= 10 ) { if ( wKeyIsBeingPressed ) { createNewBullet( 0, -1 ); } else if ( sKeyIsBeingPressed ) { createNewBullet( 0, 1 ); } else if ( aKeyIsBeingPressed ) { createNewBullet( -1, 0 ); } else if ( dKeyIsBeingPressed ) { createNewBullet( 1, 0 ); } } |
There. And check it out — no duplicated code!
Rotating the Bullets
It still looks a bit off, though, doesn’t it? We need the bullets to be pointing in the direction they’re moving.
Fortunately, all MovieClips — and therefore, all Bullets, as Bullet extends MovieClip — have a rotation property that controls the angle that the image is turned.
The way Flash handles these angles is a bit wacky; they range from -180 to 180 degrees, instead of from 0 to 360 degrees. If you want to read about why this is (or just want to know more about angles and degrees in general), check out my post Angles In Flash: Rotation, Degrees and Radians.
Basically, we just need to set the bullets rotation to:
- 90 degrees, if it’s moving to the right
- 180 degrees, if it’s moving downwards
- -90 degrees, if it’s moving to the left
- 0 degrees, if it’s moving upwards
I think the best place to do this is in the Bullet’s constructor function. Do you agree?
The code is simple, since we already know all the possible inputs:
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | public function Bullet( startingXDirection:Number, startingYDirection:Number ) { xDirection = startingXDirection; yDirection = startingYDirection; speed = 3; if ( xDirection == 0 ) { if ( yDirection == 1 ) { //moving downwards rotation = 180; } else if ( yDirection == -1 ) { //moving upwards rotation = 0; } } else if ( yDirection == 0 ) { if ( xDirection == 1 ) { //moving right rotation = 90; } else if ( xDirection == -1 ) { //moving left rotation = -90; } } } |
Now, this is a little sloppy, I’ll admit; if we change the bullet’s possible directions in AvoiderGame.as, we’ll also have to change the corresponding rotations in Bullet.as. That’s not very tidy. I’ll explain how to improve this in the next shoot-’em-up tutorial.
For now, though, it’s good enough. Take a look:
Mouse Movement
Actually, mouse movement is really easy! Remember back in Part 7 we added a boolean, useMouseControl, to make it easy to switch?
All we have to do is find this line:
64 | useMouseControl = false; |
…and set it to this:
64 | useMouseControl = true; |
Have a go:
Hmm. It doesn’t “feel” right. I think it’s because the avatar moves much faster than the bullets. Let’s change the bullets’ speed in Bullet.as:
Ah, now that’s much better
Wrapping Up
This makes the game much fairer, as you can now blast a path through enemies if they surround you.
You can download a zip of all the files I used in this tutorial (including the sound files) from this link.
In the next part of this mini-series, we’ll switch things round so that you can move with the keyboard and aim with the mouse. It’s harder than it sounds
Until then, why not try incorporating Mushyrulez’s Enemies tutorial into your new shoot-’em-up? It makes sense, given that those enemies come from multiple directions and you can now shoot in multiple directions.
Also, just as with the character, I’ve not included the ability for diagonal movement. Will I never learn? Have a go at coding that, too
One last thing: garbage collection. At the minute, bullets are only being garbage collected if they move too far off the top of the play screen. Obviously this won’t do for bullets moving left, right, or downwards. See if you can fix that.
Click here to read the next part of the shoot-’em-up tutorial.







{ 12 comments… read them below or add one }
Great! and yes, you will learn, very soon
funny
great job man. you’re good.
Something really weird happend to my game in the middle of the tut.
I’m not getting any errors or anything so I got no idea why this is happening.
if i could send you an E-mail or something with my avoidergame.as and the swf so you could look at it would be great
Hey, sure thing — my email is on the About page.
HEAWREHAWEH. am I a flash retard or what?
Even when you told me where the code is wrong is till can’t find it:S
Tho I found a mistake i’d done in the “public function onKeyRelease”
Forgot to set wasd controls to false:P but since thats not the thing i need to fix, i give up… almost;
So i tryed to change my onKeyPress function; with urs from the zip file, but its still not working as it should?!?!? it still take the focus away from the game and to the program instead:S:S:S
Oh! I thought that not setting WASD to false was the problem you were talking about!
Have you had any luck in the past few days?
been looking over it a couple of times, but with no luck ;(
It maybe be a problem with my program or something because, i get no errors in the log,
but then whe i try to play the game it works fine until i try to shoot with Wasd,
what happens is that instead of shooting it sets the Focus back to the program itself instead of the game. so when i press wasd it starts changing tools in the tool panel
So i will try to do this tut over again, and hopefully it will work then…
BYT i like the new Design
Oh! Hit Disable keyboard shortcuts in the menu of the Flash player somewhere. I forget where at the moment.
Please help!
I’m having problems when trying to add different enemy types. I’ve tried a few approaches and none seem to work for me. The game im working on is based on youre tuts and Ive done them all excpt for kong api(hoping to soon!).
I can create and add the new enemy type “miniBoss” to the stage no problem (i can also control the movement, spawn rate etc thanks to you!) but I run into problems when I try and implement hit-testing. They have they’re own class as they will have different behaviours, weapon load outs etc. If I try to push the new enemytype into the existing array I get the cant coerce mc to mc@ error. Is there a way to hit test items in an array more generally i.e. without naming the instance?
So I tried setting up a separate array but repeating the nested loop with the bullets within a when(i > -1) seemed far too repetitive and I couldn’t get it to work anyways. I never dialed in exactly why but I did manage to crash flash multiple times.
So what would be the best frame work for introducing new enemytypes? Do i need individual seperate arrays? Is there a way to have miniBoss extend the enemy class but add variables or something?
I feel like I’m right there. Any help would be hugely appreciated.
Ha!
Figured it out using a class similar to collectibles.
Thanks again for all the great knowledge… on to cutscenes.
Ok. I you happen to see this any help would be appreciated.
Im trying to implement enemies shooting back at the player and have hit a wall I can’t seem to find a way around.
Ideally I would like to instantiate the bullets within the enemy class (which would allow me to easily configure multiple variations for multiple enemy types i.e. multiple bullets multiple directions and even multiple bullet types).
I’ve done this and all was well until i tried to set up hittesting. I cant figure out how to either:
push the new instances into an array that is created in the avoidergame/playscreen class.
hit test within the bulletclass against and object/mc/avatar created in the avoidergame class.
On the otherhand if I create the bullets within the avoidergame class Im clueless how to position them on the x,y of an object within an array so it appears the ships are firing them.
Any thoughts? I feel like there’s something simple I’m missing about class communication but for all my googling im left at square one. bullets wizzing by at high speed:{
{ 2 trackbacks }