How to reduce the speed of flash memory by 600 megabytes of memory?

I am creating a flash game in ActionScript 3 with an infinite universe. Since the universe is infinite, the background is created dynamically using the following background engine:

BackgroundEngine.as

package com.tommedema.background
{
    import br.com.stimuli.loading.BulkLoader;

    import com.tommedema.utils.Settings;
    import com.tommedema.utils.UtilLib;

    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.TimerEvent;
    import flash.utils.Timer;

    public final class BackgroundEngine extends Sprite
    {
        //general
        private static var isLoaded:Boolean = false;        
        private static var bulkLoader:BulkLoader = new BulkLoader("backgroundEngine");
        private static var assetsBitmapData:Array = [];
        private static var drawTimer:Timer;

        //objects
        private static var masterContainer:Sprite;
        private static var containers:Array = [];

        //stage
        private static var stageWidth:uint;
        private static var stageHeight:uint;
        private static var stageCenterX:Number;
        private static var stageCenterY:Number;

        //moves the background X and Y coord
        public static function moveXY(xAmount:Number, yAmount:Number):void
        {
            if (!masterContainer) return;
            if (xAmount != 0) masterContainer.x += xAmount;
            if (yAmount != 0) masterContainer.y += yAmount;
        }

        //returns whether the background engine has been loaded already
        public static function loaded():Boolean
        {
            return isLoaded;
        }

        //loads the background engine
        public final function load():void
        {
            if (isLoaded) return;
            UtilLib.log("BackgroundEngine load.");

            //set stage width, height and center
            stageWidth = stage.stageWidth;
            stageHeight = stage.stageHeight;
            stageCenterX = stageWidth / 2;
            stageCenterY = stageHeight / 2;

            //load drawing timer
            drawTimer = new Timer(Settings.BG_DRAW_IV);
            drawTimer.addEventListener(TimerEvent.TIMER, updateBackground, false, 0, true);
            drawTimer.start();

            //retrieve background assets
            if ((bulkLoader.get("background/4.png")) && (bulkLoader.get("background/4.png").isLoaded))
            {
                loadAssets();
            }
            else
            {
                bulkLoader.add(Settings.ASSETS_PRE_URL + "background/1.gif", {id: "background/1.gif"});
                bulkLoader.add(Settings.ASSETS_PRE_URL + "background/2.png", {id: "background/2.png"});
                bulkLoader.add(Settings.ASSETS_PRE_URL + "background/3.png", {id: "background/3.png"});
                bulkLoader.add(Settings.ASSETS_PRE_URL + "background/4.png", {id: "background/4.png"});
                bulkLoader.addEventListener(BulkLoader.COMPLETE, assetsComplete, false, 0, true);
                bulkLoader.start();
            }

            //set isLoaded to true
            isLoaded = true;
        }

        //unloads the background engine
        public final function unload():void
        {
            if (!isLoaded) return;
            UtilLib.log("BackgroundEngine unload method has been called.");

            //unload drawTimer
            drawTimer.removeEventListener(TimerEvent.TIMER, updateBackground);
            drawTimer.stop();
            drawTimer = null;

            //clean the asset array
            assetsBitmapData = [];

            //remove containers
            for each (var container:Sprite in containers)
            {
                if (container)
                {
                    masterContainer.removeChild(container);
                    container = null;                   
                }
            }
            containers = [];

            //remove master container
            if (masterContainer)
            {
                removeChild(masterContainer);
                masterContainer = null; 
            }

            //set isLoaded to false
            isLoaded = false;
        }

        //updates the background
        private final function updateBackground(event:TimerEvent):void
        {
            if (masterContainer)
            {
                collectGarbage();
                drawNextContainer();    
            }   
        }

        //poller function for drawing next background squares
        private static function drawNextContainer():void
        {
            var curContainer:Sprite = hasBackground(stageCenterX, stageCenterY);
            if (curContainer)
            {
                if (!hasBackground(stageCenterX - stageWidth * 0.75, stageCenterY - stageHeight * 0.75)) //top left
                    drawNewSquare(curContainer.x - curContainer.width, curContainer.y - curContainer.height);
                if (!hasBackground(stageCenterX, stageCenterY - stageHeight * 0.75)) //top
                    drawNewSquare(curContainer.x, curContainer.y - curContainer.height);
                if (!hasBackground(stageCenterX + stageWidth * 0.75, stageCenterY - stageHeight * 0.75)) //top right
                    drawNewSquare(curContainer.x + curContainer.width, curContainer.y - curContainer.height);
                if (!hasBackground(stageCenterX - stageWidth * 0.75, stageCenterY)) //center left
                    drawNewSquare(curContainer.x - curContainer.width, curContainer.y);
                if (!hasBackground(stageCenterX + stageWidth * 0.75, stageCenterY)) //center right
                    drawNewSquare(curContainer.x + curContainer.width, curContainer.y);
                if (!hasBackground(stageCenterX - stageWidth * 0.75, stageCenterY + stageHeight * 0.75)) //bottom left
                    drawNewSquare(curContainer.x - curContainer.width, curContainer.y + curContainer.height);
                if (!hasBackground(stageCenterX, stageCenterY + stageHeight * 0.75)) //bottom center
                    drawNewSquare(curContainer.x, curContainer.y + curContainer.height);
                if (!hasBackground(stageCenterX + stageWidth * 0.75, stageCenterY + stageHeight * 0.75)) //bottom right
                    drawNewSquare(curContainer.x + curContainer.width, curContainer.y + curContainer.height);
            }
        }

        //draws the next square and adds it to the master container
        private static function drawNewSquare(x:Number, y:Number):void
        {
            containers.push(genSquareBg());
            var cIndex:uint = containers.length - 1;
            containers[cIndex].x = x;
            containers[cIndex].y = y;
            masterContainer.addChild(containers[cIndex]);
        }

        //returns whether the given location has a background and if so returns the corresponding square
        private static function hasBackground(x:Number, y:Number):Sprite
        {
            var stageX:Number;
            var stageY:Number;
            for(var i:uint = 0; i < containers.length; i++)
            {
                stageX = masterContainer.x + containers[i].x;
                stageY = masterContainer.y + containers[i].y;
                if ((containers[i]) && (stageX < x) && (stageX + containers[i].width > x) && (stageY < y) && (stageY + containers[i].height > y)) return containers[i];
            }
            return null;
        }

        //polling function for old background squares garbage collection
        private static function collectGarbage():void
        {
            var stageX:Number;
            var stageY:Number;
            for(var i:uint = 0; i < containers.length; i++)
            {
                if ((containers[i]) && (!isRequiredContainer(containers[i])))
                {
                    masterContainer.removeChild(containers[i]);
                    containers.splice(i, 1);
                }
            }
        }

        //returns whether the given container is required for display
        private static function isRequiredContainer(container:Sprite):Boolean
        {
            if (hasBackground(stageCenterX, stageCenterY) == container) //center
                return true;
            if (hasBackground(stageCenterX - stageWidth * 0.75, stageCenterY - stageHeight * 0.75) == container) //top left
                return true;
            if (hasBackground(stageCenterX, stageCenterY - stageHeight * 0.75) == container) //top
                return true;
            if (hasBackground(stageCenterX + stageWidth * 0.75, stageCenterY - stageHeight * 0.75) == container) //top right
                return true;
            if (hasBackground(stageCenterX - stageWidth * 0.75, stageCenterY) == container) //center left
                return true;
            if (hasBackground(stageCenterX + stageWidth * 0.75, stageCenterY) == container) //center right
                return true;
            if (hasBackground(stageCenterX - stageWidth * 0.75, stageCenterY + stageHeight * 0.75) == container) //bottom left
                return true;
            if (hasBackground(stageCenterX, stageCenterY + stageHeight * 0.75) == container) //bottom center
                return true;
            if (hasBackground(stageCenterX + stageWidth * 0.75, stageCenterY + stageHeight * 0.75) == container) //bottom right
                return true;
            return false;
        }

        //dispatched when all assets have finished downloading
        private final function assetsComplete(event:Event):void
        {
            loadAssets();
        }

        //loads the assets
        private final function loadAssets():void
        {
            assetsBitmapData.push(bulkLoader.getBitmapData("background/1.gif")); //stars simple
            assetsBitmapData.push(bulkLoader.getBitmapData("background/2.png")); //star bright
            assetsBitmapData.push(bulkLoader.getBitmapData("background/3.png")); //cloud white
            assetsBitmapData.push(bulkLoader.getBitmapData("background/4.png")); //cloud red
            init();
        }

        //initializes startup background containers
        private final function init():void
        {
            masterContainer = new Sprite(); //create master container

            //generate default background container
            containers.push(genSquareBg()); //top left
            containers[0].x = 0;
            containers[0].y = 0;
            masterContainer.addChild(containers[0]);

            //display the master container
            masterContainer.x = -(stageWidth / 2);
            masterContainer.y = -(stageHeight / 2);
            masterContainer.cacheAsBitmap = true;
            addChild(masterContainer);
        }       

        //generates a background square
        private static function genSquareBg():Sprite
        {
            var width:Number = stageWidth * 2;
            var height:Number = stageHeight * 2;
            var startX:Number = 0;
            var startY:Number = 0;

            var scale:Number;
            var drawAmount:uint;
            var tmpBitmap:Bitmap;
            var i:uint;

            //create container
            var container:Sprite = new Sprite();

            //show simple stars background
            tmpBitmap = UtilLib.copyDataToBitmap(assetsBitmapData[0], false, 0x000000);
            tmpBitmap.x = startX;
            tmpBitmap.y = startY;
            container.addChild(tmpBitmap);

            //draw bright stars
            drawAmount = UtilLib.getRandomInt(1, 2);
            for(i = 1; i <= drawAmount; i++)
            {
                tmpBitmap = UtilLib.copyDataToBitmap(assetsBitmapData[1], true, 0x000000);
                tmpBitmap.alpha = UtilLib.getRandomInt(3, 7) / 10;
                tmpBitmap.rotation = UtilLib.getRandomInt(0, 360);
                scale = UtilLib.getRandomInt(3, 10) / 10;
                tmpBitmap.scaleX = scale; tmpBitmap.scaleY = scale;
                tmpBitmap.x = UtilLib.getRandomInt(startX + tmpBitmap.width, width - tmpBitmap.width);
                tmpBitmap.y = UtilLib.getRandomInt(startY + tmpBitmap.height, height - tmpBitmap.height);
                container.addChild(tmpBitmap);
            }

            //draw white clouds
            drawAmount = UtilLib.getRandomInt(2, 4);
            for(i = 1; i <= drawAmount; i++)
            {
                tmpBitmap = UtilLib.copyDataToBitmap(assetsBitmapData[2], true, 0x000000);
                tmpBitmap.alpha = UtilLib.getRandomInt(3, 10) / 10;
                scale = UtilLib.getRandomInt(15, 40);
                tmpBitmap.scaleX = scale / 10;
                tmpBitmap.scaleY = UtilLib.getRandomInt(scale / 1.5, scale * 1.5) / 10;
                tmpBitmap.x = UtilLib.getRandomInt(startX, width - tmpBitmap.width);
                tmpBitmap.y = UtilLib.getRandomInt(startY, height - tmpBitmap.height);
                container.addChild(tmpBitmap);
            }

            //draw red clouds
            drawAmount = UtilLib.getRandomInt(0, 2);
            for(i = 1; i <= drawAmount; i++)
            {
                tmpBitmap = UtilLib.copyDataToBitmap(assetsBitmapData[3], true, 0x000000);
                tmpBitmap.alpha = UtilLib.getRandomInt(3, 10) / 10;
                scale = UtilLib.getRandomInt(5, 40) / 10;
                tmpBitmap.scaleX = scale; tmpBitmap.scaleY = scale;
                tmpBitmap.x = UtilLib.getRandomInt(startX, width - tmpBitmap.width);
                tmpBitmap.y = UtilLib.getRandomInt(startY, height - tmpBitmap.height);
                container.addChild(tmpBitmap);
            }

            //convert all layers to a single bitmap layer and return
            return container;
        }
    }
}

UtilLib.as copyDataToBitmap function:

//copies bitmap data and returns a new bitmap
        public static function copyDataToBitmap(bitmapData:BitmapData, transparency:Boolean = false, flatBackground:uint = 0x000000):Bitmap
        {
            var width:Number = bitmapData.width;
            var height:Number = bitmapData.height;
            var tmpBitmapData:BitmapData = new BitmapData(width, height, transparency, flatBackground);
            tmpBitmapData.copyPixels(bitmapData, new Rectangle(0, 0, width, height), new Point(0, 0));
            return new Bitmap(tmpBitmapData);
        }

The background images used are about 30 kilobytes, but some of them are very large: alt text http://feedpostal.com/client/assets/background/1.gif alt text http://feedpostal.com/client/assets/background/ 2.png alt text http://feedpostal.com/client/assets/background/3.png alt text http://feedpostal.com/client/assets/background/4.png

, , , . .

, Flex 3, 20 ( 1 ), , , , , 600 , 20 . , 8 64- , , ?

, . ( Photoshop).

+3
4

, .

GCs: , GC, ( ), , ( ), , - . , , GC, , , , . , Flash GC, , , , , , , , "", , - , , .

: , , , , , dispose() bitmapData. , BMD, , , .

: , (, , ), " " . , ( ) , copyPixels() (), . , displayObjects (, ) , , .. (, -, ).

framebuffer. , , , . (, , - , , .)

+4

GC - , Flash Player . , BitmapData GC, . FP GC.

GC, GC CPU/RAM ( ).


Update:

, , :

  • GC ( )

. .

+4

,

 try {
    new LocalConnection().connect('foo');
    new LocalConnection().connect('foo');
 } catch (e:*) {}
 // the GC will perform a full mark/sweep on the second call.

,

flash- gc() flash.system.System.

+2

. , , .

, 500x500 32 500x500x32 = 7,6 . , .

, , , . flash-, , .

Flash, , , , , , , , . , 8 , GC .

+1

Source: https://habr.com/ru/post/1718594/


All Articles