|
by
kirupa | 9 February 2009
So many of the animations you create directly or
indirectly are inspired by motion in the physical world.
Yet, the language of motion you use in Flash is completely
different from that of the mathematical equations that
describe physics. In this tutorial, let's look at how to
reconcile those two worlds.
The last sentence probably sounds scary. This sounds like
a flashback to physics classes which often involved
contrived examples of blocks on inclines, cars sliding down
the road, and things dropping from tall places. This
tutorial will describe things dropping from high places! (I
never said that I was original
)
The following is an example of the effect I will be
describing. Click on the ball to see it fall again.
The effect you see can be created easily using a few
simple eases, but instead, I am defining the motion manually
using good old physics equations.
In order to understand the code that powers the above
animation, it is very helpful to get an idea of the physics
involved with falling objects. Let's use the falling diagram
as an example:

The diagram is pretty representative of the animation you
saw earlier. The key things to note about this diagram are
the various labels I've given to the points of interest. The
distance your ball falls is the difference between the
initial position and the final position. The force that
causes your ball to fall is gravity, and it is always
downward. Seems simple enough.
At the end of the fall, you know where the ball is going
to be. It is going to be at the bottom where the final
position mark is:

The tricky part is knowing exactly where the ball will be
between the initial position and the final position.
Thankfully, this is something that has been well defined for
a few hundred years. The following equation describes the
position of your ball as it gets dropped:

The a stands for
acceleration, the t stands
for time, and v represents
velocity or speed. The position of my ball is determined by
my initial position combined with the distance from my
initial velocity (v * t) and
the distance from the accelerated movement (.5
* a * t2)
Before I continue, notice that we don't an initial
velocity. At the beginning of the drop, the ball is
motionless. It isn't being thrown down, so the equation can
be simplified a bit as follows:

If you happen to plug in sample numbers for the variables
and think through it, you may realize that the value of y is
actually increasing. In the real world, we would probably
want the value to drop, right? The thing to note is that the
value for acceleration for a falling object is the value of
gravity. Because gravity is a downward force, the value for
it is actually negative. Therefore, despite the initial
shock you may have had, the value actually decreases due to
the negative value gravity contains.
Anyway, keep the last equation in mind, for that defines
the structure of the code that is used to make our object
fall.
Our ball's movement is actually made up of two separate
sequences. The first is the falling sequence which I
described above. The second is the rising sequence, the
bounce, and let's look at that now.
When your ball hits the ground and bounces, the following
diagram describes what is at play on the way up:

Before, gravity was your friend. It helped move your ball
in the correct direction. Now, though, gravity is actually
fighting against you. You have the upward initial speed from
your bounce competing with the downward pressure of gravity.
Fortunately, the equation I showed you above is flexible
enough to work in this situation as well:

This time, we don't get rid of the initial speed portion
of the equation. Instead, we actually have an initial speed.
This initial speed is the final speed of the ball as it fell
earlier. How do we calculate the final speed of the ball as
it hits the ground? This requires another equation, and this
comes from the series of equations linking Kinetic Energy
and Potential Energy:

The speed of an object under constant acceleration is the
square root of two times gravity (aka acceleration) and
height. This result makes up the initial speed of the
equation that is shown just a few paragraphs ago.
Putting these two equations together, you have everything
you need for figuring out for how long the object would
bounce.
One thing you need to keep in mind is
conservation of energy. In an ideal world, a ball you drop
from a certain height will keep bouncing back to that exact
height every time. This means that your ball will be
bouncing forever. This is clearly not the case in the real
world. The reason is that, each time the ball hits the
ground, some of the energy from the impact is converted into
other things such as heat and sound.
With each subsequent bounce, your ball has a bit less
energy than it did in the bounce that preceded it. Flash
simulates an ideal world. What you will need to do is force
some energy decay to cause your ball to not bounce
indefinitely. There are numerous areas where you can do
that, but the one that I will interfere with is the initial
speed as you are about to bounce up:

