Display List: Chapter 6 - ActionScript 3.0 Cookbook
Pages: 1, 2, 3, 4

Section 6.7: Loading and Interacting with External Movies

Problem

You want to load, and be able to interact with, an external .swf movie into your own movie.

Solution

Use the new Loader class to load the .swf file, and then access the .swf file via the content property of the Loader instance.

Discussion

Recipe 6.6 demonstrates how to load external images via the Loader class. Loading external .swf movies uses the same technique--by calling the load( ) method on a Loader instance and passing a URL to a .swf instead of an image, the .swf is loaded into the movie. If the Loader is in the main display hierarchy, the .swf also appears on-screen.

This recipe involves creating two separate .swf files, ExternalMovie.swf and LoaderExample.swf. The first movie, ExternalMovie.swf, will be loaded at runtime into the second movie, LoaderExample.swf. The code for ExternalMovie.swf is as follows:

package {
  import flash.display.Sprite;
  import flash.display.Shape;
  public class ExternalMovie extends Sprite {
    private var _color:uint = 0x000000;
    private var _circle:Shape;
    
    public function ExternalMovie( ) {
      updateDisplay( );
    }
    
    private function updateDisplay( ):void {
      // If the circle hasn't been created yet, create it
      // and make it visible by adding it to the display list
      if ( _circle == null ) {
        _circle = new Shape( );  
        addChild( _circle );
      }

      // Clear any previously drawn content and draw
      // a new circle with the fill color
      _circle.graphics.clear( );
      _circle.graphics.beginFill( _color );
      _circle.graphics.drawCircle( 100, 100, 40 );
    }
    
    // Changes the color of the circle
    public function setColor( color:uint ):void {
      _color = color;
      updateDisplay( );
    }
    
    // Gets the current circle color value
    public function getColor( ):uint {
      return _color;  
    }
    
    
  }
}

The code for ExternalMovie.swf is nothing out of the ordinary--a black circle is created when the movie is executed. The main thing to notice about the code is that there are two public methods for accessing and modifying the color of the circle, getColor( ) and setColor( ). Whenever the setColor( ) method is invoked, the circle is redrawn with the updated color value.

By declaring these methods as public, the methods are able to be called from a movie that loads the ExternalMovie.swf in at runtime. In contrast, the private updateDisplay( ) method won't be available to the loading movie. See Recipe 1.13 for more information about the visibility modifiers for methods.

Now that ExternalMovie.swf is created, a new .swf needs to be created to load the external movie. This is done with LoaderExample.swf, which has the following code:

package {
  import flash.display.*;
  import flash.net.URLRequest;
  import flash.events.Event;

  public class LoaderExample extends Sprite {
    
    private var _loader:Loader;
    
    public function LoaderExample( ) {
      // Create the Loader and add it to the display list
      _loader = new Loader( );
      addChild( _loader );
      
      // Add the event handler to interact with the loaded movie
      _loader.contentLoaderInfo.addEventListener( Event.INIT, handleInit );
      
      // Load the external movie
      _loader.load( new URLRequest( "ExternalMovie.swf" ) );
    }
    
    // Event handler called when the externally loaded movie is
    // ready to be interacted with
    private function handleInit( event:Event ):void {
      // Typed as * here because the type is not known at compile-time.
      var movie:* = _loader.content;
      
      // Calls a method in the external movie to get data out
      // Displays: 0
      trace( movie.getColor( ) );
      
      // Calls a method in the external movie to set data.
      // Sets the color in the external movie, which draws
      // a circle with the new color, in this case red
      movie.setColor( 0xFF0000 );
    }
  }
}

The code for LoaderExample.swf is more interesting in that it communicates with the loaded movie. There are two main aspects in the preceding code:

  1. Listening for the init event
  2. Accessing the loaded movie via the content property

The init event is fired when the loaded movie has initialized enough that its methods and properties are available to be interacted with. The movie can be controlled only after the init event has been fired from the loader. Attempting to interact with a loaded movie before it has initialized will generate runtime errors.

To control the loaded movie, you'll first need to get a reference to it. This is done via the content property of the Loader class. In the preceding code, the loader variable refers to the Loader that pulled in the external .swf file, so you can access the movie via loader.content. If the loader variable weren't available, the event.target.content path could be used instead to get to the contents of the Loader. This is because event.target refers to the instance that generated the event, which is the same instance that the loader variable refers to.

