Turn Your Avoider Game into a Shoot-’Em-Up

by Michael James Williams on May 20, 2009 · 61 comments

in Avoider Game Extras,Tutorial

I’m getting a bit tired of running away from these enemies without having any means of defending myself.

Let’s blast them.

Snapshot_O06.png
Click to play. [Space] to shoot.

This tutorial is based on my avoider game tutorial. If you followed that, you’ll be able to add these new bullets to your existing game easily. If not, don’t worry about it — just download the base zip file; it contains all the files I’m going to be using.

Space Invaders Shooting

Let’s start with the simplest style of shooting: press space to fire a bullet upwards.

We might as well start by drawing the bullet. Open the FLA and create a new symbol of type Movie Clip (I’m sure you know the drill by now). Call it Bullet and export it for ActionScript with the same class name.

I’ve drawn this very simple shape for mine:

screenshot

This is zoomed in, of course! A rectangle would look fine.

Time for some code. Open AvoiderGame.as. The first thing we’re going to do is get it recognising the space bar.

Listening for the Space Bar

Remember, there are four places we need to change to allow the space bar to be recognised.

First, we need to define a new Boolean at the class level:

?View Code ACTIONSCRIPT3
22
23
24
25
26
public var downKeyIsBeingPressed:Boolean;
public var upKeyIsBeingPressed:Boolean;
public var leftKeyIsBeingPressed:Boolean;
public var rightKeyIsBeingPressed:Boolean;
public var spaceBarIsBeingPressed:Boolean;

Next, we have to set that Boolean to false in the constructor:

?View Code ACTIONSCRIPT3
43
44
45
46
47
downKeyIsBeingPressed = false;
upKeyIsBeingPressed = false;
leftKeyIsBeingPressed = false;
rightKeyIsBeingPressed = false;
spaceBarIsBeingPressed = false;

Finally, we have to set that boolean to either true or false in the onKeyPress() and onKeyRelease() functions. Here’s onKeyPress():

?View Code ACTIONSCRIPT3
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
public function onKeyPress( keyboardEvent:KeyboardEvent ):void
{
	if ( keyboardEvent.keyCode == Keyboard.DOWN )
	{
		downKeyIsBeingPressed = true;
	}
	else if ( keyboardEvent.keyCode == Keyboard.UP )
	{
		upKeyIsBeingPressed = true;
	}
	else if ( keyboardEvent.keyCode == Keyboard.LEFT )
	{
		leftKeyIsBeingPressed = true;
	}
	else if ( keyboardEvent.keyCode == Keyboard.RIGHT )
	{
		rightKeyIsBeingPressed = true;
	}
	else if ( keyboardEvent.keyCode == Keyboard.SPACE )
	{
		spaceBarIsBeingPressed = true;
	}
}

I’ll leave you to figure out the code for onKeyRelease().

OK, now we can detect the space bar, it’s time to make it do something. First, we can just make it create a new bullet. Move to the point in the onTick() function, after the avatar gets moved but before any collision detection takes place, and add these lines:

?View Code ACTIONSCRIPT3
166
167
168
169
170
171
172
if ( spaceBarIsBeingPressed )
{
	var newBullet = new Bullet();
	newBullet.x = avatar.x;
	newBullet.y = avatar.y;
	addChild( newBullet );
}
Snapshot_O01.png
Click to play

Well… it’s a start. If you hold down space, you can draw pictures. Er, let’s get those bullets moving.

Getting the Bullets Moving

We might as well copy the way the enemies move for this. Remember how that works?

  • All enemies are placed in an array, army
  • Every tick, we loop through that array and call moveABit() on each enemy
  • moveABit() changes the enemy’s position slightly (in my code, it just increases y)

So first we need a bullets array. Add public var bullets:Array; to the rest of the public variables, and put bullets = new Array(); in the constructor function, just as we did with army.

Next we need to push the new bullet we created in the code above into our new array:

?View Code ACTIONSCRIPT3
172
173
174
175
176
177
178
179
if ( spaceBarIsBeingPressed )
{
	var newBullet = new Bullet();
	newBullet.x = avatar.x;
	newBullet.y = avatar.y;
	addChild( newBullet );
	bullets.push( newBullet );
}

Great, now we have an array of bullets. Time to loop through them.

Looping Through the Bullets

