Connect the Dots: Create Rubber Bands with Flash’s Drawing API

ecently, I received this email:

Rich: Dug your story on filters. The drop shadow following the mouse as a light source was great. Maybe you can help me with this. How can I connect two MovieClips with a line between them, wherever they go? Thanks, Carlo.

Thanks for writing, Carlo. Actually, this is a lot easier than you may think, thanks to the Flash Drawing API. You don’t have to try to position pre-created MovieClips containing lines or anything like that. You can actually draw a line between two points using ActionScript.

The Drawing API has been around for a while, so I’d like to add a little fun to the topic. I’ll show you how to connect MovieClips with a straight line, then I’ll show you how to update those connections while the MovieClips move, and finally, I’ll show you how to connect the MovieClips with simulated rubber bands. Here’s an example.

First, take a quick peek at the results of this exercise. Figure 1 shows the traditional connections using rigid lines. The dotted line represents the left of the stage. In this example, when the circle collides with the left of the stage it bounces off the wall and (other than updating their positions) the lines are otherwise unaffected.


Figure 1. Collision: This shows the use of the Flash Drawing API lineTo() method to connect MovieClips with straight lines. When the left circle collides with the Stage edge, the circle reverses direction, but the lines remain straight.
 
Figure 2. Bounce: This figure simulates how using the curveTo() method can add a little bounce to your lines. When the circle collides with the wall, the curves are deformed as if they are made of rubber.

Figure 2 simulates an adapted version of the exercise, metaphorically connecting the MovieClips with flexible strings, rather than rigid dowels. This approach connects the clips with a curve instead of a line. When the circle collides with the wall it still bounces off, but the lines vibrate as if they were rubber bands.

Connecting the Dots
Before getting to the code, I’ll start with the basic principles behind drawing a line between two points.

  1. Create a canvas to draw into. (You can use the root as your canvas, but it’s a lot easier to work with the drawing if you dedicate a MovieClip for this purpose.)
  2. Define a style (appearance) for your line.
  3. Move to your first drawing point. If you don’t do this, the first line will draw from the origin of your canvas (upper left corner of the root, registration point of your MovieClip, etc.). In the case of the question posed, this would be the location of the first MovieClip.
  4. Draw the line to the second drawing point. Again, in this case, this would be the location of the second MovieClip.

Here’s a code example. If you copy and paste this, be sure to omit the line numbers. If you prefer to look at the source code accompanying this article, this code is in a file called “simple_line.fla.” To keep things concise, this code assumes two MovieClips, circle0 and circle1, are on stage.

1 this.createEmptyMovieClip("canvas_mc", 1);2 canvas_mc.lineStyle(1, 0x000099, 100);3 canvas_mc.moveTo(circle0._x, circle0._y);4 canvas_mc.lineTo(circle1._x, circle1._y);

Now look at the code relevant to the steps outlined previously:

  1. Line 1 creates a canvas to work with. It gives the MovieClip the name canvas_mc and creates it in level 1. Again, you can draw directly to the stage, but it is not as easy to work with the results of the drawing code later. If you want to draw directly to the stage, simply delete line 2, and replace canvas_mc with this, to refer to the root timeline.
  2. Line 2 defines the line style. In this case, it is a one-pixel line, dark blue, with an _alpha of 100% (opaque).
  3. Line 3 moves (without drawing) to the location of the first MovieClip.
  4. Line 4 draws a straight line to the location of the second MovieClip.

Moving Targets
If you want to connect two or more MovieClips while they’re moving, all you have to do is continue to update the connection during the movement and clear the canvas with each update to make sure you don’t continue to see old lines.

Before moving on to the Drawing API code, I’ll get the basic MovieClip movement out of the way. The missing lines in this script will be filled in as you continue to read. (Again, if you copy and paste, be sure to omit line numbers and hard returns. Refer to the source code and look at “no_line.fla.”)

12  for (i = 0; i < 2; i++) {3    ref = this.attachMovie("circle", "circle"+i, 100+i);4    //5    ref._x = (Math.random() * (Stage.width – ref._width))     + (ref._width / 2);6    ref._y = (Math.random() * (Stage.height - ref._height))     + (ref._height / 2);7    //8    ref.dirX = (Math.random() * 10) - 5;9    ref.dirY = (Math.random() * 10) - 5;10   //11   ref.onEnterFrame = function() {12     this._x += this.dirX;13     this._y += this.dirY;14     //15     if ((this.dirX > 0 && this._x > Stage.width) || 16     (this.dirX < 0 && this._x < 0)) {17       this.dirX *= -1;1819     }20     if ((this.dirY > 0 && this._y > Stage.height) || 21     (this.dirY < 0 && this._y < 0)) {22       this.dirY *= -1;2324     }2526   };27 }

In this example, you'll be creating the circle MovieClips dynamically using attachMovie() to place a Library symbol on stage. Line 2 encloses the code in a loop so that everything is applied to each circle, and Line 3 adds each MovieClip to the stage.

Lines 5 and 6 position the circles randomly on stage. This is a common method. It chooses a random x value by picking from the available range of values that include the width of the stage minus the width of the MovieClip. Then, to ensure that the clip is not positioned at zero, the range is offset by half the width of the MovieClip. This way, the clip can never appear offstage. The same is then done with the y value, using the stage height and MovieClip height.

Lines 8 and 9 pick a random value between -5 and 5 to add to the x and y locations of each MovieClip during movement. These values are stored within the clips so each circle can move independently of the other.

Line 11 adds an enterFrame event handler to each MovieClip. Within it, Lines 12 and 13 update the current location of each circle by adding its unique offset. Performed repeatedly, this causes movement.

Lines 15 through 19 comprise a conditional statement that says, if the circle is moving to the right AND it's past stage right, OR if the circle is moving to the left AND it's past stage left, reverse its x direction. This causes the circle to bounce off the left and right edges of the stage. Lines 20 through 24 do the same considering the y coordinates and the stage top and bottom.

You now have free moving circles that bounce off the walls of the stage, and all that remains is to connect them.

Updating Connections
To connect the two circles all you have to do is add the Drawing API code previously discussed. Before line 2 in the previous script, create a canvas:

1   this.createEmptyMovieClip("canvas_mc", 1);

Then add an enterFrame event handler to update the canvas:

28  canvas_mc.onEnterFrame = function() {29    canvas_mc.clear();30    canvas_mc.lineStyle(1, 0x000099, 100);31    canvas_mc.moveTo(circle0._x, circle0._y);32    canvas_mc.lineTo(circle1._x, circle1._y);33  };

The only thing new here is the aforementioned clear() method to erase the previous lines upon every enterFrame so only the current location of the circles will be connected.

Now It's Time to Rubberize
To add some fun, and a tiny bit of realism, I'll show you how to bounce the connecting lines a bit after a collision, as if they were rubber bands. To accomplish this, all you have to do is draw a curve instead of a line between the two MovieClips and vary the curve structure each time a circle changes direction. First, it helps to understand how a curve is created.

Drawing a straight line requires only starting and destination anchor points. However, to draw a curve, you must also provide a single control point. (To simplify the method, and increase performance, Flash uses a single control point to draw a quadratic Bezier curve, rather than one or more control points for each anchor, as in the case of cubic Bezier curves). Flash starts with the anchor points and interpolates all interim points as it draws the curve toward the control point. Figure 3 shows what a curve would look like for a hypothetical set of anchor and control points.

Figure 3. Curve: This figure shows a Flash curve created with the Drawing API's curveTo() method. All such curves are quadratic Bezier curves, meaning they have two anchor points and one control point.

So, to simulate a rubber band, all you have to do is move the control point back and forth on either side of the curve, diminishing its value over time. This has the effect of plucking a rubber band. The arc of the band is large to begin with and diminishes steadily until the band is taught again.

Using Figure 3 as a hypothetical example, consider the points pictured as (left to right) anchor1 (x:0,y:50); control (x:50, y:0); and anchor2 (x:100, y:50). All you have to do is use a pattern like this for the control point: (50,0), (50, 100), (50, 25), (50, 75), (50, 50). This causes the control point to bounce back and forth?to y values of 0, 100, 25, 75, finally settling at 50?causing the curve to be a straight line.

Of course with moving points, you can't use fixed numbers, so you have to vary the distance from each anchor point, as it continually updates. You'll see what I mean when you look at the code.

Changing from Lines to Curves
Only three small changes must be made to replace your lines with curves. First, replace the lineTo() method with the curveTo() method:

32 canvas_mc.curveTo(circle1._x - circle1.stringVib, circle1._y - circle1.stringVib, circle1._x, circle1._y);

You may notice that the curveTo() method still uses the destination anchor point, but a control point precedes the anchor. Since the circles are always moving, you merely have to subtract some x and y value from the anchor point's x and y values to always get a nearby offset for the control point. You may also notice that this value is stored in a stringVib variable in each circle individually so each line of a multi-line system can bounce independently.

Next you need to know how to get the value of this stringVib variable. All you need to do is pick a random value within a tolerance range acceptable to you (the bounce can't be too wild or you won't be able to understand what it represents), and assign it at after collision.

Because you want the value to be updated on collisions with all four sides of the stage, you need to add the necessary instruction to both x and y conditionals in your script?at lines 18 and 23, respectively. In a simple two-circle system, you can update each circle directly. (In more complex systems, you will need to update the two adjacent circles, but that's something for you to work out in the bonus code. More on that later.)

//Lines 18 and 23circle0.stringVib = circle1.stringVib = Math.random()*20 + 10;

Finally, you need to bounce the value back and forth, adding and subtracting it from the anchor point, so it will cycle like a rubber band. Also, you don't want the rubber band to bounce forever, so you need to dampen it, over time. In line 25, add this:

25  this.stringVib /= 1.1 * -1; 

This line simply divides the value by 1.1 and reassigns it, causing the value to approach zero relatively quickly. The -1 just bounces back and forth from positive to negative every iteration, as described.

With these three lines in place, you have an initial random value, from which a control point is derived, and which bounces between negative and positive values, diminishing over time. Just like a rubber band.

Bonus
I hope this very simple use of the Drawing API inspires you to create something. Ideally, you can see how much a simple thing like a little bounce can add to an otherwise static experience.

In the bonus code related to this article (look in the Related Resources in the left column for a link), I demonstrate a multi-band system, allowing you too connect many MovieClips together with many rubber bands. The system calculates which two bands must bounce when each circle hits a wall. Best of all, you don't need to change much to get it to work. (All you really have to do is work with an array instead of two hard-coded MovieClip references.)

I encourage you to experiment with this code. After all, these examples are far from finished. For example, the bounce distortion is always random. It doesn't account for the speed of the MovieClip when it hits the wall, or how long (and therefore, how taut) the rubber band is when the degree of bounce is calculated. Try to move away from random values and make your file more accurate. As always, if you come up with anything interesting, or if you can improve on these examples, I'd like to know about it.

See you next month.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

The Latest

homes in the real estate industry

Exploring the Latest Tech Trends Impacting the Real Estate Industry

The real estate industry is changing thanks to the newest technological advancements. These new developments — from blockchain and AI to virtual reality and 3D printing — are poised to change how we buy and sell homes. Real estate brokers, buyers, sellers, wholesale real estate professionals, fix and flippers, and beyond may

man on floor with data

DevX Quick Guide to Data Ingestion

One of the biggest trends of the 21st century is the massive surge in internet usage. With major innovations such as smart technology, social media, and online shopping sites, the internet has become an essential part of everyday life for a large portion of the population. Due to this internet

payment via phone

7 Ways Technology Has Changed Traditional Payments

In today’s digital world, technology has changed how we make payments. From contactless cards to mobile wallets, it’s now easier to pay for goods and services without carrying cash or using a checkbook. This article will look at seven of the most significant ways technology has transformed traditional payment methods.