Оптимизация моего динамического фонового движка для 2d flash-игры в ActionScript-3

Изменить 2: судя по отсутствию ответов, я начинаю задаваться вопросом, достаточно ли ясна моя проблема. Скажите, пожалуйста, мне нужно уточнить детали.

Примечание: обновленный код см. внизу!

Краткое введение. Я пишу двухмерную космическую флеш-игру на ActionScript. Вселенная бесконечно велика, из-за этой особенности фон должен визуализироваться динамически, а фоновые объекты (газовые облака, звезды и т. Д.) Должны располагаться случайным образом.

Я создал класс под названием BackgroundEngine, и он работает очень хорошо, однако проблема заключается в производительности рендеринга. Вот как это работает:

При запуске вокруг игрока создаются 4 фоновых контейнера (каждый размером со сцену). Вверху слева, вверху справа, внизу слева и внизу справа. Все квадраты фона добавляются в основной контейнер для облегчения перемещения фона. Теперь есть 2 функции опроса:

1) «уборщик мусора»: ищет фоновые контейнеры, которые в 2 раза превышают ширину или высоту сцены от координат X или Y игрока соответственно. Если это так, он удалит этот фоновый квадрат и разрешит сборку мусора.

2) «опросчик рендеринга»: проверяет, есть ли в данный момент фон со всех сторон проигрывателя (x - stageWidth, x + stageWidth, y - stageHeight, y + stageHeight). В противном случае он нарисует новый фоновый квадрат в соответствующем месте.

Все фоновые квадраты создаются с помощью следующей функции (те, которые создаются динамически, а четыре при запуске):

‹*** удален старый код, полный обновленный исходный код см. внизу >>>

Все случайности, которые вы видите там, следят за тем, чтобы окружение выглядело уникально на каждом квадрате. Это действительно прекрасно работает, вселенная выглядит потрясающе.

Следующие активы используются в качестве фоновых объектов:

1) Простые звездочки: http://www.feedpostal.com/client/assets/background/1.png (вероятно, вы не сможете увидеть его в браузере с белым фоном).

2) Яркие звезды: http://www.feedpostal.com/client/assets/background/2.png

3) Белые газовые облака: http://www.feedpostal.com/client/assets/background/3.png

4) Красные газовые облака: http://www.feedpostal.com/client/assets/background/4.png

Важные примечания:

1) Все ресурсы кэшируются, поэтому их не нужно постоянно загружать повторно. Они загружаются только один раз.

2) Изображения не вращаются и не масштабируются после создания, поэтому я включил cacheAsBitmap для всех объектов, контейнеров и masterContainer.

3) Мне пришлось использовать форматы PNG в Photoshop, потому что GIF-файлы, казалось, не очень хорошо обрабатывались во флэш-памяти при использовании с прозрачностью.

Итак, проблема в том, что когда я летаю, рендеринг фона требует слишком большой производительности: клиент начинает «отставать» (с точки зрения FPS). Из-за этого мне нужно оптимизировать фоновый движок, чтобы он отображался намного быстрее. Ребята, вы можете мне помочь?

