Add Collectibles to Your AS3 Avoider Game

by Michael James Williams on April 8, 2009 · 14 comments

in Avoider Game Extras,Tutorial

Table of contents for AS3 Avoider Game: Collectibles

  1. Add Collectibles to Your AS3 Avoider Game
  2. Add More Collectibles to Your AS3 Avoider Game
  3. Add Power-Ups to Your AS3 Avoider Game

Lego stormtrooper holding huge chocolate coin.
Photo by Gaetan Lee.

When I originally converted FrozenHaddock’s AS2 tutorial to my AS3 version, I never got round to adding the golden skulls that he included. I’d like to make up for this omission now with this mini-series on adding collectible items to the game.

If you followed my tutorial, you should be able to add this feature to your own game with little trouble. Otherwise, feel free to download the zip file from the last part of the tutorial to use as a guide; if you’re familiar with AS3 it should be easy enough to understand how it all fits together.

We’ll start with simple coins that add points to your score. Click the image below to see how this will play.

AvoiderGame_Collectibles1_05.png

Collectibles are like Enemies

It wouldn’t be very difficult to change our enemies into collectibles; all we’d have to do is alter the code that gives us a game over when the avatar collides with an enemy to make it give us some extra points (and remove the enemy) instead. So we can start by copying the way our enemies work, as a base.

First, though, I’m just going to reduce the number of enemies that spawn so that I can concentrate on the collectibles while testing the game. To do this, I’ll open up the LevelData.as file and change the number of points required to advance beyond the first level, like so:

?View Code ACTIONSCRIPT3
13
14
15
16
17
18
if ( levelNumber == 1 )
{
	backgroundImage = "blue";
	pointsToReachNextLevel = 999999;
	enemySpawnRate = 0.05;
}

Right, now they won’t get in the way.

On to coins, then. The first thing to do is draw some sort of coin. As in Frozen Haddock’s tutorial, I’m going to use golden skulls. Create a new symbol and draw whatever you like:

screenshot

As you can see, I’ve replaced the eyes and nose with precious rubies. Call this movie clip GoldenSkull (or whatever’s appropriate), and export it for ActionScript with the same name.

Now for some code. Open AvoiderGame.as. Just as we have an array, army, to store all the enemies, we’ll make a new array, collectibles to store all these treasures we’re going to create. I’ll rush through the next bit, as we’ve all done it before.

First, define it at the class level:

?View Code ACTIONSCRIPT3
12
13
14
15
16
17
18
public class AvoiderGame extends MovieClip 
{
	public var gameClock:Clock;
	public var gameScore:Score;
	public var backgroundContainer:BackgroundContainer;
	public var army:Array;
	public var collectibles:Array;

Then initialise it in the constructor:

?View Code ACTIONSCRIPT3
50
51
army = new Array();
collectibles = new Array();

Now we need to create the treasures. Here’s the code that generates the enemies:

?View Code ACTIONSCRIPT3
128
129
130
131
132
133
134
135
136
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();
}

I’ll use a much simpler version (for now) for the golden skulls:

?View Code ACTIONSCRIPT3
138
139
140
141
142
143
144
145
146
147
if ( Math.random() < 1/200 )
{
	var randomXCollectible:Number = 20 + Math.random() * 360;
	var randomYCollectible:Number = 20 + Math.random() * 160;
	var newCollectible:GoldenSkull = new GoldenSkull();
	newCollectible.x = randomXCollectible;
	newCollectible.y = randomYCollectible;
	collectibles.push( newCollectible );
	addChild( newCollectible );
}

Note that we have to randomly generate a y-value this time, because there’s no code to make the skull move; if we placed it above the top of the screen it’d just stay there. Also, we set the x and y values of newCollectible manually, rather then passing them through to its constructor, because, again, we don’t have any code to manage it. Other than that, the code’s much the same.

If you test it, you should get something that looks like this:

screenshot

Perhaps the one-in-200 chance I set up for a skull appearing was too high… never mind, we’ll deal with that later. Now we have to add the “collect” part.

Here’s the code for avatar-enemy collision:

?View Code ACTIONSCRIPT3
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
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;
}

Again, I’ll use this as a base for avatar-collectible collision:

?View Code ACTIONSCRIPT3
194
195
196
197
198
199
200
201
202
203
204
205
206
var j:int = collectibles.length - 1;
var collectible:GoldenSkull;
while ( j > -1 )
{
	collectible = collectibles[j];
	if ( PixelPerfectCollisionDetection.isColliding( avatar, collectible, this, true ) ) 
	{
		removeChild( collectible );
		collectibles.splice( j, 1 );
		gameScore.addToValue( 25 );
	}
	j = j - 1;
}

(Make sure you replace all your is with js or you’ll see some odd behaviour.) Here I’m just adding 25 points whenever the player grabs a golden skull. You could play a sound too, but I’m not going to go into that here.

screenshot

The above screenshot clearly proves that my code works — otherwise, how would I have a score ending in 5? ;)

Loops Within Loops

Let’s up the stakes a bit. Suppose an enemy touches a collectible. At the minute, they just pass through it — but what if they took points away from you instead? That’d make things a bit more interesting.

To do this, we have to check every single enemy against every single collectible to see if they are touching, unfortunately. Flash is more than powerful enough to handle this though.

What we need is a nested loop — a while within another while. In this case, we can add a loop through the enemies within our existing loop through the collectibles:

?View Code ACTIONSCRIPT3
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
var j:int = collectibles.length - 1;
var collectible:GoldenSkull;
while ( j > -1 )
{
	collectible = collectibles[j];
	if ( PixelPerfectCollisionDetection.isColliding( avatar, collectible, this, true ) ) 
	{
		removeChild( collectible );
		collectibles.splice( j, 1 );
		gameScore.addToValue( 25 );
	}
 
	i = army.length - 1;
	while ( i > -1 )
	{
		enemy = army[i];
		if ( PixelPerfectCollisionDetection.isColliding( collectible, enemy, this, true ) ) 
		{
			removeChild( collectible );
			collectibles.splice( j, 1 );
			gameScore.addToValue( -25 );
		}
		i = i - 1;
	}
 
	j = j - 1;
}

Only lines 206-217 are new here. I recommend adding a trace( i, j ) before line 209 to really see what’s going on. Basically, we’re re-using the enemy and i variables from above (in the avatar-enemy collision loop) in much the same way as before. If we have three collectibles and five enemies, we’ll check to see whether collectible #3 is colliding with enemies #5, #4, #3, #2 or #1, then whether collectible #2 is colliding with enemies #5, #4, #3, #2 or #1, and finally whether collectible #1 is colliding with enemies #5, #4, #3, #2 or #1. In either case, if it’s true, we destroy the collectible and reduce the player’s score.

It’d be nice to move the avatar-enemy collision loop in here as well, but I think it’s too tricky to be worth the effort.

Try the game out now. If you adjust the probability of a golden skull appearing, it’s actually possible to get a negative score.

screenshot

Tweaking the Probability

We’ve got the enemy spawn rate tied in to the LevelData; it makes sense to do the same with the collectibles. Open up LevelData.as.

I think there are two things worth adding here: the maximum number of collectibles allowed on-screen at once, and the spawn rate for new collectibles. You could also add the points value for the skull; perhaps these should be more valuable as the game progresses. I’ll leave that up to you, though.

Start by defining the new variables needed, as usual:

?View Code ACTIONSCRIPT3
3
4
5
6
7
8
9
10
public class LevelData
{
	public var backgroundImage:String;
	public var pointsToReachNextLevel:Number;
	public var enemySpawnRate:Number;
	public var levelNum:Number;
	public var collectibleSpawnRate:Number;
	public var maxCollectiblesOnScreen:Number;

Now, put values in for these new variables. I’ll show you the first pair that I selected; choose the rest yourself:

?View Code ACTIONSCRIPT3
15
16
17
18
19
20
21
22
if ( levelNumber == 1 )
{
	backgroundImage = "blue";
	pointsToReachNextLevel = 999999;
	enemySpawnRate = 0.05;
	collectibleSpawnRate = 0.02;
	maxCollectiblesOnScreen = 3;
}

(Remember to change that value from 999999 if you want to test this at higher levels!)

Of course, this won’t have any effect until we alter the code in AvoiderGame.as, so open that class file. Earlier on we wrote this if statement:

?View Code ACTIONSCRIPT3
138
139
140
141
if ( Math.random() < 1/200 )
{
	var randomXCollectible:Number = 20 + Math.random() * 360;
	var randomYCollectible:Number = 20 + Math.random() * 160;

To change this to our collectible spawning rate from LevelData, we just need to refer to that new variable:

?View Code ACTIONSCRIPT3
138
139
140
141
if ( Math.random() < currentLevelData.collectibleSpawnRate )
{
	var randomXCollectible:Number = 20 + Math.random() * 360;
	var randomYCollectible:Number = 20 + Math.random() * 160;

What about for altering the maximum number of skulls on screen? Well, remember that we can use collectibles.length to see how many items are in that array — this will be the same as the number of skulls on screen, since skulls are on screen if and only if they are in that array. So, we can just avoid adding any new skulls unless we’re below the limit.

?View Code ACTIONSCRIPT3
138
139
140
141
142
143
144
145
146
147
148
149
150
if ( collectibles.length < currentLevelData.maxCollectiblesOnScreen )
{
	if ( Math.random() < currentLevelData.collectibleSpawnRate )
	{
		var randomXCollectible:Number = 20 + Math.random() * 360;
		var randomYCollectible:Number = 20 + Math.random() * 160;
		var newCollectible:GoldenSkull = new GoldenSkull();
		newCollectible.x = randomXCollectible;
		newCollectible.y = randomYCollectible;
		collectibles.push( newCollectible );
		addChild( newCollectible );
	}
}

Earlier on we used a nested loop, and this is a nested if. Think of nested tables, rather than a bird’s nest.

Try it out!

AvoiderGame_Collectibles1_05.png

Wrapping Up

This simple little addition has already made the gameplay much more interesting. With the right balance of point values and spawn rates, and a decent difficulty curve, the game could be pretty fun even in this simple state. Sadly, that’s the tough part ;)

You can grab the zip of all the files from this tutorial here. Like I said, this is a mini-series, so we’ll extend upon this soon. In the meantime, have a play around with it, and see what simple changes are the most effective. If you trigger a sound upon collection, is this fun, or just irritating? What about if the collectibles move about? Move away from you? Let me know what you find out in the comments!

{ 13 comments… read them below or add one }

richard May 24, 2009 at 11:26 am

Hi.
I wonder how you make so the collectible spawnrate continue to increase?
Like we do with thw enemies and level :

pointsToReachNextLevel = levelNumber * 200;
enemySpawnRate = 0.5 - ( 2 / levelNumber );

Michael Williams May 24, 2009 at 5:30 pm

Hey Richard. Should be possible to use almost exactly the same code, like this:

collectibleSpawnRate = 0.3 - ( 1 / ( levelNumber * 2 ) );
maxCollectiblesOnScreen = levelNumber;

I haven’t tested that, so please let me know if there are any bugs :)

richard May 26, 2009 at 9:19 am

That code worked great!
So stupid i am for not thinking that one out myself. I had to decrease the spawning rate a little bit but it’s wotking fine.

Michael Williams May 26, 2009 at 11:06 am

Good good :)