I know a lot of people have difficulty with arrays. Just remember, they’re like a numbered list. We move through the list backwards because certain problems would occur if we didn’t.

Here’s the important part of the army loop:

?View Code ACTIONSCRIPT3
var i:int = army.length - 1;
var enemy:Enemy;
while ( i > -1 )
{
	enemy = army[i];
	enemy.moveABit();
	i = i - 1;
}

If there are ten enemies, they will take up army[0] through army[9], so we start at army[9] and keep going backwards through the list — that’s what i = i - 1 does. We only do this while the number of the enemy that we’re looking at is more than -1; army[-1] doesn’t refer to anything, so we’d get an error if we tried to use it.

Here’s the code we’ll use for the bullets:

?View Code ACTIONSCRIPT3
181
182
183
184
185
186
187
188
var j:int = bullets.length - 1;
var bullet:Bullet;
while ( j > -1 )
{
	bullet = bullets[j];
	bullet.moveABit();
	j = j - 1;
}

(I’ve put this after we create the new bullet, but before we do any collision detection.)

Note that I’m using j to move through this loop instead of i. You don’t have to do this; I just think it’s less confusing.

OK, just one more thing we need to do: create the bullet.moveABit() function. Because this code is so similar to the Enemy’s, I’m just going to paste it here for you to copy:

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package 
{
	import flash.display.MovieClip;
	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;
		}
	}
}

Save that as Bullet.as. Run the game now, and here’s what you’ll get:

Snapshot_O02.png
Click to play

That’s better, but we still need to build in collision detection, and slow down the rate of fire.

Bullet-Enemy Collision Detection

We’ve got lots of bullets and lots of enemies, and we need to check whether any bullet hits any enemy. How?

If you read my mini-series on collectibles, you’ll already know. If not, I recommend thinking about it before reading on.

The answer is nested loops.

Rather than checking all of the bullets against all of the enemies, we check all of the bullets against one of the enemies, and then check all of the bullets against the next enemy, then the next, and so on.

Here’s a simplified example:

?View Code ACTIONSCRIPT3
var i:int = army.length - 1;
var enemy:Enemy;
while ( i > -1 )
{
	enemy = army[i];
	enemy.moveABit();
	j = bullets.length - 1;
	while ( j > -1 )
	{
		bullet = bullets[j];
		//if bullet collides with enemy, destroy them both
		j = j - 1;
	}
	i = i - 1;
}

(Note: I haven’t written var j or var bullet because those are already defined earlier in the function.)

If that’s not clear, grab a piece of paper and a pencil and work through it, imagining that there are five enemies and three bullets, and bullet[1] is colliding with enemy[3].

OK, now we’ve got the basic idea, it’s time to actually plug this code into our existing enemy movement loop. Sure, we could write another loop just to handle enemy-bullet collisions, but that would be wasteful, and will slow things down.

So, here’s my existing enemy movement loop:

?View Code ACTIONSCRIPT3
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
var avatarHasBeenHit:Boolean = false;
var i:int = army.length - 1;
var enemy:Enemy;
while ( i > -1 )
{
	enemy = army[i];
	enemy.moveABit();
	if ( PixelPerfectCollisionDetection.isColliding( avatar, enemy, this, true ) ) 
	{
		gameTimer.stop();
		avatarHasBeenHit = true;
	}
	if ( enemy.y > 350 )
	{
		removeChild( enemy );
		army.splice( i, 1 );
	}
	i = i - 1;
}

…and here’s my first attempt at enemy-bullet collision detection:

?View Code ACTIONSCRIPT3
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
var avatarHasBeenHit:Boolean = false;
var i:int = army.length - 1;
var enemy:Enemy;
while ( i > -1 )
{
	enemy = army[i];
	enemy.moveABit();
	//new code starts here
	j = bullets.length - 1;
	while ( j > -1 )
	{
		bullet = bullets[j];
		if ( bullet.hitTestObject( enemy ) )
		{
			removeChild( enemy );
			army.splice( i, 1 );
			removeChild( bullet );
			bullets.splice( j, 1 );
		}
		j = j - 1;
	}
	//new code stops here
	if ( PixelPerfectCollisionDetection.isColliding( avatar, enemy, this, true ) ) 
	{
		gameTimer.stop();
		avatarHasBeenHit = true;
	}
	if ( enemy.y > 350 )
	{
		removeChild( enemy );
		army.splice( i, 1 );
	}
	i = i - 1;
}

