Doing Things with Proximity

This project is a sketch, or proof-of-concept, for a potentially useful application of our proximity trick. You could adapt this code to do some interesting things, such as providing your user with a randomized, animated stream of options, from which she can sample just by rolling the mouse in range -- no pointing and clicking involved. Like most clever hacks, this could lead in one direction toward art or poetry, and in another toward advertising or marketing.

In this project, we add animation and interactivity to the mix. As the shapes drift past along the left edge of the screen, move your mouse toward one. (When two shapes overlap, this can be a little difficult.) Mousing over the object stops its motion, and causes it to scale up and attain full opacity. You should also see a text in the field at right. Mousing out returns the object to its initial scale and alpha, and puts it back into motion.

I. Architecture

The main movie file, fallingTokens.fla, contains five layers, each of which olds an instance of the Library Symbol floater_mc. This MovieClip is linked to floaterClass, which is defined in floaterClass.as. The main movie also has a document class called fallingTokensDocClass.

II. fallingTokensDocClass

The document class is exceptionally simple. Aside from the basic structure of a MovieClip class, it contains only one statement:

  public var lockout:Boolean = false;

This sets up a Boolean flag variable at the level of the root, visible to all five instances of our tumbling object class. You'll see where this crucial variable comes into play in just a bit.

III. floaterClass

The main business of this project occurs in the object class, floaterClass. Since this class is a little lengthy, we'll skip over some details which you can see for yourself by downloading the code (e.g., the imports and some other setup features).

Before we come to the proximity effects, let's explain how we get one class to use five different shapes. The trick is not hard to figure out. The timeline for floater_mc has five keyframes, each containing one of the shapes. Also, each instance has a name ending in a number from 1 to 5. We declare a String variable called myID, then assign it a value in the Constructor, as follows:

  myID = name.charAt(name.length-1);

This line slices off the last character of the instance name and assigns it to myID. So each instance of floater_mc wears its very own number.

Having done this, we can then customize each instance, telling it which shape to show, and what text to store for display:

  switch(myID)
  {
    case "1":
      gotoAndStop(1);
      theText = "Everybody is a tiny star.";
      break;
    case "2":
      gotoAndStop(10);
      theText = "That polar bear is no longer accurate.";
      break;
    case "3":
      gotoAndStop(20);
      theText = "Write your symptoms legibly on your clothing.";
      break;
    case "4":
      gotoAndStop(30);
      theText = "It was the end of the world, and we had seen it already.";
      break;
    case "5":
      gotoAndStop(40);
      theText = "The ascended masters took away the Kleenex.";
      break;
  }

By now, you should be familiar with the switch structure. Here we pivot on the String value of myID (no need to convert it to a Number), and route the MovieClip's playback head to the relevant keyframe, then assign a particular line to a variable called theText.

With this customization out of the way, we handle a few more details before closing the Constructor:

  x = 60;
  addEventListener(Event.ENTER_FRAME, animate);
  reSet();

These statements align the falling shapes 60 pixels from the left margin; start a listener/callback arrangement for one of our familiar ENTER_FRAME loops; and finally, invoke a custom method called reSet(), which looks suspiciously like methods of the same name we've used with other projects where things start at the top of the screen and fall toward the bottom (appleSeizure, Invaders).

The most interesting code in this project resides in the callback method animate(), which looks like this:

  function animate(e:Event):void
  {
			
    XSide = x - MovieClip(root).mouseX;
    YSide = y - MovieClip(root).mouseY;
    dist = Math.floor(Math.sqrt(XSide*XSide + YSide*YSide));
			
    if(holdUp == false) y += 5;
    if(y>400+height) reSet();
	rotation +=4;

    if(dist < 101)
    {
      scaleX = 1 + (1 - dist/100);
      scaleY = scaleX;
				
      if(MovieClip(root).lockout == false)
      {
        holdUp = true;
        MovieClip(root).lockout = true;
        alpha = 0.25 + (1 - dist/1000);
        MovieClip(root).showField.text = theText;
      }
    }
    else
    {
      alpha = 0.25;
      scaleX = 1;
      scaleY = 1;

      if(holdUp)
      {
        MovieClip(root).lockout = false;
        MovieClip(root).showField.text = "";
        }
      holdUp = false;
    }

We'll skip over the first three lines of this method, handling triangulation, since they should be familiar by now.

The fourth line (following a skipped space here) handles the simple, straight-line, down-screen animation, but note that it is executed only if a local variable called holdUp is false. (We've also skipped over the declaration of this variable.) Doing this allows us to suspend the fall of the shape if this local flag variable is set to true.

The next line (familiarly enough) calls reSet() if our shape has passed offscreen at bottom. Next we have a line that performs a rotation, also a familiar trick by now.

Now we come to a fairly complicated if/else structure that represents the heart of this method.

As in our second proximity example (the scaling discs), we use a 100-pixel range factor, and if that condition is met, we adjust scale in more or less the same way as we did in the earlier example.

Then, still within the 100-pixel condition, we come to some lines that bear repeating:

      if(MovieClip(root).lockout == false)
      {
        holdUp = true;
        MovieClip(root).lockout = true;
        alpha = 0.25 + (1 - dist/1000);
        MovieClip(root).showField.text = theText;
      }

This is an embedded if condition that examines the value of a root-level variable called lockout. You will recall, that variable is declared and set to false in the document class. You'll also remember that root variables exist independent of sibling classes, but can be seen and altered by them -- exactly like the score variable you may have used in your Invader game.

Our lockout variable must reside at the root, because it must be unique. It functions as a switch that any of our five tumbling instances can set -- but only one at a time. This switch serves a very important purpose. It allows us to capture or arrest a single shape, while letting other shapes drift past.

Here's what happens when we first move the mouse leftwards and encounter one of our falling objects: that object (an instance of floater_mc) checks to see if lockout is false, referring to this value via the root.

Since this is our first encounter with a falling object, lockout is indeed false, so the lines in the if condition are executed. We set the local variable holdUp to true, which has the effect of switching off the animation for this object. We also adjust the object's alpha, and place its particular text in the display field.

Now, while we're doing this, our four other falling objects are still falling, and let's say what one of them has drifted within 100 pixels of the mouse position. Since lockout is now true, when that object performs the lines above, it routes around the four statements within the curly braces, and continues down the screen. We thus assure singular and unique interactions.

As you will recall, though, once we've set a flag variable one way, we need to make sure it is reset at some appropriate point. So we come to the else branch of our if/else structure, which once again bears repeating:

    else
    {
      alpha = 0.25;
      scaleX = 1;
      scaleY = 1;

      if(holdUp)
      {
        MovieClip(root).lockout = false;
        MovieClip(root).showField.text = "";
        }
      holdUp = false;
    }

These lines are executed if the mouse pointer is more than 100 pixels from the registration point of our object -- in our current scenario, when the mouse moves away.

We begin by returning alpha to the default 25%, and scale to 100%.

Then we ask whether the value of holdUp is true; which is to say, we ask if the object is currently under selection (or arrest, as it were). If so, then we reach out to the root and reset the value of lockout to false, restoring its initial condition. In other words, we signal our four sibling MovieClips that we're no longer the focus of attention, so one of them is free to take our place.

Note that we do this ONLY if this PARTICULAR object is being held up (holdup == true). Otherwise, every object more than 100 pixels from the mouse pointer would reset lockout, and the switch would not reflect any unique context.

Finally, outside the if(holdUp) test, we set holdUp to false. Yes, this means we reset this variable on every frame entry, when the mouse is not in range, but this extravagance is harmless enough.

After crawling though this rather complicated logic, you may be wondering why such a deceptively simple effect takes so many contortions. More elegant solutions are no doubt possible (though they would probably be harder to explain). Still, it is generally the case in programming that even simple things are often quite hard, when it comes to code.

IV. Source files

Source files for this project can be found in the directory proximity, within MULTIMEDIA in the shared account on student-iat. Look for fallingTokens.fla, fallingTokensDocClass.as, and floaterClass.as.



University of Baltimore Logo

Last updated: 06/24/08 16:26:20
Copyright © 2008 School of Information Arts and Technologies