« Why I have not posted for a while | Main | AS2.0 development gotchas »

Designing motion graphics pt3: Code

The first part in this piece introduced the high level design concepts in motion graphics. The second part introduced a metaphor for code driven motion. In this part, we will look at how to convert the metaphor into code.

All the files used in this entry are available as a zip file here (Flash MX 2004, approx 30k). I have written the code in untyped ActionScript so that Flash MX users can follow it. Flash MX 2004/Flash 8 users should not feel too left out as I will be discussing an AS2.0 class based implementation in the next post.

It is easy to use ActionScript to create movement. You simply attach an onEnterFrame event handler to a movie clip, ensuring that the handler varies one or more property of the movie clip over time. The simplest example of such an onEnterFrame applied to a movie clip, myMC, would be

1  myMC.onEnterFrame = function(){
2    this._x += 5;
3  }

Assuming you have a movie clip called myMC on the first frame of the main timeline, and the above script is attached to frame 1 of the main timeline, you will see myMC move left-right across the stage. Why? Because the onEnterFrame increases the _x property of myMC by 5 per frame, causing myMC to move 5 units to the right every frame. By changing the script to vary a different property, we can change the nature of the animation. For example, changing line 2 to the following will fade out the clip rather than move it:

2   this._alpha -= 2;

The code above explains the concept of scripted motion (i.e changing a movie clip’s property per frame creates animation) but it is not really an example of useful animation for the following reasons:

  • There is no way to define when the animation starts or stops
  • There is no way to make the movie clip, myMC end up at a particular place.
  • The animation is linear, leaving the clip with no sense of mass. A better animation would include acceleration (or 'easing' if you are an animator).

In the previous entry in this piece, I ended by introducing the following 4 points as the basis for useful motion graphics code;

  1. We need an animation trigger. The trigger is the animation start event.
  2. We need an animation target. The target defines the animation's goal. The target is similar to the end keyframe in a tween; it defines the end point of the animation. It should be noted that a scripted target is very different from a tween keyframe though. A scripted target can change during the course of an animation. Further, by changing the target every time the same animation is run, we can make the animation look different every time.
  3. We need to define code that animates the clip moving towards the target. This code defines the animated transition.
  4. We need a halt condition. The halt condition simply detects when we are at the target and stops the animation when this occurs.

I call this system target driven motion. The simplest example of target driven motion would be one that moves a movie clip to a position (x, y) on the stage. Let’s see how we would write this code via a worked example. We will make a movie clip move towards a user-selected point.

Create a new Flash document and set the frame rate to 24 fps (Modify > Document or double click the frame rate below the timeline to access the frame rate).

On the stage, draw a small circle and convert it to a movie clip called ball. Give the instance on the stage an instance name of clip.

Add a new layer called actions, placing this above the current layer. For the busy/lazy, the file targetDrivenMotion01.fla includes all steps to this point

Flash workspace

The first thing we need to define is the trigger. In the simplest case, the trigger is an event*. We will make the animation start whenever we click on the stage. Select frame 1 of layer actions, press F9 to bring up the actions panel and enter the following code

clip.onMouseDown = setTarget;

Whenever you click, function setTarget() will run.

* In more complex code, the trigger can also be a condition. For example, in a video game, you might want the enemy alien to fire its laser gun at the player’s ship when the condition ‘alien is pointing at the player’ occurs. Thus, this condition is the trigger for the laser. Incidentally, the halt condition for the laser would be ‘I am off the screen or I have hit the player’.

Next, we need to write function setTarget(). Add this function above the current line as shown

 1 function setTarget() {
 2   this.targetX = _xmouse;
 3   this.targetY = _ymouse;
 4 }
 5 //
 6 clip.onMouseDown = setTarget;

The file targetDrivenMotion02.fla contains all steps to this point.

Function setTarget() sets our target. When we test this fla we will not yet see anything change onscreen but if we debug, we can see the target being set in the code. Do this by pressing Control+Shift+Enter (or select Control > Debug Movie), then click the continue icon (green ‘play’ icon at the top right) on the debugger window. In the top left pane of the debugger, select _level0.clip and in the middle left pane select the variables tab. Every time you click on the stage, you will see the values of targetX and targetY change.

Debugger panel

NB - if you have never used the debugger before, the middle left pane may be hidden behind the call stack. Drag the call stack pane down to reveal the middle pane.

Okay so now we have a trigger for our animation, and have captured our user-defined target position. The next step is to animate our movie clip to the target.

Add the following code (lines 4 and 7-13 are new, and targetDrivenMotion03.fla contains all steps to this point):

 1 function setTarget() {
 2   this.targetX = _xmouse;
 3   this.targetY = _ymouse;
 4   this.onEnterFrame = transition;
 5 }
 6 //
 7 function transition() {
 8   var deltaX = this._x-this.targetX;
 9   var deltaY = this._y-this.targetY;
10   trace("delta = ("+deltaX+","+deltaY+")");
11   this._x -= deltaX/3;
12   this._y -= deltaY/3;
13 }
14 //
15 clip.onMouseDown = setTarget;

Function setTarget() now calls function transition() as an onEnterFrame. The new function transition() animates the movie clip towards the target position.

How does transition() do this? Well, it uses the common Flash ‘inertia effect’; every frame, the code looks at how far the target is (deltaX, deltaY) and moves 1/3 of this distance towards the target.

