Demo: Text Storm

This little demonstration project illustrates a number of useful Flash techniques:

We'll explore each of these principles as we break down and explain the various pieces of the project.

I. Basic Architecture

This project consists of one main movie file (.fla), called textStorm.fla, and one external ActionScript file (.as), called s_floaterClass.as. In the version you see here, the main movie has five layers, each with one frame, and one Symbol defined in the Library, a Movie Clip named s_floater. We use the prefix s_ here, instead of mc_, because this Movie Clip will be treated as a Sprite.

All Movie Clips have internal timelines, but the timeline within s_floater is entirely empty. That is, this Movie Clip has no associated graphics. It does have Linkage to s_floaterClass.

Each of the five layers in the main movie timeline contains one instance of s_floater. When we created these instances, we gave each one an instance name, using the Properties panel, observing a simple naming convention. Each instance is named "f_" and some number from 1 to 5. So the instance of s_floater in Layer 1 is named f_1, the instance in Layer 2 is named f_2, and so forth. As you'll see, this naming convention is crucial.

II. ActionScript Class

The external file s_floaterClass.as defines an ActionScript class called s_floaterClass (as the name indicates). At the beginning, just after the start of the package{} container, we write three import statements:

  import flash.display.Sprite;
  import flash.text.*
  import flash.events.Event;

The first import lets us work with the Sprite subclass of the MovieClip object. This is good efficiency -- Sprites are slightly smaller than Movie Clips when compiled into SWFs -- though it is entirely possible to stick with the Movie Clip class, if you don't mind a few extra kilobytes here and there.

We next import all the objects associated with the flash.text package. This gives us access to the TextField and TextFormat objects, on which we will largely depend.

Finally, we import flash.events.Event, so that we can set up a listener for the enterFrame event (allowing our program to run in a continuous loop).

Next we start the class definition in the usual way:

  public class s_floaterClass extends Sprite 
  {

Immediately after the start of the class definition body, we declare a number of variables:

    var tf:TextField = new TextField();
    var tf:TextField = new TextField();
    var tf:TextField = new TextField();
    var theFormat:TextFormat;
    var colorVal:Number;
    var sizeVal:Number;
    var depthFactor:Number;
    var textArray:Array = new Array("airborne","bitumen","et cetera");

In the first declaration, we use the native constructor method of the TextField object to create an instance of this class, the first step toward creating the dynamically sized and configured text field that lies at the heart of this exercise.

We also declare theFormat under the type TextFormat, which is another object defined in the flash.text package, this one used to manage the various visible attributes of a TextField.

The three Number variables, colorVal, sizeVal, and depthFactor, will be used along with theFormat, as you'll see below.

Finally, we create an instance of the Array object called textArray. In the interest of clarity, we've truncated the Array to only three items; in the demo it actually has 26, and can have as many as we wish. We'll have more to say about this Array below. For the moment, suffice it to observe that an Array is simply a comma-separated list of items (Strings, in this case), whose order in the list allows them to be indicated by number.

Now we come to the Constructor method (or function) of s_floaterClass.

  public function s_floaterClass():void  //CONSTRUCTOR
  {
    depthFactor = Number(name.charAt(name.length - 1));
    reSet();
    addChild(tf)
    addEventListener(Event.ENTER_FRAME, animate);
  }

Like many examples of ActionScript, this function will look pretty mysterious until you understand its relationship to some other pieces of the code. Note the use of the addChild() method on that new TextField, tf, that we created in the first declaration. This instruction simply makes the text field visible in our movie. Technically, there's nothing in the text field at this point, though since this code executes in milliseconds, no one will notice.

You'll also note the invocation of a custom method, reSet(), which we haven't yet discussed. As you might guess, reSet() is where the TextField tf picks up its contents and features.

The final line of the Constructor adds an event listener that links the enterFrame event to another custom method called animate(), which we'll also discuss in due course. Note that the syntax of addEventListener() requires us to omit the parentheses which we'd otherwise use to invoke animate(). This is another of ActionScript's not-so-charming quirks. Please observe it carefully.

We have so far omitted the first line of the Constructor, but only so we can discuss it in detail:

    depthFactor = Number(name.charAt(name.length - 1));

This line assigns a Number value to our declared variable depthFactor, pulling that value out of the instance name of the Movie Clip, which is what the property name contains for any Movie Clip. The method charAt() is a native method of the String object, a basic object in ActionScript like our old buddy, Math. We don't invoke String directly here, but rather name, which is both a property of the Sprite we are currently scripting (s_floater), and not coincidentally, also an instance of the String object. We pass as an argument to charAt() the value name.length-1, which uses another property of name (and String), namely length. Like Arrays, Strings are enumerated beginning with zero; so the last character of a 3-character String (say, "012") is character number 2, which is the length of the String (3) minus 1. To make a long story shorter, our charAt() maneuver plucks off the final character of the name.

Remember, we named our five instances of s_floater f_1, f_2, f_3, and so forth. Therefore, the statement we've written clips off a numeric character between 1 and 5. However, as it appears in the instance name, this item is not a number, but an alphanumeric character; that's why we have to wrap our charAt() method in the Number() method, which converts any alphanumeric character to a number value.

All this massaging of names and numbers serves an important purpose. Our setup for depthFactor illustrates disambiguation or indexing: it allows us to engage each of the five instances of s_floater by its distinguishing number -- and since these numbers correspond to the Layers in which the instances are installed, our ActionScript class is thus able to treat objects at various depths in specific ways. This ability allows the size, color, and speed differences you can see in the demo.

Now we come to the custom methods, the first of which is animate(). This one is refreshingly simple:

  public function animate(event:Event):void
  {
    y += depthFactor;
    if(y > 400) reSet();
  }

The argument to the function (event:Event) makes this method a callback and links it to the enterFrame event listener. See our detailed discussion of callbacks in a separate posting to this site.

The first line of this very simple function uses depthFactor to control the downward movement of the Sprite. A sprite moves faster or slower depending on the value of depthFactor, which in turn reflects how far it is from the top of the depth layering stack. By convention, the Flash Player places objects in lower-numbered layers in front of objects in higher-numbered layers.

The second line of this method is a boundary test, invoking the method reSet() if our sprite has passed below the bottom of the window.

Thus we come to the reSet() custom method, which as you'll recall is invoked in the Constructor, as well as animate(). Here's the entire method:

  public function reSet():void
  {

    //renew text
    tf.text = textArray[Math.floor(Math.random()*textArray.length)];
			
    //set text size
    sizeVal = 40 - (depthFactor*depthFactor);
			
    //set text color
    switch(depthFactor)
    {
      case 1:
        colorVal = 0x000000;
        break;
      case 2:
        colorVal = 0x555555;
        break;
      case 3:
        colorVal = 0x999999;
        break;
      case 4:
        colorVal = 0xAAAAAA;
        break;
      case 5:
        colorVal = 0xCCCCCC;
    }
			
    //set up TextFormat object to apply values
    theFormat = new TextFormat("Verdana",sizeVal,colorVal,true);
    tf.setTextFormat(theFormat);
			
    //reset height and width of text field
    tf.width = tf.textWidth+8;
    tf.height = tf.textHeight+8;
			
    //reset position
    x = Math.random()*(650-tf.width);
    y = 0 - (50+ Math.random()*200);
			
  }

We'll begin with the last two instructions of this method, under the comment //reset position. This may seem a bit backwards, but there are times when code is easier to approach that way.

Those last two instructions place the object at a random lateral position, and at a random vertical position some distance above the top of the window. However, if we simply chose a random point between one and the maximum window width (650), the text field within our sprite could overlap the boundaries left or right, cutting off its letters. Our desire to avoid this visual effect causes us to go with dynamically generated text fields in the first place.

To keep all our text in the window, we choose a random number between 1 and 650 minus the width of the field. That width value, in turn, is determined automatically by the Flash Player after we assign text to the field; which takes us our of backwards mode, and to the first instruction in the method:

    tf.text = textArray[Math.floor(Math.random()*textArray.length)];

Here we come back to our Array instance, textArray. We use our now-familiar formula for random numbers to generate a value between 0 and the total number of items in textArray, which is encoded in the length property of any Array. Now, it so happens that the items in an Array are automatically numbered beginning with zero, so our statement here plucks out a random selection from textArray. For any array,

    myArray[N]

indicates the Nth item in the Array.

We assign our random selection to the text property of tf, our TextField instance. This puts whatever word we've randomly selected into the field.

Next we choose a font size for our text field, using the Number variable sizeVal. We need to do this dynamically, because we want font size in each field to reflect its layer depth. So we start with a default size of 40 points and subtract the square of depthFactor, so that the type in our uppermost layer is at 39 points (40 minus 1 squared), and the type in Layer 5 is at 15 points (40 minus 25, or 5 squared). Using a square factor imposes an exponential difference in font sizes, making the depth distinctions more easily visible.

Next we set a color (actually, grayscale) value for the text in our field, using the Number variable colorVal. Here we take a more graduated approach, using a structure you have not previously encountered, called a switch statement. This statement allows us to respond individually to a series of discrete cases, or values in a determining variable, which for our purposes is our old friend, depthFactor.

The value assigned to colorVal at each stage of depthFactor is a hexadecimal code (that's what that 0x prefix indicates) corresponding to a defined shade of gray, from full black to something slightly off-white.

Note the break statements that follow the first four cases in our switch structure. These are absolutely required. They cause the Flash Player to break off execution after the triggering condition is met, and pass on to the next instruction outside the switch. There is no break required for the final case.

Immediately following the switch construction, we come to two lines that configure our instance of the TextFormat object, using our specialized variables, then use that object to set properties for our text field.

Finally, we come to the lines that specify the height and width of our field. Since type size is different at each layer depth, we need to perform these instructions each time one of our Sprites goes offscreen.

Since we've previously discussed the positioning instructions, we've now completely analyzed the reSet() method, which is the last piece of the class s_floaterClass.

You've now seen the whole trick, such as it is.

III. Source Files

The two source files for this project, textStorm.fla and s_floaterClass.as, may be found in the textStorm_021808 folder within the MULTIMEDIA folder in the shared account on student-iat.



University of Baltimore Logo

Last updated: 02/12/08 14:12:59
Copyright © 2008 School of Information Arts and Technologies