(I’ve used hitTestObject(), even though it’s not very accurate, because it’s much faster than PixelPerfectCollisionDetection.)

This does actually work, the bullets destroy the enemies and themselves, but error messages appear in the Output panel. The problem is, after an enemy gets destroyed by a bullet (removing it from the screen and the array), we still try to use it in the following bit of code (checking for a collision with the avatar, and to see whether it’s moved off the bottom of the screen).

To get around this, we can use the same technique we used for the avatar — the avatarHasBeenHit boolean:

?View Code ACTIONSCRIPT3
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
var avatarHasBeenHit:Boolean = false;
var enemyHasBeenHit:Boolean;
var i:int = army.length - 1;
var enemy:Enemy;
while ( i > -1 )
{
	enemy = army[i];
	enemy.moveABit();
	enemyHasBeenHit = false;	//new code
	j = bullets.length - 1;
	while ( j > -1 )
	{
		bullet = bullets[j];
		if ( bullet.hitTestObject( enemy ) )
		{
			enemyHasBeenHit = true;		//new code
			removeChild( bullet );
			bullets.splice( j, 1 );
		}
		j = j - 1;
	}
	if ( enemyHasBeenHit )	//new code
	{						
		removeChild( enemy );	//new code
		army.splice( i, 1 );	//new code
	}
	else	//new code
	{
		if ( PixelPerfectCollisionDetection.isColliding( avatar, enemy, this, true ) ) 
		{
			gameTimer.stop();
			avatarHasBeenHit = true;
		}
		if ( enemy.y > 350 )
		{
			removeChild( enemy );
			army.splice( i, 1 );
		}
	}
	i = i - 1;
}

(I’ve written //new code throughout the code to make it easier to see what’s changed.)

So now we’re only checking for enemy-avatar collisions, and whether the enemy’s left the screen, if the enemy hasn’t already been hit by a bullet. It’s still not perfect, because we’ve got the same code in two separate parts of the loop (removing the enemy after it’s been hit by a bullet or left the screen) — can you see a way to improve that?

Check out the game so far:

Snapshot_O03.png
Click to play

Getting better!

Slowing Down the Rate of Fire

Does the constant stream of bullets look familiar to you?

screenshot

It’s actually the same problem we had way back in Part 2 of the base tutorial, when a new enemy was being created every tick.

Back then we fixed it by having a one-in-ten chance of an enemy being created each tick. But applying this same “random” approach to how often the player can fire is just going to be frustrating.

We need to make it more exact: the player can only shoot if at least ten ticks have passed since his last shot.

Hey, that’s basically an if statement! if ( ticksSinceLastShot >= 10 )

Let’s work that in. First, we’ll need a public var ticksSinceLastShot:int (remember, an int is just a whole number). Set ticksSinceLastShot = 0; in the AvoiderGame constructor function. Also, in the onTick() function, put:

?View Code ACTIONSCRIPT3
139
ticksSinceLastShot = ticksSinceLastShot + 1;

Now we can use our if statement from above:

?View Code ACTIONSCRIPT3
175
176
177
178
179
180
181
182
183
184
185
if ( spaceBarIsBeingPressed )
{
	if ( ticksSinceLastShot >= 10 )
	{
		var newBullet = new Bullet();
		newBullet.x = avatar.x;
		newBullet.y = avatar.y;
		addChild( newBullet );
		bullets.push( newBullet );
	}
}

Great! Try it out:

screenshot

…what? Oh! We need to reset the ticksSinceLastShot variable after we’ve, er, shot:

?View Code ACTIONSCRIPT3
175
176
177
178
179
180
181
182
183
184
185
186
if ( spaceBarIsBeingPressed )
{
	if ( ticksSinceLastShot >= 10 )
	{
		var newBullet = new Bullet();
		newBullet.x = avatar.x;
		newBullet.y = avatar.y;
		addChild( newBullet );
		bullets.push( newBullet );
		ticksSinceLastShot = 0;
	}
}

Try it out:

Snapshot_O05.png
Click to play

Excellent.

Odds and Ends

There are a few small changes I’d like to make, but I’ve covered them all in earlier tutorials so I won’t go into much detail.

First, for garbage collection purposes, we need to remove the bullets once they get above the top of the screen:

?View Code ACTIONSCRIPT3
188
189
190
191
192
193
194
195
196
197
198
199
200
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;
}

