0

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

        }
    }
David
  • 703
  • 1
  • 15
  • 31

3 Answers3

1

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

    }
}
Dean North
  • 3,741
  • 2
  • 29
  • 30
  • Hi Dean, thanks! Here's what I discovered. I kept getting this crazy 'Breakpoint not set; No executable code at line xx' error which I didn't think could be doing much. Turned out it was the problem. The breakpoints had not been set in FB4, but rather in CS5.5. When I cleared them, it ran normally. And thanks for the reminder about GPU -- it does make a difference. -- David – David May 05 '11 at 16:01
1

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);
}
Marty
  • 39,033
  • 19
  • 93
  • 162
  • Hi Marty, thanks -- see my above comment to Dean. I did switch to for each(Object in Array). One question -- if I've got these as objects, does it matter if I convert them to Class? Where you've got var i:ArrWordsObjectClass I would just have in the code the following class ArrWordsObjectClass {}. Is there any reason to think that's better than this -- var i:Object; for each(i in arrWordObjects) – David May 05 '11 at 16:06
  • Speed of for each loop will be increased as you make the type stricter. For example - using Object on an Array full of MovieClips means that for each iteration your loop will have to work out that yes this item is an object, but it's actually a: **MovieClip** -> Sprite -> DisplayObjectContainer -> InteractiveObject -> DisplayObject -> EventDispatcher -> **Object**. Defining your type as far up the inheritance chain as possible is what makes this much more efficient. – Marty May 05 '11 at 23:03
  • 'Up' the inheritance chain really means going all the way 'up' to MovieClip, right? This has always struck me as backwards. I inherit from my grandfather, who's above me on my family tree. But I digress. :) By simply specifying a random class type, such as 'class ArrWordsObjectClass {}' I would achieve the same efficiency as if I directly referenced a built-in class such as Sprite? – David May 06 '11 at 14:48
0

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

chubbsondubs
  • 37,646
  • 24
  • 106
  • 138