Distance-Based Collision Detection

Distance-Based Collision Detection

In this section, youll abandon the built-in hitTest family of methods and take collision detection into your own hands. This involves using the distance between two objects to determine if they have collided.

Taking it to the real world, if the center of your car is 100 feet from the center of my car, you know that the two cars are far enough apart that they couldnt possibly be touching. However, if both of our cars are 6 feet wide and 12 feet long, and the center of my car is 5 feet from the center of your car, you can be pretty certain there is some twisted metal involved, and some insurance papers to fill out. In other words, there is no way for the centers to be that close together without some parts of the car touching. Thats the whole concept behind distance-based testing. You determine the minimum distance required to separate the two objects, calculate the current distance, and compare the two. If the current distance is less than the minimum, you know they are hitting .

Naturally, there is a catch. Where the simplest hitTest method worked perfectly with rectangles, but degraded with any departure from that shape, this method favors perfect circles. In fact, it works perfectly for perfect circles. So, if thats what youre dealing with, this is the way to go.

As you depart from a circular shape, you are going to see less accuracy. But rather than the problem of reporting things that arent hitting as collisions, as you saw earlier with hitTest and the bounding box, you may have the opposite problem: Things that appear to be touching dont register a collision because their centers are still not close enough.

Simple distance-based collision detection

Lets start out with the ideal situation: a couple of perfectly round circles as movie clips. Also, the registration point should be in the exact center of the circle. And as before, youll set up one of them to drag. Youll also perform your collision detection in an enterFrame handler. So, up to this point, it is pretty much identical to the first example in this chapter. But instead of using if(mc1.hitTest(mc2)) to check for a collision, youll be using distance in the if statement. You should already know how to compute the distance between two objects. Remember the good old Pythagorean Theorem from back in Chapter 3. So, you start off with something like this:

 var dx:Number = mc2._x - mc1._x; var dy:Number = mc2._y - mc1._y; var dist:Number = Math.sqrt(dx * dx + dy * dy); 

OK, now you have the distance, but how do you know if that distance is small enough to consider that a collision has occurred? Well, take a look at the picture in Figure 9-4.

image from book
Figure 9-4: The distance of a collision

Here, you see the two circular movie clips in a position where they are just touching. Consider that each movie clip is 60 pixels across (only because the ones I made are that size). This would give it a radius of 30. Thus, at the exact moment they touch, they are exactly 60 pixels apart. Aha! Theres the answer. For two circular movie clips of the same size , if the distance is less than their diameter, they are colliding . Heres the code to test that in the example ( ch09_04.fla ):

 mc1.startDrag(true); onEnterFrame = function() {       var dx:Number = mc2._x - mc1._x;       var dy:Number = mc2._y - mc1._y;       var dist:Number = Math.sqrt(dx * dx + dy * dy);       if(dist < 60)       {             trace("hit");       } } 

When you run this, youll notice that it doesnt matter from which angle you approach the target movie clip. It doesnt register a hit until that exact point when the graphics actually overlap.

Now, Ive already covered the fact that hard-coding numbers into code like that is generally a bad idea. You would need to change the code every time you had different- sized objects. Furthermore, what about the case where the two movie clips are not the same size? You need to abstract this concept into some kind of formula that will fit any situation.

Consider the picture shown in Figure 9-5. It shows two movie clips of different sizes, again, just touching. The one on the left is 60 pixels across, and the one on the right is 40 pixels. You can get this programmatically by inspecting their _width properties. Thus, the radius of one is 30, and the radius of the other is 20. So, the distance between them at the moment they touch is exactly 50.

image from book
Figure 9-5: The distance of a collision of two different sized objects

A pattern begins to emerge. The magic distance is half the _width of one movie clip, plus half the _width of the other. Actually, it doesnt matter whether you use _width or _height . Since the objects are circular, these values should be the same anyway. You can now remove all hard-coded numbers from the code and get something like this ( ch09_05.fla ):

 mc1.startDrag(true); onEnterFrame = function() {       var dx:Number = mc2._x - mc1._x;       var dy:Number = mc2._y - mc1._y;       var dist:Number = Math.sqrt(dx * dx + dy * dy);       if(dist < mc1._width / 2 + mc2._width / 2)       {             trace("hit");       } } 

Go ahead and change the size of one of the movie clips and see that this code works, even if one circle is huge and one circle is tiny.

