Back to Entry Page

Scripted Drawing

Click to run the movie. Drag the hand to draw.

Flash MX introduces an interesting new visual feature: the ability to script Movie Clips to draw lines and geometrical shapes. This page contains two demonstrations of the technique.

I. Disembodied hand

In the project at right a line is drawn following the cursor. The image of a hand and a paintbrush were added for effect.

The scripting involved in this demonstration is not especially complicated. The architecture contains a few tricks, however.

First of all, the recommended way of setting up a Movie Clip as a drawing space involves the createEmptyMovieClip() method. This method is a cousin to attachMovieClip() and loadMovie(). Instead of bringing in a pre-existing Movie Clip, however, createEmptyMovieClip() does what the name suggests: it creates a new Movie Clip with a specified name at a specified layer.

Here's how the method is used in our project. We script the first frame of the first layer of the main movie as follows:

_root.createEmptyMovieClip("pen", 1);
pen.lineStyle(5,0x333333,75);
loadMovieNum("drawingObject.swf", 2);

The first two lines of this script set up the drawing space. It's a new (blank) Movie Clip called pen loaded into Layer 1. (Remember the main movie is in Layer 0).

Any clip created in this way has two properties automatically: its width and height match those of the main movie and it's aligned at x=0, y=0, or the upper left corner of the screen. In other words, using createEmptyMovieClip() creates a blank overlay in another level atop the main movie. It's in this space that all drawing will happen.

The second line of the script sets some properties for drawing. The lineStyle() method sets width, color, and opacity (alpha) for any line drawn in the drawing space. Notice the odd syntax of the second term. This is actually a plain old hexadecimal color value, which we'd write in HTML or JavaScript as #333333. For some reason ActionScript doesn't like the hash mark (#) and uses instead the string 0x.

The third line of the script loads a separate Flash movie called drawingObject.swf into Layer 2. We have to use loadMovieNum() here in order to place the drawing object--the hand and paintbrush--in a layer above the drawing space. If it were placed in the main movie (Level 0) it would sit underneath the lines being drawn, which is fine if you're M.C. Escher.

The rest of the work is done by handlers placed on the hand-and-brush object, which is a Movie Clip residing within the movie drawingObject.swf. Here's the lot:

onClipEvent(mouseDown){
  this.startDrag(true);
}

onClipEvent(mouseUp){
  this.stopDrag();
}

onClipEvent(load){
  _level0.pen.moveTo(this._x, this._y)
}

onClipEvent(mouseMove){
  if(this.hitTest(_root._xmouse, _root._ymouse)){ 
    _level0.pen.lineTo(this._x, this._y);
    }
}

The first two handlers, mouseUp and mouseDown, do the familiar business of making the drawing object draggable. The load handler sets the drawing point of pen, the Movie Clip that was created at Level 1, to the current coordinates the drawing object in Level 2. If we don't include this statement, a line will be drawn disconcertingly from the upper left corner of the screen to the drawing object the first time we click on it.

There's something interesting (and bothersome) about that moveTo() method. Note that it's attached to the pen Movie Clip at Level 0--not at Level 1, as we might expect. This is a regrettable bit of engineering on Macromedia's part. Though the drawing clip is created at Level 1, it's created by the main movie clip, or the root object, which always lives at Level 0. The syntax you see here doesn't make much sense but it works (and I know from experiment that various alternatives don't).

Click to run the movie. Click the "erase" button to clear and start over.

The mouseMove handler does the most visually interesting work. If hitTest with the mouse position proves true (as it must if the drawing object is being dragged) then the Movie Clip pen draws a line from wherever its virtual drawing point was last located to the present location of the drawing object. Voila, a virtual drawing tool.

II. Proliferating shapes

In this second demonstration we use the scripted drawing object do draw filled shapes, not just a simple line.

As in the first example, the first frame of the main movie contains a script that creates the drawing object:

_root.createEmptyMovieClip("pen", 1); pen.lineStyle(3,0xCCCCCC,50); loadMovieNum("eraseButton.swf",2);

