Оптимизация моего динамического фонового движка для 2d флеш игры в actionscript-3 - PullRequest
2 голосов
/ 27 августа 2009

Правка 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 контейнеров. Верхний левый, верхний, верхний правый, центральный левый, центр, правый центр, нижний левый, нижний и правый нижний. Это занимает много производительности.

Edit: мне также интересно, я должен использовать 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;
        }
    }
}

Ответы [ 6 ]

3 голосов
/ 30 августа 2009

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

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

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

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

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

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

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

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

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

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

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

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

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


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


2 голосов
/ 27 августа 2009

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

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

1 голос
/ 06 сентября 2009

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

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

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

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

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

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

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

  • Может быть, вы можете поиграть с идеей только рисования звезд на заднем плане, а не добавления отдельных объектов. Количество объектов, добавленных на экран, является большой частью проблемы. Поэтому я предлагаю вам нарисовать звезды на контейнере напрямую, но с разными размерами и альфа-каналом. Затем уменьшите контейнер так, чтобы получить эффект, который вы ищете. Вы можете уменьшить занимаемую площадь дисплея с 500-1000 звезд до 0. Это было бы огромным улучшением, если бы вы могли получить необходимый эффект от него.
1 голос
/ 04 сентября 2009

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

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

1 голос
/ 02 сентября 2009

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

...