How do you detect a collision between two objects? How do you decide what to do after a collision occurs? There are two basic ways to detect collisions inside Flash. You can use the MovieClip.hitTest() method, which is useful in particular cases. As another option, you can rely on good old distance formulas, which are more flexible and not that hard to handle. So, when do you use which method? With hitTest, you are testing either the intersection of a point with an object or the intersection of the bounding boxes of two movie clips. This is fine in a lot of cases, but what if you have two spheres that are colliding or a sphere colliding with the corner point of a box? Your collision detection won't be accurate using the hitTest method. In that case, you need to get down and dirty with some good old-fashioned calculations. When do you use hitTest? It's great for simple collisions. It's also great for determining when your mouse is over a particular object. You actually used it already when you were setting up a custom cursor in Chapter 17, "Interface Techniques." Start by exploring one way in which you can use the hitTest() method. The hitTest methodThe hitTest() method is new to Flash 5. It enables you to quickly and simply determine when two objects have intersected or overlapped . It's great for determining when a simple collision occurs or when your cursor is over a particular object. You have the option of testing for just the bounding box of the target movie clip or you can test for the shape within the bounding box. If you just want to test for the simple intersection of the bounding box of one object with another, you use this syntax: myMovie.hitTest(theOtherMovie); If the two objects intersect, the method returns a value of true. This is the method you can use to detect a simple collision. If you want to get a little fancier and test for when you intersect an actual shape, you can specify the x and y coordinates of the hit area on the Stage and set the shapeFlag to true. You'll probably use this method most often when you're trying to determine if the movie clip or the mouse pointer are within a certain area. The syntax is as follows : myMovie.hitTest(x, y, shapeFlag); You've already used the second method in Chapter 17 when you created a custom cursor. In the next exercise, you'll use the first hitTest() method to determine when a futuristic speeder intersects with an immovable wall. Exercise 19.5 Using hitTest() to Detect a Collision The rocket ship is controlled by key presses. Because you already know how to add those Actions, that part has been done for you.
That's one way to detect and react to a collision. However, what if you want to be able to bounce off of what you collide with? That's a little more complex. Distance Calculations and CollisionsAnother way to calculate collisions between objects is to do a little math. If you're not already familiar with the Pythagorean theorem, now is the time for a refresher course. Figure 19.9 shows a right triangle with its sides and two corner points labeled. Figure 19.9. If you imagine a right triangle between the ball and the wall, with the hypotenuse being the distance between the two objects, you can start to set up some interesting calculations.
A and B are both sides of the triangle. C is the hypotenuse. Good old Pythagoras, a pre-Socratic Greek philosopher, came up with a theorem that states that C 2 = A 2 + B 2 . After you have this equation, you can start to rearrange it to get all kinds of interesting information. Let's say that you want to find the distance between point p and point q. A would be equal to the X-axis value of q (q.x) minus the X-axis value of p (p.x) or: A = (q.x p.x) If that's true, then the formula for B would be: B = (q.y p.y) If you substitute those values into the first formula, you get this: C 2 = (q.x p.x) 2 + (q.y p.y) 2 So to get C, which is the distance between p and q, all you have to do is take the square root of both sides of the equation. So what? Think about it for a second. If q and p are the center points of two objects on a collision course, in Flash, you can get those values from the _x and _y object properties. That means you can always figure out how far apart the two objects are. Now you're getting somewhere. Take a look at the image on the right in Figure 19.9. You have a circle approaching a wall. The line between the wall and the circle represents the direction that the circle is moving (the wall is stationary). If you imagine a right triangle between these objects, you'd see something like the image on the left in Figure 19.9. The formula that you've been looking at up to now gives you the distance between two points, in this case, the center points for the colliding objects. However, what you really want to know is when the edge of the circle impacts with the wall. That means that you'll have to take into account the radius (half the width) of the circle. After the ball hits the wall, what happens? Assuming the ball isn't of sufficient mass to knock the wall down and that it is going fast enough not to be stopped by the wall, it's going to bounce. In fact, it's going to be reflected off the wall based on the angle of incidence, which is the angle at which it originally struck the wall. The Law of Reflection states that the angle of incidence is equal to the angle of reflection, as shown in Figure 19.10. Note that if the ball approaches the wall from the right and bounces , the sign of Y remains the same, but the sign of X reverses from negative to positive. Figure 19.10. As the ball approaches from the right, the speed of X is negative and the speed of Y is positive. After the ball hits the wall, only the speed of X changes, and it becomes positive.
Those are the basics you need to know before you start writing your own code. In the next exercise, you're going to approach the coding in a slightly different fashion. Because the code that you'll be developing is going to get moderately complex, it's a good idea to start thinking about how to make it as modular as possible. What you'll be doing is creating a throwable ball again, but this time, you want to be able to bounce the ball off the sides of your movie. So, not only do you need to track information about the ball, but also you need to know something about the walls that are constraining it. A Throwable Ball for CollisionsYou've already set up a throwable ball. You know the basics for creating the ball. You'll need a button in a movie clip that will let you drag the ball. You'll also need to capture whether the ball is being dragged, and you'll need to update the speed variables when the ball isn't being dragged. What needs to happen in the ball itself? Just like in the previous exercises, the ball needs to track the new and old X and Y positions if it is being dragged. If the ball has been released, you need to update the X and Y positions based on the current speed. But what about those walls? Before you start working on setting up the walls, get the throwable ball set up and working. Instead of attaching the actions to the ball itself, you're going to attach the actions that actually control the ball's x and y positions to a controller clip inside the Ball movie clip. Why? Because you are going to set up a function to control what happens when you hit one of the walls on the first frame of the ball itself. The function controlling the behavior of the object has to be in the same timeline or attached to the object that it's controlling. So, what to do about that enterFrame handler? You can just drop a controller clip into the Ball movie and assign the actions you would normally have in the enterFrame handler to the controller clip. In the next exercise, you lay the groundwork for this exercise by building the throwable ball. You won't be adding in friction right now. We want to keep the equation simple. Exercise 19.6 Setting Up the Throwable Ball in a New Way In this section of the exercise, you use a slightly different method of setting up your throwable ball.
Locating BoundariesIf you're going to bounce the ball off of the walls, you need to know where the walls are, right? You'll need to provide Flash with the minimum and maximum X and Y values for the walls. These values are going to be constants; they won't change, so why not start by setting them up on the main timeline? Setting Up Your ConstantsThe logical place to start is to think about what constant values you'll need to track. Well, you need to know where the walls are, right? The first thing you'll do is set four variables that will contain the minimum and maximum X and Y values for the walls:
These values won't change, so a sensible place to initialize these variables would be on the first frame of the main timeline. Exercise 19.7 Setting Up Constants For this exercise, the assumption is that you are working with the default movie size of 550 pixels x 400 pixels.
Now that you've established where your walls are, it's time to start thinking about how to handle the collisions. Here's where all that nasty math stuff comes in. Actually, it's not all that bad. Figuring Out the Distance CalculationsNow is the time to think about what information you know and what information you need. You're already collecting information about how fast, and in what direction, the ball is moving. You know how to determine the X and Y coordinates of the ball for any given time. In addition, you know where the walls are. You can start by working with the left wall. You want to know when the ball intersects with the left wall, but you want to know when the edge of the ball, not the center, intersects with the wall. That means that you're going to need to account for half of the ball's width, or radius, in the equation. In fact, depending on whether you're moving in a positive or negative direction, you'll need to either add or subtract the value of the radius. Figure 19.11 (wall collision.fla) illustrates this concept. Figure 19.11. The combined position of the wall and the radius of the ball determine when a collision occurs.
So, you should see your conditional statement developing here: If this._x is less than the wall plus the radius, you need to do something. What are you going to do? Well, for one thing, you need to change the direction that the ball is moving. If you remember the discussion earlier, the angle at which the ball hits the wall is known as its angle of incidence . The angle of reflection , the angle at which the ball leaves the wall, is the opposite of the angle of incidence. Don't panic; you don't have to start calculating angles. You just have to reverse either speedX or speedY, depending on which wall you're hitting . You also can give the ball a little extra snap to its bounce by resetting its position before you reverse the direction. Why would you want to do this? While Flash is constantly updating the position of the ball, the ball still can get ahead of the calculations and go past the wall slightly. By immediately resetting the position of the ball so that its edge is on the proper side of the collision boundary, you keep the illusion of the bounce effect crisp and clean. In the next exercise, you'll set up the function with conditional statements to determine whether the ball hit a wall, and then make it bounce. Exercise 19.8 Bouncing off the Wall In this exercise, you'll create the checkWall() function that determines whether the ball hit a wall and that controls what happens to it when it does.
Okay, that's pretty cool. However, what if you want obstacles inside the walls off of which the ball can bounce? That adds yet another layer of complexity. Adding Obstacles into the EquationNow the real fun starts. You're going to add some rectangular obstacles to the Stage for the ball to bounce off. However, you're not going to use your drawing tools to place them on the Stage. You're going to create a Box object that you can replicate as many times as you want. Boxes aren't complex objects. The only parameters you need to keep track of are the left side, the right side, the top, and the bottom. How hard can that be? Well, you need to think about how you plan to get that box drawn on the Stage. The most obvious way to do this is to use attachMovie(). Thus, you'll need to create a Box movie clip in the Library and set its linkage. Creating a BoxAfter your Box movie clip is ready to go, you're ready to write the function to create your object. Start by creating a Box movie clip. Exercise 19.9 Creating the Box Movie Clip You're going to create a simple box and set up its linkage properties.
Next you're going to create the function that creates the Box object. The Box object will take four parameters:
It will use these parameters and a variable named DEPTH to attach the new Box objects to the Stage and determine their width and height. Exercise 19.10 Creating the Box Object You're going to create the function that builds your Box object on the main timeline.
Okay, your box is there, but it doesn't make for much of an obstacle . You guessed it. You have to write another function to check for box collisions. Checking for Box CollisionsChecking for collisions with boxes on the Stage is a little more complex than checking for walls. You now have to keep track of where the ball was versus where it is now in different coordinate spaces. For example, the pseudo-code to check for a ball approaching the box on the Stage from the left side of the screen would look something like this: If the old x position is less than the left wall, AND the current x position is greater than the left wall, AND the current y position is IN BETWEEN the top and bottom walls, you've had a collision with the left wall reset the x position of the ball and reverse the speed of X. So what does that look like in reality? The actual conditional statement looks like this: if (oldX < current.left && this._x > current.left && this._y > current.top && this._y < current.bottom){ this._x = current.left - this._x + current.left; speedX *= -1; } The code inside the if statement won't run unless all the conditions being tested are true. So now that you know the basics, it's time to start writing the function. Exercise 19.11 Creating the checkBoxes() Function The checkBoxes() function is going to exist in the same frame of the checkWalls() function inside the Ball movie clip.
Figure 19.13. Because the box is an object, you can attach as many instances of it as you like to your movie by adding new Box objects to the Boxes array.
Listing 19.2 The Completed ActionScript for the checkBoxes Functionfunction checkBoxes(){ for (var i=0; i<_root.BOXES.length; ++i){ var current = new Object(); current.left = _root.BOXES[i].left - radius; current.right = _root.BOXES[i].right + radius; current.top = _root.BOXES[i].top - radius; current.bottom = _root.BOXES[i].bottom + radius; if (oldX < current.left && this._x > current.left && this._y > current.top && this._y < current.bottom){ this._x = current.left - this._x + current.left; speedX *= -1; } if (oldX > current.right && this._x < current.right && this._y > current.top && this._y < current.bottom){ this._x = current.right - this._x + current.right; speedX *= -1; } if (oldY < current.top && this._y > current.top && this._x > current.left && this._x < current.right){ this._y = current.top - this._y + current.top; speedY *= -1; } if (oldY > current.bottom && this._y < current.bottom && this._x > current.left && this._x < current.right){ this._y = current.bottom - this._y + current.bottom; speedY *= -1; } } } |