Bouncing Off the Walls


A wall is any object that cannot move as a result of a collision, but that can be collided with. For instance, if you throw a tennis ball at a wall, the wall does not move, but it certainly gets hit! By this definition, even the paddles in a simple game of Pong are considered walls. In that case, they are walls that can move, but their movement is not the result of a collision. In this section we'll look at collisions with walls, and collisions with lines of any angle, which are often used as walls. (Later in this chapter we'll work with the less stationary objects.)

Object-Wall Reactions

graphics/06fig02.gif

An object-wall collision (affectionately called ball-wall) is the easiest type of collision to program a reaction for. Think of the tennis-ball example again. When the tennis ball bounces off the wall, it rebounds at the same speed at which it struck the wall. (Since this is an elastic collision more on that later there's no energy lost in the collision, and therefore no loss in speed.) This is good news for us, because it means that when we detect a collision with a wall, we can just reverse the object's speed. Note that we would only reverse the speed component that is perpendicular to the wall, as shown in the image below.

graphics/06fig03.gif

graphics/cd_icon.gif

Let's look at a practical example of this. Open pong1.fla in the Chapter06 directory. This is an unfinished version of a very simple game, Pong. In Pong, a ball moves across the screen, and it is up to you to keep it from leaving the screen on your side (the left). There are two paddles on the screen that move vertically. One paddle is on the left boundary of the game area, and the other is on the right boundary of the game area. The left paddle is controlled by the user (that's you), and the right paddle is controlled by the computer using a simple AI script (discussed in Chapter 9, "Artificial Intelligence"). There is an instance of a ball movie clip on the stage (within the board movie clip); it's a square shape, but it's still called a ball. The registration point for this movie clip is its top-left boundary. There is collision detection between the ball and the paddles, and between the ball and the top and bottom of the game area. If the ball collides with a paddle, then its x speed is reversed. If the ball collides with the top or bottom of the game area, then its y speed is reversed.

We are not going to discuss most of the ActionScript in this file. Rather, we are going to focus on the areas where collision reactions are used. If you have read through the previous chapters on collision detection and physics, then you are familiar with just about all of the ActionScript in this file. What may be new to you are two things:

  1. The game.gameAI() method (which will be discussed in Chapter 9).

  2. The manner in which the methods were created. All of the methods were created on the game object itself, rather than in the timeline. This is not a necessity, but it brings us a little bit closer to coding in an object-oriented way.

graphics/tip_icon.gif

A function that belongs to an object is called a method. This is just a terminology shift; there is really no substantive difference between the two. Like many people whose first programming language is ActionScript, I often use the terminology incorrectly. I sometimes say "method" when I mean "function," or the other way around. Wasn't it Shakespeare who said, "That which we call a function by any other name would still process data"?


