开发者

Preloader in actionscript 3 - Reference Error on getDefinition()

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?


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.


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 ?


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);
    }
    }


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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