Обновление 1: это то, что я получил после того, как получил один ответ.

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;

    public final class BackgroundEngine extends Sprite
    {
        private static var isLoaded:Boolean = false;
        private static var bulkLoader:BulkLoader = BulkLoader.getLoader("main");
        private static var masterContainer:Sprite;
        private static var containers:Array = [];
        private static var stageWidth:uint;
        private static var stageHeight:uint;
        private static var assets:Array;

        //moves the background's X coord
        public static function moveX(amount:Number):void
        {
            if (masterContainer)
            {
                masterContainer.x += amount;
                collectGarbage();
                drawNextContainer();
            }
        }

        //moves the background's Y coord
        public static function moveY(amount:Number):void
        {
            if (masterContainer)
            {
                masterContainer.y += amount;
                collectGarbage();
                drawNextContainer();
            }
        }

        //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
        {
            //set stage width and height
            stageWidth = stage.stageWidth;
            stageHeight = stage.stageHeight;

            //retreive all background assets
            bulkLoader.add(Settings.ASSETS_PRE_URL + "background/1.png", {id: "background/1.png"});
            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);
            bulkLoader.start();

            //set isLoaded to true
            isLoaded = true;
        }

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

        //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):Bitmap
        {
            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 + stageWidth > x) && (stageY < y) && (stageY + stageHeight > 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])
                {
                    stageX = masterContainer.x + containers[i].x;
                    stageY = masterContainer.y + containers[i].y;
                    if ((stageX < -stageWidth * 1.5) || (stageX > stageWidth * 2.5) || (stageY < -stageHeight * 1.5) || (stageY > stageHeight * 2.5))
                    {
                        containers[i].parent.removeChild(containers[i]);
                        containers.splice(i, 1);
                    }
                }
            }
        }

        //dispatched when all assets have finished downloading
        private final function assetsComplete(event:Event):void
        {
            assets = [];
            assets.push(bulkLoader.getBitmap("background/1.png")); //star simple
            assets.push(bulkLoader.getBitmap("background/2.png")); //star bright
            assets.push(bulkLoader.getBitmap("background/3.png")); //cloud white
            assets.push(bulkLoader.getBitmap("background/4.png")); //cloud red
            init();
        }

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

            //generate default background containers
            containers.push(genSquareBg()); //top left
            containers[0].x = 0;
            containers[0].y = 0;
            containers.push(genSquareBg()); //top
            containers[1].x = stageWidth;
            containers[1].y = 0;
            containers.push(genSquareBg()); //top right
            containers[2].x = stageWidth * 2;
            containers[2].y = 0;
            containers.push(genSquareBg()); //center left
            containers[3].x = 0;
            containers[3].y = stageHeight;
            containers.push(genSquareBg()); //center
            containers[4].x = stageWidth;
            containers[4].y = stageHeight;
            containers.push(genSquareBg()); //center right
            containers[5].x = stageWidth * 2;
            containers[5].y = stageHeight;
            containers.push(genSquareBg()); //bottom left
            containers[6].x = 0;
            containers[6].y = stageHeight * 2;
            containers.push(genSquareBg()); //bottom
            containers[7].x = stageWidth;
            containers[7].y = stageHeight * 2;
            containers.push(genSquareBg()); //bottom right
            containers[8].x = stageWidth * 2;
            containers[8].y = stageHeight * 2;

            //add the new containers to the master container
            for (var i:uint = 0; i <= containers.length - 1; i++)
            {
                masterContainer.addChild(containers[i]);    
            }

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

        //duplicates a bitmap display object
        private static function dupeBitmap(source:Bitmap):Bitmap {
            var data:BitmapData = source.bitmapData;
            var bitmap:Bitmap = new Bitmap(data);
            return bitmap;
        }

        //draws a simple star
    private static function drawStar(x:Number, y:Number, width:uint, height:uint):Sprite
    {
        var creation:Sprite = new Sprite();
        creation.graphics.lineStyle(1, 0xFFFFFF);
        creation.graphics.beginFill(0xFFFFFF);
        creation.graphics.drawRect(x, y, width, height);
        return creation;
    }

    //generates a background square
    private static function genSquareBg():Bitmap
    {
        //set 1% margin
        var width:Number = stageWidth * 0.99;
        var height:Number = stageHeight * 0.99;
        var startX:Number = 0 + stageWidth / 100;
        var startY:Number = 0 + stageHeight / 100;

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

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

        //draw simple stars
        drawAmount = UtilLib.getRandomInt(100, 250);
        for(i = 1; i <= drawAmount; i++)
        {
            tmpSprite = drawStar(0, 0, 1, 1);
            tmpSprite.x = UtilLib.getRandomInt(0, stageWidth);
            tmpSprite.y = UtilLib.getRandomInt(0, stageHeight);
            tmpSprite.alpha = UtilLib.getRandomInt(3, 10) / 10;
            scale = UtilLib.getRandomInt(2, 10) / 10;
            tmpSprite.scaleX = tmpSprite.scaleY = scale;
            container.addChild(tmpSprite);
        }

        //draw bright stars
        if (Math.random() >= 0.8) drawAmount = UtilLib.getRandomInt(1, 2);
        else drawAmount = 0;
        for(i = 1; i <= drawAmount; i++)
        {
            tmpBitmap = dupeBitmap(assets[1]);
            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(1, 4);
        for(i = 1; i <= drawAmount; i++)
        {
            tmpBitmap = dupeBitmap(assets[2]);
            tmpBitmap.alpha = UtilLib.getRandomInt(1, 10) / 10;
            scale = UtilLib.getRandomInt(15, 30);
            tmpBitmap.scaleX = scale / 10;
            tmpBitmap.scaleY = UtilLib.getRandomInt(scale / 2, scale) / 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, 1);
        for(i = 1; i <= drawAmount; i++)
        {
            tmpBitmap = dupeBitmap(assets[3]);
            tmpBitmap.alpha = UtilLib.getRandomInt(2, 6) / 10;
            scale = UtilLib.getRandomInt(5, 30) / 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
        var bitmapData:BitmapData = new BitmapData(stageWidth, stageHeight, true, 0x000000);
        bitmapData.draw(container);
        container = null;
        var bitmapContainer:Bitmap = new Bitmap(bitmapData);
        bitmapContainer.cacheAsBitmap = true;
        return bitmapContainer;
    }
    }
}

Когда игрок движется, фоновые методы moveX и moveY вызываются с обратным направлению игрока. Это также приведет к вызову методов collectGarbage и drawNextContainer.

Проблема с этой настройкой заключается в том, что постоянно активными являются как минимум 9 контейнеров. Верхний левый, верхний, верхний правый, центральный левый, центральный, центральный правый, нижний левый, нижний и нижний правый. Это требует большой производительности.

Изменить: мне также интересно, следует ли использовать cacheAsBitmap? Если да, то на каких изображениях? На контейнерах и главном контейнере или только на одном из них? Когда я включаю его для всех изображений (даже для временных объектов-спрайтов), он на самом деле отстает еще больше.

Обновление 2:

В этой версии используются квадраты, которые в два раза больше сцены. Одновременно следует загружать только один или два квадрата. Это лучше, но я все равно замечаю снижение производительности при движении. Это заставляет клиента замирать на очень короткое время. Есть идеи, как его оптимизировать?

BackgroundEngine2.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;

    public final class BackgroundEngine2 extends Sprite
    {
        //general
        private static var isLoaded:Boolean = false;        
        private static var bulkLoader:BulkLoader = BulkLoader.getLoader("main");
        private static var assets:Array;

        //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's X coord
        public static function moveX(amount:Number):void
        {
            if (!masterContainer) return;
            masterContainer.x += amount;
            collectGarbage();
            drawNextContainer();
        }

        //moves the background's Y coord
        public static function moveY(amount:Number):void
        {
            if (!masterContainer) return;
            masterContainer.y += amount;
            collectGarbage();
            drawNextContainer();
        }

        //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
        {
            //set stage width, height and center
            stageWidth = stage.stageWidth;
            stageHeight = stage.stageHeight;
            stageCenterX = stageWidth / 2;
            stageCenterY = stageHeight / 2;

            //retreive background assets
            bulkLoader.add(Settings.ASSETS_PRE_URL + "background/1.png", {id: "background/1.png"});
            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);
            bulkLoader.start();

            //set isLoaded to true
            isLoaded = true;
        }

        //poller function for drawing next background squares
        private static function drawNextContainer():void
        {
            var curContainer:Bitmap = 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):Bitmap
        {
            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:Bitmap):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
        {
            assets = [];
            assets.push(bulkLoader.getBitmap("background/1.png")); //star simple
            assets.push(bulkLoader.getBitmap("background/2.png")); //star bright
            assets.push(bulkLoader.getBitmap("background/3.png")); //cloud white
            assets.push(bulkLoader.getBitmap("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);
        }

        //duplicates a bitmap display object
        private static function dupeBitmap(source:Bitmap):Bitmap {
            var data:BitmapData = source.bitmapData;
            var bitmap:Bitmap = new Bitmap(data);
            return bitmap;
        }

        //draws a simple star
        private static function drawStar(x:Number, y:Number, width:uint, height:uint):Sprite
        {
            var creation:Sprite = new Sprite();
            creation.graphics.lineStyle(1, 0xFFFFFF);
            creation.graphics.beginFill(0xFFFFFF);
            creation.graphics.drawRect(x, y, width, height);
            return creation;
        }

        //generates a background square
        private static function genSquareBg():Bitmap
        {
            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 tmpSprite:Sprite;
            var i:uint;

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

            //draw simple stars
            drawAmount = UtilLib.getRandomInt(100, 250);
            for(i = 1; i <= drawAmount; i++)
            {
                tmpSprite = drawStar(0, 0, 1, 1);
                tmpSprite.x = UtilLib.getRandomInt(startX, width);
                tmpSprite.y = UtilLib.getRandomInt(startY, height);
                tmpSprite.alpha = UtilLib.getRandomInt(3, 10) / 10;
                scale = UtilLib.getRandomInt(5, 15) / 10;
                tmpSprite.scaleX = tmpSprite.scaleY = scale;
                container.addChild(tmpSprite);
            }

            //draw bright stars
            drawAmount = UtilLib.getRandomInt(1, 2);
            for(i = 1; i <= drawAmount; i++)
            {
                tmpBitmap = dupeBitmap(assets[1]);
                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 = dupeBitmap(assets[2]);
                tmpBitmap.alpha = UtilLib.getRandomInt(1, 10) / 10;
                scale = UtilLib.getRandomInt(15, 40);
                tmpBitmap.scaleX = scale / 10;
                tmpBitmap.scaleY = UtilLib.getRandomInt(scale / 2, scale * 2) / 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 = dupeBitmap(assets[3]);
                tmpBitmap.alpha = UtilLib.getRandomInt(2, 6) / 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
            var bitmapData:BitmapData = new BitmapData(width, height, true, 0x000000);
            bitmapData.draw(container);
            container = null;
            var bitmapContainer:Bitmap = new Bitmap(bitmapData);
            //bitmapContainer.cacheAsBitmap = true;
            return bitmapContainer;
        }
    }
}

person Tom    schedule 27.08.2009    source источник
comment
Кстати, я хотел бы увидеть вашу космическую игру, когда она будет готова ...   -  person RCIX    schedule 01.09.2009
comment
К сожалению, сначала мне придется исправить эту фоновую проблему. Back2Dos оказал мне интересную помощь, но у меня возникло слишком много вопросов, чтобы продолжить без дальнейших комментариев. Сама игра не будет закончена в ближайшие годы, так как это многопользовательская игра, и я только начал изучать основы.   -  person Tom    schedule 01.09.2009
comment
Также, если мне удастся решить все те вопросы, на которые я сначала не ожидал. Например, проблема, о которой идет речь.   -  person Tom    schedule 01.09.2009
comment
эй ... простите, сейчас довольно занят, но я обновлю свой ответ до окончания награды ...;)   -  person back2dos    schedule 03.09.2009
comment
Потрясающе, не торопись, приятель!   -  person Tom    schedule 04.09.2009
comment
ответ обновлен ... теперь мне нужно пиво ... :)   -  person back2dos    schedule 05.09.2009


