AS3 Avoider Game Tutorial, Part 6: Several Small Improvements

by Michael James Williams on February 10, 2009 · 90 comments

in Avoider Game Base,Tutorial

Introduction

This part of my AS3 and Flash CS3 conversion of Frozen Haddock’s avoiding game tutorial is split into several separate parts, rather than being one long tutorial. I’ve received a number of suggestions for improvements to make to the game, and here I’m going to show you how to implement them. Some are obvious to the player — like hiding the mouse cursor and improving the collision detection — while some will mainly benefit you, the programmer. Because all the changes are separate, there’s no need to work through the entire post at once.

Click the image below to see the result of this part in action:

screenshot

If you’ve not been following the tutorial so far, grab the zip file of the game so far here and extract it somewhere. Otherwise, copy the files you’ve been working on so far to a new folder, as usual.

Open the FLA file, and let’s get started.

Now You See It, Now You Don’t

We’ll start with the most requested feature ever: hiding that damn mouse cursor.

In case you hadn’t noticed it before, check this image:

screenshot

The mouse cursor appears to be holding the nose of the avatar. This is actually very easy to change. Just open the AvoiderGame.as file from your Classes directory, and add the following line to the constructor function (line 16 below):

?View Code ACTIONSCRIPT3
14
15
16
public function AvoiderGame() 
{
	Mouse.hide();

Actually Mouse is one of those keywords you have to import, so:

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
package 
{
	import flash.display.MovieClip;
	import flash.utils.Timer;
	import flash.events.TimerEvent;
	import flash.ui.Mouse;

(Line 6 above.) Save it and run it, and you’ll get the following:

screenshot

Perfect! Well, almost. Once you get Game Over you’ll find that the mouse is still missing in that screen, making it difficult (and irritating) to click the Restart button. So, go into your GameOverScreen.as file and add this line to the constructor:

?View Code ACTIONSCRIPT3
11
12
13
public function GameOverScreen() 
{
	Mouse.show();

Of course, you’ll have to import flash.ui.Mouse again for this file.

For good measure, do the same for the MenuScreen.as class. I know that at the minute there’s no need, because it’s impossible to get to the menu screen after entering the play screen, but you might decide to let the player go back to it later.

screenshot

Making Comments

Comments are incredibly useful for programmers. The basic idea is that Flash will completely ignore anything you write on a line after “//”.

For example:

?View Code ACTIONSCRIPT3
//Flash ignores this
var demo:Number = 7; //Flash sets demo to 7, but it ignores this text right here

The code box above sets the colour of these comments to green; Flash’s internal AS file editor turns them grey. In each case, the unusual colour indicates that the text is for the programmer’s benefit only.

What’s the point? Two reasons for using comments:

  1. Write notes all over your code to remind yourself (or another person) what it does
  2. Temporarily stop a piece of code from working without deleting it

Take a few minutes now to go through everything we’ve done and use comments to write notes about any part you find tricky. If you read a line and have to stop and think for a second to remember what it does, write a comment to make it clear. Try to make this a habit for any new code from now on as well.

For example, I’m going to write myself a comment for this line of AvoiderGame.as:

?View Code ACTIONSCRIPT3
gameTimer = new Timer( 25 );

I always forget whether it’s seconds or milliseconds or centiseconds or what. So:

?View Code ACTIONSCRIPT3
gameTimer = new Timer( 25 );    //ticks every 25ms = 0.025s or 40 times per second

Now I don’t have to think about it every time I look at it.

P.S. If you want to do multi-line comments (or “comment out” — that is, temporarily remove — several lines of code at once), you can use “//” repeatedly:

?View Code ACTIONSCRIPT3
//This is a long,
//multi-line comment.
//It might contain an
//explanation as to how
//an entire function works,
//or what a class is for.

But you can also use “/*” to mark the start, and “*/” to mark the end, like so:

?View Code ACTIONSCRIPT3
/* This is a long,
multi-line comment.
It might contain an
explanation as to how
an entire function works,
or what a class is for. */

Another use for comments: sign your code with your name and website address :)

Don’t Be So Hard On The Code

There are a few areas in the code where I’ve “hard-coded” certain values. In other words, I’ve written a value that we might want to change later inside some code that will make it hard to change later. Here’s an example, from Enemy.as:

?View Code ACTIONSCRIPT3
12
13
14
15
public function moveDownABit():void 
{
	y = y + 3;
}

First, I’ve called the function moveDownABit(). That’s a terrible name! This is the function that’s run every tick. By giving it a name that includes the direction of motion, I’ve pretty much ruled out the possibility of making the enemies move up, or left and right, or towards the avatar.

Second, I’ve written the number 3 directly into the code. 3 is the number that determines how far the enemy moves per tick; it’s effectively the enemy’s speed. By putting it directly into the code like that, I’ve make it harder to change later. All right, so it’s not exactly buried in such a way that we couldn’t find it later, but that’s only because we have a very simple movement function at the minute.

I’m going to change it to this:

?View Code ACTIONSCRIPT3
12
13
14
15
16
17
18
19
public function moveABit():void 
{
	var xSpeed:Number = 0;	//pixels moved to the right per tick
	var ySpeed:Number = 3;	//pixels moved downwards per tick
 
	x = x + xSpeed;
	y = y + ySpeed;
}

Now it’s easy to alter the behaviour of the enemies; just change xSpeed and ySpeed and they’ll move in a different direction. In fact, let’s improve this further by moving them from the function-level to the class-level (i.e. outside of any specific function but inside the class):

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package 
{
	import flash.display.MovieClip;
	public class Enemy extends MovieClip 
	{
		public var xSpeed:Number;	//pixels moved to the right per tick
		public var ySpeed:Number;	//pixels moved downwards per tick
 
		public function Enemy( startX:Number, startY:Number ) 
		{
			x = startX;
			y = startY;
 
			xSpeed = 0;
			ySpeed = 3;
		}
 
		public function moveABit():void 
		{
			x = x + xSpeed;
			y = y + ySpeed;
		}
	}
}

Now the actual direction and speed of movement is defined in the constructor, but are available to be read or changed from anywhere else in the class. If you wanted to, you could make a new function inside this class which the AvoiderGame class could use to pass the enemy the position of the avatar, so that it could move towards it. But I’ll leave that as a challenge for you.

A slightly simpler challenge to demonstrate how this works would be to change the lines:

?View Code ACTIONSCRIPT3
xSpeed = 0;
ySpeed = 3;

to

?View Code ACTIONSCRIPT3
xSpeed = Math.random();
ySpeed = Math.random();

Check it out! The enemies will tend to move towards the bottom-right corner of the screen — can you work out why, and fix it?

Oh! Also, because I changed the name of the function from moveDownABit() to moveABit(), you’ll have to alter AvoiderGame.as appropriately, from:

?View Code ACTIONSCRIPT3
47
48
49
for each ( var enemy:Enemy in army ) 
{
	enemy.moveDownABit();

to

?View Code ACTIONSCRIPT3
47
48
49
for each ( var enemy:Enemy in army ) 
{
	enemy.moveABit();

There are other places in the code where I’ve hard-coded values that should really be soft-coded. Can you find them?

You Only Die Once

Here’s a good bug someone spotted. Can you see it? (AvoiderGame.as)

?View Code ACTIONSCRIPT3
47
48
49
50
51
52
53
54
55
for each ( var enemy:Enemy in army ) 
{
	enemy.moveABit();
	if ( avatar.hitTestObject( enemy ) ) 
	{
		gameTimer.stop();
		dispatchEvent( new AvatarEvent( AvatarEvent.DEAD ) );
	}
}

I didn’t spot it myself at first, which is why I let it slip through. Suppose your avatar is touching two enemies at once (i.e. within one tick). It’s rare, but it does happen sometimes. Well, then, the AvatarEvent.DEAD is going to be dispatched twice within one tick. Whoops! My mistake.

Why is that a problem? Well, at the minute, it’s not, because nothing much happens when the avatar dies. But imagine if we had a lives system, and removed one life per death. Or if we added a feature to the scoring system whereby death added a bonus to your score. In either case, being able to die “twice at once” like this is just going to lead to confusing problems.

How to fix it: we’re going to use a type of data that I don’t think we’ve used before. It’s called a Boolean, which is a silly word meaning a variable that can only be set to either true or false.

?View Code ACTIONSCRIPT3
47
48
49
50
51
52
53
54
55
56
57
58
59
60
var avatarHasBeenHit:Boolean = false;
for each ( var enemy:Enemy in army ) 
{
	enemy.moveABit();
	if ( avatar.hitTestObject( enemy ) ) 
	{
		gameTimer.stop();
		avatarHasBeenHit = true;
	}
}
if ( avatarHasBeenHit )
{
	dispatchEvent( new AvatarEvent( AvatarEvent.DEAD ) );
}

In line 47 we define our new Boolean variable, avatarHasBeenHit. As you can guess from the name, it’s going to let us know whether or not the avatar has been hit. We set it to false to begin with, because the avatar hasn’t been hit yet.

Line 54 replaces the AvatarEvent dispatch call with a new line that simply sets avatarHasBeenHit to true. Note that nowhere else within the for loop can set avatarHasBeenHit back to false, so if the avatar is hit once, by any enemy, it’ll be recorded.

Lines 57-60 simply check this record to see whether the avatar was indeed hit, and, if so, runs the AvatarEvent dispatch call from before. Because this code isn’t in the for loop, it won’t be run more than once per tick (and since the gameTimer is stopped when the hit is detected, no more ticks will be run anyway).

Pixel-Perfect Hit Detection

Since we started with a popular request, let’s end with one, too.

This image shows a “collision” — at least by the game’s standards:

screenshot

If you comment out the line in AvoiderGame.as that dispatches the AvatarEvent, so that the GameOverScreen isn’t shown and the game just pauses (like in Part 2), you’ll see this is true.

Why is this considered a collision? Well, the avatar.hitTestObject( enemy ) call only checks to see whether the two objects’ bounding boxes are colliding. If I tell you that an object’s bounding box is the smallest upright rectangle that can contain that object, you’ll understand why the above situation counts as a hit:

screenshot

That’s what Flash “sees”, and so it’s not surprising the collision detection is so bad. (It’s one of Stephen Calender’s Obstacles for Flash Game Development).

Now, I would very much like to walk you, step-by-step, through building your own pixel-perfect collision detection function. However (and I hate to say this) there simply is not enough room here. Hit testing in Flash is a huge area of discussion, and any time anyone states an opinion on it, a thousand other developers jump in for a good old argument. Maybe I’ll write that post one day, but not here.

Instead, I’m going to show you how to use someone else’s brilliant AS3 collision detector. It’s written by a guy named Troy Gilbert (who invented Mockingbird — The Game Making Game) and it does its job very well.

His original code, along with its accompanying notes, is here. I have copied it into the below (collapsed) code box (click the triangle):

?View Code ACTIONSCRIPT3
1
2
3
4
5
6
7
8
9
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
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
package
{
	import flash.display.BitmapData;
	import flash.display.BitmapDataChannel;
	import flash.display.BlendMode;
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	public class PixelPerfectCollisionDetection
	{
		/** Get the collision rectangle between two display objects. **/
		public static function getCollisionRect(target1:DisplayObject, target2:DisplayObject, commonParent:DisplayObjectContainer, pixelPrecise:Boolean = false, tolerance:Number = 0):Rectangle
		{
			// get bounding boxes in common parent's coordinate space
			var rect1:Rectangle = target1.getBounds(commonParent);
			var rect2:Rectangle = target2.getBounds(commonParent);
			// find the intersection of the two bounding boxes
			var intersectionRect:Rectangle = rect1.intersection(rect2);
			if (intersectionRect.size.length> 0)
			{
				if (pixelPrecise)
				{
					// size of rect needs to integer size for bitmap data
					intersectionRect.width = Math.ceil(intersectionRect.width);
					intersectionRect.height = Math.ceil(intersectionRect.height);
					// get the alpha maps for the display objects
					var alpha1:BitmapData = getAlphaMap(target1, intersectionRect, BitmapDataChannel.RED, commonParent);
					var alpha2:BitmapData = getAlphaMap(target2, intersectionRect, BitmapDataChannel.GREEN, commonParent);
					// combine the alpha maps
					alpha1.draw(alpha2, null, null, BlendMode.LIGHTEN);
					// calculate the search color
					var searchColor:uint;
					if (tolerance <= 0)
					{
						searchColor = 0x010100;
					}
					else
					{
						if (tolerance> 1) tolerance = 1;
						var byte:int = Math.round(tolerance * 255);
						searchColor = (byte <<16) | (byte <<8) | 0;
					}
					// find color
					var collisionRect:Rectangle = alpha1.getColorBoundsRect(searchColor, searchColor);
					collisionRect.x += intersectionRect.x;
					collisionRect.y += intersectionRect.y;
					return collisionRect;
				}
				else
				{
					return intersectionRect;
				}
			}
			else
			{
				// no intersection
				return null;
			}
		}
		/** Gets the alpha map of the display object and places it in the specified channel. **/
		private static function getAlphaMap(target:DisplayObject, rect:Rectangle, channel:uint, commonParent:DisplayObjectContainer):BitmapData
		{
			// calculate the transform for the display object relative to the common parent
			var parentXformInvert:Matrix = commonParent.transform.concatenatedMatrix.clone();
			parentXformInvert.invert();
			var targetXform:Matrix = target.transform.concatenatedMatrix.clone();
			targetXform.concat(parentXformInvert);
			// translate the target into the rect's space
			targetXform.translate(-rect.x, -rect.y);
			// draw the target and extract its alpha channel into a color channel
			var bitmapData:BitmapData = new BitmapData(rect.width, rect.height, true, 0);
			bitmapData.draw(target, targetXform);
			var alphaChannel:BitmapData = new BitmapData(rect.width, rect.height, false, 0);
			alphaChannel.copyChannel(bitmapData, bitmapData.rect, new Point(0, 0), BitmapDataChannel.ALPHA, channel);
			return alphaChannel;
		}
		/** Get the center of the collision's bounding box. **/
		public static function getCollisionPoint(target1:DisplayObject, target2:DisplayObject, commonParent:DisplayObjectContainer, pixelPrecise:Boolean = false, tolerance:Number = 0):Point
		{
			var collisionRect:Rectangle = getCollisionRect(target1, target2, commonParent, pixelPrecise, tolerance);
			if (collisionRect != null && collisionRect.size.length> 0)
			{
				var x:Number = (collisionRect.left + collisionRect.right) / 2;
				var y:Number = (collisionRect.top + collisionRect.bottom) / 2;
				return new Point(x, y);
			}
			return null;
		}
		/** Are the two display objects colliding (overlapping)? **/
		public static function isColliding(target1:DisplayObject, target2:DisplayObject, commonParent:DisplayObjectContainer, pixelPrecise:Boolean = false, tolerance:Number = 0):Boolean
		{
			var collisionRect:Rectangle = getCollisionRect(target1, target2, commonParent, pixelPrecise, tolerance);
			if (collisionRect != null && collisionRect.size.length> 0) return true;
			else return false;
		}
	}
}

Using this is very simple. Make a new ActionScript file, and call it PixelPerfectCollisionDetection.as. Copy the above code into it, and save it in your Classes directory.

Now, head back to AvoiderGame.as, and replace the line:

?View Code ACTIONSCRIPT3
51
if ( avatar.hitTestObject( enemy ) )

with this line:

?View Code ACTIONSCRIPT3
51
if ( PixelPerfectCollisionDetection.isColliding( avatar, enemy, this, true ) )

Let’s look at that call in detail:

  • PixelPerfectCollisionDetection — Name of the class.
  • isColliding — This is a function within that class. Specifically, it’s a static function, which means it belongs to the class rather than to a specific instance of the class. (We used the static keyword when making our own Events.)
  • avatar, enemy — These are the objects we want to check to see if they’re colliding.
  • this — The function requires the third parameter to be the “common parent” of the two objects we’re checking for collisions. Since we’ve run addChild() on each of them from within this class, this is the common parent of them both.
  • true — The function requires the fourth parameter to be a Boolean specifying whether or not to be pixel-precise. I figured it might as well be.

Wrapping Up

That’s it for this week. As usual you can grab the zip file here. Next week we’ll let the user choose between mouse and keyboard controls, and make the avatar follow the mouse rather than be a replacement cursor.

Update: I just found out about Corey O’Neil’s AS3 Collision Detection Kit (thanks to Ryan of Untold Entertainment). How I missed this before I do not know. It’s a set of classes that give all sorts of different ways of doing collision detection and dealing with it. If you want to do more with your collision detection, check it out.

{ 89 comments… read them below or add one }

Ryan February 10, 2009 at 4:29 pm

Sweet ive been stoked for these tutorials, they have helped a lot and set a good foundation for any other project that I work on. Ive been working on converting my banner to the same type of programming style with 1 frame and no code in symbols or frames. I start my college flash class today and am stoked with the headstart that I have gotten from this. Thanks a lot for the help and keep up the good clean style of programming!

Ryan

FrozenHaddock February 15, 2009 at 4:46 pm

Excellent list of tweaks :) And I’ll be using that pixel-perfect hittest ;P Needed one of those!

Mushyrulez February 15, 2009 at 10:40 pm

Are there circular HitTests?

Also I can’t finish this challenge:
xSpeed = Math.random();
ySpeed = Math.random();

I know that the xSpeed must be both negative AND positive, but I do not know how to go about doing this!

Mushyrulez February 15, 2009 at 10:41 pm

XD never mind about that top comment!

Mushyrulez February 15, 2009 at 10:44 pm

Also, in the place where you use booleans, you made it moveDownABit instead of moveABit, you might want to change that.

Great tutorial – look forward to seeing the next one!

MichaelJWilliams February 15, 2009 at 10:50 pm

Whoops! Thanks for pointing that out, I’ve corrected it now.

And re. your xSpeed: you know that Math.random(); is between 0 and 1, and you know you want it to be negative or positive… how about if it was between (some negative number) and (some positive number)? Like, oh I dunno, -0.5 and 0.5? :)

Mushyrulez February 15, 2009 at 11:01 pm

Oh, Now i see!
Thanks!

Travis February 16, 2009 at 2:58 am

hey, great tutorials. Thanks, Michael. I went through them knowing very little Actionscript, and they’ve helped a ton. I’m having a great time developing my game further. The style of this last one was good; I liked throughout them when you made me implement some previously covered concepts myself.

One question – it seems bad to let each enemy continue out to infinity, since we keep making more… I tried implementing the code below, and the game crashes after the first enemy hits y = 400 and disappears. Your thoughts on if the enemies should stop existing at some point and what the right way to do it is?

this is on tick, right after the bit about collision detection:

for each ( var enemy:Enemy in army )
{
if ( enemy.y > 400 )
{
enemy.parent.removeChild(enemy);
}
}

MichaelJWilliams February 16, 2009 at 4:57 pm

Thank you Travis, I’m glad to have helped :)

You’re right, it is a bad idea to keep these enemies around forever; I haven’t noticed any lag yet but if we were using more complicated graphics or a longer game time we’d certainly see some nasty effects.

You’ve made a good start with your code. I’m guessing the error you get is something like “…must be a child of the DisplayObjectContainer”. I expect what’s going wrong is that you’re telling the enemy’s parent to removeChild(enemy) multiple times — once in the tick immediately after it gets below the play field, and then every single tick after that. While you can run addChild() multiple times on one object, you can’t do the same with removeChild().

A solution that I often use is:

for each ( var enemy:Enemy in army ) 
{ 
    if ( enemy.y > 400 ) 
    { 
        if ( enemy.parent == this )
        {
            removeChild( enemy ); 
        }
    } 
}

This way, if the enemy has already been removed from the playScreen class, it won’t be removed twice. (We can also drop the enemy.parent bit.)

Thing is, this only removes the image of the enemy from the screen. The enemy object (as an instance of Enemy) still exists, and still has properties like x and y that are taking up memory.

We can stop this by setting it to null, as we do with the various screens, but this leads to a further problem. Suppose this particular enemy was at army[10]. Well, now army[10] is pointing to null, rather than an enemy. This means that the part of the code that loops through the army array is going to throw an error when it gets to this point, because it can’t do collision detection, set x and y values, etc, on a null object!

We can hack around this by putting something like:

if ( army[i] != null )

around anything that needs to deal with an element of the array. That’s not necessarily a bad way of doing things. Alternatively we could use the Array.splice() or Array.filter() methods to compress the army array so that every element was an actual enemy.

One common strategy is reusing old enemies. When your enemy reaches the bottom of the screen, you mark it as “inactive” somehow (perhaps this is a property of the enemy, perhaps you move it to another array, whatever works best) and then the next time you need a new enemy to appear at the top of the screen, you use this one again. Rather than checking whether an enemy is null before doing collision detection, movement, etc, you check whether it’s active.

Following on from this is object pooling. Start with an array of, say, 100 enemies, all marked as inactive, and only make them active as you need new ones. As above, reuse the old ones. The key here is not to use more than 100 enemies at once ever. This way, in theory, your enemies will use the same amount of memory all the way through the game, because you’re never creating new ones.

On from there you start getting in to AS3 “Design Patterns”. The definitive website for these is here: http://www.as3dp.com/. Take a look at the Factory pattern, in particular.

Of course, as we’ve seen, we can get away with using a bit of inefficient code for simple games at least :)

