2 Stimmen

Optimierung meiner dynamischen Hintergrund-Engine für ein 2d-Flash-Spiel in Actionscript-3

Edit 2: Nach dem Mangel an Antworten zu urteilen, frage ich mich, ob mein Problem klar genug ist. Bitte sagen Sie mir, wenn ich mehr ausführen muss.

Hinweis: siehe unten für ein Code-Update!

Kurze Einführung: Ich schreibe ein 2-dimensionales Flash-Raumspiel in Actionscript. Das Universum ist unendlich groß, deshalb muss der Hintergrund dynamisch gerendert werden und die Hintergrundobjekte (Gaswolken, Sterne, etc.) müssen zufällig positioniert werden.

Ich habe eine Klasse namens BackgroundEngine erstellt und sie funktioniert sehr gut, das Problem ist jedoch die Rendering-Leistung. Dies ist, wie es funktioniert:

Beim Start werden 4 Hintergrundcontainer (jeder so groß wie die Bühne) um den Spieler herum erstellt. Oben links, oben rechts, unten links und unten rechts. Alle Hintergrundquadrate werden zu einem Hauptcontainer hinzugefügt, damit der Hintergrund leicht verschoben werden kann. Jetzt gibt es 2 Abfragefunktionen:

1) "garbage poller": sucht nach Hintergrundcontainern, die 2 mal die Bühnenbreite oder -höhe von der X- bzw. Y-Koordinate des Spielers entfernt sind. Ist dies der Fall, wird das Hintergrundquadrat entfernt und für die Müllabfuhr freigegeben.

2) "Rendering Poller": prüft, ob an allen Seiten des Spielers (x - stageWidth, x + stageWidth, y - stageHeight, y + stageHeight) bereits ein Hintergrund vorhanden ist. Wenn nicht, wird ein neues Hintergrundquadrat an der entsprechenden Stelle gezeichnet.

Alle Hintergrundquadrate werden mit der folgenden Funktion erstellt (diejenigen, die dynamisch erstellt werden, und die vier beim Starten):

<<< alten Code entfernt, siehe unten für aktualisierten vollständigen Quelltext >>>

All die Randoms, die man dort sieht, sorgen dafür, dass die Umgebung auf jedem Platz ganz einzigartig aussieht. Das funktioniert wirklich großartig, das Universum sieht ziemlich fantastisch aus.

Die folgenden Assets werden als Hintergrundobjekte verwendet:

1) Einfache Sterne : http://www.feedpostal.com/client/assets/background/1.png (in einem Browser mit weißem Hintergrund werden Sie das wahrscheinlich nicht sehen können).

2) Helle Sterne : http://www.feedpostal.com/client/assets/background/2.png

3) Weiße Gaswolken : http://www.feedpostal.com/client/assets/background/3.png

4) Rote Gaswolken: http://www.feedpostal.com/client/assets/background/4.png

Wichtige Hinweise:

1) Alle Inhalte werden zwischengespeichert, so dass sie nicht immer wieder neu heruntergeladen werden müssen. Sie werden nur einmal heruntergeladen.

2) Die Bilder werden nicht gedreht oder skaliert, nachdem sie erstellt wurden, also habe ich cacheAsBitmap für alle Objekte, Container und den masterContainer aktiviert.

3) Ich musste in Photoshop PNG-Formate verwenden, da GIFs in Flash nicht sehr gut gerendert werden, wenn sie mit Transparenz verwendet werden.

Also, das Problem ist, dass wenn ich herumfliege das Rendering des Hintergrunds zu viel Leistung nimmt: der Client beginnt "lagging" (FPS-weise). Aus diesem Grund muss ich die Hintergrund-Engine optimieren, so dass es viel schneller gerendert wird. Könnt ihr mir hier helfen?

Aktualisierung 1: Dies ist der Stand der Dinge nach der einen Antwort, die ich erhalten habe.

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

Wenn sich der Spieler bewegt, werden die Hintergrundmethoden moveX und moveY mit der umgekehrten Richtung des Spielers aufgerufen. Dies führt auch dazu, dass die Methoden collectGarbage und drawNextContainer aufgerufen werden.

Das Problem bei dieser Konfiguration ist, dass immer mindestens 9 Container aktiv sind. Oben links, oben, oben rechts, Mitte links, Mitte, Mitte rechts, unten links, unten und unten rechts. Das erfordert eine Menge Leistung.

Bearbeiten: Ich frage mich auch, sollte ich cacheAsBitmap verwenden? Wenn ja, auf welchen Images? Auf den Containern und dem Master-Container oder nur auf einem von ihnen? Wenn ich es für alle Bilder (auch die temporären Sprite-Objekte) aktivieren, ist es tatsächlich mehr verzögert.

Update 2:

Bei dieser Version werden Quadrate verwendet, die doppelt so groß sind wie die Bühne. Es sollten immer nur ein oder zwei Quadrate auf einmal geladen werden. Das ist besser, aber ich bemerke immer noch einen Leistungsabfall beim Bewegen. Dadurch friert der Client für einen kurzen Moment ein. Hat jemand eine Idee, wie man das optimieren kann?

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

0 Stimmen

Nebenbei bemerkt, würde ich gerne dein Weltraumspiel sehen, wenn es fertig ist...

0 Stimmen

Leider muss ich zuerst dieses Hintergrundproblem beheben. Back2Dos hat mir eine interessante Hilfe gegeben, die aber zu viele Fragen aufgeworfen hat, als dass ich ohne weitere Kommentare weitermachen könnte. Das Spiel selbst wird in den nächsten Jahren nicht fertig werden, da es ein Multiplayer-Spiel ist und ich gerade erst mit den Grundlagen begonnen habe.

0 Stimmen

Das heißt, auch wenn es mir gelingt, all die Probleme zu lösen, mit denen ich anfangs nicht gerechnet habe. Zum Beispiel das Problem, um das es in diesem Thread geht.

3voto

back2dos Punkte 15464

OK, diese sollte zeigen, dass man mit anderen Ansätzen wirklich eine andere Kategorie von Zahlen erhalten kann ...

das Limit ist nicht die Anzahl der Sterne, sondern die Dichte, d.h. die Anzahl der gleichzeitig sichtbaren Sterne ... mit deaktiviertem Text erreiche ich bis zu 700 @ 30fps, auf einem Core2Duo, mit einer recht aktuellen Version des Debug Players ...

ich habe gemerkt, dass der Flash Player nicht sehr gut im Clipping ist ... und dass man, wenn man die einfachste Methode verwendet, eine Menge Zeit damit verbringt, Objekte zu bewegen, die weit davon entfernt sind, sichtbar zu sein ...

um wirklich in der Lage sein, Dinge zu optimieren, entschied ich mich für MVC hier ... nicht in der klassischen aufgeblähten Weise ... die Idee ist, das Modell zu behandeln, und wenn alle Elemente sichtbar sind, erstellen Sie Ansichten für Sie ...

Die beste Vorgehensweise ist nun der Aufbau eines räumlicher Baum ...

  1. Sie haben Blätter, die Objekte enthalten, und Knoten, die Blätter oder Knoten enthalten
  2. Wenn Sie ein Objekt zu einem Blatt hinzufügen und es eine bestimmte Größe überschreitet, wird es zu einem Knoten mit n x n Blätter, die ihre Kinder zwischen
  3. Jedes Objekt, das dem Hintergrund hinzugefügt wird, wird einem Raster hinzugefügt, das durch die Koordinaten des Objekts bestimmt wird ... Raster werden just-in-time erstellt und beginnen als Blätter

