A loop in enterframe?

4k views Asked by At

I'm animating a bunch of words in AS3. Because I'm going to be using this on a mobile device, I want to use bitmaps rather than Sprites. So I've created WordObjects, which have a .bitmap property that I can access.

I have the following code, which fires on the click event and loops through an array inside an enterframe event. This is probably a bad idea, but I'm not sure how to do it better. (What is surprising is that it runs just fine in Flashbuilder, but slows to a crawl in Flash CS5.)

Is there some better way to do this? I just want an efficient way to animate the array of bitmaps.

    private function clickhandler (e:MouseEvent){

        this.addEventListener(Event.ENTER_FRAME, blowemup);
    }
    private function blowemup(e:Event){
        var newPosition:Number;
        for(var i:int=0; i<arrWordObjects.length; i++)
        {
            newPosition = updatePosition(arrWordObjects[i].bitmap);
            arrWordObjects[i].bitmap.x += newPosition;
            arrWordObjects[i].bitmap.y += getRandomNumber();

        }
    }
3

There are 3 answers

3
Marty On BEST ANSWER

Something that will make a huge difference is using for each(Object in Array) rather than the standard for loop.

private function blowemup(e:Event):void
{
    var newPosition:Number;

    var i:ArrWordsObjectClass; // <-- don't know what the class for this is, just replace
    for each(i in arrWordObjects)
    {
        newPosition = updatePosition(i.bitmap);
        i.bitmap.x += newPosition;
        i.bitmap.y += getRandomNumber();
    }
}

A for each loop is typed, meaning a lot of time is saved where normally it'd be trying to work out what arrWordObjects[i] is every iteration.

Also, side note: using one ENTER_FRAME driven function and looping through everything in your application that you want to handle each frame is much more efficient than applying hundreds of listeners for objects.

I normally create a handler class that contains the ENTER_FRAME and an array storing my objects, like so:

package
{
    import flash.events.Event;
    import flash.display.Sprite;

    public class Handler extends Sprite
    {
        // vars
        public var elements:Array = [];

        /**
         * Constructor
         */
        public function Handler()
        {
            addEventListener(Event.ENTER_FRAME, _handle);
        }

        /**
         * Called on each dispatch of Event.ENTER_FRAME
         */
        private function _handle(e:Event):void
        {
            var i:Element;
            for each(i in elements)
            {
                i.step();
            }
        }
    }
}

Then I create a base class for all the objects that I want to handle, containing the step() function called above.

package
{
    import flash.display.DisplayObject;

    public class Element extends Object
    {
        // vars
        public var skin:DisplayObject;

        /**
         * Called on each dispatch of Event.ENTER_FRAME at Handler
         */
        public function step():void
        {
            // override me
        }
    }
}

Now just extend Element with your objects:

package
{
    import flash.display.Sprite;

    public class MyThing extends Element
    {
        /**
         * Constructor
         */
        public function MyThing()
        {
            skin = new Sprite();

            skin.graphics.beginFill(0);
            skin.graphics.drawCircle(0,0,40);
            skin.graphics.endFill();
        }

        /**
         * Override step
         */
        override public function step():void
        {
            skin.x += 4;
        }
    }
}

And get it all going!:

var handler:Handler = new Handler();

var m:MyThing;
var i:uint = 0;
for(i; i<10; i++)
{
    m = new MyThing();
    m.y = Math.random()*stage.stageHeight;

    handler.elements.push(m);
    addChild(m.skin);
}
1
chubbsondubs On

I would suggest looking at writing a custom effect on Adobe's website over registering for ENTER_FRAME event. What you've put up there means this code will forever run as long as the program is running. If you wanted to stop the effect or run for 10 frames and stop then you'll have to write more code. It gets even more complex if you want to apply this to several instances. You're going to have to resolve problems that custom effects framework solves.

I'd read how to write custom effects here:

http://livedocs.adobe.com/flex/3/html/help.html?content=createeffects_1.html

1
Dean North On

How many bitmaps do you plan to have on the stage at a time?

I have had 40 900x16px bitmaps animating on the stage at full speed running on my iphone using air 2.6.

I used a foreach loop in an enterframe event which i added on mouseclick and removed once the animation was finished.

Remember to compile it for the mobile with gpu rendering enabled. (gpu in your app.xml if you are using air 2.6)

This is worth a read too, it explains a lot about performance for mobile devices http://help.adobe.com/en_US/as3/mobile/WS901d38e593cd1bac-3d719af412b2b394529-8000.html

Here is a basic example of what I had...

package
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;

    [SWF(frameRate="30", backgroundColor="#FF00FF")]
    public class Test extends Sprite
    {
        private var fields:Vector.<Bitmap> = new Vector.<Bitmap>();

        public function Test()
        {
            this.stage.scaleMode = StageScaleMode.NO_SCALE;
            this.stage.align = StageAlign.TOP_LEFT;

            for(var i:int = 0; i< 37; i++){
                var bd:BitmapData = new BitmapData(960, 16, true, 0x000000);

                bd.fillRect(new Rectangle(0, 0, 900, 16), Math.round( Math.random()*0xFFFFFFFF ));

                var b:Bitmap = new Bitmap(bd);

                b.x = 0;
                b.y = i*16;

                stage.addChild(b);
                fields.push(b);
            }

            stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
        }

        private var inertia:Boolean = false;
        private var yCurrent:Number;
        private var ySpeed:Number;
        private var startY:Number;

        private var cy:Number = 0;

        private function onEnterFrame(e:Event):void{
            if(!inertia){
                ySpeed = (startY - yCurrent) ; // / 16;
                startY = yCurrent
            } else {
                ySpeed *= 0.8;

                if(ySpeed < 0.01 && ySpeed > -0.01){
                    inertia = false;
                    stage.removeEventListener(Event.ENTER_FRAME, onEnterFrame);
                }
            }

            cy += ySpeed;
            if(cy > 640)
                cy -= 640;

            var ty:Number = cy;
            for each(var tf:Bitmap in fields){
                tf.y = ty;
                ty += 16;

                if(ty > 640)
                    ty -= 640;
            }
        }

        private function onMouseDown(e:MouseEvent):void{
            inertia = false;
            startY = e.stageY;
            yCurrent = e.stageY;
            stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
            stage.addEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
            stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
        }

        private function onMouseMove(e:MouseEvent):void{
            yCurrent = e.stageY;
        }

        private function onMouseUp(e:Event):void{
            inertia = true;
            stage.removeEventListener(MouseEvent.MOUSE_MOVE, onMouseMove);
            stage.removeEventListener(MouseEvent.MOUSE_UP, onMouseUp);
        }

    }
}