Ответы (6)


хорошо, это должно показать, что вы действительно можете получить другую категорию чисел с другими подходами ...

ограничение здесь не на количество звезд, ограничение - это плотность, то есть количество звезд, видимых одновременно ... с отключенным текстом я могу получить до 700 @ 30 кадров в секунду на Core2Duo с довольно последней версией проигрывателя отладки ...

Я понял, что flash player не очень хорош в отсечении ... и что на самом деле, используя самый простой способ, вы проводите много времени, перемещая объекты, которые далеко не видны ...

чтобы действительно иметь возможность оптимизировать вещи, я решил использовать здесь MVC ... не классическим раздутым способом ... идея состоит в том, чтобы обрабатывать модель, и если какие-либо элементы видны, создавать для них представления ...

теперь лучший подход - построить пространственное дерево ...

  1. у вас есть листья, содержащие объекты, и узлы, содержащие листья или узлы
  2. если вы добавляете объект к листу, и он превышает определенный размер, вы превращаете его в узел с nxn листьями, перераспределяя его дочерние элементы между
  3. любой объект, добавленный к фону, будет добавлен в сетку, определяемую координатами объекта ... сетки создаются точно в срок, начало как листья

большим преимуществом этого является то, что вы можете быстро изолировать видимые узлы / листья. на каждой итерации интересны только те узлы / листья, которые либо становятся видимыми, либо уже видны (и могут стать невидимыми). вам не нужно делать никаких обновлений в остальной части дерева. после нахождения всех видимых объектов вы создаете представления для объектов, которые становятся видимыми, обновляете положение тех, которые просто остаются видимыми, и уничтожаете представления для объектов, которые становятся невидимыми ...

это очень много всего экономит ... память и вычислительную мощность ... если вы попробуете с огромным размером мира (100000), вы увидите, что у вас быстро закончится оперативная память, задолго до того, как процессор что-нибудь сделает ... создание экземпляра 500000 звезд используют память 700 МБ, из которых видно около 50 звезд, работающие со скоростью 70 кадров в секунду без огромной загрузки процессора ...

движок - это просто доказательство концепции ... и код выглядит ужасно ... единственная особенность, которой я сейчас горжусь, это то, что он поддерживает объект, занимающий определенную область, поэтому объект может быть частью нескольких листов. ... я думаю, что это то, что я удалю, потому что это должно дать мне большую скорость ... вы также видите, он немного тормозит при добавлении звезд, что происходит, когда листы переворачиваются на узлы ...

я совершенно уверен, что это не лучший способ для дерева пространственного деления, просто все, что я нашел, казалось мне бесполезным ... вероятно, тот, кто изучал (и понимал) CS, может улучшить мой подход ...: )

кроме этого, я использовал пул объектов для представлений и Haxe, поскольку он работает лучше ... вещь, которая вероятно, не так уж и умно, это перемещать все виды по отдельности, вместо того, чтобы помещать их на один слой и перемещать его ...

некоторые люди также визуализируют вещи в BitmapDatas вручную, чтобы повысить производительность, что, похоже, работает довольно хорошо (просто не могу найти вопрос прямо сейчас) ... однако вам следует подумать об использовании copyPixels вместо draw ...

надеюсь это поможет ... ;)


edit: я решил превратить свой подробный ответ в сообщение в блоге ... приятного чтения ...;)


person back2dos    schedule 30.08.2009
comment
Приятно видеть, как ты так часто появляешься с такими же замечательными ответами. :) Но у меня есть несколько вопросов. Под листьями вы имеете в виду массив, содержащий все объекты (звезды и т. Д.)? Узлы похожи на контейнеры (другой массив), содержащие массивы с листьями? Если да, я думаю, что это совместимо с моей старой настройкой. Второй вопрос: следует ли мне опрашивать, чтобы узнать, находится ли объект вне диапазона обзора, и если да, установить .visible = false? Вопрос 3: если я добавляю все контейнеры в один главный контейнер (предлагается внизу для перемещения), как мне перемещать только видимые контейнеры по отдельности? И зачем мне это делать, раз уж ... - person Tom; 31.08.2009
comment
... поскольку он изменит только X и Y главного контейнера, не вызывая рендеринга невидимых частей, верно? Это просто переменная. Вопрос 4: ссылка на то, что вы сделали, выглядит красиво. Источник общедоступен? Или я могу посмотреть, как вы это сделали? Возможно, ответит на некоторые мои вопросы. Вопрос 5: что вы имеете в виду под пулом объектов, просто массивом с объектами? Вопрос 6: haXe выглядит отлично, впервые вижу. Вы порекомендуете мне выучить этот язык и заново переписать весь клиент на этом языке? Он предназначен для огромного проекта, 2D-игры, как вы, наверное, уже знаете. Спасибо! - person Tom; 31.08.2009
comment
Похоже, награда скоро закончится, хотя у меня осталось так много вопросов, которые остались без ответа. Однако я не могу принять ваш ответ без них. - person Tom; 03.09.2009

Возможно, вы захотите увидеть, сможете ли вы по ходу собрать все части вместе в плоское растровое изображение. Нарисуйте все слои, а затем используйте метод рисования BitmapData, чтобы объединить их в одно растровое изображение.

Даже с включенным cacheAsBitmap для всех частей Flash все равно приходится комбинировать все эти части в каждом кадре.