As you can see, I've varied the lineStyle() method a little to create a fine, gray line with 50% transparency. These settings apply only to the outlines, by the way. The colors that fill the triangles are determined elsewhere.

Once again we load an object into Level 2, this time the erase button that allows us to reset the display. Obviously this button needs to be on a higher level so it will remain visible.

As in the first example the rest of the action is attached to particular Movie Clip handlers. Since there's no visible object associated with the drawing this time, that object is a scripting dummy (my personal term for it) a simple orange dot set up in a second layer (not Level) of the main movie and located just off the upper left edge of the visible stage. Here are the scripts attached to the dummy:

onClipEvent(load){
	
  colors = new Array(0xFF0000, 0x00FF00, 0x0000FF, 
  0xFFFF00, 0x00FFFF, 0xFF00FF, 0xCCCCCC);	
}

onClipEvent(enterFrame){
	
  startX = Math.round(Math.random()*350);
  startY = Math.round(Math.random()*350);
  sideLength = 10+Math.round(Math.random()*30);
  whichColor = colors[Math.floor(Math.random()*colors.length)];
  whichAlpha = 10+Math.round(Math.random()*70);
  _root.pen.beginFill(whichColor, whichAlpha);
  //draw triangle
  _root.pen.moveTo(startX, startY);
  _root.pen.lineTo(startX+sideLength, startY+sideLength);
  _root.pen.lineTo(startX+(sideLength*2), startY);
  _root.pen.lineTo(startX, startY);
  _root.pen.endFill();	
	
}

The load handler sets up an array containing a set of colors, again using that odd "0x" prefix instead of the usual hash mark.

The enterFrame handler draws and fills an equilateral triangle.

The two lines of the script choose a random starting point for the top vertex of the triangle, somewhere within a 350x350 square. (The screen is 400x400 but we want to prevent triangles being drawn entirely offscreen.)

The next line sets the length value for each side of the triangle. (In equilaterals all three sides have the same length.) The line after that chooses a color at random from the colors array, and the line after that sets the variable whichAlpha to a number between 10 and 80.

The sixth line of the script, containing the beginFill() method, plugs in the color and alpha values to specify a fill color and alpha strength. Note that here we address the drawing object pen as _root.pen, not _level0.pen as we did in the first example. In this case we're scripting an object that is attached directly to the main movie (our scripting dummy), so even though pen sits at Level 1, we invoke it via the root. Makes sense, no? No. But the code does work this way and doesn't otherwise. Thanks, Macromedia!

The lines following the comment "//draw triangle" do the main geometrical business. As you can see, it's a matter of moving the virtual pen point from one position to another. First we go down and to the right for the distance specified in sideLength. Then we continue to the right but come back up to reach a point that is parallel to our start point but two side lengths distant. Finally we connect from the second point to our original starting point. The result is an equilateral triangle.

The endFill() method applied at the end of all this does what the name suggests, turning off the color fill.

There's one last detail to explain here, namely how the erase button works. Here's the script attached to the button in its own, separate movie:

on(release){
  _level0.createEmptyMovieClip("pen", 1);
  _level0.pen.lineStyle(3,0xCCCCCC,50);
}

As you can see, the drawing object is erased by creating a new, blank drawing object in its place. Once again we have to go through Level 0 to affect Level 1, but here perhaps the logic will seem less twisted. Recall that we originally created the drawing object in Level 1 by scripting commands in the main movie (Level 0). Here our button in Level 2 reaches back down to Level 0 in order to regenerate the drawing clip in Level 1.

Whatever you do, don't tell the Internal Revenue Service about ActionScript.

III. Source files

The source file for the first example is called handDrawing.fla. Source for the second example is triangles.fla. Both are available in MMShare/drawing on Crow. You'll also find drawingObject.fla and eraseButton.fla, source files for the subsidiary movies included in each project.


University of Baltimore Logo

Copyright © 2002 School of Information Arts and Technologies