2

I am writing a program in AS3 and want to preload movie clips that will be needed in the future. Unfortunately, I'm running into a problem: the movie clips play their sound as soon as they are loaded (before I add them to the stage).

Stopping the movie clip as soon as it is loaded almost solves the problem, but sometimes I still hear the start of the clip. Apparently the clip doesn't wait for completed event handlers to finish before starting...?

// example of stopping a clip after it is loaded
// sound may still be heard for a moment
var loader: Loader = new Loader();
loader.addEventListener(Event.COMPLETE, function(e : *) : void {
    (loader.contentLoaderInfo.content as MovieClip).stop();
});
loader.load("a_loud_movie_clip.swf");

I've also considered muting everything while loading clips, but this places a pretty serious constraint on the rest of the program with respect to when sounds can be played.

Is there a standard solution to this problem? I feel like it should be a very common issue.

Craig Gidney
  • 17,763
  • 5
  • 68
  • 136

1 Answers1

3

Are you preloading because your SWF is located on a web server? If so, one option would be to preload the bytes from the server without actually interpreting them by using a URLLoader. Then later, when you need to display the MovieClip, inject the preloaded bytes (URLLoader.data) into a Loader using Loader.loadBytes to actually instantiate the MovieClip (and start playing, sound and all). There may be a small lag at instantiation-time, but it'll be much less than without a preloader.

Per your question about the type of the loader.content:

package
{
  import flash.display.Sprite;
  import flash.display.Loader;
  import flash.display.Bitmap;
  import flash.display.MovieClip;
  import flash.events.Event;
  import flash.utils.ByteArray;
  import flash.utils.getQualifiedClassName;

  public class tst extends Sprite
  {
    [Embed(source="any_image.png", mimeType="application/octet-stream")]
      private var img_bytes:Class;

    [Embed(source="any_swf.swf", mimeType="application/octet-stream")]
      private var swf_bytes:Class;


    public function tst():void
    {
      var b1:ByteArray = new img_bytes();
      var l1:Loader = new Loader();
      l1.contentLoaderInfo.addEventListener(Event.COMPLETE, function():void {
        trace(getQualifiedClassName(l1.content));
        trace(l1.content is Bitmap);
      });
      l1.loadBytes(b1);

      var b2:ByteArray = new swf_bytes();
      var l2:Loader = new Loader();
      l2.contentLoaderInfo.addEventListener(Event.COMPLETE, function():void {
        trace(getQualifiedClassName(l2.content));
        trace(l2.content is MovieClip);
      });
      l2.loadBytes(b2);

    }
  }
}

(NOTE: this is merely demonstrative code - DO NOT add event listeners with a function closure like this, it will cause memory leaks. I'm also skipping the URLLoader and simply embedding to get my bytes, but they're the same bytes either way).

And the output is:

flash.display::Bitmap
true
test_fla::MainTimeline
true

However, Bitmap and MovieClip aren't the only options for loader.content - loading a SWF compiled from code can be derived from Sprite and may show the fully qualified class name, though as www0z0k points out, they're all derived from DisplayObject. Though I believe any SWF output from Flash Professional is always derived from MovieClip.

Per your note that Loader bytes are always MovieClips

It turns out you're right - a Loader that loads a regular old image, the contentLoaderInfo.bytes property contains the bytes for a generated SWF that is a simple MovieClip wrapper with the image as a Child. Who would have guessed?!

  var l3:Loader = new Loader();
  l3.contentLoaderInfo.addEventListener(Event.COMPLETE, function():void {
    trace("- l3:");
    trace(getQualifiedClassName(l3.content));
    trace(l3.content is Bitmap);

    // This ByteArray is a SWF!
    var b3:ByteArray = l3.contentLoaderInfo.bytes;

    var l4:Loader = new Loader();
    l4.contentLoaderInfo.addEventListener(Event.COMPLETE, function():void {
      trace("- l4:");
      trace(getQualifiedClassName(l4.content));
      trace(l4.content is Bitmap);
      trace(Object(l4.content).getChildAt(0) is Bitmap);
    });
    l4.loadBytes(b3);
    addChild(l4);

  });
  l3.load(new URLRequest("any_image.png"));

Output is:

- l3:
flash.display::Bitmap
true
- l4:
flash.display::MovieClip
false
true
Jeff Ward
  • 16,563
  • 6
  • 48
  • 57
  • This is exactly the sort of thing I was looking for. – Craig Gidney Apr 23 '12 at 18:20
  • Related issue: if I don't actually load the object, then what's the best way to determine if it's supposed to be an image? I've noticed in the past that loading a byte array of image data produces a movie clip. – Craig Gidney Apr 23 '12 at 18:23
  • I'm not sure I understand your question exactly, but you after use Loader.loadBytes you'll get a Event.COMPLETE, at which time Loader.content should either be a Bitmap (if the bytes were a JPEG or PNG, for example) or a MovieClip (if the bytes were a SWF), so you'd know whether you could interact (stop/play) with it or not. – Jeff Ward Apr 23 '12 at 20:31
  • `Loader.content` is a `DisplayObject`, not a `MovieClip` really – www0z0k Apr 23 '12 at 23:08
  • Well, I'll try it. The last time I tried storing the bytearray and loading it later it kept converting images into movie clips. I worked around it by checking the type of the content the byte array came from. – Craig Gidney Apr 23 '12 at 23:46
  • Very strange. When I use the ByteArray from the URLLoader it works but when I use the ByteArray from a Loader it turns into a MovieClip. In any case this solution worked (after I set the loader's data format to binary). Thanks! – Craig Gidney Apr 25 '12 at 17:45
  • Strilanc, I verified (see addition to answer) that the bytes retrieved from Loader.contentLoaderInfo.bytes do, in fact, generate a MovieClip that wraps the loaded image. How odd! – Jeff Ward Apr 25 '12 at 21:45