person Branden Hall    schedule 27.08.2009
comment
Конечным результатом является уже одно изображение, контейнер. Я не могу создать статическое изображение с помощью внешней программы, поскольку объекты позиционируются, масштабируются, альфа (-ed?) И вращаются случайным образом. - person Tom; 28.08.2009
comment
Что ж, насколько я могу судить по опубликованному вами коду, результатом является спрайт, содержащий несколько растровых изображений. Я не предлагаю вам использовать какую-либо внешнюю программу, вместо этого я предлагаю, чтобы после создания квадрата вы сгладили все растровые изображения в одно растровое изображение, просто используя BitmapData.draw для захвата скомпонованных растровых изображений. Затем вы выбрасываете осколки. Теперь flash нужно преобразовать только одно растровое изображение, а не четыре на квадрат. - person Branden Hall; 28.08.2009
comment
Это определенно первое, что вам стоит попробовать. - person fenomas; 28.08.2009
comment
Если я выброшу контейнеры, как я могу их удалить, когда игрок удаляется от них слишком далеко, но все еще удерживает близлежащие контейнеры? - person Tom; 28.08.2009
comment
Что ж, в том-то и дело - вы их технически не выбрасываете - вы их просто не показываете. Вы никогда не добавляете спрайт, содержащий все ваши активы (звезды, облака и т. Д.), В список отображения. Вместо этого вы просто продолжаете захватывать его части, используя метод рисования BitmapData. Вы просто перемешиваете эти части, как и в любой игре с плитками. - person Branden Hall; 28.08.2009
comment
Понятия не имею, что вы имеете в виду, кроме того, что вы хотите, чтобы я использовал метод BitmapData.draw. Не могли бы вы привести пример или что-то в этом роде? - person Tom; 28.08.2009
comment
Прямо сейчас ваш объект отправляет спрайт. Вместо этого заставьте его отправлять обратно Bitmap. Создайте это растровое изображение, определив объект BitmapData, равный размеру вашего квадрата. Затем заполните его, вызвав myBitmapData.draw (контейнер). Вставьте это в объект растрового изображения - myBimtap = new Bitmap (myBitmapData) и просто верните этот объект. В результате вы превратили каждый квадрат своего мира в одно растровое изображение, а не в десятки. - person Branden Hall; 28.08.2009
comment
О, и вы сделаете это в конце вашего метода, когда вы все создадите. Кроме того, вы можете захотеть сделать квадраты (плитки) меньшего размера. Таким образом можно было создать несколько десятков случайных плиток и просто повторно использовать их. - person Branden Hall; 28.08.2009
comment
Хорошо, у меня проблема в том, что когда я меняю тип контейнера в функции на растровое изображение, я не могу добавлять к нему дочерние элементы (запуски, облака и т. Д.). Как мне тогда добавить их в контейнер растрового изображения? - person Tom; 29.08.2009
comment
Контейнер по-прежнему будет спрайтом. Вы просто записываете спрайт в растровое изображение. Функция просто меняет свой возвращаемый тип. - person Branden Hall; 29.08.2009
comment
Спасибо, думаю, теперь лучше. Однако фактическая загрузка контейнера по-прежнему требует слишком большой производительности. Особенно 500 простых звезд, кажется, очень популярны на выступлениях. Есть ли у вас другие предложения? - person Tom; 29.08.2009
comment
Я подумал, что могу позволить вспышке рисовать эти звезды самостоятельно, поскольку они представляют собой не что иное, как белые пиксели 1x1. Не уверен, что было бы лучше всего, и если это вообще хорошая идея. - person Tom; 29.08.2009
comment
Обратите внимание, что при использовании этого метода растрового изображения максимальное использование памяти составляет 75 МБ вместо 4 МБ. Но я думаю, оно того стоит. - person Tom; 29.08.2009

Попробуйте растянуть окно плеера, как больше, так и меньше. Если это существенно влияет на частоту кадров, самый быстрый и простой способ повысить производительность - это уменьшить размер рабочей области. Это обычно непопулярный ответ, когда его представляют людям, особенно художникам, но если ваше узкое место связано с размером вашей сцены, вы мало что можете сделать в коде, чтобы это исправить.

person Sean    schedule 01.09.2009
comment
Частота кадров averga вообще не проблема. Это мгновенная загрузка нового контейнера, которая вызывает проблему на несколько миллисекунд. - person Tom; 02.09.2009

Что, если вместо того, чтобы уничтожать фоновые квадраты, вы просто поместите их в кучу «готовых к работе» квадратов, которые вы можете рисовать, ограничив их примерно четырьмя? тогда вам не нужно создавать его, когда он вам нужен, просто переместите его в нужное место и, возможно, перемешайте звезды или что-то в этом роде.

[добавил бы пример, но я не пишу код AS3 :(]

person RCIX    schedule 04.09.2009
comment
Поскольку все объекты внутри одного квадрата помещаются в один слой, я не могу изменить ни один из приоритетов отдельных объектов, таких как X и Y каждой звезды. Не помещать их в один слой тоже не вариант, так как это будет стоить слишком много производительности. - person Tom; 04.09.2009
comment
Хорошая точка зрения. Можете ли вы тогда жить с повторяющимся фоном? :) - person RCIX; 05.09.2009
comment
Хм .... Что если вы кешируете объект, который построен, но не сплющен, тогда вы просто перейдете к нему, перемешаете его и получите сплющенную копию этого изображения, когда вам понадобится новый? - person RCIX; 05.09.2009
comment
Интересная идея, поиграю с ней. Спасибо. - person Tom; 05.09.2009

  • Вы должны иметь возможность упростить некоторые математические операции, используя сохраненные переменные вместо stageCenterX + stageWidth * 0.75 и тому подобное, поскольку они не меняются.
  • Думали ли вы об использовании вместо этого HitTestPoint? делать математику, чтобы проверить положение контейнеров? Это встроенная функция, поэтому она может быть быстрее.
  • Вы должны использовать Shape вместо Sprite, если вам не нужно добавлять дочерние элементы к объекту. например, ваша звезда. Это может немного помочь.
  • Что, если бы вы создали набор звездных фонов в начале программы. Затем преобразовал их в растровые изображения и сохранил для дальнейшего использования. например, создать звездный фон, преобразовать его в растровые данные и сохранить в массиве. Сделайте это, скажем, 10 раз, а затем, когда вам понадобится фон, просто случайным образом выберите один и примените к нему другие формы. Преимущество этого заключается в том, что вам не нужно рендерить 100–250 спрайтов или фигур каждый раз, когда вы создаете новый фон - это требует времени.

РЕДАКТИРОВАТЬ: Новая идея:

  • Может быть, вы можете поиграть с идеей рисовать только звезды на фоне, а не добавлять отдельные объекты. Количество объектов, добавленных на экран, - большая часть проблемы. Поэтому я предлагаю вам нарисовать звезды прямо на контейнере, но с разными размерами и альфами. Затем уменьшите размер контейнера, чтобы получить желаемый эффект. Вы можете уменьшить площадь экрана с 500–1000 звезд до 0. Это было бы огромным улучшением, если бы вы могли получить от этого нужный эффект.
person Glenn    schedule 05.09.2009
comment
Я знаю мелочи в вычислениях, которые делают приложение потенциально медленнее, чем могло бы быть. Однако это определенно не то, что вызывает мою проблему, поскольку общая задержка составляет незаметное количество милли или даже микросекунд. На данный момент эти расчеты легче понять, что позволяет мне лучше улучшить общий алгоритм, что приведет к более быстрому написанию кода. Когда я знаю, какую фоновую систему я собираюсь использовать, я могу выполнять оптимизацию, например, постоянные переменные расчета. Форма хорошая, спасибо. Я поиграю с последней точкой, кажется правильным. - person Tom; 05.09.2009
comment
Если вы попробуете мое последнее предложение, сделайте это как с текущим методом растрового изображения, так и с объектами в их исходных формах. Я не совсем уверен, что создание растровых изображений целиком - лучший способ. Возможно, вам удастся уменьшить объем памяти, если вы сохраните все в их родном формате. - person Glenn; 06.09.2009
comment
Я имею в виду от второго до последнего. Просто добавил еще одну идею. - person Glenn; 06.09.2009

Попробуйте использовать один большой BitmapData с его собственным Bitmap, больше, чем сцена (хотя вы можете выйти за пределы BitmapDatas, если у вас действительно большая сцена и / или вы используете flash 9), и рисовать на нем новые фоновые изображения с помощью copyPixels метод (действительно быстрый способ копирования пикселей, быстрее, чем draw (), по крайней мере, насколько я видел).

Перемещайте большое растровое изображение, когда хотите, и когда вы достигнете края, переместите растровое изображение на другую сторону, скопируйте все пиксели обратно туда, где оно было ранее (так что изображение должно оставаться в том же месте относительно сцены, но Bitmap должен переместиться) и скопировать новые изображения в пикселях там, где они отсутствуют.

Поскольку вы также используете альфа-канал для изображений, вы можете проверить все параметры copyPixels, если он не копирует альфа-канал, как вы этого хотели (возможно, mergeAlpha?).

Итак, напомним, используйте один большой Bitmap, который хорошо выходит за границы сцены, подготовьте изображения как BitmapDatas, выполните трюк с переносом и заполните пробелы с помощью copyPixels из изображений.

Я не знаю, будет ли этот способ работать лучше (copyPixels по всему растровому изображению меня немного беспокоит), но это определенно что-то попробовать. Удачи :)

person SmilyOrg    schedule 06.09.2009