Built-in hitTest

For quite a while now, Flash movie clips have had a built-in method called hitTest . You can use this method a couple of different ways: to test if one movie clip is hitting another or to test if a specific point is hitting a movie clip. Which technique it uses depends on what you pass it as parameters. If you pass hitTest a reference to another movie clip, it will perform a hit test against that movie clip. If you feed it two numbers , it will take those as x, y coordinates and test against the point they define. Lets start out with how to detect if two movie clips are colliding .

Hit testing two movie clips

Using hitTest to see if two movie clips are hitting each other is probably the simplest method of collision detection. It is also the easiest to program and the quickest to execute. You call the function as a method of one movie clip and pass it in a reference to another movie clip. It looks like this:

 mc1.hitTest(mc2) 

This would normally go within an if statement, like so:

 if(mc1.hitTest(mc2)) {    // react to collision } 

The method will return true if there is a collision, and the statements within the if block will execute.

However, as with all things, there is a trade-off. As collision detection methods get easier, they get less accurate. As they get more accurate, they become more complex and time-consuming . So, while this is the easiest method, it is also the least accurate.

Now, what do we mean by accuracy in hit testing? Either something is hitting or its not, right? I wish it were that easy. It goes back to this question: Based on the positions of two movie clips, how do you determine if they are hitting?

Heres the simplest method of determining if a collision has occurred: You take the first object and draw a rectangle around it. The top edge of the rectangle goes on the topmost visible pixel of the objects graphics, the bottom edge goes on the lowest visible pixel, and the left and right edges are on their furthest visible pixels. Then you do the same for the object youre testing against. Then you check if these two rectangles are intersecting in any way. If so, you have a collision.

This rectangle around the object is known as a bounding box . Every movie clip has one. If you click on a movie clip on the stage, youll see it as a blue outline, as shown in Figure 9-1.

image from book
Figure 9-1: A bounding box

Of course, in the Flash player, nothing is actually drawing rectangles and checking them. Its all done mathematically, based on the movie clips positions and sizes.

Now, why would this be inaccurate? One would think that if the bounding boxes intersected, the objects must be touching. Well, take a look at the pictures in Figure 9-2. Which pairs would you say are touching each other?

image from book
Figure 9-2: Which ones are touching?

Obviously, only the squares are actually hitting, right? Well, lets draw in the bounding boxes and see what Flash sees. Figure 9-3 shows the results.

image from book
Figure 9-3: Not what you expected?

What do you know? All of these pairs are colliding as far as Flash is concerned . If you dont believe me, do this simple test. Create any two shapes and convert them to movie clips. Leave them on stage and give them the instance names mc1 and mc2 . Add the following code to frame 1 (available in the file ch09_01.fla) :

 mc1.startDrag(true); onEnterFrame = function() {       if(mc1.hitTest(mc2))       {             trace("hit");       } } 

Test it with a variety of shapes. Youll find that anything but a roughly rectangular shape is going to give you a false positive on the corners. The more irregular the shape, the more inaccuracies youll find. So, you should be very careful about using this method for anything other than rectangular shapes. For rectangles though, it works just wonderfully.

Lets try an example that demonstrates a simple hitTest with rectangles. The idea is that when you click on the stage with your mouse, a box is attached. So, youll need a movie clip in the library with the linkage name of box . This should contain a square graphic, with the registration point centered. When the box is placed, it falls down until it hits the bottom of the stage or collides with another box. If it hits another box, it positions itself so it is sitting right on top of it. Heres the code ( ch09_02.fla ):

 var count:Number = 0; var gravity:Number = 0.3; onMouseDown = function() {       // create a box       var box:MovieClip = attachMovie("Box", "box" + count, count);       count++;       // position it on the mouse position       box._x = _xmouse;       box._y = _ymouse;       // randomly scale it       box._xscale = box._yscale = Math.random() * 100 + 10;       // set it up to fall       box.vy = 0;       box.onEnterFrame = drop; } function drop(Void):Void {       // general motion code       this.vy += gravity;       this._y += this.vy;       // if it reached the bottom, stop it       if(this._y > Stage.height - this._height / 2)       {             this._y = Stage.height - this.height / 2;             delete this.onEnterFrame;       }       // loop through all the other boxes       for(var i=0;i<count;i++)       {             var testBox:MovieClip = _root["box" + i];             // make sure you are not testing it against itself             if(this != testBox && this.hitTest(testBox))             {                   // it hit; position it on top of the box it hit                   this._y = testBox._y                              testBox._height / 2                              this._height / 2;                   delete this.onEnterFrame;             }       } } 

The heart of this is the hitTest line, where you determine if the box has hit any other box, reposition it to be on top of the one it hit, and turn off the falling action. If you really want to see the inaccuracy of this method in action, change the box graphic to a circle or some other irregular shape, and see the objects hovering in mid air as they hit other objects.

You might notice that this example presents the possibility of potentially testing many objects against each other. I showed you a simple brute-force method for clarity in this case. In the Multiple-object collision detection strategies section later in this chapter, Ill show you a much more efficient method of doing the same thing.

Hit testing a movie clip and a point

The second version of hitTest works quite a bit differently, and even has a couple of options of its own. The truth be told, this method by itself is not usually very useful for testing for the collision between two movie clips. This method takes two numbers as arguments and uses them to define a point. It then returns true or false , based on whether or not that point is hitting the movie clip in question. In its most basic form, it might be used like this (where 100, 100 refers to the x and y locations of a point):

 mc.hitTest(100, 100) 

Again, you would use this within an if statement to designate some conditional code to run only if the hit test is true .

But once again, we come back to the question: What constitutes a hit? And, once again, we see our old friend the bounding box coming into play. Flash merely checks if the point provided is within the movie clips bounding box.

Lets do a quick test to see that in action. Place a movie clip on stage with either a circle or some irregular-shaped graphic inside. Name the instance mc and put this code on frame 1 ( ch09_03.fla ):

 function onEnterFrame():Void {       if(mc.hitTest(_xmouse, _ymouse))       {             trace("hit");       } } 

This uses the x, y coordinates of the mouse position as the point the movie clip will test against. As you move the mouse close to the movie clip, you will see that it probably starts registering a hit before you actually hit the graphics of the clip.

So again, this method seems to be useful only for rectangle-shaped objects. But there is another option here, called shapeFlag .

Hit testing with shapeFlag

shapeFlag is the third and optional parameter to the hitTest method. It is a Boolean value, so its true or false . Setting shapeFlag to true means that now your hit test will check against the visible graphics of movie clip, rather than just the bounding box. Note that shapeFlag is applicable only for testing a movie clip against a point. If you are testing two movie clips, you cannot use shapeFlag as an option.

Lets play with this one for a bit. Its as simple as adding in that one value. Just take the preceding example and add to it. Change your code so that the hitTest line reads like this:

 if(mc.hitTest(_xmouse, _ymouse, true)) 

(If you want to explicitly say that you dont want to use shapeFlag , you could set the parameter to false , but this would be the same as if you left it off entirely.) Test this version with an irregular shape. Youll see that now you must actually touch the shape itself with the mouse in order to trigger a hit.

So, here you have a perfectly accurate hit test. Accurate, but perhaps not entirely useful for collision detection. The problem is that it tests only a single point, which makes it rather difficult to see if any part of a movie clip is touching any part of another. Your first impulse might be to do something like this:

 mc1.hitTestPoint(mc2._x, mc2._y, true) 

But in that case, all youre checking is whether or not the registration point of mc2 is within mc1 . Thats pretty limited. Any other part of the clip could be touching mc1 . In fact, half of mc2 could be overlapping mc1 . But as long as that registration point were outside, Flash would consider it as not colliding. In practice, hit testing against a point is best used for mouse interaction or for very small movie clips, where there might be only a pixel or two between the registration point and outer edges.

One way people have attempted to use this method for collision detection is to test several points along the perimeter of the object. For example, say you had a star-shaped movie clip. You could calculate where the five points of the star were, and do a hit test from another movie clip to each of those five points. But then if you had two stars, you would need to test each one against the others five points. For a star, this would probably work pretty well. A more complex shape would obviously need a few more points. You can see this gets very complex and CPU- intensive pretty quickly. Just two stars, and youre up to ten times the number of hit tests you would use for the more simple method. Accuracy costs you.

I even saw an example once where someone had set up a motion path around the edge of a complex shape, and created a motion tween of an empty movie clip going around that path. On each frame, he stepped the tween through all of its frames , using gotoAndStop . Then he used the position of the tweened empty movie clip as a point for the hit test. While this wound up being pretty accurate, it seemed much more like a trick than something Id consider doing in a real project.

Summary of hitTest

So, how do you do collision detection between two irregularly shaped movie clips, so that if any part of one touches the other, you get a hit? Sadly, the answer is that this is not supported in movie clips with the hitTest methods.

To summarize, your basic options are as follows :

  • For roughly rectangular clips, use hitTest(mc) .

  • For very small movie clips, you can get away with hitTest(x, y, true) (note that shapeFlag is set to true ).

  • For very irregularly shaped movie clips, you either live with the inaccuracy or custom program some sort of solution, probably using hitTest(x, y, true) .

Of course, the chapter is far from done yet, and there are solutions beyond MovieClip s hitTest . If you have circular or roughly circular objects, distance-based collision detection will probably be your best bet. Actually, youd be surprised at how many shapes fall into the roughly rectangular or roughly circular categories.



Foundation ActionScript. Animation. Making Things Move
Foundation Actionscript 3.0 Animation: Making Things Move!
ISBN: 1590597915
EAN: 2147483647
Year: 2005
Pages: 137
Authors: Keith Peters

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net