richard May 26, 2009 at 7:04 pm

Is there any free program like cs4? i tried flash develop that you mentioned but i cannot seem edit and create .flas in there. Is it only i that have missed it or doesn’t it work?
Sorry for my bad english.

Michael Williams May 26, 2009 at 7:55 pm

I don’t know of any other programs that let you create FLA files (though there might be some) but there are other ways of making Flash games without actually having Flash.

I don’t know much about these methods, but check out these links:

http://www.brighthub.com/internet/web-development/articles/11010.aspx
http://www.scriptedfun.com/make-a-flash-game-for-free/
http://www.dreamincode.net/forums/showtopic92205.htm

I hope they’re helpful.

If you can wait a little longer, the PushButton Engine will be released soon: http://pushbuttonengine.com/ — take a look :)

DudexD January 30, 2010 at 12:47 pm

i’m getting an Error:

Warning: 3596: Duplicate variable definition.

The game is working and stuff but, i don’t want my game to be filled with errors :P

            var j:int = collectibles.length - 1; // The Error is located here
            var collectible:GoldenSkull;
        while ( j > -1 )
            {
                collectible = collectibles[j];
                if ( PixelPerfectCollisionDetection.isColliding( avatar, collectible, this, true ) ) 
                {
                    removeChild( collectible );
                    collectibles.splice( j, 1 );
                    gameScore.addToValue( 25 );
                }
                i = army.length - 1;
                while ( i > -1 )
                {
                    enemy = army[i];
                    if ( PixelPerfectCollisionDetection.isColliding( collectible, enemy, this, true ) ) 
                    {
                        removeChild( collectible );
                        collectibles.splice( j, 1 );
                        gameScore.addToValue( -25 );
                    }
                    i = i - 1;
                }
                j = j - 1;
            }

Is it just me or Is the comment thing here kind of… Anoying?? since it keep on deleting half of my comment:?!

Michael Williams January 31, 2010 at 5:10 am

It’s deleting half your comment? Dang. I’ve never seen that before. Uh, which half?

Warnings often don’t matter very much, but I understand wanting to have everything clean :)

The warning you’re getting just means that somewhere in your code, you have written var someVariable twice for the same variable inside the same function. Any idea where?

DudexD January 31, 2010 at 1:42 pm

the deleting is inside codes. It just changes after i sumbit the comment, like i wrote this on first try

var j:int = collectibles.length - 1; // The Error is located here

But instead i ended up with this

var j:int = collectibles.length - 1; -1 </pre -1 ) 

its kind of wird..

And to the error;
I found the Var. it was the var j:int

it was both on

var j:int = bullets.length - 1;

and:

var j:int = collectibles.length - 1;

so i just changed the J in the bullet var to k instead and the warning was gone :P

Michael Williams February 2, 2010 at 7:06 pm

Ah, I think my spam detection is a little overzealous; it assumes that if you’re writing loads of code you must be trying to h4ck t3h internetz or something.

Hey, great work finding and fixing that bug!

Melon Bomb July 26, 2011 at 7:43 pm

I need help there is a problem which is coming from the “PixelPerfectCollisionDetection”,
whenever a coin spawns or the avatar collides with an enemy it sends a wall of these errors:

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()

When a coin spawns the game lags horribly and the avatar can’t pick it up and when he dies there’s a short stop before the GameOverScreen appears where it sends these errors.
I know it’s coming from the collision detection because this never happened until I added it.
Help would be appreciated, thanks.

ExplosionsHurt September 14, 2011 at 11:25 am

Ah, the classic Error #1009.

The problem is that it can occur in almost any situation. Have you removed the playScreen’s event listeners before the gameOverScreen appears?

MelonBomb September 14, 2011 at 11:07 pm

ExplosionsHurt, no I have tested the game without PixelPerfectCollisionDetection, which somehow removes the problem.
Which AS file?

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: