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 }

Michael Williams November 21, 2009 at 5:41 pm

Hey Farout,

Not sure why you’d still be getting the two deads. To test it myself I put the traces in the onAvatarDeath() function in the document class.

Thanks for sharing your xSpeed and ySpeed generation code :)

And wow, seems the Flash game dev community is even more varied than I thought :P What’s EE and stats?

Glad you’ve found this useful!

Josh December 24, 2009 at 10:52 pm

So I just put in the whole collision detection thing and – after fixing some problems with capitalization – I test it and it goes directly to the game over screen after I push the start button. What is wrong this time?

TypeError: Error #1009: Cannot access a property or method of a null object reference.

at DocumentClass/onAvatarDeath()

at flash.events::EventDispatcher/dispatchEventFunction()

at flash.events::EventDispatcher/dispatchEvent()

at AvoiderGame/onTick()

at flash.utils::Timer/_timerDispatch()

at flash.utils::Timer/tick()

and this pops up around a hundred times on the output bar thing.

Josh December 24, 2009 at 11:31 pm

AAAAAAAAAHHHHHHH

after fixing that problem now for some reason the gameover screen wont pop up when the avatar hits an enemy. I have no idea why since the only thing I changed is that one line.

Michael Williams December 29, 2009 at 12:34 am

Not sure — sorry! I need more details.

Acke February 10, 2010 at 3:41 am

Hi,
Thanks for the excellent tutorials!
Even though i’m an experienced programmer they are helping a lot!

Just a useless fact Boolian is not just a silly name!
Its named after George Boole who defined a system of logic in the 19th century.

Nik February 10, 2010 at 7:55 am

i’m using xSpeed = (Math.random() – Math.random()) // so you’ll get -1, 0, 0, 1 for Xspeed
don’t know if it’s a good solution though??

Michael Williams February 13, 2010 at 2:07 am

Hey Nik,

Yep, that’ll work! You’ll find that you get more results closer to 0 than you do close to -1 or 1, though — can you figure out why?

Meiji March 4, 2010 at 6:52 pm

Awesome tut! Even when I have some AS3 OOP knowledge, I’m really enjoying it! =D Just a question, I placed the Mouse.show() inside the ‘onTick’ function in AvoiderGame.AS and seems to work pretty fine. That way I don’t have to write it in any other class nor import it again. I think there’s no difference, right?

for each(var enemy:Enemy in _army) {
                enemy.moveABit();
                if (PixelPerfectCollisionDetection.isColliding(_avatar,enemy,this,true)) {
                    Mouse.show(); //Here I'm placing the Mouse.show()
                    _gameTimer.stop();
                    avatarHasBeenHit = true;
                }
            }

Thank you very much for this great tutorial!

Michael Williams March 20, 2010 at 11:20 am

Thanks, Meiji!

Yep, that works fine :) I’ve done it the way I did so because I wanted to control the visibility of the mouse depending on the screen rather than anything else, but it doesn’t really matter.

ThomasK April 3, 2010 at 6:55 pm

Hi, I have a question about soft coding constants. Let’s say I have a constant in the documentClass, or playScreen, that I want to access in its child classes. I have STAGE_WIDTH and STAGE_HEIGHT in my mind (this may be a bad example if there’s a code to access those parameters directly), but it could be any constant. How can I reach them from the avatar and the enemy class?

Obviously, manually setting them for each class is a lot of trouble if I want to change them later (at least in more complex programs). What seems like a solution, is to pass them down through functions, but then they aren’t really constants anymore. I’m wondering, is there a better way?

Michael Williams April 13, 2010 at 4:27 pm

Hey ThomasK,

Ooh, that’s a big question. Seems simple, but there are a lot of arguments on that topic. For example, passing them through functions is nice, because it means you can re-use your classes later on, in any project, as long as you make sure you always pass the values. But it can complicate things.

What you could do here is use a public static const. All this means is, at the top of your DocumentClass (where your public variables are set), you type:

public static const STAGE_WIDTH:Number = 400;
public static const STAGE_HEIGHT:Number = 300;

Then, from anywhere else in your code, you can access DocumentClass.STAGE_WIDTH. Note that you’re accessing DocumentClass not documentClass — static consts belong to the class itself, not to any particular instance of the class.

You can create a new class whose only purpose is to contain such static consts (and static vars, if you like). It’s a bit sloppy, but it works.

Timo van Niedek May 8, 2010 at 1:14 pm

Hi Michael,

I’m working on another game using my new gained knowledge, but I have a little problem. What do I have to fill in at the 3rd parameter at the collision detection if the common parent isn’t this class? I tried the name of the class, but that gave me two errors:

1067: Implicit coercion of a value of type Class to an unrelated type flash.display:DisplayObject.

Michael Williams May 10, 2010 at 7:22 pm

Hey Timo,

You’re very close! Instead of using the name of the class, use the name of the instance of the class.

