Вопрос

I am having a problem with my flash/as3 application. I've created most of it and at the moment I'm struggling to get my external resources work.

My application consists of a Controller class, that takes control of an application flow. At the beginning it initializes AppInitializer class, that loads / generates the whole app content (it is a simple point'n'click game).

In AppInitializer I create an array of items available in a game. Item's constructor takes path as a parameter (String) to the resource (image). Then, inside the constructor I call a static method of my AssetsLoader class which looks like that:

public static function loadImage(path:String):BitmapData
    {
        completed = false;
        var loader:Loader = new Loader();
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event){completed = true;
        trace("Loading completed");
        e.target.removeEventListener(Event.COMPLETE, check);});
        if (path == "")
            loader.load(new URLRequest("images/default.png"));
        else
            loader.load(new URLRequest("images/" + path));
        //while (!completed);
        var image:Bitmap = new Bitmap((loader.content as BitmapData));
        return image.bitmapData;
    }

Where completed is a static variable of AssetsLoader.

First problem is: I create many Item objects in a row, so the method loadImage should not be static I guess (same with completed variable), since that may cause problems when loading.

Second problem is: At the moment I'm unable to return the bitmapData (or bitmap, it does not really matter), because the return statement will always return null - because the resource is loaded asynchronously and is not loaded at the time application reaches return statement. If I uncomment the wile loop, the complete event is never called.

I would like to ask more experienced ActionScript developers to point out any solutions that would require minimal changes to the rest of my app and would solve those problems. (I think first problem can be eliminated by using some sort of queueing method for the loader, but I'm stuck with the second problem for few days so far and came up with no possible solution).

I could also consider changes in my logic, so I could preload all image resources into "somewhere" and after that just make copies of these images for my purposes. If that's easier to do.

Это было полезно?

Решение

So as I suggested in the comments, a minimal change resolution could simply be to pass a function to call as part of the parameters for loadImage(). This is known as a callback function, and it would look something like this:

First create the callback function:

public function addImage( bitmapData:BitmapData ):void {
    //do something with the bitmapData returned
}

Next adjust your loadImage() local function to use the callback with the bitmap data when the event has completed:

public static function loadImage(path:String, callback:Function):BitmapData {
    completed = false;
    var loader:Loader = new Loader();
    loader.contentLoaderInfo.addEventListener(Event.COMPLETE, function(e:Event){completed = true;
    trace("Loading completed");
    var image:Bitmap = new Bitmap((loader.content as BitmapData));
    callback( image ); //call the callback with the bitmap
    e.target.removeEventListener(Event.COMPLETE, check);});
    if (path == "")
        loader.load(new URLRequest("images/default.png"));
    else
        loader.load(new URLRequest("images/" + path));
}

Then just you make the call to loadImage() like so:

loadImage( myPathToImage, addImage );

That is a simply resolution and does exactly what you need it to.

Другие советы

Super, you commented that insane line of code with while ;) Here for you, simple QueueLoader (It loads items one by one, when you add item to the queue, you can store id of item in queue), that will help you with your task:

package {

    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;

    public class StackOverflow extends Sprite {

        public function StackOverflow() {
            addEventListener(Event.ADDED_TO_STAGE, onAdded);
        }

        private function onAdded(e:Event):void {
            removeEventListener(Event.ADDED_TO_STAGE, onAdded);

            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;

            setup();
        }

        private function setup():void {
            //Store somewhere reference on QueueLoader to reuse it, and use queue
            var loader:QueueLoader = new QueueLoader();
            loader.addEventListener(QueueLoaderEvent.COMPLETE, onCompleteItem);
            //Add as many images to load as you want, and store Id's of items that
            //will be loaded in future, if you want...
            loader.addItem("someUrl1");
            loader.addItem("someUrl2");
            var importantId:int = loader.addItem("someUrl3");
            loader.addItem("someUrl4");
            loader.addItem("someUrl6");
        }

        private function onCompleteItem(e:QueueLoaderEvent):void {
            trace("Item loaded");
        }
    }
}

import flash.display.Loader;
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.IEventDispatcher;
import flash.net.URLRequest;

internal class QueueLoader extends EventDispatcher {
    private var _list:Array;
    private var _cursor:int;
    private var _loading:Boolean;

    public function QueueLoader(target:IEventDispatcher = null) {
        super(target);
        _list = [];
    }

    public function addItem(url:String):int {
        var item:Object = {url: url, id: ++_cursor};
        _list.push(item);
        loadNext();
        return item.id;
    }

    override public function dispatchEvent(evt:Event):Boolean {
        if (hasEventListener(evt.type) || evt.bubbles) {
            return super.dispatchEvent(evt);
        }
        return true;
    }

    protected function loadNext():void {
        if (_list.length > 0 && !_loading) {
            var loader:Loader = new Loader();
            var data:Object = _list[0];
            var request:URLRequest = new URLRequest(data.url);
            loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
            loader.load(request);
            _loading = true;
        }
    }

    private function onComplete(e:Event):void {
        var data:Object = _list.shift();
        data.content = e.currentTarget.content;
        dispatchEvent(new QueueLoaderEvent(QueueLoaderEvent.COMPLETE, data.id, data));
        _loading = false;
        loadNext();
    }
}

internal class QueueLoaderEvent extends Event {

    public static const COMPLETE:String = "queueLoaderEventComplete";

    private var _id:int;
    private var _data:Object;

    public function QueueLoaderEvent(type:String, $id:int, $data:Object, bubbles:Boolean = false, cancelable:Boolean = false) {
        _id = $id;
        _data = $data;
        super(type, bubbles, cancelable);
    }

    override public function clone():Event {
        return new QueueLoaderEvent(type, id, data, bubbles, cancelable);
    }

    public function get id():int {
        return _id;
    }

    public function get data():Object {
        return _data;
    }
} 

Your loadImage mehtod will look at the end:

public static function loadImage(path:String):int
{
    return queueLoader.addItem(path);
}
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top