Der große Vorteil dieser Methode ist, dass man die sichtbaren Knoten/Blätter schnell isolieren kann. In jeder Iteration sind nur die Knoten/Blätter von Interesse, die entweder sichtbar werden oder bereits sichtbar sind (und möglicherweise unsichtbar werden). Sie müssen keine Aktualisierungen im Rest des Baums vornehmen. Nachdem Sie alle sichtbaren Objekte gefunden haben, erstellen Sie Ansichten für Objekte, die sichtbar werden, aktualisieren die Position derjenigen, die einfach sichtbar bleiben, und zerstören Ansichten für Objekte, die unsichtbar werden ...

das spart unheimlich viel von allem ... Speicher und Rechenleistung ... wenn du es mit einer riesigen Weltgröße (100000) versuchst, wirst du sehen, dass dir schnell der RAM ausgeht, lange bevor die CPU etwas tut ... 500000 Sterne zu instanziieren verbraucht 700MB RAM, mit etwa 50 sichtbaren Sternen, die mit 70 fps laufen, ohne enorme CPU-Auslastung ...

die Engine ist nur ein Proof of Concept ... und der Code sieht furchtbar aus ... das einzige Feature, auf das ich im Moment stolz bin, ist, dass es Objekte unterstützt, die einen bestimmten Bereich einnehmen, weshalb ein Objekt Teil mehrerer Blätter sein kann ... ich denke, das ist etwas, das ich entfernen werde, weil es mir einen großen Geschwindigkeitsvorteil verschaffen sollte ... man sieht auch, dass es ein wenig ins Stocken gerät, während man Sterne hinzufügt, was passiert, wenn die Blätter zu Knoten werden ...

Ich bin mir ziemlich sicher, dass dies nicht der beste Weg für einen räumlichen Unterteilungsbaum ist, es ist nur so, dass alles, was ich gefunden habe, mir irgendwie nutzlos erschien ... wahrscheinlich kann jemand, der CS studiert (und verstanden) hat, meinen Ansatz verfeinern ... :)

Ansonsten habe ich einen Objektpool für die Ansichten verwendet, und Haxe ... eine Sache, die wahrscheinlich nicht so clever ist, ist es, alle Ansichten einzeln zu verschieben, anstatt sie auf eine Ebene zu legen und diese zu verschieben ...

manche Menschen machen auch Dinge zu BitmapData s manuell, um die Leistung zu steigern, was recht gut zu funktionieren scheint (ich kann die Frage gerade nicht finden) ... Sie sollten jedoch die Verwendung von copyPixels anstelle von draw ...

Ich hoffe, das hilft ... ;)


bearbeiten: Ich habe beschlossen, meine ausführliche Antwort in eine Blog-Beitrag ... viel Spaß beim Lesen ... ;)


0 Stimmen

Schön, dass Sie so oft mit ebenso tollen Antworten auftauchen :) Ich habe allerdings ein paar Fragen. Meinst du mit "Blätter" ein Array, das alle Objekte (Sterne etc.) enthält? Sind Knoten wie Container (ein weiteres Array), die die Arrays mit Blättern enthalten? Wenn ja, denke ich, dass es mit meinem alten Aufbau vergleichbar ist. Zweite Frage: Soll ich abfragen, ob ein Objekt außerhalb des Sichtbereichs liegt, und wenn ja, .visible = false setzen? Frage 3: Wenn ich alle Container zu einem Hauptcontainer hinzufüge (wie unten für die Bewegung vorgeschlagen), wie sollte ich dann nur die sichtbaren Container separat bewegen? Und warum sollte ich das tun, da...

0 Stimmen

...da es nur die X- und Y-Werte des Hauptcontainers ändert und nicht wirklich bewirkt, dass die nicht sichtbaren Teile gerendert werden, richtig? Es ist nur eine Variable. Frage 4: Der Link zu dem, was du gemacht hast, sieht gut aus. Ist die Quelle öffentlich? Oder kann ich sehen, wie Sie es gemacht haben? Das würde vielleicht einige meiner Fragen beantworten. Frage 5: was meinst du mit einem Objektpool, einfach ein Array mit Objekten? Frage 6: haXe sieht großartig aus, das erste Mal, dass ich es sehe. Empfiehlst du mir, diese Sprache zu lernen und den ganzen Client mit dieser Sprache neu zu schreiben? Es ist für ein großes Projekt gedacht, ein 2D-Spiel, wie Sie wahrscheinlich schon wissen. Vielen Dank!