The content property is read-only, and returns an object of type DisplayObject. In the LoaderExample.swf code, you'll notice that instead of typing the movie variable as a DisplayObject, the same type as what the content property returns, the * type was used. This is necessary because trying to call the getColor( ) or setColor( ) methods on the movie reference generates compile-time errors if movie is typed as a DisplayObject.

The movie being loaded, ExternalMovie.swf, has two public methods available for interaction. These methods are not part of the DisplayObject class; therefore, trying to call one of the methods from a variable of type DisplayObject is an error. The * type allows you to call any method that you'd like on the loaded movie. If the method does not exist in the loaded movie, a ReferenceError is thrown during execution.

The getColor( ) method returns the color of the circle in ExternalMovie.swf to LoaderExample.swf. The LoaderExample.swf reports the color as 0, which is the same as 0x000000, or the color black. The setColor( ) method allows LoaderExample.swf to change the color of the circle drawn by ExternalMovie.swf. In this case, the color of the circle is set to red, and you can see that the ExternalMovie.swf updates the display after the new circle color value is set.

It is only possible to interact with .swf files of Version 9 and above using this technique. When loading Version 8 and below .swf files, this technique won't work because ActionScript 3.0 code runs independently of ActionScript 1.0 and 2.0. Communication with these .swf files is not trivial and involves using LocalConnection as a workaround to send and receive messages. See Chapter 19 for details.

See Also

Recipes 1.13 and 6.6

Section 6.8: Creating Mouse Interactions

Problem

You want users to interact with your movie using their mouse.

Solution

Use the various mouse events to listen for mouse interactions on display objects of type InteractiveObject. Use the read-only mouseX and mouseY properties from DisplayObject to examine the mouse location relative to a display object, or the localX and localY properties from the MouseEvent passed to a mouse event handler.

Discussion

Basic mouse interaction can be created with the SimpleButton class, as described in Recipe 6.5. The SimpleButton class provides an easy way to create a clickable button with different button visual states: up, over, and down.

However, there are times when buttons just don't provide enough interactivity. By listening to the various mouse events, you can create interesting interactive experiences. For instance, consider that you want to track the mouse cursor to create an interactive drawing program, drawing lines on-screen based on the user's mouse movement. Or, consider that you have a maze that a user must navigate their mouse through without colliding with the walls to find the exit. Or, perhaps the user's mouse movement needs to control the direction of a golf club, and the mouse button is used to swing.

These situations require use of the special InteractiveObject display object, which provides the ability to respond to the user's mouse. If you go back to the introduction for this chapter, you'll recall that the InteractiveObject class is a base class fairly high in the display object class hierarchy. Because of this, the Sprite, Loader, TextField, and MovieClip classes are all examples of the InteractiveObject class since they fall underneath InteractiveObject in the hierarchy, and you may already be familiar with their use.

Instances of the InteractiveObject dispatch the necessary events specific to mouse interaction. The following is a list of more useful mouse events:

click
Generated when the user presses and releases the mouse button over the interactive display object.
doubleClick
Generated when the user presses and releases the mouse button twice in rapid succession over the interactive display object.
mouseDown
Generated when the user presses the mouse button over the interactive display object.
mouseUp
Generated when the user releases the mouse button over the interactive display object.
mouseOver
Generated when the user moves the mouse pointer from outside of the bounds of interactive display object to inside of them.
mouseMove
Generated when the user moves the mouse pointer while the pointer is inside the bounds of the interactive display object.
mouseOut
Generated when the user moves the mouse pointer from inside the bounds of an interactive display object to outside of them.
mouseWheel
Generated when the user rotates the mouse wheel while the mouse pointer is over the interactive display object.

Using these events is simply a matter of calling addEventListener( ) on the InteractiveObject and defining an event handler to handle the MouseEvent passed to it.

The following code snippet creates a Sprite, draws a red circle inside of it, and outputs a message to the console whenever the mouse moves over the circle:

package {
  import flash.display.Sprite;
  import flash.events.*;
  import flash.geom.Point;

  public class InteractiveMouseDemo extends Sprite {
    public function InteractiveMouseDemo( ) {
      var circle:Sprite = new Sprite( );
      circle.x = 10;
      circle.y = 10;
      circle.graphics.beginFill( 0xFF0000 );
      circle.graphics.drawCircle( 0, 0, 5 );
      circle.graphics.endFill( );
      
      circle.addEventListener( MouseEvent.MOUSE_MOVE, handleMouseMove );

      addChild( circle );
    }
    
    // Event handle to capture the move event over the circle
    private function handleMouseMove( event:MouseEvent ):void {
      trace( "mouse move" );
    }
  }
}

In this example, notice that the message appears only when the mouse is moved while the pointer is over the circle. The circle defines the bounds for the Sprite in this case.

Mouse events are generated from a particular interactive display object only when the pointer is within the bounds of that object.

Another common use of mouse events stems from wanting to inspect the location of the mouse pointer to create mouse interactivity. For example, to draw a line with the mouse, the mouse location needs to be known so the line can be plotted accurately. There are two ways to determine the location of the mouse pointer:

  • Using the mouseX and mouseY properties available on any DisplayObject instance.
  • Using the localX and localY properties available from the MouseEvent instance passed to the mouse event handler.

The mouseX and mouseY properties can be inspected to determine the location of the mouse cursor relative to the top-left corner of the DisplayObject. Both of the properties are read-only; it is not possible to set the location of the mouse cursor, only to examine the location.

So, imagine that a rectangle is at x location 20 and y location 50 and the user moves the mouse pointer to x location 25 and y location 60. The mouseX property of the rectangle returns 5 and mouseY of the rectangle reports 10 because from the rectangle's perspective, the mouse is 5 pixels in from the left and 10 pixels down from the top.

The localX and localY properties of the MouseEvent are also relative. In the MouseEvent case, the properties are relative to interactive display object that dispatched the event. Therefore, consider that a rectangle reports mouseX of 10 and dispatches a mouseMove event. The event's localX property is also 10.

To get the global position of the mouse from local coordinates, use the localToGlobal( ) method of the DisplayObject class. The localToGlobal( ) method takes flash.geom.Point as a parameter that specifies the local coordinates, and returns a new Point with the coordinates converted to the global space. The following code snippet focuses on the event handler and demonstrates how to convert localX and localY to global coordinates:

// Event handler to respond to a mouseMove event
private function handleMouseMove( event:MouseEvent ):void {
  /* Displays:
  local x: 3.95
  local y: 3.45
  */
  trace( "local x: " + event.localX );
  trace( "local y: " + event.localY );
  
  // Create the point that localToGlobal should convert
  var localPoint:Point = new Point( event.localX, event.localY );
  // Convert from the local coordinates of the display object that
  // dispatched the event to the global stage coordinates
  var globalPoint:Point = event.target.localToGlobal( localPoint );
  
  /* Displays:
  global x: 13.95
  global y: 13.45
  */
  trace( "global x: " + globalPoint.x );
  trace( "global y: " + globalPoint.y );
}

A complete working example of creating interactivity through handling the various mouse events can be demonstrated by the simple drawing program that follows. Whenever the mouse is pressed, the drawing starts. As the user moves the mouse around the screen, a line is drawn that follows the movement of the mouse pointer. When the user releases the mouse button, the drawing stops:

package {
  import flash.display.Sprite;
  import flash.events.MouseEvent;
  public class DrawingDemo extends Sprite {
    
    // Flag to indicate whether the mouse is in draw mode
    private var _drawing:Boolean;
    
    public function DrawingDemo( ) {
      // Configure the line style
      graphics.lineStyle( 2, 0xFFCC33 );
      
      // Drawing is false until the user presses the mouse
      _drawing = false;
      
      // Add the mouse listeners on the stage object to be 
      // notfied of any mouse event that happens while the 
      // mouse is over the entire movie
      stage.addEventListener( MouseEvent.MOUSE_DOWN, startDrawing );
      stage.addEventListener( MouseEvent.MOUSE_MOVE, draw );
      stage.addEventListener( MouseEvent.MOUSE_UP, stopDrawing );
    }
    
    public function startDrawing( event:MouseEvent ):void {
      // Move to the current mouse position to be ready for drawing
      graphics.moveTo( mouseX, mouseY );
      _drawing = true;
    }
    
    public function draw( event:MouseEvent ):void {
      if ( _drawing ) {
        // Draw a line from the last mouse position to the 
        // current one
        graphics.lineTo( mouseX, mouseY );  
      }
    }
    
    public function stopDrawing( event:MouseEvent ):void {
      _drawing = false;
    }
    
  }
}

See Also

Recipes 6.4 and 6.8

This excerpt is from ActionScript 3.0 Cookbook. Well before Ajax and Windows Presentation Foundation, Macromedia Flash provided the first method for building "rich" web pages. Now, Adobe is making Flash a full-fledged development environment, and learning ActionScript 3.0 is key. That's a challenge for even the most experienced Flash developer. This Cookbook offers more than 300 solutions to solve a wide range of coding dilemmas, so you can learn to work with the new version right away.

buy button