Second, I want to alter the scoring system, so that the player only gets points if he shoots an enemy. This means removing the points awarded when the enemy appears:

?View Code ACTIONSCRIPT3
141
142
143
144
145
146
147
148
149
if ( Math.random() < currentLevelData.enemySpawnRate )
{
	var randomX:Number = Math.random() * 400;
	var newEnemy:Enemy = new Enemy( randomX, -15 );
	army.push( newEnemy );
	addChild( newEnemy );
	//gameScore.addToValue( 10 );
	sfxSoundChannel = enemyAppearSound.play();
}

…and adding the score in the bullet-enemy collision detection:

?View Code ACTIONSCRIPT3
215
216
217
218
219
220
221
if ( bullet.hitTestObject( enemy ) )
{
	enemyHasBeenHit = true;		//new code
	removeChild( bullet );
	bullets.splice( j, 1 );
	gameScore.addToValue( 10 );
}

It might be a good idea to make the player lose points if an enemy gets past the bottom of the screen — but I’ll leave that up to you.

Third, I want to add more sound effects. For this, I’m going to use the awesome (and free!) tool sfxr:

sfxr, great free sound effects tool

I’ve used the Laser/Shoot setting to make the noise to be played when the player shoots, and the Explosion setting to be played when the bullet hits an enemy. (We’ll look at making explosion animations in a later tutorial.)

Here’s the result:

Snapshot_O06.png
Click to play

Wrapping Up

You can download a zip of all the files I used in this tutorial (including the sound files) from this link.

This small addition has changed the entire game. It’s very simple, though. Next time we’ll improve this, so that the player can aim before they fire.

Until then, why not try adding a different kind of bullet that can be fired with the shift key?

Click here to read the next part of this mini-series: Move with the Mouse, Aim with the Keyboard.

{ 60 comments… read them below or add one }

tomdeaap May 20, 2009 at 8:49 pm

“I know a lot of people have difficulty with arrays.” aha …….

Nice tutorial however :)

Michael Williams May 20, 2009 at 11:19 pm

:P

Thanks :)

Snurre May 21, 2009 at 9:40 am

Great tut once again :)

And a great introduction to sfxr.

mitomane May 22, 2009 at 2:41 pm

nice change to the avoider game

cool job man

Michael Williams May 22, 2009 at 3:58 pm

Cheers :)

fred May 23, 2009 at 9:18 pm

Hey MJW, another great tutorial

On the topic of shooters, I’ve seen that some have levels with different enemies, that come in a specific order, at specific times. How could you control what enemies come on your levels, and set them up so they come at specific times?
(eg. after 3 seconds, make enemy1; after 5 seconds, make enemy1 and enemy3; at 9 seconds, make level1boss…etc)

I found a link on Kongregate which has the same issue, but I can’t make heads or tails of the proposed solution. Any tips?
http://www.kongregate.com/forums/4/topics/6306?page=1

Thanks

Michael Williams May 23, 2009 at 11:59 pm

Thanks Fred

Hmm, interesting question. There’s a huge number of ways you could do this, so I don’t want to say “here is the One True method for doing that”. I’m actually working on a level editor for a game at the moment that’s easy to use but tricky to code.

I like what Phantasmagorica outlined in that forum post, but let’s see if we can make it simpler. Here’s an idea I just came up with, let me know what you think:

We start with an Array called enemyScript. This array is actually going to contain multiple other arrays, like so:

enemyScript = new Array();
enemyScript.push( [ 3, "enemy1" ] );    //seconds, followed by names of enemies
enemyScript.push( [ 5, "enemy1", "enemy3" ] );
enemyScript.push( [ 9, "level1boss" ] );

(By the way, you can use square brackets like that to create an array without having to write “var xyz:Array = new Array(); xyz.push()…”)

So here, enemyScript[0] is { 3, “enemy1″ }, enemyScript[1] is [ 5, "enemy1", "enemy3" ], and so on. Therefore,

Inside the AvoiderGame class, we have a class-level public var called currentScriptNumber which is an int that is set to 0 in the constructor function. We also have a public var secondsPassed which is a Number, and keeps track of how many seconds have passed since the start of the game.