Collision-based springing

The problem with giving you a good working example of distance-based hit testing is that a complete program would involve a lot of issues related to things I havent covered yet, such as the reaction of two objects when they hit and how to efficiently handle interactions between many objects. But I did manage to create something that demonstrates hit testing without too much stuff that you havent already seen.

Here is the idea: Place one large circular movie clip, called centerBall , in the center of the stage. Then add in a bunch of smaller circular movie clips, giving them random sizes and velocities. These will move with basic motion code and bounce off the walls. On each frame, do a distance-based collision check between each moving ball and the center ball. If you get a collision, calculate an offset spring target based on the angle between the two balls and the minimum distance to avoid collision. OK, that might not be crystal clear. All it really means is that if a moving ball collides with the center ball, you make it spring back out again. You do this by setting a target just outside the center ball. The moving ball springs to that target. Then, once it reaches the target, it is no longer colliding, so the spring action ends, and it just moves with its regular motion code.

The result is kind of like bubbles bouncing off a large bubble, as shown in Figure 9-4. The little bubbles enter into the big one a bit, depending on how fast they are going, but then spring back out. (Ah, you see my obsession with springs has resurfaced.)

image from book
Figure 9-6: Collision-based springing

Here is the code ( ch09_06.fla ):

 var left:Number = 0; var right:Number = Stage.width; var top:Number = 0; var bottom:Number = Stage.height; var centerBall:MovieClip; var numBalls:Number = 10; init(); function init() {       for (var i = 0; i<numBalls; i++)       {             var ball:MovieClip = attachMovie("Ball", "ball" + i, i);             ball._x = Math.random() * Stage.width;             ball._y = Math.random() * Stage.height;             ball._width = ball._height = Math.random() * 40 + 20;             ball.vx = Math.random() * 4 - 2;             ball.vy = Math.random() * 4 - 2;       }       centerBall = attachMovie("Ball", "centerball", i);       centerBall._width = centerBall._height = 150;       centerBall._x = Stage.width / 2;       centerBall._y = Stage.height / 2; } function onEnterFrame(Void):Void {       for (var i = 0; i<numBalls; i++) {             var ball:MovieClip = this["ball" + i];             move(ball);       } } function move(ball:MovieClip) {       ball._x += ball.vx;       ball._y += ball.vy;       if (ball._x + ball._width / 2 > right)       {             ball._x = right-ball._width / 2;             ball.vx *= -1;       }       else if (ball._x - ball._width / 2 < left)       {             ball._x = left+ball._width / 2;             ball.vx *= -1;       }       if (ball._y + ball._width / 2 > bottom)       {             ball._y = bottom-ball._width / 2;             ball.vy *= -1;       }       else if (ball._y - ball._width / 2 < top)       {             ball._y = top+ball._width / 2;             ball.vy *= -1;       }       var dx:Number = ball._x - centerBall._x;       var dy:Number = ball._y - centerBall._y;       var dist:Number = Math.sqrt(dx * dx + dy * dy);       var minDist = ball._width/2+centerBall._width/2;       if (dist < minDist)       {             var angle:Number = Math.atan2(dy, dx);             var targetX:Number = centerBall._x +                                  Math.cos(angle) * minDist;             var targetY:Number = centerBall._y +                                  Math.sin(angle) * minDist;             ball.vx += (targetX-ball._x) * .1;             ball.vy += (targetY-ball._y) * .1;       } } 

Yes, this is a whole lot of code, but youve already seen most of these techniques in earlier chapters. Lets walk through it quickly.

Starting with the init function, you loop through and create the smaller, moving balls. They are given a random size, position, and velocity. Then you create the centerBall .

The enterFrame handler just loops through and gets a reference to each ball. To separate functionality, Ive moved the motion code to a function called move . This takes a reference to one of the balls and applies all the motion code to it. The first half of this function should be old hat to you. Its basic velocity code with bouncing. Then you find the distance from this ball to the centerBall , and compute the minimum distance to determine a collision. If there is a collision, you find the angle between the two, and use that plus the minimum distance to calculate a target x and y. This target will be right on the outer edge of the centerBall .

From there, you just apply basic spring code to spring to that point (as described in Chapter 8). Of course, once it reaches that point, its no longer colliding and will fly off in whatever direction its heading.

See how you can build up and layer simple techniques to wind up with some very complex motion?



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