In this particular file, collision reaction is implemented in every place where a collision-detection script sits. We check for collisions in the methods game.checkForWalls() and game.checkPaddleCollisions(). Here is the ActionScript for the game.checkForWalls() method:

 1   game.checkForWalls = function() { 2      if (this.ball.tempy<0) { 3          //hit top wall 4          this.ball.tempy = 0; 5          this.ball.ymov *= -1; 6      } else if (this.ball.tempy+this.ball.radius>this.height) { 7          //this bottom wall 8          this.ball.tempy = this.height-this.ball.radius; 9          this.ball.ymov *= -1; 10     } 11  }; 

If you remember from the previous chapters, we usually create a temporary position in memory describing where an object will be, tempx and tempy. We use this temporary position to check for collisions. The upper boundary of the game board is 0; the lower boundary is game.height. In line 2 of the ActionScript above, we check to see if ball.tempy is less than 0. If it is, then a collision is occurring with the top wall, and the if statement is entered. Line 4 sets ball.tempy = 0, which positions the ball right up against the top wall. This is a necessary step. Due to the frame-based nature of Flash MX, ball.tempy could be substantially less than 0, and if we did not set it right up against the boundary and we only reversed the speed when a collision occurred, then it would look as if the ball went into the wall rather than bouncing off the wall. In line 5 we finally perform the very simple collision reaction: We reverse the y speed of the ball.

The else if piece of the conditional statement, in line 6, checks to see if the y position of the ball's bottom edge is greater than the y position of the game's lower boundary. (The bottommost part of the ball is its y position plus its height.) You may recall that the value for the y position of the game's bottom boundary was set earlier in the frame as game.height. Since the checkForWalls() method belongs to the game object, then we can access this property by using this.height, which we do in line 6. If this condition is satisfied, then we know the ball is colliding with the bottom wall, and we enter this piece of the if statement (lines 7 9). In line 8 we set the position of the ball so that it sits right up against the bottom wall. We do this for the same reason as we did it for the top wall so the ball doesn't look as if it has gone through the wall. In line 9 we reverse the ball's y speed; this is our collision reaction.

Now let's look at the game.checkPaddleCollisions() method:

 1   game.checkPaddleCollisions = function() { 2      if (this.ball.tempx        <this.leftPaddle.x+this.leftPaddle.width        && this.ball.tempx+this.ball.radius        >this.leftPaddle.x         && this.ball.tempy+this.ball.radius        >this.leftPaddle.y && this.ball.tempy        <this.leftPaddle.y+this.leftPaddle.height) { 3          //left paddle collision detection 4          this.ball.tempx = this.leftPaddle.x            +this.leftPaddle.width; 5          this.ball.xmov *= -1; 6      } 7      if (this.ball.tempx        <this.rightPaddle.x+this.rightPaddle.width         && this.ball.tempx+this.ball.radius        >this.rightPaddle.x        && this.ball.tempy+this.ball.radius        >this.rightPaddle.y && this.ball.tempy        <this.rightPaddle.y+this.rightPaddle.height) { 8          //right paddle collision detection 9          this.ball.tempx = this.rightPaddle.x            -this.ball.radius; 10         this.ball.xmov *= -1; 11     } 12  }; 

In this ActionScript, the if statements look complicated, but the concept here is one that you have already used in the Collision Detection chapter, when we talked about rectangle-rectangle collision detection. They are each simply checking for a collision between two rectangles the ball and a paddle. We saw how to perform rectangle-rectangle collision detections in the previous chapter. Here, we check for the following conditions:

  • The left side of the ball has a smaller x than the right side of the paddle.

  • The right side of the ball has a greater x than the left side of the paddle.

  • The top side of the ball has a smaller y than the bottom side of the paddle.

  • The bottom side of the ball has a greater y than the top side of the paddle.

If all four of the above conditions are met, then a collision has occurred between the ball and the paddle. This check is done in exactly the same way in lines 2 and 7: once for the left paddle and once for the right paddle. If the condition in line 2 is met, then the ball is colliding with the paddle, and lines 4 and 5 are executed. Line 5 positions the ball so that it is just barely touching the paddle on the right side. Line 5 reverses the ball's x speed. Likewise, if the condition in line 7 is met, then the ball is placed so that it is just barely touching the paddle on the right, and its speed is reversed.

graphics/tip_icon.gif

Keep in mind that we are not using frame-independent collision detection here. This means that if you were to take this young game and try to make it into a full-grown game in which the ball gets steadily faster, then you might encounter the typical "snapshot" collision-detection problems. So if the ball is moving fast enough, it may move straight through a paddle. You can, if you choose, use the ball-line collision-detection scripts developed in the previous chapter for each wall of the paddles. This would give you frame-independent Pong.

graphics/cd_icon.gif

Now let's look at another example: a ball falling under gravity and bouncing off a floor. Open ball_floor.fla in the Chapter06 directory. On the stage you see the movie-clip instances ball_clip and floor_clip. The ActionScript in this file creates an object called ball to store information about ball_clip, sets a temporary position for the ball, checks for a collision with the floor, and renders the ball. If the script detects a collision between the ball and the floor, then the ball is set to be touching the floor, and its speed is reversed. There is one added feature involved in this process that has not yet been introduced: decay. In this example, a variable called decay is set with a value of .6. Here is the ActionScript for the collision-detection function.

 1   function ballFloorDetection() { 2      if (ball.tempy+ball.radius>floorY) { 3         ball.tempy = floorY-ball.radius; 4         ball.ymov *= -1*decay; 5      } 6   } 

If a collision is happening, then the ball's speed is reversed and multiplied by the variable decay. Decay is a way to have the ball bounce less high on each successive bounce; it lowers the magnitude of the speed by a percentage each time a collision happens. All decay values are between 0 and 1. For instance, in this file we are using a decay of .6 (set at the top of the frame, not shown here). That means each successive bounce will rebound with only 60 percent of the speed with which it collided in the first place.

The possible values for decay are all positive numbers (including zero), but the realistic range is between 0 and 1. If decay is set to 0, then the ball will stick to the ground. If decay is set to 1, then the ball will bounce to the same height forever. If the decay is set to a value greater than 1, the ball will bounce higher on each successive bounce (which is no longer a decay). When you generate a SWF from this file, you'll see that the ball will bounce a few times and then stop. This mimics the behavior of a regular old basketball. The goal of this chapter is to show you how to program realistic reactions. Adding a decay factor is one easy way to add realism to your floor or wall collision reactions.

Circle-Line Reactions

Circle-line, or ball-line, reactions occur when a ball collides with a line at any angle (such as the angled walls in a game of pinball). There are three steps to finding the resultant x and y velocities of a ball after a collision with a line:

  1. Project the x and y velocities onto the line of action (for example, the line, or the bank, if you're still thinking pinball).

  2. graphics/hand_icon.gif

    Reverse the velocity that lies along the line of action. This is done because the only piece of the velocity affected by the collision is the one that lies along the line of action. The velocity that lies along the "real" line (the one that the ball collides with) is unaffected by the collision.

  3. Project the velocities that are along the line and the line of action back onto the x and y axes. This gives us the final result we were looking for: the x and y velocities after the collision.

When two objects collide, the momentum affected is the component that lies along the line of action the imaginary line that runs perpendicular to the tangent line that goes through the point of collision.

graphics/06fig05.gif

Project xmov and ymov onto line of action and the line.

graphics/06fig06.gif

Now that you've seen the quick overview of how to find those velocities, we'll jump in and go through it step by step. Let's call the x velocity of the ball vxi (v for "velocity," x for "x direction," i for "initial") and the y velocity of the ball vyi. The projection of vxi and vyi onto the line of action is vyip (p stands for "prime"):

 vyip = vyi*cos(alpha)-vxi*sin(alpha)  

The projection of vxi and vyi onto the line itself is vxip.

 vxip = vxi*cos(alpha)+vyi*sin(alpha)  

Now that step 1 is complete, we move on to step 2 and reverse vyip (remember that f stands for "final").

 vyfp = -vyip  vxfp = vxip 

Notice that the final velocity along the line (vxfp) is unchanged. Now we move on to step 3 to project vyfp and vxfp back to the x and y axes to arrive at the final velocities, vxf and vyf.

 vxf = vxfp*cos(alpha)-vyfp*sin(alpha)  vyf = vyfp*cos(alpha)+vxfp*sin(alpha) 

graphics/cd_icon.gif

These two values, vxf and vyf, are the x and y velocities of the ball at the moment after the collision. Using this in ActionScript is not difficult. Let's take a look at an example. Open the file ball_line.fla in the Chapter06 directory. The ActionScript in this file is very similar to that in the ball_line.fla file you saw in Chapter 5, "Collision Detection." The main addition to the ActionScript in this file is the function ballLineReaction(). Here is that function:

 1   function ballLineReaction(tempLine, point, x, y) { 2      var lineDecay = tempLine.lineDecay; 3      var alpha = tempLine.angle; 4      var cosAlpha = math.cos(alpha); 5      var sinAlpha = math.sin(alpha); 6      //get the x and y velocities of the ball 7      var vyi = point.ymov; 8      var vxi = point.xmov; 9      //project the x and y velocities onto the line of action 10     var vyip = vyi*cosAlpha-vxi*sinAlpha; 11     //project the x and y velocities onto the line 12     var vxip = vxi*cosAlpha+vyi*sinAlpha; 13     //reverse the velocity along the line of action 14     var vyfp = -vyip*lineDecay; 15     var vxfp = vxip; 16     //translate back to Flash's x and y axes 17     var vyf = vyfp*cosAlpha+vxfp*sinAlpha; 18     var vxf = vxfp*cosAlpha-vyfp*sinAlpha; 19     //set the velocities of the ball based from the results 20     point.xmov = vxf; 21     point.ymov = vyf; 22     point.tempx = point.x+point.xmov; 23     point.tempy = point.y+point.ymov; 24  } 

This function is called from the getFrames() function when a collision has been detected. A reference to the line object is passed in as tempLine, and a reference to the object that represents the ball is passed in as point. The x and y positions of the ball at the point of contact are also passed into this function. Line 2 sets a variable called lineDecay. This is a value between 0 and 1 that reduces the rebound velocity. (The concept of decay was introduced earlier in this chapter. If decay has a value of 1, then there is no decay.) Next we set variables to store the cosine and sine of the line angle (lines 4 and 5). We store these values because they are needed more than once throughout the ActionScript, and as you've already seen, it is quicker for Flash to reuse the variables than to calculate the sine and cosine repeatedly. To match variable names with what we worked out in this section, we set two variables, vyi and vxi, from the y and x velocities of the ball (lines 7 and 8). In line 10 we project vyi and vxi onto the line of action, and in line 12 we project vxi and vyi onto the line. We then reverse the velocity along the line of action, vyip, and multiply it by lineDecay. The act of reversing this velocity is the reaction to the collision. Next, in lines 17 and 18, we project back onto the x and y axes. In lines 20 and 21 we update xmov and ymov on the ball object with the new velocities. Finally, we update tempx and tempy on the ball object (lines 22 and 23).

graphics/06fig07.gif

Generate a SWF from this file to see it in action. You will note that three lines are created. The ball falls under gravity onto a line and bounces around a bit. Eventually the ball comes to a rest in a small dip.



Macromedia Flash MX Game Design Demystified(c) The Official Guide to Creating Games with Flash
Macromedia Flash MX Game Design Demystified: The Official Guide to Creating Games with Flash -- First 1st Printing -- CD Included
ISBN: B003HP4RW2
EAN: N/A
Year: 2005
Pages: 163
Authors: Jobe Makar

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