Every tick, we run code like this:

var currentScript:Array = enemyScripts[currentScriptNumber];
if ( currentScript[0] > secondsPassed )
{
    var currentInstructionNumber:int = currentScript.length - 1;
    while ( currentInstructionNumber > 0 )
    {
        if ( currentScript[currentInstructionNumber] == "enemy1" )
        {
            //create new enemy1
        }
        else if ( currentScript[currentInstructionNumber] == "enemy3" )
        {
            //create new enemy3
        }
        //...etc
        currentInstructionNumber = currentInstructionNumber - 1;
    }
    currentScriptNumber = currentScriptNumber + 1;
}

I’ll admit, that looks confusing. But if you followed the tutorial through you should be able to understand it — write it out on paper and follow it through if not.

Any questions, bugs or improvements?

fred May 24, 2009 at 10:00 pm

Thanks!
I haven’t had time to try this out, but when I do, I’ll be sure to tell you. This makes a little more sense than the post I read.
I think I need to understand arrays a little better, but this helped me get the big picture.

Poe May 27, 2009 at 2:35 pm

how would i set up the shooter to respond to a mouse click instead of the space bar?

Michael Williams May 28, 2009 at 12:29 pm

Hey Poe. If you just want to make a click shoot a bullet upwards, then you need to change spaceBarIsBeingPressed to mouseIsBeingClicked.

This involves changing the name of the variable, and also creating a couple new event listeners — one for MouseEvent.MOUSE_DOWN (call it onMousePress()) and one for MouseEvent.MOUSE_UP (call it onMouseRelease()).

I’ll go into this in more detail in the third part of this mini-series, where we’ll also make the bullet shoot towards the mouse cursor.

Waari^ May 29, 2009 at 1:44 am

Hi, I found your tutorials two days ago, when I was looking for some advice for Action Script coding. I enjoyed your 12 part tutorial.

Anyway I followed this tutorial and got problem. When I play and first enemy spawns, it pops up Error #1009 and after that only moves last created enemy on screen, until bullet is shot and moving. Multiple bullets can be shot.