Lines 8 and 9 define deltaX and deltaY. Lines 11 and 12 move the clip 1/3 of the distance towards the target position (targetX, targetY).

Relationship between deltaX, deltaY and targetX, targetY

If you test the movie, you will see that the clip moves to the last position you click, and then stops. Well, that’s what it seems to do, but the trace at line 10 tells us a different story. The output panel will continiously fill with trace values because the clip is constantly moving, even though the animation seems to stop after a second or so. The clip seems to stop because the distance it travels per frame gets smaller every frame; so small that we no longer see a movement, but the code is still creating animation.

Our animation never stops because we have not put in a halt condition. It’s important to realize that although the animation appears to stop, this doesn’t mean that the code stops executing. If every animation in a site never stopped once started, the Flash player would slow down, resulting in sluggish performance. Many designers complain about the slow speed of the Flash player, when in fact the real problem may be caused by motion graphics code that never stops when it is no longer needed!

The halt condition is almost always an if() condition that checks whether we have reached the target position.

Add the following code (lines 13-17) to the function transition() to add out halt condition (or look at targetDrivenMotion04.fla:

 7 function transition() {
 8   var deltaX = this._x-this.targetX;
 9   var deltaY = this._y-this.targetY;
10   trace("delta = ("+deltaX+","+deltaY+")");
11   this._x -= deltaX/3;
12   this._y -= deltaY/3;
13   if ((Math.abs(deltaX)<1) && (Math.abs(deltaY)<1)) {
14     this._x = targetX;
15     this._y = targetY;
16     delete this.onEnterFrame;
17   }
18 }

The if() checks for the clip being within one pixel of the target position (in both x and y). When this occurs, the code moves the clip to the target position (lines 14, 15) and stops the animation (line 16). When line 16 executes, we delete the onEnterFrame, and no code is running.

The trace at line 10 is starting to get a little intrusive, so delete it.

Our full listing now contains the basic code for usable and efficient motion graphics. Here it is in its entirety:

 1 function setTarget() {
 2   this.targetX = _xmouse;
 3   this.targetY = _ymouse;
 4   this.onEnterFrame = transition;
 5 }
 6 //
 7 function transition() {
 8   var deltaX = this._x-this.targetX;
 9   var deltaY = this._y-this.targetY;
10   this._x -= deltaX/3;
11   this._y -= deltaY/3;
12   if ((Math.abs(deltaX)<1) && (Math.abs(deltaY)<1)) {
13     this._x = targetX;
14     this._y = targetY;
15     delete this.onEnterFrame;
16   }
17 }
18 //
19 clip.onMouseDown = setTarget;

The following version of the code has liberally added trace() actions to show what is actually happening (lines 2, 5, 10, 16). See also targetDrivenMotion05.fla.

 1 function setTarget() {
 2   trace("\n\nSetting target...");
 3   this.targetX = _xmouse;
 4   this.targetY = _ymouse;
 5   trace("Starting animation towards target...");
 6   this.onEnterFrame = transition;
 7 }
 8 //
 9 function transition() {
10   trace("Animating.");
11   var deltaX = this._x-this.targetX;
12   var deltaY = this._y-this.targetY;
13   this._x -= deltaX/3;
14   this._y -= deltaY/3;
15   if ((Math.abs(deltaX)<1) && (Math.abs(deltaY)<1)) {
16     trace("Reached target.")
17     this._x = targetX;
18     this._y = targetY;
19     trace("Stopping animation.");
20     delete this.onEnterFrame;
21   }
22 }
23 //
24 clip.onMouseDown = setTarget;

If you test this version of the code, you will see the different stages of the animation listed in realtime via the output panel:

Flash output panel

The first trace, Setting target... shows the code setting the target position, and this occurs as soon as you click. The code then sets the onEnterFrame that will control the motion to the target (Starting animation towards target...). The animation runs over a number of frames (the exact number is shown by the number of times you see Animating.). Finally, the halt condition detects that the target has been reached (Reached target.) and the animation is halted (Stopping animation).


The animation has a definite start and stop point, and the code only runs for as long as the animation occurs. More importantly, we can easily change this code to create different transitions:



  • If we want a different type of motion instead of the intertia effect, we change function transition()

  • If we want to change the trigger event, we simply change line 19

  • If we want to change the conditional at line 12


So are there any good examples of this sort of code? Well, a better way of looking at the question would be to try to find ActionScript based Flash sites that don't use something like this code! Try deconstructing a few cutting edge sites and see all the places where target driven code is used.


Whilst writing this entry, it occurred to me that the code is a good candidate to be converted to classes, and I'll be looking at how to do this in the next post.


The content in this article is futher expanded in chapter 7 of the Book Flash 8 Savvy (Sybex), chapter 7, and a forthcoming Flash 9 book I am writing with Adam Phillips (O'Reilly).

Posted by motiongraphics on October 2, 2006 02:28 PM

Comments
Comment by Andy

"Flash MX 2004/Flash 8 users should not feel too left out as I will be discussing an AS2.0 class based implementation in the next post."

...just wondering when that next post is going to be :)?

Posted on June 27, 2007 12:18 PM

Post a comment




Remember Me?

(you may use HTML tags for style)