Hack 38 Use Acceleration to Simulate Gravity and Friction 
Many real-world phenomena, such as gravity and
friction, affect the velocity of objects over time. Model such
changes by a simple acceleration equation to create realistic motion
under simulated physical conditions.
Some physical effects are
hard to get right in scripted animation unless you know the
underlying math. Acceleration calculations are easier than they might
appear. Like all forces acting on masses, gravity and friction cause
acceleration (or deceleration). When you are performing iterative
calculations (which you are always doing when you are animating
sprites over time), the math works out to be surprisingly easy.
When modeling real-world movement, you often have an accelerating
force and a decelerating force acting on the
same body (such as gravity and air friction acting on a falling
body). If the forces are balanced (such as when the thrust of an
airplane counteracts the air resistance), the velocity of the object
is constant (acceleration is zero). The trick is simply adding the
forces together to determine the overall force on the object.
A force such as gravity provides constant acceleration, meaning that
the velocity of the object changes over time (just as your car speeds
up if you provide enough gas to accelerate). The velocity of an
object at any time is equal to its existing velocity, plus the effect
of acceleration over the time interval, such as:
newV = oldV + (acceleration * time)
Likewise, the position of an object at any time is equal to its old
position, plus the effect of its velocity over the time interval,
such as:
newPos = oldPos + (newV * time)
If we recalculate the position and velocity often enough that the
speed doesn't change during
each iteration, and we use 1 as the time interval so the units drop
out, we can simplify the position equation as:
newPos = oldPos + newV
In words, this means, "To calculate the new
position, take the old position and add the current
velocity." For example, if I drive at 60 miles per
hour (mph) for one hour, I've traveled 60 miles from
my original position. Therefore, in each frame of the animation, we
add a small amount to the position in the direction of the velocity
vector.
At the end of an interval, we recalculate the velocity to account for
acceleration. The velocity calculation can likewise be simplified as:
newV = oldV + acceleration
In words, this means, "To calculate the current
velocity, take the old velocity and add the
acceleration." For example, if I'm
driving at 60 mph and I accelerate by 10 mph per second for one
second, I'm now traveling at 70 mph. Therefore, in
each frame of the animation, we add a small amount to the velocity in
the direction of the acceleration vector (usually pointing down in
the case of gravity).
Acceleration
due to Earth's gravity is approximately 32 feet per
second squared (or 9.8 meters per second squared), but if you
aren't providing an accurate physics calculation,
you can use any constant value for the acceleration that makes your
animation run at the rate you'd like.
Resting friction (the friction acting on a body at rest) tends to be
greater than rolling friction (the friction acting on, say, a rolling
ball). Air friction generally increases in proportion to the
object's speed, so we use a simple
coefficient of
friction to approximate the resulting force as
a fraction of the current velocity.
The following code generates a number of particles [Hack #33] and animates them falling
under the acceleration of gravity and being resisted by air friction:
function fall( ) {
// Add acceleration due to gravity
this.speedY += GRAVITY;
// Reduce the speed due to friction
this.speedY *= FRICTION;
// Assume both forces work exclusively in the Y direction
this._y += this.speedY;
// Make the clip bounce up when it hits the "floor" (a line)
if (this._y > 400) {
this._y = 400;
this.speedY = -this.speedY * ELASTICITY;
}
}
function drag( ) {
// When the user clicks on a clip, make it draggable
// and stop animating it via onEnterFrame.
this.startDrag( );
delete this.onEnterFrame;
// We could save a function call by using the following:
// this.onMouseMove = updateAfterEvent;
// because the onMouseMove( ) handler calls only one function.
// However, we use the following function definition in case
// you want to add extra features, such as range checking
// to prevent clips from being dragged off stage.
this.onMouseMove = function( ) {
updateAfterEvent( );
};
}
function drop( ) {
// Initialize the drop animation and
// stop the clip being draggable.
// The initial y velocity is zero.
this.speed.y = 0;
this.stopDrag( );
this.onEnterFrame = fall;
}
// MAIN CODE
// Create 20 ball clips
for (var i = 0; i < 20; i++) {
var ball:MovieClip = this.createEmptyMovieClip("ball" + i, i);
ball.lineStyle(6, 0x0, 100);
ball.moveTo(0, -3);
ball.lineTo(1, -3);
ball._x = Math.random( ) * 550;
ball._y = Math.random( ) * 200;
ball.speedY = 0;
ball.onEnterFrame = fall;
ball.onPress = drag;
ball.onRelease = ball.onReleaseOutside = drop;
}
//Initialize physical constants
var GRAVITY:Number = 0.5;
var FRICTION:Number = 0.995;
var ELASTICITY:Number = 0.85;
// Draw the ground line
this.lineStyle(0, 0xDDDDDD, 100);
this.moveTo(0, 400);
this.lineTo(550, 400);
Gravity is simulated by applying the following line every frame. It
changes the speed in the Y direction over time:
this.speedY += GRAVITY;
If the ball is falling, gravity increases the velocity and speeds the
fall. If the ball is climbing, gravity decreases the velocity and
slows the ascent, just as a bouncing ball would act.
The effect of air friction is simulated via the line:
this.speedY *= FRICTION;
If FRICTION is less than 1, it slows the ball down
regardless of the direction it is moving, just like real friction. If
set equal to 1, there is no frictional effect. If set greater than 1,
it creates thrust, like that of a rocket (it speeds up the clip
rather than slowing it down).
When the ball hits the floor and bounces, we want to simulate the
loss of energy typical in such collisions. The
elasticity coefficient is applied
via the line:
this.speedY *= ELASTICITY;
If ELASTICITY is less than 1, the ball bounces
with less energy each time it hits the floor, just like in the real
world. If set equal to 1, the bouncing ball is perfectly elastic (it
would bounce forever to the same height if not slowed by friction).
If elasticity is set greater than 1, the ball bounces higher with
each successive bounce (neat trick!).
Final Thoughts
Because animation works on a per-frame basis, equations involving
motion are easier to code up than the equations of motion you learned
in physics class. If you consider motion per frame, such motion
always results in fundamentally linear
equations, making your scripts short and efficient. See if
you can simulate motion in the X direction as well as the Y direction
(hint: gravity has no effect in the X direction, and you should give
your ball an initial horizontal velocity).
You can add all sorts of variations to an animation by setting the
gravity to a different value (or even a negative value to make things
float). You can set gravity to 0 to achieve a 2D effect in which the
camera appears overhead (such as balls on a billiard table, so you
better add bumpers all around). Throw sliders on stage [Hack #61] to control the gravity,
friction, and elasticity settings, and go wild! You can also add
sounds or make the ball squish when it bounces.
|