With debugging it points to “if (bullet.hitTestObject(enemy)) {” line. This occurs when no bullets are shot/on screen, so does this have something to do with bullet having “null” value or did I mess something else where?

Michael Williams May 29, 2009 at 2:07 pm

Hey Waari. It sounds to me like your loops aren’t nested correctly. Do you have your bullet movement loop, followed by a separate enemy movement loop which contains another loop that does the enemy-bullet collision detection?

Waari^ May 29, 2009 at 5:40 pm

I did compare my code with yours in zip file. Loops are nested in same way as yours. I checked also other bullet and enemy related code and didn’t seem to find anything.

Enemies and bullets do move normally without “if (bullet.hitTestObject(enemy)) { ……. }” colliding test code. With that code, problem occurs only when no bullets are in array and when there is at least one enemy in army array. I tried to add collide check “if (bullets[0] != null) {” before collide check while loop, but with it some bullets got through enemies.

Waari^ May 29, 2009 at 8:50 pm

Oh wow, I found small typo in word “length”, which caused all that mess. Altough it hilights that word normally, I didn’t notice it before I did same typo when adding collectibles. :P

Michael Williams May 30, 2009 at 2:25 pm

D’oh! Ah, I hate when small mistakes like that send you off on huge searches for a problem that isn’t even there. Glad you fixed it :)

Patrick June 18, 2009 at 3:16 am

Michael, great tutorial, all yours are great, they helped me make Circle Dash (www.kongregate.com/games/djice555/circle-dash)
but im working on another game, a tank needs to shoot a projectile in the direction of the tank, i used asgamer.coms asteroids style 360 degree movement tut for the tank movement (www.asgamer.com/2009/as3-character-movement-asteroids-style-360-degree-movement)
can you help me get the bullets to move, they just kinda sit there on the end of the turret.

Michael Williams June 18, 2009 at 7:11 pm

Thanks Patrick!

Have you given the bullets a moveABit() function, and are you calling that function of each bullet every tick?

Patrick June 18, 2009 at 9:27 pm

i added them and now the bullets wont show up, is there a way to send my swf and classes to you

Michael Williams June 19, 2009 at 1:41 am

I’m pretty busy currently but if you send them to the address on my About page I’ll try to get to them tomorrow.

Bigfoot August 11, 2009 at 11:41 pm

I like this article so far. I just had a bug that reminded me of the need to watch for capitalization errors…

Every time I would press the space bar, my bullets would immediately be at the top of the screen. After using trace() and commenting out lines for a while, I changed

y -= speed;

to

 y -= 3;

and it worked fine!

So after a couple more minutes I noticed I named the constructor function bullet instead of Bullet and so speed was being seen as NaN… D’oh.

.-= Bigfoot´s last blog ..MJW Avoider Game ep11ch1 =-.

Bigfoot August 12, 2009 at 5:42 pm

Hey Michael-
I’m trying to troubleshoot this bug. It happens when I try to remove a bullet, after it hits an enemy:

    j = bullets.length - 1;
    while ( j > -1 )
    {
        bullet = bullets[j];
        if( bullet.hitTestObject( enemy ) )
        {
            enemyHasBeenHit = true;
trace("Before Error");
            removeChild( bullet );
            trace( bullet );
trace("After Error");
bullets.splice( i, 1 ); } j -= 1; }

I shoot, and when it hits an enemy, I get at least one (usually a bunch) of these in my Output window:

Before Error?
ArgumentError: Error #2025: The supplied DisplayObject must be a child of the caller.
    at flash.display::DisplayObjectContainer/removeChild()
    at AvoiderGame/gameTick()
    at AvoiderGame/onTick()
    at flash.utils::Timer/flash.utils:Timer::_timerDispatch()
    at flash.utils::Timer/flash.utils:Timer::tick()
Before Error?
[object Bullet]
After Error?

This page
http://lassieadventurestudio.wordpress.com/2008/09/29/common-as3-errors/
had some interesting information on errors, but I wasn’t able to figure out what the name of the ‘parent’ would be…

.-= Bigfoot´s last blog ..MJW Avoider Game ep11ch1 =-.

Bigfoot August 12, 2009 at 6:11 pm

D’oh… my code said

bullets.splice( i, 1 );

instead of

bullets.splice( j, 1 );

.-= Bigfoot´s last blog ..MJW Avoider Game ep11ch1 =-.

Michael Williams August 12, 2009 at 6:45 pm

Hey Bigfoot,

Interesting that you happened to come across that bug with the bullets appearing at the top of the screen. Scarybug said he came across it as well, but I wasn’t sure why. Anything I could do to make it clearer?

Re. that second error… d’oh! ;)

Taylor Lodge September 8, 2009 at 10:23 am

Hi so I’ve been looking around the internet for better way’s of hittesting then in my current game, Since when I have 25 enemies as soon as the bullet count hits about 10 the FPS of the game drops to about 5.

The code is basically doing the same array search method as in this but it still laggs.
Do you remove bullets when there off the screen? Since I didn’t see any code for it but I wasn’t really looking

Here is my code (Bit messy but hey I didn’t write it lol other developer)

for (var i:Number=0; i =1)
                {
                    eRemaining+=1;
                    for (var j:Number=0; j < bulletArray.length; j++)
                    {
                        if (bulletArray[j].hitObject==false)
                        {
                            var htobj:Rectangle=com.adventuresinactionscript.collisions.HitTest.hitTestObject(bulletArray[j],level_mc.walls,true,255);
                            if (htobj!=null)
                            {
                                bulletArray[j].killSelf ();
                                bulletArray.splice (j,1);
                            }
                            if (enemyArray[i].hitTestObject(bulletArray[j])==true)
                            {
                                try
                                {
                                    bulletArray[j].hitObject=true;
                                    bulletArray[j].killSelf ();
                                    bulletArray.splice (j,1);
                                }
                                catch (e:Error)
                                {
                                }
                                try
                                {
                                    enemyArray[i].healthRem-=player_mc.weaponDamage;
                                }
                                catch (e:Error)
                                {
                                }
                                try
                                {
                                    if (enemyArray[i].healthRem<0)
                                    {
                                        enemyArray[i].healthRem=0;
                                    }

                                hbArray[i].setHealth (enemyArray[i].healthRem);
                            }
                            catch (e:Error)
                            {
                            }
                        }
                    }
                }
            }
            else
            {
                hbArray[i].killSelf ();
                hbArray.splice (i,1);
                enemyArray[i].killSelf (&quot;player&quot;);
                enemyArray[i].killed=true;
                enemyArray.splice (i,1);

            }

Michael Williams September 8, 2009 at 1:57 pm

Hey Taylor,

Yep, bullets get removed once they get above the top of the screen (see the ‘Odds and Ends’ section).

I’m not really sure what you’re asking here… are you saying the game started to lag after you followed this tutorial, so you wrote some new code (pasted) which still lagged?

Taylor Lodge September 8, 2009 at 6:53 pm

No the other developer on this project (www.code.google.com/p/advancedflashengine) had written this code before I went looking for a more efficient way of bull,enemy hit testing. In this game I was able to fill the play to the top of the screen with bullets and still have 20 enemies before it started to lagg. And seeing as my code is just the same hittesting with some health calculations in it I don’t see why it laggs so badly.

Michael Williams September 9, 2009 at 12:29 pm

Oh OK I see. Well, let’s narrow it down. Try temporarily commenting out or removing every line of code that has nothing to do with bullet-enemy hit testing, like the health calculations and the bullet-wall hit tests. What’s the fps like then?

Taylor Lodge September 9, 2009 at 8:46 pm

Well I did remove the wall hittesting didn’t make a difference. Haven’t tried removing the health calculations I’ll try at school (Most likely won’t be able to post back since this website has the word “Game” in it and my school will block it)

Michael Williams September 10, 2009 at 3:01 pm

OK, let me know how it goes.

Taylor Lodge September 10, 2009 at 8:53 pm

Problem solved the other developer on the project had for some reason unknown to everyone done the same hit testing twice with different pixel perfect hit detection.

Michael Williams September 11, 2009 at 1:12 pm

Aha, well, that explains it :)

AlexSeb December 18, 2009 at 5:08 pm

hey guys can anyone help, i have a basic error but can not solve it,

1114: The public attribute can only be used inside a package.

Source:

 public function getFinalScore():Number 

i thought this meant that public function getFinalScore():Number simply needed to be within a public, private, etc, can anyone help???
thanks
Alex

Michael Williams December 20, 2009 at 3:48 am

Note for anyone else reading: Alex and I talked about this via email; turned out it was caused by a mismatched brace.

JimE January 22, 2010 at 2:08 pm

Micheal great stuff you have here.

The pixleperfect hit detection is awesome but certainly it can cause strains correct?

So wouldn’t it be a good idea to first run a loop to hittestobject then if hit is detected to then run pixelperfect collision, to reduce overhead?

A nested loop inside of a loop?

Then the next question if this approach is valid how to write the nested loop?

Thanks

Michael Williams January 26, 2010 at 7:31 am

Hey JimE — thanks!

Yep, you’re right, it can slow things down. You’ve got a good idea there, doing the simpler hitTest before the slow pixel perfect check.

Actually, you wouldn’t need to nest two loops, but rather two if-statements, like so:

if ( avatar.hitTestObject( enemy ) )
{
    if ( pixelPerfectCollisionDetection.isColliding( avatar, enemy ) )
    {
        //they are colliding
    }
}

Make sense?

Tyler March 12, 2010 at 8:32 pm

Me again.. I keep getting errors when I attempt this tutorial :/

if ( spaceBarIsBeingPressed )
                {
                    var newBullet = new Bullet();
                    newBullet.x = avatar.x;
                    newBullet.y = avatar.y;
                    addChild( newBullet );
                }
                var j:int = bullets.length - 1;
                var bullet:Bullet;
                while ( j > -1 )
                {
                    bullet = bullets[j];
                    bullet.moveABit();
                    j = j - 1;
                }

Is there an error in there that would cause either of these errors?
1119: Access of possibly undefined property length through a reference with static type Bullet.
Warning: 3596: Duplicate variable definition.

Tyler March 15, 2010 at 7:32 pm

Ignore previous comment haha..

Now whenever I let off the spacebar my bullets don’t move.. Solution?

Michael Williams March 20, 2010 at 11:55 am

Hey Tyler,

My guess is you’ve got your curly braces in the wrong place — I’ll bet the braces belonging to the if ( spacebarIsBeingPressed ) are around the bullet.moveABit() line, am I right?