Each time the ball is about to bounce up (stages II and
IV), I decay the value of the initial speed by a certain
amount. This gives you the effect of your ball bouncing with
each subsequent bounce being weaker than the one that
preceded it. In the physical world, the amount of energy
your ball has with each bounce is decreased when it hits the
ground.
Ok, now that you have a primer on how all of this works,
let's look at the code and see how the world of physics maps
with how we do things in Flash.
Downloading the Application
The following source file contains a working copy of the
bouncing ball animation:
Once you have downloaded, extracted, and opened the
source file in Flash CS4 (or Flash CS3), open the
BlueBall.as file to see the code that makes this all work.
You
now have a high-level overview of the physics involved with
a falling object. Let's look at the code and how it maps
with the theory you saw before. While the mapping is not
exact, I hope it strikes a good balance between realism and
simplicity.
The code is broken up into two parts. The first part is
the falling action. The second part is the rising action. By
breaking up our bounce into these two parts, it allows me to
have much simpler code. The connection between these two
parts is the initial velocity of the ball as I am bouncing.
As long as I know (and can manipulate) that initial
velocity, I am set!
Let's look at the code in the order in which things get
executed:
- function
BlueBall()
{
- startFallingBall();
-
- setupResetFunctionality();
- }
The above code represents my constructor, and it gets
called only when my BlueBall object gets created. This
constructor is responsible for calling my
startFallingBall and
setupResetFunctionality
methods. Let's look at
startFallingBall next.
- function
startFallingBall()
{
- timer=0;
- initialPos=this.y;
- this.addEventListener(Event.ENTER_FRAME,
moveBallDown);
- }
As this method's name implies, the code here is
responsible for starting the ball's fall. I initialize two
variables first. I set the timer
to 0, and I set my initialPos
to the current position. While all of this may not make a
whole lot of sense right now, this is all part of my stated
goal of having the code split into a falling part and a
rising part.
The final thing I do is set up my
ENTER_FRAME event and have
it call my moveBallDown
event handler. Speaking of which, let's look at that next.
- // Responsible for moving the
ball down
- function
moveBallDown(e:Event)
{
- timer+=1;
- this.y
=
initialPos + .5*gravity*(timer
*
timer);
- checkBottomBoundary();
- }
The moveBallDown event handler is what is responsible for
actually moving your ball down. I first increment the value
of my timer value to
indicate a clock tick has occurred. Once I do that, I set
the position of our ball using the equation you saw earlier:

That equation translated into our code can be seen here:
- this.y
=
initialPos + .5*gravity*(timer
*
timer);
Finally, each time we move our ball down a bit, I call the
checkBottomBoundary method
to figure out what to do next. So, let's look at that
method.
- function
checkBottomBoundary()
{
- if
(this.y+this.height>stage.stageHeight)
{
- finalPos=this.y;
-
- stopFallingBall();
- }
- }
This method checks to see if your ball's current position
is below that of your ground which is represented by your
stage's height. I can't simply compare the Y position and
call it a day. Because the ball has some height as well, I
want to stop the falling when the bottom of the ball hits
the ground. That is why I am including the height of the
ball in my calculation for stage height.
When the ball hits the ground, I call
stopFallingBall, so let's
look at what it does next.
- function
stopFallingBall()
{
- this.removeEventListener(Event.ENTER_FRAME,
moveBallDown);
- // If the bounce is a 10%
of the ball's height, just stop
- // the bounce
- if
(finalPos-initialPos<.1*this.height)
{
- stopRisingBall();
- }
else
{
- startRisingBall();
- }
- }
The stopFallingBall method does two things. First, it
kills the event listener for moveBallDown as shown by the
call to removeEventListener. This means that your ball has
stopped falling.
The second, equally important, task this method does is
check whether we are done with the bouncing permanently. The
way I measure that is by comparing where my final position
will be compared to where my initial position is. If they
are very close to each other, that means the bounce is
coming to an end.
The "very close" in the code is 10% of the ball's height.
If your ball's bounce is going to be less than 10% of its
height, then it's time to call it a day:
-
function
stopFallingBall()
{
-
this.removeEventListener(Event.ENTER_FRAME,
moveBallDown);
- //
If the bounce is a 10% of the ball's height, just
stop
- //
the bounce
- if
(finalPos-initialPos<.1*this.height)
{
- stopRisingBall();
- }
else
{
- startRisingBall();
- }
- }
The call to stopRisingBall
ends your sequence of bounces, but if your final bounce
position is greater than 10% of your ball's height, then
your code lives another day. The
startRisingBall method gets called. Since we just
wrapped up what happens when your ball is falling, let's go
ahead and look at what happens when your ball starts to
rise.
- // Sets up what is needed to
start bouncing the ball up
- function
startRisingBall()
{
- initialSpeed=decay*Math.sqrt(2*Math.abs(finalPos-initialPos));
- timer=0;
- currentPos=this.y;
- this.addEventListener(Event.ENTER_FRAME,
moveBallUp);
- }
The startRisingBall method is responsible for taking your
ball from the bottom and moving it to the top. Like I
explained a few pages ago, the speed your ball has as your
bouncing up is exactly the same speed it had when it fell to
the ground. The equation for that is what you saw earlier:

Because of conservation of energy, if the same speed was
used, that would mean your ball would be bouncing
indefinitely, so I introduce a decay value that I briefly
talked about before to dampen the speed a bit. Putting it
all together, you get the following line of code:
- initialSpeed=decay*Math.sqrt(2*Math.abs(finalPos-initialPos));
The final task is just resetting some of our variables to
start the ball moving up. Because I am treating
the up movement as a separate, independent action, I reset
my timer and
currentPos to 0 and my ball's current position
respectively.
The final thing this method does is set up the animation
by registering the ENTER_FRAME event with the moveBallUp
event handler...which we'll look at next.
- // Responsible for moving the
ball up
- function
moveBallUp(e:Event)
{
- timer+=1;
-
- //Storing the position of
the ball before and after it moves
- var
positionA:Number=this.y;
- this.y
=
currentPos -
initialSpeed*timer
+ .5*gravity*(timer
*
timer);
- var
positionB=this.y;
-
- checkTopBoundary(positionA,
positionB);
- }
The moveBallUp event is the arch-nemesis of your
moveBallDown method. This method gets called at each frame,
and it is responsible for moving the ball up. It does this
by first incrementing the timer and then setting the current
position to our favorite equation for position:

The line of code mapping to the above diagram is:
- this.y
=
currentPos -
initialSpeed*timer
+ .5*gravity*(timer
*
timer);
The only difference now is that the initialSpeed actually
matters. That is why it is visible in the code whereas it
was omitted in its counterpart for making the ball fall
earlier.
The final thing is to make a call to the
checkTopBoundary method that takes
positionA and
positionB as
arguments. Notice that the positionA and positionB variables measure
the position of the ball before and after it gets moved. I
am doing this to figure out whether the ball is still moving
up or whether the ball's direction has changed.
- // Checks when the ball has
hit the top of the bounce
- function
checkTopBoundary(firstPos:Number,
secondPos:Number)
{
- if
(secondPos>firstPos)
{
- stopRisingBall();
- startFallingBall();
- }
- }
While I gave some of details away in the preceding
section, I check the direction of the ball movement in the
checkTopBoundary method. The reason is that, if I didn't
check the direction, the ball would automatically reverse
direction and start moving down. I already have code
dedicated to making the ball fall, so I by comparing whether the
secondPos is greater than the
firstPos, I can stop the
current animation and switch to all of the code you saw
before for making the ball fall:
If the second position is greater than the first
position, I
immediately call both the stopRisingBall and
startFallingBall methods! You've already seen both of these
methods in great detail already. In fact, you've gotten a
detailed look at all of the methods for making your ball
both
fall and rise! The only thing to look at is our variables.
- var
timer:Number=0;
- var
initialPos:Number=0;
- var
finalPos:Number=0;
- var
currentPos:Number=0;
- var
initialSpeed:Number=0;
- var
startPosition:Number=0;
- var
gravity:Number
= 1;
-
- //Adjust this to increase or
decrease the
- //number of bounces
- var
decay:Number
= .9;
The variables at the vary top help keep track of the
various things that help your ball either fall or bounce.
The most important variable is the one for decay that I am
using to dampen the bounce each time it hits the ground. A
really small value for decay means that the bouncing will
stop very quickly. A larger value (such as .95 or .99)
indicates that the bounce will go on a bit longer. A value
of 1 means the bouncing will continue indefinitely.
I hope you
found this tutorial useful. While the end result was a look
at the code that is responsible for making something bounce,
I think the more important part is understanding the physics
behind why the code is written the way it does. Best of all,
I probably brought back memories of having seen some of
these concepts in your classes in the real world!
|