Interval- and Timer-based Animation

Interval- and Timer-based Animation

In all the examples in the book so far, animation has been done by placing motion code inside an onEnterFrame function. Ive always found this to be the simplest method, as the concept of frames is so deeply ingrained in Flash, and its sitting right there ready for you to use.

However, a lot of people, particularly those coming from non-Flash programming environments, are not so comfortable with this model. To them, an interval-based animation model (using the setInterval function) seems to offer more precise control over the speed of the animation. So well take a look at that.

Then well look at timer-based animation, a technique that can be used with either frames or intervals.

Interval-based animation

The key methods for interval-based animation are setInterval and clearInterval . Youll also need some other function, analogous to onEnterFrame , that will contain your motion code. Here is the basic syntax for setInterval and clearInterval :

 var id:Number = setInterval(myFunction, milliseconds); clearInterval(id); 

As you can see, setInterval takes two parameters. (There is also a three-parameter version, which youll find especially useful for class-based code. See the Flash help files for the syntax.) The first is a reference to a function, and the second is how often to run that function, in terms of milliseconds. For example, you could say

 var id:Number = setInterval(move, 50); 

As soon as you call this, Flash waits 50 milliseconds, and calls the move function. It calls it again 50 milliseconds later, and again, continuously, until you say

 clearInterval(id); 

This ends the execution of that interval. If you are absolutely sure that you never want to stop the interval from running, you dont need to capture its ID. But if you dont, realize that there is no easy way to stop that function from executing.

So, theoretically, setInterval gives you millisecond-level control over the speed of your animationa vast improvement over frames, which are notoriously inaccurate. However, I say theoretically, because there are several things to be aware of.

Lets see how to create the first example. File ch19_10.fla is an almost complete copy of ch6_04.fla , which has a ball that moves around the stage and bounces off the edges. All I did was rename the onEnterFrame function to move , and set an interval to run it every 20 milliseconds. I also decreased the frames per second of the movie down to 1, since intervals supposedly have nothing to do with frame rate, right?

 init(); function init() {       top = 0;       left = 0;       bottom = Stage.height;       right = Stage.width;       ball = attachMovie("ball", "ball", 0);       ball._x = Stage.width / 2;       ball._y = Stage.height / 2;       vx = Math.random() * 10 - 5;       vy = Math.random() * 10 - 5;  id = setInterval(move, 20);  }  function move(Void):Void  {       ball._x += vx;       ball._y += vy;       if (ball._x + ball._width / 2 > right)       {             ball._x = right - ball._width / 2;             vx *= -1;       }       else if (ball._x - ball._width / 2 < left)       {             ball._x = left + ball._width / 2;             vx *= -1;       }       if (ball._y + ball._height / 2 > bottom)       {             ball._y = bottom - ball._height / 2;             vy *= -1;       }       else if (ball._y - ball._height / 2 < top)       {             ball._y = top + ball._height / 2;             vy *= -1;       } } 

This should wind up calling the move function about 50 times per second (1,000 milliseconds divided by 20), which should be sufficient to move the ball around the screen quite smoothly. But, when you run this, you get something entirely unexpected (at least I didnt expect it the first time I saw it). The ball is jumping across the stage at a rate of about one jump per secondwhich is the frame rate. But each jump is much more than 5 pixels, which is the maximum it could move in any direction per jump (per the random vx and vy assigned). Whats going on?

Well, if you think back to Chapters 1 and 2, where I talked about basic animation, you may remember that the model needs to be updated, and then the screen needs to be refreshed based on the new model. Here, the interval function is reliably updating the model and moving the ball 5 pixels every time it runs, but still the screen is only being refreshed when Flash enters a new frame. Simply running a function does not force Flash to redraw .

Fortunately, the good people at Macromedia saw this coming and gave you another little tool to use: updateAfterEvent . This is a function you can call from within certain other functions to force Flash to refresh the screen, even if it is not a scheduled frame redraw.

The updateAfterEvent function cant just be called any time from any old function. As its name implies, it updates the screen after an event . It can be used within onClipEvent blocks and event handler functions for most mouse and key events. And fortunately, it can also be used in a function specified in setInterval . OK, so you fix up the example to add the line

 updateAfterEvent(); 

as the last statement in the move function ( ch19_11.fla ).

Now things are definitely better. Much smoother. But if you realize that the ball should be updated 50 times per second, you are basically looking at what should be a 50 fps movie. This means that the ball should be moving quite a bit faster than the original movie from Chapter 4, which was coded at 31 fps. But its actually moving more slowly.

The problem, it turns out, is that setInterval is actually somewhat tied to frame rate. Supposedly, Flash is able to run about ten intervals in any given frame. Since your frame rate is 1 fps, you are actually maxing out at about 10 updates per second. So, if you change the frame rate to 5, you should be able to get the equivalent of 50 fps. On my PC, that still looks pretty jumpy, but if I go up to about 10 fps, it starts looking good.

There is something more should you know about how setInterval works, as it affects how accurate the timing will be. What actually happens is that as soon as the setInterval function is called, Flash waits the specified amount of time and then calls the named function. Only when that function is finished executing does the count for the next interval begin. As an example, say you have set the interval to run every 20 milliseconds. But say there is so much code in that function, that it takes 30 milliseconds to execute it all. In this case, your function will actually run about every 50 milliseconds. As there is no way to accurately determine how fast code will run on the end user s machine, in many cases, interval-based animation winds up being no more accurate than frame-based animation.

If you really need accuracy, timer-based animation is the way to go.

Timer-based animation

Timer-based animation is the method to use if the speed of objects in your movie needs to be constant. This might be the case in many types of games . As youve seen, neither frame- nor interval-based animation can be counted on for a specific rate of playback. A complex movie on an older, slower computer may run at a third or less of the speed it was designed for. As youll see shortly, when you use a timer, youll get speed you can count on, no matter what frame rate the final movie ends up running at.

The first thing you need to do is change the way you think about velocity. Up to now, when I said, for example, vx = 5, the units you were using were pixels per frame . In other words, the object would move 5 pixels on the x-axis each time a new frame was encountered . In interval-based animation, of course, it would be 5 pixels per interval.

For timer-based animation, youll actually start using real measurements of time, like seconds. Since you are dealing with a whole second, rather than a small fraction of one, the value needs to be much higher. If you had something moving at 10 pixels per frame at 31 frames per second, that is very roughly 300 pixels per second. Again, I hijacked the bouncing ball movie from Chapter 6, changing the init function to the following ( ch19_12.fla ):

 function init() {       top = 0;       left = 0;       bottom = Stage.height;       right = Stage.width;       ball = attachMovie("ball", "ball", 0);       ball._x = Stage.width / 2;       ball._y = Stage.height / 2;  vx = 300;   vy = 300;   start = getTimer();  } 

I bumped up the velocities, as described previously, and left them at hard-coded values, rather than randomly determined. I then created a variable called start , and set it to the result of the function getTimer . The getTimer function is often misunderstood, but is actually quite simple. It returns the number of milliseconds the movie has been runningthats all it does. There is no way to clear it, reset it, change it, or anything else. Its just a counter.

Now, that might seem rather useless, but if you call getTimer once and store its value, and then call it again later and subtract the two values, you will know exactlydown to the millisecondhow much time elapsed between the two.

So here is the strategy: you call getTimer at the beginning of each frame and see how many milliseconds elapsed since the last frame. If you divide that by 1,000, youll have what fraction of a second elapsed. Since your vx and vy are now in terms of pixels per second, you can multiply them by this fraction and know how much to move the object. Also, dont forget to reset the value of the start variable so you can measure the next frame. Here is the onEnterFrame function of ch19_12.fla :

 function onEnterFrame(Void):Void {  var elapsed:Number = getTimer() - start;   start = getTimer();   ball._x += vx * elapsed / 1000;   ball._y += vy * elapsed / 1000;  if (ball._x + ball._width / 2 > right)       {             ball._x = right - ball._width / 2;             vx *= -1;       }       else if (ball._x - ball._width / 2 < left)       {             ball._x = left + ball._width / 2;             vx *= -1;       }       if (ball._y + ball._height / 2 > bottom)       {             ball._y = bottom - ball._height / 2;             vy *= -1;       }       else if (ball._y - ball._height / 2 < top)       {             ball._y = top + ball._height / 2;             vy *= -1;       } } 

Test that out and youll see that the ball moves at about the same speed as the original. But the really amazing thing is that you can publish the movie at any frame rate you want, and it will still move at the same speed! Try as high as 120 fps, or as low as 12 fps, and youll see that the speed of the ball is the same. Of course, the higher rates make for a much smoother movie, and the lower ones are quite jumpy, but the velocity itself should be consistent.

You can apply this technique to any example in the book that contains velocity. In doing so, youll also need to apply a similar technique to any acceleration or repeated forces, such as gravity, as these are also time based. Values for acceleration actually have to be much larger when converted to this type of animation, because acceleration is defined as distance-per-time interval, per time interval. For example, gravity is approximately 32 feet per second, per second.

So where gravity might be something like 0.5 in a frame-based movie of around 30 fps, it would have to be more like 450 here. Thats 0.5 * 30 * 30. Then you would apply it like so:

 vy += gravity * elapsed / 1000; 

Go ahead and try gravity applied like this to the last example, with a value of 450. You should find it just about equal to the same frame-based movie with a gravity of 0.5.

One tactic with this kind of technique is to set the movies frame rate to something very high, like 60. Although nobodys machine will likely meet that actual frame rate, it will guarantee that anybody viewing the movie will see it at the smoothest possible playback possible.



Foundation ActionScript. Animation. Making Things Move
Foundation Actionscript 3.0 Animation: Making Things Move!
ISBN: 1590597915
EAN: 2147483647
Year: 2005
Pages: 137
Authors: Keith Peters

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