0 Stimmen

Es sieht so aus, als würde das Kopfgeld bald enden, auch wenn ich noch so viele Fragen habe, die unbeantwortet bleiben. Ohne diese kann ich deine Antwort aber nicht wirklich akzeptieren.

2voto

Branden Hall Punkte 4458

Sie können versuchen, alle Teile zu einer flachen Bitmap zusammenzufügen, während Sie arbeiten. Zeichnen Sie alle Ebenen und verwenden Sie dann die Zeichenmethode von BitmapData, um sie zu einer einzigen Bitmap zu kombinieren.

Selbst wenn cacheAsBitmap für alle Teile aktiviert ist, muss Flash immer noch alle Teile in jedem Frame kombinieren.

0 Stimmen

Das Endergebnis ist bereits ein einziges Bild, der Container. Ich kann kein statisches Bild mit einem externen Programm erstellen, da die Objekte nach dem Zufallsprinzip positioniert, skaliert, alpha(-ed?) und gedreht werden.

1 Stimmen

Nun, so gut ich das aus dem von Ihnen geposteten Code erkennen kann, ist das Ergebnis ein Sprite, das mehrere Bitmaps enthält. Ich schlage nicht vor, dass Sie ein externes Programm verwenden, stattdessen schlage ich vor, dass Sie, nachdem Sie ein Quadrat erstellt haben, alle Bitmaps zu einer einzigen Bitmap verflachen, indem Sie BitmapData.draw verwenden, um die zusammengesetzten Bitmaps zu erfassen. Dann wirfst du die Teile weg. Jetzt muss Flash nur noch eine Bitmap überlagern, nicht mehr vier pro Quadrat.

1 Stimmen

Das ist auf jeden Fall das erste, was Sie versuchen sollten.

1voto

Sean Punkte 501

Versuchen Sie, das Fenster des Players zu vergrößern oder zu verkleinern. Wenn dies eine signifikante Auswirkung auf die Bildrate hat, ist der schnellste und einfachste Weg zur Verbesserung der Leistung die Größe der Bühne zu verringern . Das ist eine eher unpopuläre Antwort, wenn man sie Leuten - vor allem Künstlern - gibt, aber wenn der Engpass in der Größe der Bühne liegt, gibt es nicht viel, was man im Code tun kann, um das zu beheben.

0 Stimmen

Die durchschnittliche Framerate ist eigentlich überhaupt kein Problem. Es ist die sofortige Laden eines neuen Containers, die ein Problem für ein paar Millisekunden verursacht.

1voto

RCIX Punkte 37016

Wie wäre es, wenn man, anstatt Hintergrundquadrate zu zerstören, sie einfach in einen Stapel "fertiger" Quadrate packt, auf denen man zeichnen kann, mit einer Obergrenze von etwa 4? Dann muss man nicht erst eines erstellen, wenn man eines braucht, sondern man verschiebt es einfach an die richtige Stelle und mischt vielleicht die Sterne oder so.

[würde Beispiel hinzufügen, aber ich schreibe keinen AS3-Code :(]

0 Stimmen

Da alle Objekte innerhalb eines Quadrats in eine Ebene geschoben werden, kann ich keine der invidiualen Objektprioritäten wie X und Y der einzelnen Sterne ändern. Sie nicht in eine Ebene zu schieben, scheint auch keine Option zu sein, da es zu viel Leistung kosten würde.

0 Stimmen

Gutes Argument. Kannst du dann mit einem sich wiederholenden Hintergrund leben? :)

1 Stimmen

Hmm.... Wie wäre es, wenn Sie ein Objekt zwischenspeichern, das zwar gebaut, aber nicht reduziert ist, und dann einfach zu diesem Objekt gehen und es mischen und eine reduzierte Kopie dieses Bildes erhalten, wenn Sie ein neues brauchen?

1voto

Glenn Punkte 5286
  • Sie sollten in der Lage sein, einige Ihrer Berechnungen zu vereinfachen, indem Sie gespeicherte Variablen anstelle von stageCenterX + stageWidth * 0.75 und ähnliche, da sie sich nicht ändern.
  • Haben Sie die Verwendung von [HitTestPoint](http://livedocs.adobe.com/flex/3/langref/flash/display/DisplayObject.html#hitTestPoint()) anstatt die Positionen der Container zu überprüfen? Es ist eine systemeigene Funktion, also könnte es schneller sein.
  • Sie sollten eine Shape anstelle einer Sprite wenn Sie dem Objekt keine Kinder hinzufügen müssen, z. B. Ihren Stern. Das könnte eine große Hilfe sein.
  • Wie wäre es, wenn Sie zu Beginn des Programms eine Reihe von Sternenhintergründen erstellen würden? Dann konvertiert man sie in Bitmaps und speichert sie für die spätere Wiederverwendung. z.B. erstellt man einen Sternenhintergrund, konvertiert ihn in Bitmap-Daten und speichert diese in einem Array. Machen Sie das, sagen wir, 10 Mal, und wenn Sie dann einen Hintergrund brauchen, wählen Sie einfach zufällig einen aus und wenden Ihre anderen Formen darauf an. Der Vorteil dabei ist, dass Sie nicht jedes Mal 100-250 Sprites oder Shapes rendern müssen, wenn Sie einen neuen Hintergrund erstellen - das kostet Zeit.

EDIT: Neue Idee:

  • Vielleicht können Sie mit der Idee spielen, nur die Sterne auf die Hintergründe zu zeichnen, anstatt einzelne Objekte hinzuzufügen. Die Anzahl der Objekte, die dem Bildschirm hinzugefügt werden, ist ein großer Teil des Problems. Ich schlage also vor, die Sterne direkt auf den Container zu zeichnen, aber mit unterschiedlichen Größen und Alphas. Dann verkleinern Sie den Container so, dass Sie den gewünschten Effekt erzielen. Sie könnten die Anzeigefläche von 500-1000 Sternen auf 0 reduzieren. Das wäre eine enorme Verbesserung, wenn Sie damit den gewünschten Effekt erzielen können.

0 Stimmen

Ich bin mir der kleinen Dinge innerhalb der Berechnungen bewusst, die die Anwendung potenziell langsamer machen, als sie sein könnte. Diese sind jedoch definitiv nicht die Ursache meines Problems, da die Gesamtverzögerung eine unmerkliche Menge von Milli- oder sogar Mikrosekunden beträgt. Im Moment sind diese Berechnungen einfacher zu verstehen, was es mir ermöglicht, den allgemeinen Algorithmus zu verbessern, was zu einem schnelleren Code führt. Wenn ich weiß, welches Hintergrundsystem ich verwenden werde, könnte ich Optimierungen wie konstante Berechnungsvariablen vornehmen. Die Form ist eine gute Idee, danke. Ich werde mit dem letzten Punkt herumspielen, das scheint ein guter Punkt zu sein.

0 Stimmen

Wenn Sie meinen letzten Vorschlag ausprobieren, machen Sie es sowohl mit der aktuellen Bitmap-Methode als auch mit den Objekten in ihrer ursprünglichen Form. Ich bin nicht ganz davon überzeugt, dass es der richtige Weg ist, alles in Bitmaps darzustellen. Vielleicht können Sie den Speicherbedarf gering halten, wenn Sie alles in seinem ursprünglichen Format speichern.

0 Stimmen

Ich meine den vorletzten Platz. Ich habe gerade eine weitere Idee hinzugefügt.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X