Bob May 19, 2010 at 7:32 pm

Michael,

Is there a way to disable to alt button? I notice if you are using the mouse to control the avatar, and if you hit the alt button and then move your mouse to a new location and hit the alt button again, the avatar will move over to the new location without being killed by the enemy.

Timo van Niedek May 28, 2010 at 9:23 pm

Hi Michael,

I have another question, is there a way to run functions or get variables from parent class files? I saw somewhere something about Game(this.parent).setValue();
Does this work? Do I have to use the instance name of the class? And what do I do if the class in which the setValue() function is is the document class?

Michael Williams May 29, 2010 at 12:26 am

@Bob: Good point. Check out Daniel Sidhion’s tutorial here.

@Timo: Yes, that works. Instead of Game() you use the name of the parent’s class (not instance). It doesn’t matter whether it’s the document class or not.

Be careful, though; this makes it very difficult to change things later. Your classes will then need to always be used with the same class of parent.

Josh July 19, 2010 at 9:12 pm

I have a question that is completely un-tutorial related.

I was messing around with physics related things, and decided to mess about in my almost finished avoider game and add a dynamic background.

I went to the Document Class and added the movieclip using addChild, but I’m not sure how to make it move based on the rules in the Physics class.

I have a MovingBackground class that extends by the Physics class, which I think is the right thing to do…

If I add the background inside MovingBackground.as, then nothing shows up, if I add it in anything else and it does show up, attempting to change the x or y values in a different class makes it disappear.

This was probably covered somewhere in the tutorial but I don’t remember it… =p

Michael Williams July 23, 2010 at 12:09 pm

Hey Josh,

Hmm, this is really hard to answer without knowing more about your Physics class or your MovingBackground class, or what you’re trying to do :S

Have you addChild()-ed an instance of MovingBackground to the display list?

ReeseHollandGames August 17, 2010 at 12:41 am

I decided to go with a bit of a Brownian motion and slight random acceleration, since I made spiky glowing snowflakes for my enemies.

        public function moveABit():void
        {
            x = x + xSpeed + ((Math.random() * 2) -1);
            y = y + ySpeed + (Math.random() / 10);

    }

Other randomization could be fun to play with as well.

Michael Williams August 21, 2010 at 12:56 am

@ReeseHollandGames: Brownian motion — geeeeek :) I like it. Great idea!

FranAren September 11, 2010 at 9:03 pm

Thanks! I were looking for a pixel perfect collision solution. It’s awesome.
Great tutorial

Michael Williams October 5, 2010 at 12:58 am

Hey FranAren,

Yeah! Troy’s class is awesome. Corey O’Neill’s Collision Detection Kit is great too.

Ashley January 30, 2011 at 7:25 am

Hi hi,

I’m at the the part where we can adjust which direction the enemies fall in; I apologize for my very silly question but I don’t understand why the enemies tend to float to the bottom-right hand corner of the screen, when we insert the ‘Math.random’ instruction. I’m not sure how to fix it so that they float towards the left as well. Could someone please help me? Sorry about this! ^^;

Mika February 10, 2011 at 5:14 pm

Hi. i wanted to make a hitbox for the enemy so that you have a certain spot that kills.
what I did was this:
I draw a new mc inside the enemy mc and named it hitbox

but when I tried to test hit detection with

 if(Avatar.hitTestObject(enemy.hitbox))

It dosent work. Damn you overcomplicated AS3!

Jeff April 19, 2011 at 8:08 am

How to Disable the maximize buttone?

Frage April 25, 2011 at 7:39 pm

Hi I know this is very basic stuff but I am trying to pass the enemy the avatar position. I have the function for tracking and this works fine, but i am directly accessing the mouseX and mouseY positions using the built in function, to pass the avatar position.

I would like to know how to pass the data using functions to pass the data from avoiderGame.as to Enemy.as

Thanks.

Mistakenness May 4, 2011 at 4:57 pm

Hi !
Great tutorial for starters !

I have made I little improvement to the Enemy.as, great for harder levels. See if you like it.

public function Enemy( startX:Number, startY:Number )
        {
            x = startX;
            y = startY;

        xSpeed = (Math.random() * mouseX) / 7;
        ySpeed = (Math.random() * mouseY) / 11;
    }

I think it’s self-explanatory ! Just Move, Move, Move ! Higher the values (7 or 11) slower the enemy’s movements.

ExplosionsHurt September 9, 2011 at 8:26 am

I got it!

Math.random() always returns a number between 0 and 1. Because every number between 0 and 1 is a positive number, the enemy will always move a positive amount of x and a positive amount of y.

Now… how do I fix it? I could create 2 Math.random() functions and have something like

enemy.x += Math.random();
enemy.x -= Math.random();

But that’s kinda weird and it changes the movement entirely…

Michael James Williams September 9, 2011 at 9:18 am

