3

I'm writing a preloader:

package {
    import flash.display.DisplayObject;
    import flash.display.Sprite;
    import flash.text.TextField;
    import flash.display.LoaderInfo;
    import flash.events.MouseEvent;
    import flash.events.Event;
    import flash.events.ProgressEvent;
    import flash.system.System;

    public class Preloader extends Sprite {
            private var t:TextField = new TextField();
            private var ver:String = "1000";
            public function Preloader() {
                    t.y = 570;
                    addChild( t );
                    t.text = ver;

                    addEventListener( Event.ADDED_TO_STAGE, init );
            }

            private function init( e:Event ):void {
                    this.root.loaderInfo.addEventListener( ProgressEvent.PROGRESS, onLoadingProgress );
                    this.root.loaderInfo.addEventListener( Event.COMPLETE, onLoadingCompleted );

                    // See if it's already completed
                    var percent:int = int( root.loaderInfo.bytesLoaded / root.loaderInfo.bytesTotal );
                    if ( percent == 1 )
                            onLoadingCompleted();
            }

            private function onLoadingProgress( event:Event ):void {
                    var percent:int = int(root.loaderInfo.bytesLoaded / root.loaderInfo.bytesTotal * 100);
                    t.text = "Loading.. " + percent + "%";
            }

            private function onLoadingCompleted( event:Event = null ):void {
                    root.loaderInfo.removeEventListener( ProgressEvent.PROGRESS, onLoadingProgress );
                    root.loaderInfo.removeEventListener( Event.COMPLETE, onLoadingCompleted );

                    var mainClass:Class = loaderInfo.applicationDomain.getDefinition("Main") as Class;
                    var main:DisplayObject = new mainClass() as DisplayObject;

                    parent.addChild( main );
                    parent.removeChild( this );
            }
    }
}

with this as the Main class:

package {
    import flash.display.Sprite;

    public class Main extends Sprite {

            public function Main() {
            }
    }
}

so this is close to as barebones as I could make it.

However, it greets me with a:

ReferenceError: Error #1065: Variable Main is not defined.
at flash.system::ApplicationDomain/getDefinition()
at ...

I use frames.frame to insert Main already. I am compiling using ant and the linux SDK directly (mxmlc). Am I missing anything obvious?

Timmy
  • 12,468
  • 20
  • 77
  • 107

4 Answers4

4

When you're making a preloader in this "style" what really happens is that the preloader is put in the first frame of the application, and the rest in a second frame. What you're missing here is to tell the compiler that you want your Main class compiled in, so right now it doesn't even exist in the swf. That's why getDefinition won't work.

Nor can you simply refer to it in the preloader since that would make it load in the first frame before the preloader can be shown. So, you need a little custom argument magic.

Add this line to your compiler options and you should be good to go:

-frame start Main

Remember that if your Main class is in a package you need to get a full reference in there:

-frame start com.grapefrukt.examples.Main

Same goes for the getDefinition call.

EDIT:

When looking over my code that does this I see that I use a different approach from what you did, maybe this works better:

var mainClass:Class = getDefinitionByName("com.grapefrukt.examples.Main") as Class;
addChild(new mainClass() as DisplayObject);

EDIT AGAIN: If it works using a button I'd guess the complete event is fired too early for some reason. It may be so that everything is not inited properly event though all the bytes are loaderd. Try using this code to check for completion instead:

if (currentFrame == totalFrames) onLoadingCompleted()

It might also be a good idea to add a stop() command in your onLoadingCompleted() method just to make the playhead won't be screwing things up, but that's a later issue really.

grapefrukt
  • 27,016
  • 6
  • 49
  • 73
  • Cool! I didn't know that. I hope this solves Timmy's question – George Profenza Nov 18 '09 at 12:19
  • I already do this, unfortunately. I wasn't clear when I said "I use frames.frame to insert Main". – Timmy Nov 18 '09 at 13:03
  • Nope, no luck. I wonder if there's some sort of timing thing involved - I added a "start" button with an onclick and it worked every time. Still trying to avoid going that route though, and the above is pretty much as simple as I can make it. – Timmy Nov 18 '09 at 15:44
  • The code also appears to work in FlashDevelop, but not when I do it straight from mxmlc. Odd. – Timmy Nov 18 '09 at 17:56
  • 1
    Ya, I was thinking the same thing. I rewrote it (as a MovieClip) and checking for frames and it seems to work okay now. Thanks! – Timmy Nov 18 '09 at 18:51
1

This is a bit long for a comment, although it's really an expansion on grapefrukt's answer. It's also a rather late answer. But I haven't seen this documented anywhere by Adobe or anyone else:

One reason why Event.COMPLETE may seem to be triggered "early" (before the next frame is actually ready) is when using runtime shared libraries. Event.COMPLETE will be triggered when the current swf is done loading - which may (and usually does) happen before any RSL's are done loading.

RSL's loaded "automatically" (e.g. using "Import for runtime sharing" in Flash Pro) will be loaded when the library symbols linking to them are loaded, which - in the usual preloader implementation - means when the playhead tries to enter frame 2.

Until they're finished loading, you cannot advance to frame 2 - and as long as you can't advance, you won't have access to any classes or instances on that frame. Any call to gotoAndStop(2) or nextFrame() will not have any immediate effect (i.e., eLouai's answer won't work in this case), and what's more, the call won't be "queued" either. The playhead will simply stay on frame 1. It may be that internally some reference errors occur that are swallowed somewhere in FlashPlayer. At least in my tests, an error is never reported. It just silently fails. play(), however, will go to the next frame - when it's ready.

In other words - as grapefrukt suggests - if the swf is playing, you can wait for currentFrame == 2 (or whatever frame number) in an ENTER_FRAME listener. Alternatively, you can call nextFrame() or gotoAndStop(2) (or whatever frame number) in an ENTER_FRAME listener until detecting that currentFrame == 2. The latter seems like something that might potentially break in the future, since, again, errors may actually be occurring internally, although the player is not outputting any.

In short:

play() will wait until frame 2 is ready. gotoAndStop(2) or nextFrame() will (appear to) have no effect whatsoever unless frame 2 is ready when called. And Event.COMPLETE doesn't guarantee that all frames are ready to display. In particular not if using RSL's - there may be other reasons.

JimmiTh
  • 7,389
  • 3
  • 34
  • 50
1

Is it any different if you have the Game class in the default package("Game" instead of "game.Game") ?

I've never used anything like new mainClass( this.root ), new mainClass() should be fine.

Also, is there any difference if you use Event.INIT instead of Event.COMPLETE ?

George Profenza
  • 50,687
  • 19
  • 144
  • 218
  • Moving the package = same result. I'm passing in the root as a variable to the class (so I can read the flashvars) Event.INIT didn't change anything. Thanks. – Timmy Nov 17 '09 at 23:38
  • is class Game public ? Also, is this handy : http://www.kirupa.com/forum/showthread.php?t=274871 ? – George Profenza Nov 18 '09 at 00:11
  • Ya, it's public. I find that if I added a button or a timer it seems to find it. It's almost as if it fires the events before it is actually ready.. – Timmy Nov 18 '09 at 04:05
  • Thanks for the link, but no go.. =( – Timmy Nov 18 '09 at 04:08
1

You are missing nextFrame();
In the function onLoadingComplete add nextFrame();

Here is a bare bones version:

    public class Preloader extends MovieClip 
    {
    public function Preloader() 
    {
        stop();
        addEventListener(Event.ENTER_FRAME, onEnterFrame);
        this.loaderInfo.addEventListener(Event.COMPLETE, startup);
        // Set up progress bar / graphics
    }

    private function onEnterFrame(e:Event):void 
    {
        // Display progress bar, loadedbytes, animation, etc...
    }

    private function startup(e:Event):void 
    {
        nextFrame();
        removeEventListener(Event.ENTER_FRAME, onEnterFrame);
        loaderInfo.removeEventListener(Event.COMPLETE, startup);
        var gameClass:Class = getDefinitionByName("Main") as Class;
        if (gameClass) addChild(new gameClass() as Sprite);
    }
    }

eLouai
  • 696
  • 1
  • 8
  • 21