Kevin February 23, 2009 at 6:23 pm

Hi Michael,

I love the idea of pixel perfect hitTesting, which will allow me to create a symbol named “level1_walls” for example but when I put the code in my hero(avatar) treats the code just like hitTestObject, as though no change was made. I seen a comment on Troy Gilberts page where the person had the same problem. I want to accomplish projectile hits against walls aswell as other ideas, and avoid being inefficent, doing what I did in AS2..

if (hero.hand1.arrow.hitTest(wall1){
if (hero.hand1.arrow.hitTest(wall2){
if (hero.hand1.arrow.hitTest(wall3){

funny becuase in AS2 the
While (hero._x , //etc hitTest(wall)){ worked making hero(the player) not go thru walls yet projectiles and line of sight wouldnt work unless i seperated walls into symbols(fun)

So in AS3 what should I do? If you have some temperary code to work for now that would be great, and a tutorial if you had time would benefit many of us, unless its a simple fix that I just haven’t figured out yet?

MichaelJWilliams February 23, 2009 at 7:44 pm

Hey Kevin

Great idea with the walls. That would actually be a really good way to make separate levels. Heck, you could practically make Pacman with a few changes here and there.

Actually, I don’t think splitting the walls up into different symbols would be inefficient (though granted it would be boring for the level designer!). However, rather than write a big ‘if’ statement by hand, I’d recommend creating an object called Walls that contained an array that in turn contained all your individual rectangular wall blocks, then use a “for” loop to check if the Avatar is hitting any of them in the same way that you check if it’s hitting any of the enemies.

But that wasn’t your question. I’ll take a look at the pixel-perfect hittest tonight, and see if I can figure out why it’s not doing what you expect.

MichaelJWilliams February 23, 2009 at 8:41 pm

Hmm so I had a go and I’m not sure what was going wrong with yours, mine detects it fine. (Actually, the hard part was working out how to stop the avatar from moving once it had hit a wall; I used the code from Part 7 as a base which made it much easier, since I only had to deal with 4-directional movement).

The steps I took:

  1. Created a new symbol, linked it to AS (though this was unnecessary) and draw a bunch of lines in it
  2. Added this to the PlayScreen symbol and gave it an instance name of “walls”
  3. In AvoiderGame.as I checked inside the onTick code, after the avatar had been moved, to see if the avatar was colliding with “walls”, using the same code as for checking for collisions with enemies
  4. If a collision occurred, I moved the avatar back to its original position

Not perfect, but it worked. You could get the enemies to notice the walls with the same code (and, I suppose, change direction after that?) I’ve put a zip of this up here if you’d like it: http://gamedev.michaeljameswilliams.com/downloads/tutorials/AvoiderGame/misc/AvoiderGame-MJW-Walls.zip

Would you like to send me yours so I can see if I can find what the difference is? My email’s on the About page.

Kevin March 2, 2009 at 7:28 pm

Hi Again,

F.Y.I. pixelperfect works. I added the code, and forgot to delete the old code “if (hero.hitTestObject(enemy){” in there, so I assume the old code was overwriting the new code. My mistake.

this is my game so far, feel free to delete it off this comment when you post, the code for you to look at. also the code wont work without the other .as files like GameMechanics, etc. So far I’ve accomplished (with your help, picking random code online, and a book I bought)making each enemy able to follow hero, follow and pick up weapon(staff) if hero not present (and within distance). also hitTest walls successfully, hero vs enemy, but NOT enemy VS enemy.
I beleive I need to restructure my enemy array into a for loop somehow,
maybe for (var i: = 0, i < 3; i++)
im using while right now, b/c it works. and feels limited

(tried but its probably got to be in a different function(). then use hitTest in a for(){for(){}} double loop.

This code I want to highlight: I’m trying to get the enemies to hitTest each other, without hitTesting themselves, which causes them to move faster toward hero (as a glitch)

for (var i:int = 0; i < army.length - 1; i ++) {

         var enemyA:Enemy = army[i] as Enemy;
            for (var j:int = i + 1; j &lt; army.length - 1; j ++) {

                var enemyB:Enemy = army[i] as Enemy;
                trace(enemyA.hitTestObject(enemyB));
                //output: true, true, true, true,
             }

if there’s code with // or /* */ its likely things im working on that don’t work. or do what I want it to do. Code not cleaned up , which means some var are “extra” so please dont dwell on what is sitting there and not being used.

package {
    //NEW in AS3:Import everything that needs to be imported in this .as file! (required) 
    import flash.events.KeyboardEvent;//keyboard
    import flash.ui.Keyboard;//more keyboard 
    import flash.display.MovieClip;//all movie clips involved
    import flash.events.Event;//events used
    import flash.ui.Mouse;
    import flash.utils.Timer;//temperary placeholder to make Enemy AI have a working event listener
    import flash.events.TimerEvent;//read above
    import flash.utils.Timer;
    import flash.events.TimerEvent;
    //Definition of MainConstructor
    public class MainConstructor extends MovieClip {
        public var downKeyIsBeingPressed:Boolean;
        public var upKeyIsBeingPressed:Boolean;
        public var leftKeyIsBeingPressed:Boolean;
        public var rightKeyIsBeingPressed:Boolean;
        public var hero:Hero;
        public var cursor:mouseCursor;
        public var gameTimer:Timer;
        public var projectile:Projectile;

public var heroMomentum = "";
public var wall:Wall;
public var enemy:Enemy;
public var army:Array;
public var projArray:Array;

public var staff:Staff;
public var inventory:String = "empty";

public function MainConstructor() {
    Mouse.hide();
    downKeyIsBeingPressed = false;
    upKeyIsBeingPressed = false;
    leftKeyIsBeingPressed = false;
    rightKeyIsBeingPressed = false;
    //Hero Placed
    hero = new Hero();
    addChild(hero);
    hero.x = 300;
    hero.y = 500;

//mouse cursor placed
        cursor = new mouseCursor();
        addChild(cursor);
        cursor.x = mouseX;
        cursor.y = mouseY;</p>

   gameTimer = new Timer( 25 );
    gameTimer.addEventListener( TimerEvent.TIMER, onTick );
    gameTimer.start();

    projArray = new Array();
    //var proj = new Projectile( 100, 300 );
    //projArray.push( proj );
    //addChild( proj );


    army = new Array();
    var enemy = new Enemy( 100, 300 );
    army.push( enemy );
    addChild( enemy );

    //staff Placed
    staff = new Staff();
    addChild(staff);
    staff.x = 200;
    staff.y = 100;

    //wall Placed
    wall = new Wall();
    addChild(wall);
    wall.x = 300;
    wall.y = 300;



    //Event Listeners
    addEventListener( Event.ENTER_FRAME, EnemyAI, false, 0, true );
    addEventListener( Event.ADDED_TO_STAGE, onAddToStage );

}
//KEYBOARD
public function onAddToStage( event:Event ):void {
    stage.addEventListener( KeyboardEvent.KEY_DOWN, onKeyPress );
    stage.addEventListener( KeyboardEvent.KEY_UP, onKeyRelease );
}
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;
    }
    //Walls Defined-check also EnemyAI() function for Wall VS Enemy

    //

}
public function onTick( timerEvent:TimerEvent ):void {
    //Hero Moves 
    if (( downKeyIsBeingPressed ) &amp;&amp; (!(wall.hitTestPoint(hero.x, hero.y+hero.height/2, true)))) {
        hero.speed( 0, 1 );
    }
    if (( upKeyIsBeingPressed )&amp;&amp; (!(wall.hitTestPoint(hero.x, hero.y-hero.height/2, true)))) {
        hero.speed( 0, -1 );
    }
    if (( leftKeyIsBeingPressed )&amp;&amp;(!(wall.hitTestPoint(hero.x-hero.width/2, hero.y, true)) )) {
        hero.speed( -1, 0 );
    }
    if (( rightKeyIsBeingPressed )&amp;&amp;(!(wall.hitTestPoint(hero.x+hero.width/2, hero.y, true)))) {
        hero.speed( 1, 0 );
    }
}
public function onKeyRelease( keyboardEvent:KeyboardEvent ):void {
    if ( keyboardEvent.keyCode == Keyboard.DOWN ) {
        downKeyIsBeingPressed = false;
    } else if ( keyboardEvent.keyCode == Keyboard.UP ) {
        upKeyIsBeingPressed = false;
    } else if ( keyboardEvent.keyCode == Keyboard.LEFT ) {
        leftKeyIsBeingPressed = false;
    } else if ( keyboardEvent.keyCode == Keyboard.RIGHT ) {
        rightKeyIsBeingPressed = false;
    }
}
//AI 
public function EnemyAI( evt:Event):void {//reminder: this function is a FOR LOOP! watch what you edit!


    for each (var enemy:Enemy in army) {
        //hero's mouse rotation
        hero.rotation = GameMechanics.Direction(hero.x, hero.y, mouseX, mouseY);
        //mousecursor
        cursor.x = mouseX;
    cursor.y = mouseY;
        //hero.rotation = GameMechanics.Direction(hero.x, hero.y, mouseX, mouseY);

        //
        //test var
        var radiansNegative:Number = GameMechanics.deg2radNegative(enemy.rotation);
        //
        var radians:Number = GameMechanics.deg2rad(enemy.rotation);
        var xVel:Number = Math.cos(radians) * 3;
        var _xVel:Number = Math.cos(radiansNegative) * 3;//negative version of this var
        var yVel:Number = Math.sin(radians) * 3;//enemy follow speed
        var _yVel:Number = Math.sin(radiansNegative) * 3;//negative ""
        var xVel2:Number = Math.cos(radians) * 8;
        var yVel2:Number = Math.sin(radians) * 8;//projectile follow speed
        //var to calculate distance between nearest enemy to staff or hero, try tracing it 
        var heroDist = GameMechanics.Distance(enemy.x, enemy.y, hero.x, hero.y);
        var staffDist = GameMechanics.Distance(enemy.x, enemy.y, staff.x, staff.y);
        var wallDist = GameMechanics.Distance(enemy.x, enemy.y, wall.x, wall.y);

        //generic enemy code(arrays) , this statement says when 3 enemies enter stage, stop putting in enemies
        //the 4th enemy is required , code doesnt work otherwise, his code is in the MainConstructor public function.
        while (army[3] == null) {//null means the value of army is nothing. 
            var randomX:Number = Math.random() * 400;
            var randomY:Number = Math.random() * 400;
            var newEnemy:Enemy = new Enemy( randomX, randomY );
            army.push( newEnemy );
            addChild( newEnemy );

        }
        //if distance between hero and enemy is &lt;= 200 pixels, then go xVel speed
        //xVel is a var I made up. it tells the enemy to move toward hero at a certain speed
        if (heroDist &lt;= 200) {
            enemy.rotation = GameMechanics.Direction(enemy.x, enemy.y, hero.x, hero.y);
            enemy.x += xVel;
            enemy.y += yVel;
        } else if ((staffDist &lt;=200) &amp;&amp; (inventory == "empty")) {
            enemy.rotation = GameMechanics.Direction(enemy.x, enemy.y, staff.x, staff.y);
            enemy.x += xVel;
            enemy.y += yVel;
        }
        //Collect Staff
        if (enemy.hitTestObject(staff)) {
            staff.x = enemy.x;
            staff.y = enemy.y;
            staff.rotation = enemy.rotation;
            inventory = "full";
        }
        //hitTest Enemy vs another w/o hitting itself//testing code


        ///
        /*for (var i:int = 0; i &lt; army.length - 1; i ++) {

            var enemyA:Enemy = army[i] as Enemy;
            for (var j:int = i + 1; j &lt; army.length - 1; j ++) {

                var enemyB:Enemy = army[i] as Enemy;
                trace(enemyA.hitTestObject(enemyB));
                if (enemyA.hitTestPoint(enemyB.x+enemyB.width/0, enemyB.y, true)) {
                    enemyB.x -= 6;
                }
                if (enemyA.hitTestPoint(enemyB.x, enemyB.y-enemyB.height/0, true)) {
                    enemyB.y += 6;
                }
                if (enemyA.hitTestPoint(enemyB.x-enemyB.width/0, enemyB.y, true)) {
                    enemyB.x += 6;
                }
                if (enemyA.hitTestPoint(enemyB.x, enemyB.y+enemyB.height/0, true)) {
                    enemyB.y -= 6;
                }
            }*/
        //}
        //Wall VS Enemy
        //not cover y- or x+ hit
        if (wall.hitTestPoint(enemy.x+enemy.width/2, enemy.y, true)) {
            enemy.x -= 6;
        }
        if (wall.hitTestPoint(enemy.x, enemy.y-enemy.height/2, true)) {
            enemy.y += 6;
        }
        if (wall.hitTestPoint(enemy.x-enemy.width/2, enemy.y, true)) {
            enemy.x += 6;
        }
        if (wall.hitTestPoint(enemy.x, enemy.y+enemy.height/2, true)) {
            enemy.y -= 6;
        }
        /////

        //Enemy VS Hero and vise versa
        if ( !PixelPerfectCollisionDetection.isColliding( wall, enemy, this, true ) ) {
            if (hero.hitTestPoint(enemy.x+enemy.width/2, enemy.y, true)) {
                enemy.x -= 3;
            }
            if (hero.hitTestPoint(enemy.x-enemy.width/2, enemy.y, true)) {
                enemy.x += 3;
            }
            if (hero.hitTestPoint(enemy.x, enemy.y+enemy.height/2, true)) {
                enemy.y -= 3;
            }
            if (hero.hitTestPoint(enemy.x, enemy.y-enemy.height/2, true)) {
                enemy.y += 3;

            }
        }
        //
        if ( !PixelPerfectCollisionDetection.isColliding( hero, wall, this, true ) ) {
            if (enemy.hitTestPoint(hero.x+hero.width/2, hero.y, true)) {
                hero.x -= 6;
            }
            if (enemy.hitTestPoint(hero.x-hero.width/2, hero.y, true)) {
                hero.x += 6;
            }
            if (enemy.hitTestPoint(hero.x, hero.y+hero.height/2, true)) {
                hero.y -= 6;
            }
            if (enemy.hitTestPoint(hero.x, hero.y-hero.height/2, true)) {
                hero.y += 6;
            }
        }

        //wall vs projectile
        //if (wall.hitTestPoint(projectile.x+projectile.width/2, projectile.y, true)) {
        //if ((projectile.numChildren &gt; 0)) {
        //trace ("5 roj");
        //}

        //}
        //if (wall.hitTestPoint(projectile.x-projectile.width/2, projectile.y, true)) {
        //removeChild(projectile);
        //}
        ///if (wall.hitTestPoint(projectile.x, projectile.y+projectile.height/2, true)) {
        //removeChild(projectile);
        //}
        //if (wall.hitTestPoint(projectile.x, projectile.y-projectile.height/2, true)) {
        //removeChild(projectile);

        //}
        /*if ((heroDist &lt;=150) &amp;&amp; (Math.random() &lt; 0.1)) {//projectile placed at currect position
        var projectile:Projectile = new Projectile( enemy.x,enemy.y );
        projArray.push( projectile );
        addChild( projectile );

        projectile.rotation = GameMechanics.Direction(projectile.x, projectile.y, hero.x, hero.y);
         //projectile.x = enemy.x;
         //projectile.y = enemy.y;


        }
        if ((heroDist &lt;=150)) {//projectile placed at currect position


        projectile.x += xVel2;
        projectile.y += yVel2;
        } //else if (heroDist &gt; 150) {
        //}//unload MovieClip*/
    }
}

}
}

Kevin March 2, 2009 at 10:01 pm

Regarding wall hitTests, this code works, for any direction the movieclip is heading, although it creates a bounce effect. i believe if the “enemy.x -= 6;” is changed, then the bounce effect will be removed. also when using this code, its only flaw, is you have to keep the .x and/or .y number for (hero VS wall) higher than the sum of (hero VS enemy) + (hero’s movement toward wall)+ (anything else pushing hero toward wall); keeping the hero from moving thru wall.

I managed to remove the bounce effect for enemy,
using the var’s (xVel,yVel) these var’s define the direction the enemy is headed, so i reversed the enemies direction.

Although the glitch is if the enemy touches -x +y of wall or +x -y of wall(any outer corners) he goes thru the wall using the outer corners. So you wont see this change in the code provided(put it back the way it was.)

//Wall VS Enemy

        if (wall.hitTestPoint(enemy.x+enemy.width/2, enemy.y, true)) {
            enemy.x -= 6;
        }
        if (wall.hitTestPoint(enemy.x, enemy.y-enemy.height/2, true)) {
            enemy.y += 6;
        }
        if (wall.hitTestPoint(enemy.x-enemy.width/2, enemy.y, true)) {
            enemy.x += 6;
        }
        if (wall.hitTestPoint(enemy.x, enemy.y+enemy.height/2, true)) {
            enemy.y -= 6;
        }

MichaelJWilliams March 3, 2009 at 2:05 am

Wow, Kevin, I’m impressed! Thank you for sharing your code. Do you have a copy of the SWF uploaded somewhere so we can play it? I’d really like that.

Nice work getting the wall hittest working too. I think you could remove the “bounce” effect (if you wanted to) by keeping the code you pasted in the onTick() function, but putting it after the avatar had been moved. That way, the bounce effect occurs, but it happens so quickly that it can’t be seen.

It sounds like you’re really close with the enemy-enemy collision detection as well. Try this:

for (var i:int = 0; i < army.length - 1; i ++) {
                var enemyA:Enemy = army[i] as Enemy;
                for (var j:int = i + 1; j < army.length - 1; j ++) {

                var enemyB:Enemy = army[j] as Enemy;
                if ( enemyA != enemyB )
                {
                    trace(enemyA.hitTestObject(enemyB));
                }
                else
                {
                    trace( false );
                }
                //outputs "true" if and only if enemyA and enemyB are different (i.e. not equal) and colliding
             }

Was that what you were after?

Cheers

EDIT: Oh, I see you had already overcome this by setting j = i + 1. Oops.

Kevin March 4, 2009 at 7:08 pm

The code works. The problem is there is 2 different variables in the array: enemy and newEnemy. If I can just use

while (army[3] == null) {//null means the value of army is nothing.
var randomX:Number=Math.random() * 400;
var randomY:Number=Math.random() * 400;
var newEnemy:Enemy=new Enemy(randomX,randomY);
army.push(newEnemy);
addChild(newEnemy);

            }

to create enemies without the

army=new Array ;
var enemy=new Enemy(100,300);
army.push(enemy);
addChild(enemy);

or in otherwords, deal with only the variable “enemy” and take newEnemy completely out of the code, then All enemies might listen to each other. At the moment, the code tells the first created “enemy” at (100,300) to listen to ONE newEnemy
(likely in argument army[0]).

I have tryed to take out “enemy” although when I do that everything listed under
for each (var enemy:Enemy in army) {
doesn’t work.

Kevin March 4, 2009 at 7:37 pm

Nevermind I made it work, I selected the “find” search tool, clicked case-sensitive, typed newEnemy, and replaced all with “enemy”. If possible though I want to get rid of the enemy that is placed at specific x,y cordinates. I just want all of them to generate from the while statement. Reason is becuase the enemy placed at specific x,y still doesnt listen to other enemies. If I delete any of his 4 lines of code (in the MainConstructor Public Function) then anything under
for each (var enemy:Enemy in army) { doesn’t work.

Kevin March 4, 2009 at 10:03 pm

The enemy VS enemy FIXED
the for loop I originally had was flawed a bit. Here’s a for loop that you should post on your page for those like myself who are trying to make the enemies notice each other without noticing themselves.
please reformat ths comment, i notice most my comment code is hard to read b/c it squishes together.

army=new Array ;

        //generic enemy code(arrays) , this statement says when 3 enemies enter stage, stop putting in enemies
        //the 4th enemy is required , code doesnt work otherwise, his code is in the MainConstructor public function.
        for (var i:int=0; i < 10; i++) {//null means the value of army is nothing. 
            var randomX:Number=Math.random() * 400;
            var randomY:Number=Math.random() * 400;
            var enemy:Enemy=new Enemy(randomX,randomY);
            army.push(enemy);
            addChild(enemy);

var enemyA:Enemy;
var enemyB:Enemy;

            for (var i:int = 0; i < army.length; i++) {
                enemyA = army[ i ];

            for (var j:int = 0; j &lt; army.length; j++) {
                enemyB = army[ j ];

                if (enemyA != enemyB &amp;&amp; enemyA.hitTestObject(enemyB)) {



                    if (enemyA.hitTestPoint(enemyB.x + enemyB.width / 2,enemyB.y,true)) {
                        enemyB.x--;
                        enemyA.x++;
                    }
                    if (enemyA.hitTestPoint(enemyB.x,enemyB.y - enemyB.height / 2,true)) {
                        enemyB.y++;
                        enemyA.y--;
                    }
                    if (enemyA.hitTestPoint(enemyB.x - enemyB.width / 2,enemyB.y,true)) {
                        enemyB.x++;
                        enemyA.x--;
                    }
                    if (enemyA.hitTestPoint(enemyB.x,enemyB.y + enemyB.height / 2,true)) {
                        enemyB.y--;
                        enemyA.y++;
                    }
                }
            }
        }

now I still have a problem with the bouncing effect, youll see in the swf i sent. you wont see the enemy vs enemy hitTesting in there, cas its the older swf. Can you go into more detail about what you were saying? your qoute”but putting it after the avatar had been moved” or just throw in some code i can try/manipulate.

MichaelJWilliams March 4, 2009 at 10:05 pm

Hmm, to be honest I don’t know why that would make a difference. It shouldn’t matter what name you give the variable that’s used for referring to the enemy within the army.

In other words, you should be able to say:

var abc:Enemy = new Enemy( 100, 300 );
army.push( abc );

and then say:

var xyz:Enemy = army[0];
xyz.x += 10;

So I’m not entirely sure what’s causing that. But if you’ve fixed it, that’s great :P

Anyway, regarding the creation of the enemy placed at specific (x,y)-coordinates… you mentioned deleting any of his 4 lines of code. Do you mean these?

        army = new Array();
        var enemy = new Enemy( 100, 300 );
        army.push( enemy );
        addChild( enemy );

If so, I can understand why you couldn’t just delete any old line, but you should be able to delete the last three (you still need to initialise the army Array).

What errors are you getting?

MichaelJWilliams March 4, 2009 at 10:19 pm

Oh, huh, we posted at the same time. That happens more often than I would expect…

I think it would be best to continue this conversation via email, since your version has added quite a bit to the game that most other readers won’t be using. I’ll send you an email now :)

joe March 5, 2009 at 7:22 pm

This is by far the best tutorial out there right now. I have read books and have done some coding but building off this has been amazingly easy and fast. I have been slowly but surely turning this into a universal space shooter engine where you can simply add elements through oop to make it do whatever you want, one problem though, the main loop don’t work none. I have been getting null object and undefined property references. I am just desperate at this point. I just cant figure it out. I have been adding other things to the code putting it off for later but I cant figure it out. Here’s the problem code:

for (var i:int=actors.length-1; i>=0; i--) {
   actors[i].moveme();
   if (actors[i].hitpoints < 1) {
      actors[i].kill();
      actors.splice(actors[i],1);
      i++;
      break;
   }
   for (var j:int=ammo.length-1; j>=0; j--) {
      ammo[j].moveme();
      if (ammo[j].hitTestObject (actors[i])) {
         ammo[j].hit(actors[i]);
         ammo.splice(ammo[j],1);
         j++;
         break;
      }
   }
}

The kill function simply removes from stage for now. and the hit function reduces hitpoints. It all seems to work for a while, then once a hittest is triggered I get errors. Some happen once, some repeat every tick. It may be that the target is being hit by two bullets, but that should just push it negative as its a Number class. No problem. Then it hits the kill function, is removed from stage and removed from array. simple. Identical process for the bullets minus hitpoint test. Another problem is before the first enemy spawns the bullets don’t move I set the loop to >=0 just to avoid this and it runs without errors this way but it did not fix that. I have been spawning one before the loop to compensate but its gonna be a problem after I put in level spawning code. Could you please enlighten me to my fault? Oh and I tried wrapping it in error handling “if..null” code and that didn’t do it.

joe March 5, 2009 at 7:27 pm

Oh sorry about the terrible format, I did not know how to submit code properly. I’ll format that and resubmit the comment if you email me. Is it just a code tag?

MichaelJWilliams March 5, 2009 at 7:42 pm

Thanks Joe!

Your space shooter sounds interesting, I’d love to see it when it’s done.

Regarding your code, I can see two potential causes of problems.

First, the splice() method takes an integer for its first argument (specifically, the index of the (first) item you wish to remove), not an object. So “actors.splice(actors[i],1);” should simply be “actors.splice(i,1);”, and ditto for ammo.

Second, Flash doesn’t necessarily know what kind of object is in the array. So, a command like “actors[i].moveme();” may be causing that ‘undefined property reference’ error because Flash cannot be sure that the object at actors[i] has a function called moveme(). Assuming that whatever object is in the array is of the class “Actor”, you can get around this by writing “Actor( actors[i] ).moveme()”. This is called casting; putting the name of a class in brackets around an object tells flash to treat it as an instance of that class.

An alternative method (and the one I prefer) is writing “var currentActor:Actor = Actor( actors[i] );”, and then using this new variable, like “currentActor.moveme();”. This makes things easier if you change the class of the contents of the array, because there are less lines to alter later.

With both those changes applied, your code would look something like this:

var currentActor:Actor;
var currentAmmo:Ammo;
for (var i:int=actors.length-1; i>=0; i--) {
   currentActor = Actor( actor[i] );
   currentActor.moveme();
   if (currentActor.hitpoints < 1) {
      currentActor.kill();
      actors.splice(i,1);
      i++;
      break;
   }
   for (var j:int=ammo.length-1; j>=0; j--) {
      currentAmmo = Ammo( ammo[j] );
      currentAmmo.moveme();
      if (currentAmmo.hitTestObject (currentActor)) {
         currentAmmo.hit(currentActor);
         ammo.splice(j,1);
         j++;
         break;
      }
   }
}

Also, I’m not sure you need those “i++” and “j++” lines, because iterating through the array backwords like this should avoid skipping over any objects. But I’ll leave you to test that.

Please let me know how you get on! If those changes don’t work, feel free to send me an email with your zip and I’ll be happy to take a look at it properly. My address is on the About page.

MichaelJWilliams March 5, 2009 at 7:43 pm

And to answer your second question, it’s a <pre> tag :)
Also, you can write <pre lang=”actionscript3″> to do the fancy code boxes I use in the tutorial itself.

MichaelJWilliams May 7, 2009 at 12:21 pm

Just found out about Corey O’Neil’s AS3 Collision Detection Kit thanks to Ryan of Untold Entertainment. If you want to improve your collision detection further, check it out.

Jay May 13, 2009 at 9:17 am

I made my own game with this tutorial but instead of avoid the enemies you should hit the enemies with a mouse click. I’m removing the the enemy with removeChild() when its hit by avatar. It works fine but the sound of the enemy is still looping in the background for some reason. Its like the removeChild() is not deleting the object, just hiding it. How can I remove the sound for the enemy when I kill it? I have the sound in a layer in the same movie clip as the enemy.

Michael Williams May 13, 2009 at 5:53 pm

Hey Jay

You’re right, removeChild() just hides the object rather than getting rid of it. Have you read the whole tutorial? I think in Part 12 your question will be answered :)

Ardavanski May 27, 2009 at 8:49 pm

Hi, again! :D
I got a problem at where i must hide the mouse (I hate it 2!)
when i put the code and such i try to play the game and i get=

1021: Duplicate function definition.

and

5000: The class ‘Avatar’ must subclass ‘flash.display.MovieClip’ since it is linked to a library symbol of that type.

AND

5000: The class ‘AvoiderGame’ must subclass ‘flash.display.MovieClip’ since it is linked to a library symbol of that type.

Any ideas how i could fix?
Btw thanks again for this tut, really nice especially the Collition hitest :) thx!

Michael Williams May 28, 2009 at 11:56 am

Hmmm. Can you post the line(s) that cause these errors? If you need help finding them, check out my debugging tutorial.

At a guess, I’d say you’ve either put Mouse.hide() above the constructor function, or perhaps not closed a curly bracket somewhere (i.e. put a { without a }).

Ardavanski May 28, 2009 at 11:57 am

Yes 2 seconds

Ardavanski May 28, 2009 at 12:02 pm

}
        public function getFinalScore():Number {
            return gameScore.currentValue;
        }
        public function getFinalClockTime():Number {
            return gameClock.currentValue;
        }
    }
    public function AvoiderGame() {
        Mouse.hide();
    }
}

its from line 55 to 56 (i have AUTO FORMAT it so its easyer for me to read the codes..)
last lines

Ardavanski May 28, 2009 at 12:04 pm

This is for the ::::5000: The class ‘Avatar’ must subclass ‘flash.display.MovieClip’ since it is linked to a library symbol of that type::

package {

thats when i click source at the error
same as the

5000: The class ‘AvoiderGame’ must subclass ‘flash.display.MovieClip’ since it is linked to a library symbol of that type.

package

those :< + that post up

Michael Williams May 28, 2009 at 12:09 pm

Ah, I see. Sorry, I didn’t make that very clear in the tutorial. I meant that you should add Mouse.hide() to the existing constructor function, i.e. the public function AvoiderGame() that’s at the top of the class :)

Ardavanski May 28, 2009 at 12:12 pm

Yipiiiiiiiiiiieeeeeeeeeeeeeeeeee! Thats perfect! And no need to say sorry, i must say sorry for bothering you :) But i got a new problem, i might could solve it, how long are you gonna stay here?

Michael Williams May 28, 2009 at 12:15 pm

Haha OK. Well I’m just checking my emails, so probably quite a while ;)

Ryan June 4, 2009 at 12:31 am

Plus you can always post and get a response from someone later

Kingfish June 15, 2009 at 2:43 pm

A much easier way to do what you did above with less code to fix your bug:

Bug code:
for each ( var enemy:Enemy in army ) 
{
    enemy.moveABit();
    if ( avatar.hitTestObject( enemy ) ) 
    {
        gameTimer.stop();
        dispatchEvent( new AvatarEvent( AvatarEvent.DEAD ) );
    }
}

All you got to do is break out as soon as the condition is met. It runs MUCH faster and gives the same results. Loop ends as soon as condition is met:

for each ( var enemy:Enemy in army ) 
{
    enemy.moveABit();
    if ( avatar.hitTestObject( enemy ) ) 
    {
        gameTimer.stop();
        dispatchEvent( new AvatarEvent( AvatarEvent.DEAD ) );
        break;
    }
}

See how much easier that was :)

Michael Williams June 15, 2009 at 3:28 pm

*facepalm*

Nice one Kingfish, that is a much neater solution :)

phil June 25, 2009 at 9:53 pm

These are good tutorials, although you could spend a little more time explaining things…that would be helpful. And make sure you document things a little better. You get hard to follow at times. ( The code gets screwed up and it becomes very difficult to fix). I am sure everyone that has gone through these tutorials step by step has learned a great deal and this will benefit them for there own Flash games and applications. Thanks for putting these on your site for free.

Michael Williams June 26, 2009 at 11:21 am

Thanks Phil

I’m going to work on improving these. Any specific points you think I should focus on?

Jamie Steel August 7, 2009 at 10:43 am

I did not expect the Mouse Hiding to be so simple :)

And that pixel file really adds to the quality of the game!

Again very good tutorial Micheal!

(my last message can be ignored, saved the finalScore text box as static, DOH!)

Michael Williams August 7, 2009 at 3:00 pm

Cheers, Jamie.

marty September 7, 2009 at 4:45 am

Hey, ive just tried the pixel perfect collision thing, and its not working for me i get 2 errors when i try it.

Michael Williams September 7, 2009 at 4:11 pm

@Marty: What are the errors?

Mokey / mokshal1663 September 9, 2009 at 2:21 pm

Michael, THANKS! You’re a genius (And so is Troy!).

Nemo October 1, 2009 at 8:52 am

Hi Michael,

The pixel perfect collision seems to work well, but I’m getting a strange compiler error:

TypeError: Error #1009: Cannot access a property or method of a null object reference.
at DocumentClass/onAvatarDeath()
at flash.events::EventDispatcher/flash.events:EventDispatcher::dispatchEventFunction()
at flash.events::EventDispatcher/dispatchEvent()
at AvoiderGame/onTick()
at flash.utils::Timer/flash.utils:Timer::_timerDispatch()
at flash.utils::Timer/flash.utils:Timer::tick()

I just copied the code over, and changed the hitTest object as you said to. Any ideas on what I’m doing wrong?

Nemo October 1, 2009 at 9:56 am

Sorry Michael, I managed to fix the problem. I was including the condition to check the avatarHit boolean in the for each loop.

I had:
for each (var enemy:Enemy in army) {
enemy.moveABit();
if ( pixel perfect check){
gameTimer.stop();
avatarHasBeenHit = true;
} //close if

//this is where I should’ve close the for each loop

if (avatarHit){
dispatchEvent (new AvatarEvent(AvatarEvent.DEAD));
}

} //this is where I was closing the for each loop.

I found the error by checking out your source code :) thanks!
(I tried to delete my comment but it wouldn’t let me, by the way)