You’re very close! Now think, if you have var rnd=Math.random();, what could you do to rnd to turn it into a number between 0 and 10? How about 1 and 2? 1 and 10? Try all this without using another Math.random() call.

Ilviann Zed September 14, 2011 at 12:12 am

Good day!
Here is a simple class with function for a good hit detection:

package
{
    import flash.display.BitmapData;
    import flash.display.DisplayObject;
    import flash.geom.Matrix;
    import flash.geom.Point;
    import flash.geom.Rectangle;

public class HitTestEx
{
    public static function TestCollision(itemA:DisplayObject, itemB:DisplayObject, commonParent:DisplayObject):Boolean
    {
        var rectA:Rectangle = itemA.getBounds(commonParent);
        var rectB:Rectangle = itemB.getBounds(commonParent);
        var intersect:Rectangle = rectA.intersection(rectB);
        var imgX:int;
        var imgY:int;
        var imgW:int;
        var imgH:int;
        if (intersect.size.length > 0)
        {
            imgX = Math.floor(intersect.left);
            imgY = Math.floor(intersect.top);
            imgW = Math.ceil(intersect.right) - imgX;
            imgH = Math.ceil(intersect.bottom) - imgY;
            var bmpA:BitmapData = new BitmapData(imgW, imgH, true, 0x00000000);
            var bmpB:BitmapData = bmpA.clone();
            bmpA.draw(itemA, new Matrix(1, 0, 0, 1, itemA.x - imgX, itemA.y - imgY));
            bmpB.draw(itemB, new Matrix(1, 0, 0, 1, itemB.x - imgX, itemB.y - imgY));
            return bmpA.hitTest(new Point(), 127, bmpB, new Point(), 127);
        }
        return false;
    }
}

}

Echo51 October 1, 2011 at 10:56 am

From my view, the correct way to get correct random movement is a simple

x += ((Math.random()2)-1)xSpeed

Wherein xSpeed in the multiplier, which you increase to get more movement per function call ;)

Then again, alot of people claim that random() is not really random, some fixing that by doing Math.random+Math.random+Math.random/3, claiming they get more evenly distributed numbers that way :P

Echo51 October 1, 2011 at 10:59 am

Apparently the pre tag stripped out my multiplication of xspeed. Oh well.

Chloe February 2, 2012 at 11:33 am

Hiya
um, I downloaded the zip file from this page containing the parts so far, but the score counter does not work? It’s just invisible, and the game over screen doesn’t show any score at all?

Drew April 5, 2012 at 3:02 pm

For some reason when I change moveDownABit to moveABit in both Enemy and AvoiderGame I get the error…
1061: Call to a possibly undefined method moveABit through a reference with static type Enemy.
If I change it back to moveDownABit I do not get the error.
Is there another reference somewhere that I am missing?

1009 oh no. July 2, 2012 at 7:09 am

hey, I used the collision detection thing you had posted, and set it up exactly. I ran it and came up with the same 1009 error that josh had. I then removed the pixelperfectdetection class entirely and reset exactly how the hitTestObject was, and yet the 1009 error persists.

Is there a place I could look into what on earth is up with that?

Jordan October 9, 2012 at 7:23 pm

Did you forget the “import PixelPerfectCollisionDetection;” line by any chance?

Knut October 29, 2012 at 6:27 pm

For anyone that wants better collision detection:
For circles, perfect collision is easy. (If your MovieClips are not perfect circles, it’s still better than rectangle collision.)
lets say you have the movieclip mc1 and mc2. with radii r1 and r2. (the radii will be heigth/2 for circles).

To check if they collide, write:
if( (r1 + r2) > Math.sqrt((mc1.x+mc2.x)(mc1.x+mc2.x)+(mc1.y+mc2.y)(mc1.y+mc2.y))

Kerry June 12, 2013 at 2:18 am

I don’t know what on Earth happened but upon getting the code error free the game would load, I clicked start and then nothing. No skull, no smilies, the score and clock are frozen.
I even went to the length of downloading your ZIP and copying the code from that but still to no avail.
Help?

Taylor August 8, 2013 at 8:08 am

I’ve been able to solve the hit detection well by checking distance between the enemies and the avatar. It looks something like this

for each (var enemy:Enemy in army)
            {
                enemy.moveABit();
                var distance = Math.sqrt(Math.pow(enemy.x - avatar.x, 2) + Math.pow(enemy.y - avatar.y, 2))
                trace(distance)
                if(distance <= 32.5)
                {
                    gameTimer.stop();
                    avatarHit = true
                }
            }

This basically uses the distance formula to calculate the distance between the avatar and each enemy, and checks it against the radius of the avatar plus the radius of the enemy (in this case it’s 17.5 + 15 respectively). Obviously this only works for circles, but it’s still quite accurate.

Thanks for the awesome lessons, I really appreciate 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: