28
Jan
08

More Motion (Gravity)

Chapter 7 of the book contains an overview of motion programming techniques. This post expands on some of these techniques.


 

Gravity

We'll start off with a code snippet that can be run in your timeline:

ActionScript 3.0:
  1. // create a ball sprite
  2. var ball:Sprite = new Sprite();
  3. ball.graphics.beginFill(0);
  4. ball.graphics.drawCircle(0, 0, 10);
  5. ball.x = 275;
  6. ball.y = 390;
  7. addChild(ball);
  8.  
  9. // setup position and velocity variables
  10. var xPos:Number = ball.x;
  11. var yPos:Number = ball.y;
  12. var xVel:Number = (Math.random() * 10) - 5;
  13. var yVel:Number = (Math.random() * -10) - 10;
  14.  
  15. addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
  16. function onLoop(evt:Event):void {
  17.     // continuously increment xPos and yPos by
  18.     // their corresponding velocities
  19.     xPos += xVel;
  20.     yPos += yVel;
  21.          
  22.     // apply calculations to ball
  23.     ball.x = xPos;
  24.     ball.y = yPos;
  25. }

This code shows the implementation of a simple rule that you can use when programming any kind of complex motion: First calculate your position, and then apply it to your graphics. The above code is just a simple velocity example. The ball will move on the y axis at a velocity between -10 and -20 and will move on the x axis at a velocity between -5 and +5. Try running it a few times to get a feel for how it works.

To add some gravity we can just add one line of code right at the beginning of the function (after line 16, in the original code above, for instance):

    yVel += 1;

Because we've forced the starting y velocity to be negative, the above code will cause the ball to move in an arc. You can try tracing out yVel to see the values change.

-6.433345764875412
-5.433345764875412
-4.433345764875412
-3.433345764875412
-2.433345764875412
-1.433345764875412
-0.433345764875412
 0.566654235124588
 1.566654235124588
 2.566654235124588
 3.566654235124588
 4.566654235124588

In this example case, my Output panel shows that the yVelvariable started off at roughly -6.5. By continuously adding 1 to the value we slow the ball's upward motion until eventually yVel becomes positive. At this point the ball will move downward at an increasing rate. If you've read Chapter 7 this concept should be familiar.

The number used to alter y can be thought of as gravity. For organizational purposes, and to allow on-the-fly updates, we can wrap the value 1 up in a variable called grav: You can group the variable initialization with your other variable declarations (after line 13 in the original code above, for example), and then just replace the hard-coded 1 in the line you just added, with the variable name:

// this variable definition can go after your yVel definition
var grav:Number = 1;

// you can replace yVel += 1 with this:
yVel += grav;

 

Collisions

The next step is to prevent the ball from going off the screen. This can be done with a conditional, added in the function, just before the x and y positions of the ball are updated. The new function will look like this:

function onLoop(evt:Event):void {
    yVel += grav;
    xPos += xVel;
    yPos += yVel;

    if (yPos > 390) {
        yPos = 390;
        yVel *= -1;
    }

    ball.x = xPos;
    ball.y = yPos;
}

With this conditional, we're saying that, if the y position is greater than 390, set it back to the "bounce point" of 390 and reverse the y velocity. This will cause the ball to start moving up again until it is pushed back down by gravity. If you run this file, you will see that the effect will simulate a bouncing ball.

The limitation of this example, however, is that the ball keeps bouncing back up to the same point, never losing any velocity when it hits the ground. We can change this behavior by multiplying the y velocity by a negative decimal value instead of by -1. Multiplying by -.7 would cause the ball to lose 30% of its y velocity each time it hits the ground. Give it a try, changing the last line of the new conditional from yVel *= -1 to this:

yVel *= -.7;

The last step is to apply the same technique to xVel. I usually ask my students to try and figure this one out for themselves, because there's a small implementation issue that needs to be discovered. Some try:

xVel *= -.7;

This produces an interesting result. The ball will reverse its x velocity each time it hits the ground causing it to jump back and forth. I could see this being used in a game to create the motion for an enemy. The correct solution is to simply multiply xVel by positive .7 so that eventually xVel will equal 0 and the ball will stop moving on both axis. The final code looks like this:

ActionScript 3.0:
  1. var ball:Sprite = new Sprite();
  2. ball.graphics.beginFill(0);
  3. ball.graphics.drawCircle(0, 0, 10);
  4. ball.x = 275;
  5. ball.y = 390;
  6. addChild(ball);
  7.  
  8. var xPos:Number = ball.x;
  9. var yPos:Number = ball.y;
  10. var xVel:Number = (Math.random() * 10) - 5
  11. var yVel:Number = (Math.random() * -10) - 10;
  12. var grav:Number = 1;
  13.  
  14. addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
  15. function onLoop(evt:Event):void {
  16.     yVel += grav;
  17.     xPos += xVel;
  18.     yPos += yVel;
  19.    
  20.     if (yPos> 390){
  21.         yPos = 390;
  22.         yVel *= -.7;
  23.         xVel *= .7;
  24.     }
  25.    
  26.     ball.x = xPos;
  27.     ball.y = yPos;
  28. }

Next we'll look at two examples that implement these concepts.

 

Basic Gravity Demo

The first example is nearly the same. The code has been wrapped in a document class called Gravity and a few additional features have been added. When the screen is clicked the simulation is reset:

This movie requires Flash Player 9. Please update your player.

Download: Wall Bounce  Download Wall Bounce (9 KB, 310 hits)

Let's take a closer look at how we manage to bounce the ball off the sides of the screen and how the ball rotation is achieved.

In our constructor we figure out which values to use for the size of the screen, based on the size of our ball graphic and the size of the stage:

_left = _ball.width / 2;
_top = _left;
_right = stage.stageWidth - _left;
_bottom = stage.stageHeight - _left;

In our ENTER_FRAME loop we have a conditional for each side of the screen:

if (_yPos > _bottom) {
    _xVel *= .5;
    _yVel *= -.5;
    _yPos = _bottom;
}
if (_yPos < _top) {
    _yVel *= -1;
    _yPos = _top;
}
if (_xPos < _left) {
    _xVel *= -1;
    _xPos = _left;
}
if (_xPos > _right) {
    _xVel *= -1;
    _xPos = _right;
}

The same technique applied earlier in the post is applied here. The ball only loses velocity when it hits the ground, all other sides reverse the direction of the ball.

Rotation
Our ball sprite has a sprite nested within it called texture that contains red and black stripes. We rotate this sprite in accordance with the x position of the ball. Above the texture in the same sprite is a highlight gradient that does not rotate. These two layers, and a simple line of ActionScript that follows, create the illusion that the ball is rolling in the direction in which it is traveling. Here are the ball layers...


ball sprite elements

...and here is the code:

_ball.texture.rotation = _xPos;

Resetting the Simulation
The ability to reset the simulation is also worth noting. This is achieved with a method called setupGravity. It works in a manner similar to the previous discussion, setting random values for the velocity variables and positioning the ball at the center of the stage:

private function setupGravity():void {
    _xVel  = Math.random() * 20 - 10;
    _yVel = -(Math.random() * 20);
    _ball.x = stage.stageWidth / 2;
    _ball.y = stage.stageHeight / 2;
    _xPos = _ball.x;
    _yPos = _ball.y;
}

The setupGravity method is called from within the constructor to start off the simulation and, to reset the behavior, when the stage is clicked

 

Bouncing Off Platforms

The second demo shows how to bounce a ball off platforms. This demo makes use of the hitTestObject method and the getRect method.

This movie requires Flash Player 9. Please update your player.

Download: Platform Bounce  Download Platform Bounce (9.5 KB, 228 hits)

The code is very similar to the previous example. We've added three sprites to the stage with the instance names platform0 platform1 and platform2. To check if the ball is colliding with any of these platforms we've created a method called checkPlatforms.

Here is the complete class. read through the comments to get a better idea of how this works.

ActionScript 3.0:
  1. package {
  2.  
  3.    import flash.display.*;
  4.    import flash.events.*;
  5.    import flash.geom.*;
  6.  
  7.    public class GravityPlatforms extends Sprite {
  8.  
  9.       private var _ball:Sprite;
  10.  
  11.       private var _xvel:Number;
  12.       private var _yvel:Number;
  13.       private var _xpos:Number;
  14.       private var _ypos:Number;
  15.  
  16.       private var _grav:Number;
  17.  
  18.       private var _left:Number;
  19.       private var _right:Number;
  20.       private var _bottom:Number;
  21.       private var _top:Number;
  22.  
  23.       public function GravityPlatforms() {
  24.  
  25.          stage.align = StageAlign.TOP_LEFT;
  26.  
  27.          _grav = 1;
  28.  
  29.          _ball = new Ball();
  30.  
  31.          setupGravity();
  32.          
  33.          _left = _ball.width / 2;
  34.          _top = _left;
  35.          _right = stage.stageWidth - _left;
  36.          _bottom = stage.stageHeight - _left;
  37.  
  38.          addChild(_ball);
  39.          addEventListener(Event.ENTER_FRAME, onLoop, false, 0, true);
  40.          stage.addEventListener(MouseEvent.MOUSE_DOWN,
  41.                                 onDown, false, 0, true);
  42.       }
  43.      
  44.       private function onDown(evt:MouseEvent):void {
  45.          setupGravity();
  46.       }
  47.      
  48.       private function setupGravity():void {
  49.          // for demo purposes, we don't randomize here
  50.          // so that the ball hits all platforms
  51.          _xvel  = 6;
  52.          _yvel = 5;
  53.          _ball.x = 75;
  54.          _ball.y = 75;
  55.          _xpos = _ball.x;
  56.          _ypos = _ball.y;
  57.       }
  58.      
  59.       private function onLoop(evt:Event):void {
  60.          _yvel += _grav;
  61.          _xpos += _xvel;
  62.          _ypos += _yvel;
  63.  
  64.          if (_ypos>_bottom) {
  65.             _xvel *= .8;
  66.             _yvel *= -.8;
  67.             _ypos = _bottom;
  68.          }
  69.          if (_ypos<_top) {
  70.             _yvel *= -1;
  71.             _ypos = _top;
  72.          }
  73.          if (_xpos<_left) {
  74.             _xvel *= -1;
  75.             _xpos = _left;
  76.          }
  77.          if (_xpos>_right) {
  78.             _xvel *= -1;
  79.             _xpos = _right;
  80.          }
  81.          
  82.          _ball.texture.rotation = _xpos;
  83.  
  84.          _ball.x = _xpos;
  85.          _ball.y = _ypos;
  86.  
  87.          checkPlatforms();
  88.  
  89.       }
  90.       private function checkPlatforms():void {
  91.          // reset bottom variable
  92.          _bottom = stage.stageHeight - _left;
  93.          var hit:Boolean = true;
  94.          // check each platform for collision
  95.          for (var i:uint = 0; i <3; i++) {
  96.             var platform:Sprite = this["platform" + i];
  97.  
  98.             // is the ball above the platform
  99.             if (_ball.y <platform.y) {
  100.  
  101.                // is the ball hitting the platform?
  102.                if (_ball.hitTestObject(platform)) {
  103.  
  104.                   // get platform rectangle
  105.                   var bounds:Rectangle = platform.getRect(this);
  106.  
  107.                   // set bottom variable to platform top
  108.                   // minus the radius of the ball
  109.                   _bottom = bounds.top - _top;
  110.  
  111.                   // set hit flag and exit
  112.                   hit = true;
  113.                   break;
  114.                }
  115.             }
  116.          }
  117.          // if there was a collision, calculate the bounce
  118.          // and apply changes to the x and y properties
  119.          // of the ball
  120.          if (hit) {
  121.             if (_ypos> _bottom) {
  122.                _xvel *= .7;
  123.                _yvel *= -.7;
  124.                _ypos = _bottom;
  125.             }
  126.             _ball.x = _xpos;
  127.             _ball.y = _ypos;
  128.          }
  129.       }
  130.    }
  131. }

The real key here is when to call the checkPlatform method. The trick is to call it last on our ENTER_FRAME, after we've updated the ball's position:

_ball.x = _xPos;
_ball.y = _yPos;

checkPlatforms();

The reason for this is so that the hitTestObject() method has the most up-to-date bounding rectangle for our ball. If there's no collision, checkPlatforms() won't alter the ball's x and y properties. If there is a collision, the same logic used to bounce off the floor is used to bounce off the platform and the x and y properties are change.

That's it for now. We plan on doing more posts about motion so be sure to check back.

Share This:
  • Digg
  • del.icio.us
  • Netvouz
  • DZone
  • ThisNext
  • MisterWong
  • Wists
  • blogmarks
  • BlogMemes
  • Fark
  • feedmelinks
  • Furl
  • Ma.gnolia
  • Netscape
  • Reddit
  • Slashdot
  • SphereIt
  • Spurl
  • StumbleUpon
  • Technorati
  • YahooMyWeb
  • BlinkList
  • DotNetKicks
  • LinkaGoGo
  • NewsVine
  • blinkbits
  • co.mments
  • MyShare
Print This Post Print This Post

Related Content



0 Responses to “More Motion (Gravity)”


  1. No Comments

Leave a Reply