Tyler March 22, 2010 at 8:00 am

Aha! Thanks so much man! That was very annoying..

Also. I’m working on a second bullet, which may be a shield that follows the player for x ticks or may just be a bullet that goes through many enemies undecided, but it seems that with identical scripts I can’t even get the same function out of two bullets.. Any thoughts on what may be causing this? I’ll plug it through and hopefully get it up, but thanks again for your help!

Michael Williams March 22, 2010 at 11:00 am

Interesting idea :) What do you mean by getting “the same function out of two bullets”?

Tyler March 23, 2010 at 5:41 pm

I mean I basically copied the code.. Is this a problem?

Michael Williams March 23, 2010 at 5:43 pm

Ooh. Not necessarily. Want to paste it? Not the whole thing, just the relevant bits.

Tyler March 23, 2010 at 7:14 pm

 if ( pKeyIsBeingPressed )
                {
                    var newBullettwo = new Bullettwo();
                    var h:int = bulletstwo.length - 1;
                    var bullettwo:Bullettwo;
                    if ( ticksSinceLastShot >= 7 )
                    {
                        newBullettwo.x = avatar.x;
                        newBullettwo.y = avatar.y;
                        addChild( newBullettwo );
                        bulletstwo.push( newBullettwo );
                        ticksSinceLastShot = 0;
                    }
                    while ( h > -1 )
                    {
                        bullettwo = bulletstwo[l];
                        if ( bullettwo.y < -10 )
                        {
                            removeChild( bullettwo );
                            bulletstwo.splice( l, 1 );
                        }
                        h = h - 1;
                    }
                }

It complains that moveABit is not a function, and yet my original bullet works..

Also: I get Error #1009: Cannot access a property or method of a null object reference. whenever I destroy an enemy. I can’t fix it at all.. did I put the relevant code in the wrong place again..? It is under the avatarHasBeenHit boolean like on yours.. Any thoughts on this?

Michael Williams March 24, 2010 at 12:50 pm

Hey Tyler,

Have you checked out my debugging guide? It should help you figure out which exact line is causing the #1009. (You can also hit ctrl-shift-enter instead of ctrl-enter to run the SWF in Debug Mode, which will tell you the line number.)

Let me know what line is causing it and we’ll move on from there :)

Tyler May 14, 2010 at 7:25 pm

Long time since post lol

Well it’s supposedly a problem with enemy.moveABit();

Dunno if that helps

Michael Williams May 16, 2010 at 2:07 pm

Put a trace( "enemy: ", enemy ) right before that line, enemy.moveABit(). What does it say?

Tyler May 18, 2010 at 5:14 pm

It repeats the same thing over and over and then says:
enemy: null
TypeError: Error #1009: Cannot access a property or method of a null object reference.
at AvoiderGame/onTick()
at flash.utils::Timer/_timerDispatch()
at flash.utils::Timer/tick()

Michael Williams May 29, 2010 at 12:21 am

OK. See that enemy: null line? That means that your enemy variable is not set to anything. Find the most recent point before that line where you type: enemy = [something]. That [something] doesn’t exist, which is causing the problem.

Richard July 23, 2010 at 6:21 pm

Hey, great tutorials I am having allot of fun going through them however I have encountered a problem.

I want to create an explosion when an enemy gets hit, im trying to do this by when the bullet hits an enemy the enemy sprite changes to one with an explosion over it then disappears. However for some reason it doesn’t work. Code below.

                    bullet = bullets[j];
                    if ( bullet.hitTestObject( enemy ) )
                    {

                    enemyHasBeenHit = true; 
                    enemy.gotoAndPlay("NanaBoom");
                    removeChild( bullet );
                    bullets.splice( j, 1 );
                    gameScore.addToValue( 10 );
                    sfxSoundChannel = explodeSound.play();

            }

Michael Williams July 26, 2010 at 3:55 pm

Hey Richard, glad to hear you’re enjoying them.

Cool idea. I’m not sure why that wouldn’t work; your code looks fine to me. What happens if you call enemy.gotoAndPlay("NanaBoom") on every new Enemy instance as soon as you create them?

Leave a Comment

Writing code? Write <pre> at the start and </pre> at the end to keep it looking neat.

Anti-Spam Protection by WP-SpamFree

{ 1 trackback }

Previous post:

Next post: