Ultrashock Tutorials > Flash MX > Moose Tutorial Series #1: Time-based Scripted Animation - Part I  
 
by Aral Balkan, BitsAndPixels.co.uk
 Download PDF file of this tutorial 
Download Source Files 
 
Moose Tutorial Series #1: Time-based Scripted Animation - Part I
 

Flash MX Most Wanted Components

Aral Balkan is
co-author of
 Flash MX 
 Most Wanted 
 Components 

 Check it out 
 at Amazon! 
Bug fix/patch: Please replace the move.as file with the one here if you downloaded the source files before 2003-04-18. This patch fixes the bug with looping and repeated Move instances.

Moose Tutorial Series #1:
Time-based Scripted Animation – Part I

Introducting The Moose

Welcome to the Moose tutorials. The what tutorials? Moose! Moose is a new open-source ActionScript library for Flash MX by yours truly. What does it do? It makes your life as a Flash developer easier!

Before we start on the tutorial, make sure you download this week's source files and unzip them into your working directory. There you will find all the necessary Moose Modules, as well as finished versions of the FLAs we'll be creating here.

Time-Based Motion vs. Frame-Based Motion

In today’s tutorial we will be tackling a subject that usually intimidates novice developers: time-based scripted animation. Unlike frame-based animations, time-based animations execute independently of the frame rate. In other words, if you ask a ball to travel from point A to point B in one second, it will always go from point A to point B in one second, regardless of whether the frame rate of your movie is set to 12 frames per second (fps) or 120. This is not to say that your chosen frame rate will have no effect whatsoever on the animation. Quite the contrary, your frame rate will determine how smooth the resulting animation is. Let’s see why this is the case:

Look at the following two diagrams, showing the ball animating at 4fps and 8fps. Do you see how, in the latter, the ball gets redrawn more often and at closer increments? This is what allows the animation to be smoother than at 4fps. To illustrate this further, let’s take an extreme case: If you were to set your movie’s frame rate to 1 frame per second, you would see the ball redrawn twice: First at the beginning, when time (t) = 0 seconds, and then at the end point at t = 1 second. Of course, this will make for a very jumpy animation. This is why it is always a good idea to set your movie’s frame rate as high as you believe attainable for your target audience.

Notice how I didn’t say “set your frame rate as high as possible”. If that were the case, we could all set our frame rates at 120 (the current maximum) and leave it at that. Unfortunately, not all computers are fast enough to play back every Flash movie at 120fps. Depending on how complex your movie is and how many things are going on at the same time, even the fastest computer may not be able to attain an actual frame rate of 120fps while running your movie. Flash tries to play back your movies at the rate you set but if it cannot it settles for the best it can do. This may mean that although your movie’s frame rate is set at 120fps, a user on a slow computer can only run your movie at, say, 8fps. This user is thus not going to see a very smooth time-based animation. However, you can be sure when using time-based animation that even on this user’s machine, the ball will arrive at point B in two seconds. We cannot say the same thing had you used frame-based animation. This brings us to the first very important advantage of time-based movies: They will play back in perfect synchronization, regardless of the frame rate.

To see why this is such a great advantage, consider for a moment that we had used frame-based, tweened animation for our ball. In fact, let’s go ahead and quickly create such an animation to see its shortcomings.

  1. Start a new movie in Flash.
  2. Select Modify -> Document and set the Dimensions to 500 x 100 and the Frame Rate to 12fps.
  3. Draw a ball using the Circle Tool.


  4. Select it by double-clicking on its fill (or use the Arrow Tool to draw a selection rectangle around it) and convert it to a movie clip symbol using Insert -> Convert To Symbol (F8). Call the symbol, "ball".


  5. On Layer 1, go to Frame 24 and create a new Keyframe (select the frame and use Insert -> Keyframe or press F6.)


  6. With the ball selected in Frame 24, move it to the opposite side of the Stage.


  7. Select the ball on Frame 1 and Right-Click (Ctrl-Click) and select Create Motion Tween (or select Motion from the Tween drop-down menu in the Property Inspector)

When you test the movie (Control->Test Movie; Ctrl-Enter), you should see the ball animate from one side of the screen to the other in 2 seconds. (Unless you put a stop action on the last frame, the animation will loop.) The reason it takes two seconds is because your frame rate is set at 12 frames per second. So, for every second that passes, Flash plays 12 frames. Since our animation has 24 frames, it will take 24 frames / 12 fps = 2 seconds. All right, great, you probably already knew how to do all this. It’s Flash 101. Now on to the problems with this method:

Let’s say that your boss, having seen the animation, thought that it was too choppy and she wants it “smooth as milk”. OK, no problem… you can just up the frame rate, right?

  1. Select Modify -> Document and set the Frame Rate to 120fps. Test the movie.

Woah! What happened? Instead of the motion being smoother, it speeded up like crazy! Of course, when you think about it, we've raised the frame rate to 120 fps so the animation will now take 24 frames / 120 fps = 0.2 seconds! So if you were asked to change the frame rate of the movie but needed to keep the length of the animation at 2 seconds, what would you do? Simple: You would need to lengthen the tween to 240 frames (240 frames / 120 fps = 2 seconds.) Are you beginning to see the drawbacks of frame-based animation? For one thing, we'd have to have a huge timeline. For another, imagine for a second that you had not one but one hundred movie clips, each with its own tweened animation and you discovered for one reason or other that you needed to change the frame rate of the movie: It would be absolute hell!

Let's be very optimistic and say for a moment that you don't believe you'll ever need to change the frame rate of your projects during development: What will you do about people with slower systems? If your movie is set to 30 fps but your user's computer can only handle 10fps, your animations are going to take three times as long to play back! A ten second animation timed for 30 fps will thus take 30 seconds! Of course what we want is for the animation to play back in ten seconds regardless of the speed of the user's computer. The only way to achieve this is to use time-based motion.

Time-Based Motion

Most people are afraid of Time-Based Motion because it involves ActionScript. In fact, any type of programmatic motion, by its very definition, requires ActionScript. Programmatic motion (using ActionScript to control the movement of movie clips) is nothing new. It was introduced into Flash with Flash 4 (until then, we could only use tweens.) Time-based motion goes beyond simple programmatic motion in that calculations must be made for each frame to calculate how long the last frame took to execute in real time. In case you're getting ready to dolt away from the computer after having read that last line, calm yourself: You won't have to do any of this. That's what the Move and MoveScript modules in the Moose Library are for. They will be handling all the ugly math and calculations so that you can concentrate on the really important business of whether the ball should animate to the other side of the Stage in two seconds or three!

The best way to get started is to jump right in. We're going to convert our previous ball animation so that it uses time-based motion instead of frame-based motion.

We're going to continue from where we left off in the last example:

  1. Since we won't be needing the tweened animation, let's get rid of it. To do this, first remove the tween by Right-Clicking (Ctrl-Clicking) on the first frame and selecting Remove Tween from the context-sensitive menu. (You can also set the Tween drop-down menu to None in the Property Inspector.)


  2. Next, let's remove frames 2 to 24. To do this, click on Frame 2 to select it, then, holding down the Shift key, click on the Keyframe on Frame 24 to select Frames 2 to 24. Right-Click (Ctrl-Click) on one of the selected frames and choose Remove Frames from the context-sensitive menu (you can also use Insert -> Remove Frames; Shift-F5.)

  3. All right, you should now have the ball by itself on Frame 1 of the movie. Let's go ahead and give the layer that its on a name: Double-click on "Layer 1" and rename it to ball.


  4. Next, we're going to give our ball an instance name so we can refer to it via ActionScript: Click on the movie clip of the ball to select it and, using the Property Inspector, give it the instance name ball_mc.


  5. We're done with our ball movie clip, so let's lock its layer to make sure we don't accidentally change any of its attributes. Click on the dot in Lock Layer column of the ball layer to lock it.
  6. Create a new layer using the Insert Layer button under the Timeline (or Insert -> Layer from the menu.)
  7. Call the new layer actions. Lock this layer also so we don't accidentally drop any movie clips into it (we won't be creating any other movie clips in this exercise but its a good habit to get into.)


  8. Now we get to the yummy ActionScript. You are going to be surprised at how little there will be. Let's start off by including the necessary Moose modules. Enter the following code in Frame 1 of the actions layer:

    #include "moose/init.as"
    #include "moose/frame_event.as"
    #include "moose/timer.as"
    #include "moose/chronos/init.as"
    #include "moose/move.as"
  9. The init.as module initializes the Moose Library framework and you need to include it before including any other module. The frame_event.as module contains a frame event broadcaster. This useful little object allows other objects to know when Flash has entered a new frame. If you're already familiar with using the old-style onClipEvent action or the onEnterFrame event handler, then you won't have trouble understanding it. The timer.as module and chronos/init.as contain time-based routines and the move.as module contains our time-based movement (animation) module. The beauty of using a library like this is that you don't need to care at all about the hundreds of lines of ActionScript that you've just loaded in using the #include directive. Instead, all you need to do is learn how to use the Move class.

    Add the following line of code to the script:

    new moose.Move(Math.noEase, this, "ball_mc", 2, {_x: 400}, true);
  10. Test the movie. The ball should move from one end of the Stage to the other in two seconds! Now tell me, how easy was that! One line of code! Let's dissect that line a little -- I know it might be looking a little cryptic right now but it's really very simple.

    What we're doing is creating a "move instance." You can think of move instances as animation segments. In our example, we ask the ball to move 400 pixels along the horizontal axis in two seconds. We articulate this request by first specifying the movement function to be used. If you're familiar with Robert Penner's easing equations then you're already familiar with the interface for our movement functions. The Move Module comes with the following Penner easing equations:

    Math.easeInQuad, Math.easeOutQuad

    In addition to these, I've added another movement equation called noEase which we're using here. Simply put, this movement function emulates the built-in Flash tweening (without any easing.) The best way to see the effects of the various movement functions is to use them in place of the noEase function in our one line of code. Try them all out in turn and note how they affect the speeding up and slowing down of the ball as it moves towards its target. Note that no matter which movement function you use, the animation will take exactly 2 seconds.
  11. Remember how, in our frame-based motion example, we weren't able to change the frame rate without affecting the duration of the animation. Let's try that again with our new time-based animation:

    Select Modify -> Document and set the Frame Rate to 120fps. Test the movie.
  12. Well, would you take a look at that! The animation still takes exactly 2 seconds and is much smoother.

All right, so now you know how to create time-based animation. But how exactly does the Move class work? What else can we do with it? That's what we'll be tackling in the next section.

The Move Module Explained

In the previous example, we saw how one line of code was enough to create a time-based scripted animation, albeit a simple one. We still have no idea, however, of the full power of the Move Module. In this section we will go into detail about the interface of the Move class and show the various ways in which we can create Move instances.

To start with, here's the full method signature for the Move class constructor, showing you all the parameters that it accepts. Don't let it scare you. I will be explaining the purpose of each parameter individually and you will see that there is an alternate way to create Move instances (using the setter methods of the Move class) where you don't have to memorize the order of the parameters.

new moose.Move ( movementFn, theTimeline, theClip, theDuration, deltaProps, run, nextMove, updateObj, updateFunc, updatePara, callbackObj, callbackFunc, callbackPara, async)

Parameters A Plenty

Here is a detailed explanation of each of the parameters for the Move class constructor:

  • movementFn is a reference to the movement function to use for this move instance. As mentioned in the previous example, movement functions use the Robert Penner easing equation interface. Moose currently contains the regular Penner easing equations as well as a noEase function. You can, however, create your own equations for different types of motion and plug them in.
  • theTimeline and theClip together define the movie clip or object that is to be manipulated by the Move instance. In our previous example, these pointed to our ball movie clip instance. theTimeline is a reference to the timeline that a movie clip is on and theClip is a string that holds the name of the movie clip.
  • theDuration is the duration you want the move to take, in seconds.
  • deltaProps is an object that contains the movie clip properties you wish to affect. In our previous example, there was only one delta property, _x (the horizontal coordinate of the clip). By passing 400 for the value of _x, we told the move instance that it should vary the current _x coordinate of the clip by 400. Since values on the horizontal axis increase from left to right, this has the effect of moving the clip horizontally towards the right by 400 pixels. If we wanted to move it to the left instead, we would pass -400 for the value of _x. This is where the "delta" comes from. Delta, in mathematics, stands for "change in". Thus, we are describing the magnitude of change in the value of a movie clip property. What's really great is that we are not limited to changing the _x and _y coordinates. We can affect any movie clip parameter over time, including the _xscale and _yscale, _width, _height and, in a round-about way that we shall see later, even alter its color.
  • run is a boolean (true/false) value that determines whether or not the move instance should start animating immediately. It is false by default. To run a move instance manually, you call its startMove() method, which we shall see a little later.
  • nextMove is a reference to a (different) Move instance. If it exists, it determines the move that should be started after the current move is complete (if the async parameter, see below, is true, then the nextMove Move instance will be started at the same time as the current move, without waiting for it to complete.) With this parameter, the Move class allows you to string together Move instances to create more complex scripted animations. As we shall see later, we have another module, called MoveScript, that will make this even easier.
  • updateObj, updateFunc and updatePara define a function to be called while the animation is taking place. We will see how they are used later on in the scripted color tween example.
  • callbackObj, callbackFunc and callbackPara define the callback function -- the function that gets called after the current move instance is complete.
  • async is a boolean value that determines whether or not the current move instance executes asynchronously. By default, move instances execute synchronously. That is, the nextMove move instance, if it exists, is called after the current move is complete. By setting async to true, you force the nextMove Move instance to be called at the start of the current move. Use this if you want two or more Move instances to execute simultaneously.

An Easier Way

Whew! I know what you're thinking: Too many parameters... How will we ever remember them all, much less their order? The good news is that you don't have to. Although it is entirely possible to create move instances by supplying all parameters to the class constructor, there is an easier way: You can use the various setter methods that the Move class has to set the same parameters. These setter methods are merely functions that set the values of the parameters listed above. Using them may make your code longer but it will be easier to decipher, debug and maintain. So, without further ado, let me introduce you to the setter methods:

  • setMovementFn ( movementFn ) : defines the movement function to be used
  • setTarget ( theTimeline, theClip ) : defines the movie clip to target for the move
  • setDuration ( theDuration ) : sets the duration of the move
  • setDeltaProps ( deltaProps ) : sets the delta properties object
  • setNextMove ( nextMove ) : defines the next move
  • setUpdateFn ( updateObj, updateFunc, updatePara ) : defines the update function
  • setCallbackFn ( callbackObj, callbackFunc, callbackPara ) : defines the callback function
  • setAsync ( async ) : sets the async flag

As you can see, the setter functions conveniently break up the parameter definitions into managable chunks. They can also be called in any order so the order of the parameter definition is not important.

In addition to the setter methods, the Move class has a few other methods that will help you in creating your animations. These are:

  • addDeltaProp ( deltaProp, value ) : instead of creating the delta properties object yourself and passing it to the move instance, you can use this method to add delta properties one at a time. For example, if we wanted to move a movie clip to the right by 200 pixels and downwards by 150 pixels, we could call addDeltaProp twice like this:

    myMove = new moose.Move();               // create a new move instance
    myMove.setTarget(_root, "myClip_mc");    // set the target movie clip
    myMove.setDuration( 1.5 );               // set the duration of the move at 1.5 seconds
    myMove.addDeltaProp ( "_x", 200 );       // change the x coordinate by 200
    myMove.addDeltaProp ( "_y", 150 );       // change the y coordinate by 150
  • startMove () : unless you pass true for the run parameter while creating a Move instance, your Move instance will not automatically execute. In order to run it, you have to call the startMove method. This method does not take any arguments.
  • stopMove () : you can, at any moment, stop an executing Move instance. To do this, call its stopMove method.
  • isRunning () : you can check to see if a move instance is currently running by calling its isRunning method. If the instance is currently running, the method will return true. Otherwise, it will return false.

Well, that's all there is to the Move module. Now that you know its interface, let's take a look at some other examples of how it can be put to use.

You Can Move Anything!

Move instances are not limited to changing the X and Y coordinates of movie clips. In fact, their true power comes from the fact that they can affect any movie clip property over time. This includes the clip's scale, dimensions and, as we shall see later, even its tint. In this example, we're going to use a Move instance to make a clip scale up over time.

  1. Let's begin where we left off in the previous example. We're going to begin by making the Stage larger to give our ball room to scale. To do this, Select Modify -> Document and set the Dimensions to 500 x 300.
  2. Next, select the ball_mc movie clip instance and drag it to the middle of the Stage (unlock the ball layer if you need to.) When you're done, your stage should resemble the one below.


  3. Now we need to modify the action in Frame 1 of the actions layer. Instead of affecting the _x property of the movie clip, as we did previously, we're now going to affect the _xscale (horizontal scale) and _yscale (vertical scale) properties. Additionally, instead of passing a delta properties object, we're going to add each delta property separately to make our code easier to understand, debug and maintain. Modify the script in Frame 1 of the actions layer using the listing below (the modified and newly added lines are shown in boldface.)

    #include "moose/init.as"
    #include "moose/frame_event.as"
    #include "moose/timer.as"
    #include "moose/chronos/init.as"
    #include "moose/move.as"

    scaleMove = new moose.Move();             // create new move instance
    scaleMove.setMovementFn(Math.noEase);     // set the movement function: no easing
    scaleMove.setTarget(this, "ball_mc");     // set the target movie clip
    scaleMove.setDuration(2);                 // set the move duration
    scaleMove.addDeltaProp("_xscale", 200);   // add property to animate (horizontal scale)
    scaleMove.addDeltaProp("_yscale", 200);   // add property to animate (vertical scale)
    scaleMove.startMove();                    // run the move

  4. Test the movie. You should see the circle scale up from its original size to twice its size in two seconds.

Now you see me...

  1. Another movie clip property that you can easily animate is a movie clip's alpha (transparency.) Let's say that we wanted our ball to fade out as it scales to twice its size. Achieving this is as easy as adding a new delta property to our previous code, this time to affect the _alpha property of the clip. Modify the script in Frame 1 of the actions layer to add the line shown below in boldface:

    #include "moose/init.as"
    #include "moose/frame_event.as"
    #include "moose/timer.as"
    #include "moose/chronos/init.as"
    #include "moose/move.as"

    scaleMove = new moose.Move();             // create new move instance
    scaleMove.setMovementFn(Math.noEase);     // set the movement function: no easing
    scaleMove.setTarget(this, "ball_mc");     // set the target movie clip
    scaleMove.setDuration(2);                 // set the move duration
    scaleMove.addDeltaProp("_xscale", 200);   // add property to animate (horizontal scale)
    scaleMove.addDeltaProp("_yscale", 200);   // add property to animate (vertical scale)

    scaleMove.addDeltaProp("_alpha", -100);   // add property to animate (alpha)


    scaleMove.startMove();                    // run the move

  2. Test the movie. The circle should now fade out as it scales. Easy, isn't it?

Synchronous motion

All right, we've seen how we can animate any movie clip property over time using the Move class. That's great but what if we want to have more complex animations where, say, we have multiple balls and we want one ball to start moving after another one has stopped. (Or, we may have a single ball that we want to first go to the right edge of the screen, then return to the left edge.) We can achive this by stringing together Move instances using their nextMove parameters. Here's an example to show you how:

  1. We're going to modify our example by making two copies of our ball. To do this, select the ball_mc movie clip instance on the Stage and choose Edit -> Duplicate (Ctrl-D; Cmd-D).
  2. Repeat this once more so that you end up with three balls total.
  3. Arrange the balls one under another along the left edge of the screen as shown below.


  4. Select the middle ball and rename its instance name to ball2_mc using the Property Inspector.


  5. Similarly, select the last ball and rename it to ball3_mc.
  6. Next modify the code in Frame 1 of the Actions layer so that it matches the listing below.

    #include "moose/init.as"
    #include "moose/frame_event.as"
    #include "moose/timer.as"
    #include "moose/chronos/init.as"
    #include "moose/move.as"

    ball3Move = new moose.Move(Math.noEase);  // create new move instance
    ball3Move.setTarget(this, "ball3_mc");    // set the target movie clip
    ball3Move.setDuration(2);                 // set the move duration
    ball3Move.addDeltaProp("_x", 400);        // add property to animate (horizontal coordinate)

    ball2Move = new moose.Move(Math.noEase);  // create new move instance
    ball2Move.setTarget(this, "ball2_mc");    // set the target movie clip
    ball2Move.setDuration(2);                 // set the move duration
    ball2Move.addDeltaProp("_x", 400);        // add property to animate (horizontal coordinate)
    ball2Move.nextMove = ball3Move;           // move to run after this one

    ball1Move = new moose.Move(Math.noEase);  // create new move instance
    ball1Move.setTarget(this, "ball_mc");     // set the target movie clip
    ball1Move.setDuration(2);                 // set the move duration
    ball1Move.addDeltaProp("_x", 400);        // add property to animate (horizontal coordinate)
    ball1Move.nextMove = ball2Move;           // move to run after this one

    ball1Move.startMove();                    // run the first move

  7. Test the movie. Notice how the top ball starts moving and when it reaches the right edge of the stage, the middle ball starts moving and so on. We refer to this in the Move module as synchronous motion since one move has to end before the next one begins. To see how we specified this to happen, take a look at the order in which we created our move instances and pay special attention to the nextMove parameter.

    You should notice right away that we are creating our Move instances in reverse order. That is, we create the Move instance for the last ball to move, first. Then we create the Move instance for the middle ball (the second ball to move) and lastly, we create the move instance for the first ball to move (the top one.) We need to do things this way since the third Move instance needs to be in existence before we can specify it as the nextMove for the second Move instance. Likewise, the second Move instance must exist before we can specify it as the nextMove for the first Move instance. If you're thinking that this is all very counter-intuitive, then you're right! That is why, in addition to the Move module, I created the MoveScript module which allows us to create our moves in whatever order we like and add them to a MoveScript instance. We shall see how to do this a little later but first, let's take a look at another way of stringing together Move instances, this time asynchronously.

Asynchronous Motion

We now know how to string together Move instances to create synchronous motion. Well, what if we want two Move instances to start running at the same time? We could simply create them, one after the other and call their startMove() methods but this way of doing things is pretty limited. Take our three-ball movie, for example: What if we wanted the second and third balls to start animating at the same time after the first ball reaches the right edge of the screen? The solution is as simple as ever and requires the use of the async parameter.

  1. Modify the script in Frame 1 of the Actions layer to add the line of code shown in boldface:

    #include "moose/init.as"
    #include "moose/frame_event.as"
    #include "moose/timer.as"
    #include "moose/chronos/init.as"
    #include "moose/move.as"

    ball3Move = new moose.Move(Math.noEase);  // create new move instance
    ball3Move.setTarget(this, "ball3_mc");    // set the target movie clip
    ball3Move.setDuration(2);                 // set the move duration
    ball3Move.addDeltaProp("_x", 400);        // add property to animate (horizontal coordinate)

    ball2Move = new moose.Move(Math.noEase);  // create new move instance
    ball2Move.setTarget(this, "ball2_mc");    // set the target movie clip
    ball2Move.setDuration(2);                 // set the move duration
    ball2Move.addDeltaProp("_x", 400);        // add property to animate (horizontal coordinate)
    ball2Move.nextMove = ball3Move;           // move to run after this one

    ball2Move.async = true;              // make the ball2 move instance asynchronous

    ball1Move = new moose.Move(Math.noEase);  // create new move instance
    ball1Move.setTarget(this, "ball_mc");     // set the target movie clip
    ball1Move.setDuration(2);                 // set the move duration
    ball1Move.addDeltaProp("_x", 400);        // add property to animate (horizontal coordinate)
    ball1Move.nextMove = ball2Move;           // move to run after this one

    ball1Move.startMove();                    // run the first move

  2. Test the movie. When the first ball reaches the right edge of the screen, the second and third balls will start to move together. This is because we have set the second ball's move instance to be asynchronous. That is, it will start running its nextMove instance (the third Move instance) without waiting for its own move to end.

Introducing MoveScript

All right, so we can string together Move instances to create complicated time-based scripted animations. What use is it, really, if we have to put up with a counter-intuitive process where we have to create our Move instances in reverse order. It may be bearable for small examples like the two we just covered but it can easily become a major pain for more complicated, real-world scripted animations. To overcome this limitation, I created yet another Moose Module called MoveScript!

Let's jump right in and see how the MoveScript module can simplify our lives in creating time-based scripted animations.

  1. Modify the script in Frame 1 of the Actions layer as shown in the listing below.

    #include "moose/init.as"
    #include "moose/frame_event.as"
    #include "moose/timer.as"
    #include "moose/chronos/init.as"
    #include "moose/move.as"

    #include "moose/move_script.as"

    ball1Move = new moose.Move(Math.noEase);  // create new move instance
    ball1Move.setTarget(this, "ball_mc");     // set the target movie clip
    ball1Move.setDuration(2);                 // set the move duration
    ball1Move.addDeltaProp("_x", 400);        // add property to animate (horizontal coordinate)

    ball2Move = new moose.Move(Math.noEase);  // create new move instance
    ball2Move.setTarget(this, "ball2_mc");    // set the target movie clip
    ball2Move.setDuration(2);                 // set the move duration
    ball2Move.addDeltaProp("_x", 400);        // add property to animate (horizontal coordinate)
    ball2Move.async = true;

    ball3Move = new moose.Move(Math.noEase);  // create new move instance
    ball3Move.setTarget(this, "ball3_mc");    // set the target movie clip
    ball3Move.setDuration(2);                 // set the move duration
    ball3Move.addDeltaProp("_x", 400);        // add property to animate (horizontal coordinate)

    ballScript = new moose.MoveScript();      // create a new MoveScript instance
    ballScript.addMove( ball1Move );          // add the first ball move
    ballScript.addMove( ball2Move );          // add the second ball move
    ballScript.addMove( ball3Move );          // add the third ball move
    ballScript.runScript();                   // run the scripted animation

  2. Test the movie. You should notice that it runs exactly as it did before. So let's see what has changed.
  3. First of all, you should notice that we have included another file, moose/move_script.as. This is the file that contains the MoveScript module. Next, notice how we are creating our Move instances in the order that they will execute. This makes it much easier for us to visualize the animation and makes the process much less error-prone. Also, notice how we're not specifying the nextMove parameter for our Move instances. This is because the order of the Move instances is now controlled by our new MoveScript instance.

    Creating a MoveScript is a very straightforward affair: We first create the MoveScript instance and then we call its addMove method and pass our Move instances in the order in which we want them to execute. When all the Move instances have been added, we call its runScript method to begin executing the MoveScript.

Deeper Into MoveScript

There really isn't all that much more to the MoveScript module than what we've covered in the above example. The MoveScript class constructor has the following signature:

new moose.MoveScript ( initialMoves, run, loop )

In our previous exercise, we didn't pass it any parameters. If we had wanted to, we could have passed it an array containing the Move instances (initialMoves) and, instead of invoking its runScript() method, we could have passed true for the run parameter, which would have had the same effect. We could have, thus, replaced the five lines of code in the listing above with this one line:

ballScript = new moose.MoveScript( [ball1Move, ball2Move, ball3Move], true);

The last parameter, loop, decides whether or not the MoveScript should loop continuously. The loop parameter can also be set by calling a MoveScript instance's setLoop() method with a value of either true or false. By default, MoveScripts are set to not loop.

Aside from the runScript() and setLoop() methods, MoveScript instances have two other public methods. These are:

  • getCurrentMove() which returns a reference to the currently executing Move instance in the MoveScript and
  • stopScript() which halts the script (especially useful if your script is set to loop.)

A Final Example: Color Transformations

Well, we've covered pretty much all there is to the Move and MoveScript modules. Together, they allow you an easy and powerful way to create scripted time-based animations in Flash MX. This final example will showcase a more complicated use to show you how Move instances are not limited to modifying movie clip properties but can affect any property of any object. In this example, we will use a Move instance to transform the color of a bitmap.

  1. Open up the file color_transformation.fla from the source files and take a look at how it is structured. As you can see, we have three layers: actions, lizard and bg. The lizard layer contains a movie clip of our lizard, called lizard_mc and our bg layer contains the original image of the lizard on the red pepper.


  2. Click on Frame 1 of the actions layer to see the script:

    #include "moose/init.as"
    #include "moose/frame_event.as"
    #include "moose/timer.as"
    #include "moose/chronos/init.as"
    #include "moose/move.as"

    // create new instance of the Color class
    this.a_color = new Color(lizard_mc);

    // create a new color transformation object
    this.a_color.colorObj = new Object();

    // update function
    this.a_color.colorObj.rbChanged = function ( anim, obj ){
        obj.setTransform(obj.colorObj);
    }

    // create the color animation
    var colorChange = new Moose.Move();
    colorChange.setMovementFn(Math.easeOutQuad);
    colorChange.setTarget(this.a_color, "colorObj"); // our target is the color transformation object
    colorchange.setDuration(30);
    colorChange.addDeltaProp("rb", 255); // increase red
    colorChange.addDeltaProp("bb", -25); // reduce blue
    colorChange.addDeltaProp("gb", -75); // reduce green
    colorChange.setUpdateFn(this.a_color.colorObj, "rbChanged", this.a_color);
    colorChange.startMove();

  3. Well, this is definitely a bit more complicated than what we had seen before. The main difference here is that we are using a Move instance to affect object properties instead of movie clip properties. Also, due to the roundabout way that Flash affords us for applying color transformations, we need to use the update function to apply our color transformation object to our lizard movie clip each time the red, green and blue values are changed. Before we start dissecting the code, take a moment to test the movie and see the effect. Ever so slowly, the color of the lizard will change from green towards red.
  4. So let's dive into the code then, shall we? First off, we start by creating a Color instance called a_color. Next, we create a color transformation object called colorObj as a property of the a_color object. The rbChanged function is used to apply the transformation object to the color object -- a roundabout way of doing things that results in the color of the movie clip being affected. (Don't blame me for this, it's the way that Flash handles color transformations for movie clips that is unnecessarily confusing!) Notice how the rbChanged function takes two arguments: anim and obj. Since rbChanged is going to be called automatically by the Move instace, the first argument it receives is a reference to the Move instance that is calling it. (In this case, we ignore this first argument.) The second argument is the one that we pass to it manually, with a reference to the color object.

    Now let's look at the code to create the color transformation itself. Notice that our target clip this time is not a movie clip but rather an object (the color transformation object.) Instead of animating movie clip parameters, we are animating the red, green and blue component offsets (rb, gb, bb), which are properties of this object.

    Note how we set the update function to call the rbChanged method on the color tranformation object. This is necessary since updating the color offsets in the color transformation object does not automatically apply them to the movie clip's color object. To do this, we need to call the setTransform method of the color object and this is exactly what the rbChanged method does.

    Finally, we run the Move instance by calling its startMove() method.

Well, there we are. This last example demonstrated perhaps the most complicated use of the Move module you could hope to encounter (although I'm sure that given enough time people will find yet other weird and wonderful uses for it!)

We've achived quite a bit in this tutorial. For one thing, you have now been introduced to the Moose ActionScript Library in what is the first of twelve planned tutorials. You've also learned how to create time-based scripted motion quickly and easily using the Move and MoveScript modules. In part two of this tutorial, we will learn to use the DoIn, DoEvery, AnimGif (animated GIF) and FPS (frames per second) modules. Using these modules, you will be able to easily create frame-based animations that play back independently of the movie's main frame rate.


 
©2003 Ultrashock.com - All rights reserved