Michael Williams October 1, 2009 at 10:57 am

Ahh I see. Well, well done for finding and fixing it :)

farout November 19, 2009 at 4:46 pm

Hello,

2 points:

  1. hitting two enemies at once in one tick.
    to see this happening I put a trace(“Dead”) in the AavatarEvent constructor.
    yup – it happened.
    so I tried Michael’s solution. Gosh – it was still happening.
    so I tried Kingfish solution – Nope still getting double Deads in the output screen.
    Hmm.
    Questions: is putting a trace in AvatarEvent a good way to see if this problem happening? If not what is a better test. What tests did you use to show?

  2. xSpeed and ySpeed
    here are my formulas:
    xSpeed = ((Math.random() – Math.random()) * 10) + Math.random()*3
    ySpeed = Math.random() *3 //so we can have 0-3 speed for y

Math.random() goes from 0 to 1. All positive numbers therefore you get a positive velocity vector.
For xSpeed we want negative speed to go the other way. To achieve this, subtract another random number. However this generates really small numbers -1 to 1 so we multiply it by 10. I still thought it was slow so I added random number of 0 to 3 to it.

Michael, you are totally awesome. I am an old woman doing game programming (EE and stats gal). I love your tutorial. Dude you rock! Thank you for taking the time to do this. It is truly impressive. And your help on the questions – wow. I admire your style.

farout November 19, 2009 at 5:20 pm

same for package Pixel package. Two deads still.

Oh and for Marty, I got 2 errors too but I copied it in Safari from Troy’s site. Apparently target:Display Object got mangled to target isplayObject and in another function the return variable was Point but said oint so…either copy from Michael’s or correct the 2 errors.
The code is here on this site – as he says hit the triangle to see it all.

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: