Inter-Object Communication

Joseph Craig and Damian Hart contributed greatly to my understanding of this topic.

So far, we've learned how to create and animate objects within a main movie, as in our projects involving a bouncing ball, and a shower of words. With document classes, we've begun to glimpse how one piece of code (a document class) can modify others. Now we'll take this a step further, looking at two simple cases in which one ActionScript object sends information to, or receives information from, another object.

I. Reading properties of an object

In the example below, drag the blue ball around the screen. The contents of the text field will change accordingly.

The blue ball and the text field are visible elements of two distinct Movie Clips, instance-named reference and readout, respectively. To keep things simple, we're not using a document class with this project, so both Movie Clips were created visually in the Flash interface. Likewise, their instance names were assigned in the Property Editor. In the readout Movie Clip, the text field (also created by hand) is given its own instance name, tf.

For this project, one of our two Movie Clips must be linked to a class file. Which one doesn't really matter, though the nature of the code in the class file depends on that choice. For the example above, we've scripted the moveable ball, linking it to referenceClass.as.

Here's the entirety of the class:

package
{
  import flash.display.MovieClip;
  import flash.events.*;
	
  public class referenceClass extends MovieClip
  {
    public function referenceClass():void  //CONSTRUCTOR
    {
      addEventListener(Event.ENTER_FRAME, update);
      addEventListener(MouseEvent.MOUSE_DOWN, dragOn);
      addEventListener(MouseEvent.MOUSE_UP, dragOff);
    }
		
    public function update(event:Event):void
    {
      MovieClip(root).readout.tf.text = x + ", "+ y;
    }
		
    public function dragOn(event:MouseEvent):void
    {
      startDrag(true);
    }
		
    public function dragOff(event:MouseEvent):void
    {
      stopDrag();
    }
  }	
}

This class file is relatively simple (not nearly as long as our bouncing ball class), and mainly uses familiar concepts, with a few new twists thrown in.

First, the importations. We import flash.display.MovieClip, since we're scripting Movie Clips. (Actually these could be sprites, but we'll stick with Movie Clip for the sake of simplicity.) We need to import the flash.events package because we'll be working with both a system event (ENTER_FRAME) and two mouse events.

The Constructor method for this class consists entirely of addEventListener statements, setting up listeners for frame entry, mouse button press (MOUSE_DOWN), and mouse button release (MOUSE_UP). By now, you should be somewhat familiar with event listeners and callback functions.

Each listener has its own callback (or custom method).

The update() method writes the current value of the native Movie Clip variables x and y into the text property of the text field tf in the other Movie Clip, readout. (We'll skip over that curious business of movieClip(root) for the moment.)

The other two callbacks respond to mouse events. Note that their parameters indicate event:MouseEvent and not event:Event. This difference is crucial.

When the mouse button is pressed, calling the dragOn() method, we invoke a Movie Clip method called startDrag(). This method locks the current object to the coordinates of the mouse pointer. The argument true sets the lock on the registration point of the current object.

As you can guess, dragOff() invokes stopDrag(), which releases the lock.

The structure you see above -- two event listeners, two callbacks, startDrag() and stopDrag() -- constitutes the basic mechanism for any drag-and-drop element in Flash.

If you're wondering why we need callbacks -- why we can't simply invoke startDrag() as the target of the MOUSE_DOWN listener, for instance -- there's a simple answer. Listeners require callbacks. You must always write a corresponding custom method whenever you use an event listener. If you try to get around this requirement, you will encounter errors.

II. Reaching the root

As promised, we need to say a few more things about the update() method in our first example. Note that we reach out to the Movie Clip containing the text field in a very particular way:

      MovieClip(root).readout.tf.text = x + ", "+ y;

It would after all be simpler simply to invoke the other MovieClip on a first-name basis, as it were:

      readout.tf.text = x + ", "+ y;

However, this invocation does not work. It generates an "undefined property" error, because the Flash Player does not know what readout means. Now, this may strike you as odd. Readout is after all an instance name. Why can't the Flash Player recognize it?

The answer involves a concept called scope. By default, an ActionScript class is only aware of variables defined within it. The class we're using here, referenceClass, does not have any information about properties of the other object, readout.

Luckily for us, though, there is a way to obtain this missing information, by appealing to a construct called root. For any Flash project (.swf), root represents the highest level in the data hierarchy, or the parent of all the components.

In older ActionScript days, we invoked this construct simply as _root, or sometimes _level0. Often we worried a bit when we did this, because in older versions of ActionScript, the meaning of _root could change, especially if we loaded more than one .swf file into a project.

To correct this problem, ActionScript 3 introduces a new level of abstraction called a Display Object. For the moment, all you need to know is that root in ActionScript 3 is a Display Object; and that Display Objects are not especially useful in and of themselves.

To make it useful, you must convert the Display Object root into a Movie Clip, which you can do with this expression:

MovieClip(root)

This expression is exactly like the conversion method Number(), which we used in our text shower project to convert an alphanumeric character into an arithmetic value. Number() is in fact the conversion method of the class Number. MovieClip() is the conversion method of MovieClip. Most objects in ActionScript 3 have conversion methods, and will allow you to convert any equal or higher-order object, meaning any object that contains at least as much data as the object into which you are converting. Since a Display Object has more data than a Movie Clip, this conversion is valid.

Now, if Movie Clip is a subclass of Display Object (which it is), you might wonder why we need to convert. The answer is somewhat counter-intuitive: for some reason, while Display Objects contain all the data available to Movie Clips, they don't contain the same methods. That's why we need to convert.

Admittedly, this is all a bit mysterious. It's not important that you fully understand the context of MovieClip(root) at this point. If Flash scripting is as far as you ever want to go with code, you can simply memorize MovieClip(root) as a formula. It's the incantation, in effect, that lets you communicate data from one Movie Clip to another.

III. Hit testing

In this next example, drag the blue object until it touches the text field.

What you see above demonstrates hit testing, a very important and useful operation in interactive design. ActionScript provides a native method for determining when two objects occupy the same space within the X-Y grid of the visible window.

In this case, we've decided to script both Movie Clips. The draggable object uses a simpler version of referenceClass, given here in its entirety:

package
{
  import flash.display.MovieClip;
  import flash.events.MouseEvent;
	
  public class referenceClass extends MovieClip
  {
    public function referenceClass():void  //CONSTRUCTOR
    {
      addEventListener(MouseEvent.MOUSE_DOWN, dragOn);
      addEventListener(MouseEvent.MOUSE_UP, dragOff);
    }
		
    public function dragOn(event:MouseEvent):void
    {
      startDrag(true);
    }
		
    public function dragOff(event:MouseEvent):void
    {
      stopDrag();
    }
  }	
}

If you look closely, you'll see we've cut out the update() method and its ENTER_FRAME event listener. All we have here is the drag-and-drop code.

The crucial business of this project happens in the second linked class file, called readoutClass. Here's the entirety of that class:

package
{
  import flash.display.MovieClip;
  import flash.events.*;
  import flash.text.TextField;
	
  public class readoutClass extends MovieClip
  {

    public function readoutClass():void  //CONSTRUCTOR
    {
      addEventListener(Event.ENTER_FRAME, hitOrMiss);
    }
		
    public function hitOrMiss(event:Event):void
    {
      if(hitTestObject(MovieClip(root).reference))
      {
        this.tf.text = "Mom, the other Movie Clip is touching me!"
      }
        else
      {
        this.tf.text = "";
      }
    }
  }
}

We add an importation for flash.text.TextField, since we need to work with a text field.

As before, the Constructor simply sets up an event listener, which refers to a callback method called hitOrMiss(), keyed to the ENTER_FRAME event.

The hitOrMiss() method (as in all custom methods, the name is arbitrary), uses an if/else structure to test for contact between the moveable object (reference) and the readout Movie Clip that contains the text field.

To detect this condition, we use a native Movie Clip method called hitTestObject(). Written as you see it here, this method tests for geometrical intersection between the current Movie Clip (readout) and a second Movie Clip named in the argument to the method. Again, we use MovieClip(root) to convert the root Display Object into a Movie Clip, then navigate down from it to its child Movie Clip, reference. (Or if you prefer, we say the magic word, allowing one Movie Clip to talk directly to another.)

As you can see, the if/else condition puts the line "Mom, the other Movie Clip is touching me!" into the text field tf if the hit test resolves to true, and instead inserts nothing (or a blank, "") in the opposite case.

That's all there is to this little demonstration.

IV. Source files

You may download the source files for these two projects from the folder interobjectDemos_022508 within MULTIMEDIA in the shared account on student-iat. Note that there are two subfolders, Ex 1 and Ex 2. Each of these folders contains a file called referenceClass.as, but despite the identical names, the two files are different. (This is not the best file naming practice.)



University of Baltimore Logo

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