AS3:为什么这种流体模拟运行得越来越慢?

时间:2013-08-14 16:52:13

标签: actionscript-3 debugging memory-management fluid

我有两节课。模拟流体。这两个类都非常直接和简短,但运行2秒后,模拟变得非常慢,看起来像是内存泄漏。但我在这段代码中看不到任何泄漏。

如果你能弄清楚,为什么会发生这种情况,请告诉我?

FluidLayer.as

package  {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.display.BitmapData;
import flash.filters.BlurFilter;
import flash.display.Bitmap;
import flash.geom.Point;
import flash.geom.ColorTransform;
import flash.display.IBitmapDrawable;
import flash.events.Event;
import flash.display.MovieClip;


public class FluidLayer extends MovieClip {

    private var canvas:Sprite;
    private var b:Bitmap;
    private var blur:BlurFilter = new BlurFilter(20,20,3);  
    private var bmd1:BitmapData;

    public function FluidLayer() {
        canvas = new Sprite();

        for(var i:int = 0;i < 600;i++){

            var p:Particle = new Particle();
            canvas.addChild(p);
            p.x = stage.stageWidth * Math.random();
            p.y = stage.stageHeight* Math.random();
            p.initi(stage);
        }

        canvas.filters = new Array(blur);
        addEventListener(Event.ENTER_FRAME, render);


    }


    private function render(e:Event):void{

        remove();

        b = new Bitmap(makeFluid(canvas),"auto", true);
        b.alpha = 0.7;
        addChild(b);

    }

    private function makeFluid(o:Sprite):BitmapData{

        bmd1 = new BitmapData(stage.stageWidth, stage.stageHeight, true);
        bmd1.draw(o,null,null,null,null,true);
        bmd1.threshold(bmd1, bmd1.rect, new Point(0,0), ">", 0XFF2b2b2b, 0x55FFFF, 0xFFFFFF, false);

        return bmd1;
    }


    private function remove():void{
            if(numChildren > 1)
            removeChildAt(1);

            if(bmd1){ 
                bmd1.dispose();
                bmd1 = null;
            }
    }

}}

Particle.as

package  {

import flash.display.MovieClip;
import flash.events.Event;
import flash.display.Stage;


public class Particle extends MovieClip {

    private var speedX:int;
    private var speedY:int;
    private var _s:Stage;       

    public function Particle() {
        this.graphics.beginFill(0x00CCFF);
        this.graphics.drawCircle(0,0,Math.random() * 30);

        speedX = Math.random() * 10 - 5;
        speedY = Math.random() * 10 - 5;

        this.addEventListener(Event.ADDED_TO_STAGE, initi);

    }

    public function initi(s:Stage):void{
        this._s = s;
        addEventListener(Event.ENTER_FRAME, render);
    }       

    private function render(e:Event):void{

        this.x += Math.random()*speedX;
        this.y += Math.random()*speedY;


        if(this.x > _s.stageWidth || this.y > _s.stageHeight){

            //this.x =  Math.random()*_s.stageWidth;
            //this.y =  Math.random()*_s.stageHeight;

            removeEventListener(Event.ENTER_FRAME, render);
            this.parent.removeChild(this);
        }
    }
}}

3 个答案:

答案 0 :(得分:0)

你必须调用bmd1.dispose();在重新实例化之前,它不会从内存中释放位图。

修改

仔细查看代码,优化代码并清理一些内容后,我得出的结论是,模拟不再运行得越来越慢。

问题在于你计算粒子的x和y速度的方式。基本上它会减速,直到一起停止。除了你所放置的大量高质量模糊以及没有释放位图之外,你的电影也会出现并且实际上是在慢慢爬行。

这是我修改过的代码。我对它进行了基准测试,并且在15分钟后它没有超过10%cpu或40k内存。

<强> Particle.as

package  {

import flash.display.MovieClip;
import flash.events.Event;
import flash.display.Stage;


public class Particle extends MovieClip {

    private var speedX:int;
    private var speedY:int;  
    private var deleted:Boolean = false;

    public function Particle() {
        this.graphics.beginFill(0x00CCFF);
        this.graphics.drawCircle(0,0,Math.random() * 30);

        //The max prevents particle speed from rounding to zero.
        speedX = Math.ceil(Math.random() * 10 - 5);
        speedY = Math.ceil(Math.random() * 10 - 5);
    }     

    public function render():void{
        //It originally appeared to be slowing down. In-fact, it was.
        x += Math.random() * speedX;
        y += Math.random() * speedY;

        deleted = (x > FluidLayer.w || y > FluidLayer.h);
        //Comment this below if you want particles to be removed once they go out of bounds.
        if(deleted) {
            x = Math.random() * FluidLayer.w;
            y = Math.random() * FluidLayer.h;
            deleted = false;
        }
    }

    public function isDeleted():Boolean {
        return deleted;
    }
}
}

<强> FluidLayer.as

package  {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.display.BitmapData;
import flash.filters.BlurFilter;
import flash.display.Bitmap;
import flash.geom.Point;
import flash.geom.ColorTransform;
import flash.display.IBitmapDrawable;
import flash.events.Event;
import flash.display.MovieClip;


public class FluidLayer extends MovieClip {

    private var canvas:Sprite;

    private var bitmap:Bitmap;
    private var bitmapData:BitmapData;

    private var particleArray:Array = new Array();

    public static const w:uint = 550;
    public static const h:uint = 400;


    public function FluidLayer() {
        addEventListener(Event.ADDED_TO_STAGE, initialize);
    }

    //By only one event handler to render, we prevent the overhead of 599 bubbling events.
    private function render(e:Event):void {
        for each(var p:Particle in particleArray) {
            p.render();
            //uncomment below if you want particles to become removed when they navigate out of bounds.
            //if(p.isDeleted()) {
                //canvas.removeChild(p);
                //particleArray.splice(particleArray.indexOf(p),1); 
            //}
        }
        bitmapData.fillRect(bitmapData.rect, 0); //clear the bitmapdata
        bitmapData.draw(canvas,null,null,null,null,true);
        bitmapData.threshold(bitmapData, bitmapData.rect, new Point(0,0), ">", 0XFF2b2b2b, 0x55FFFF, 0xFFFFFF, false);
    }


    //We call initialize once the fluid layer has been added to stage 
    //or else stage values will be null.
    private function initialize(e:Event):void {
        canvas = new Sprite();


        //You DEFINITELY want to lower the blur amount here.
        //This is what is ultimately slowing your SWF down to a crawl.
        //canvas.filters = new Array(new BlurFilter(20,20,1));

        for(var i:uint = 0; i < 600; i++) {
            var p:Particle = new Particle();
            p.x = Math.random() * w;
            p.y = Math.random() * h;
            canvas.addChild(p);
            particleArray.push(p);
        }

        //The bitmap and bitmapData only need to be initialized once
        bitmapData = new BitmapData(w, h, true);

        bitmap = new Bitmap(bitmapData, "auto", true);
        bitmap.alpha = 0.7;
        addChild(bitmap);

        addEventListener(Event.ENTER_FRAME, render);
    }


}
}

答案 1 :(得分:0)

原始减速似乎是画布上的模糊滤镜。 (注释过滤器以查看证据)

通过flash(CacheAsBitmap和位图滤镜缓冲区)自动创建位图以渲染滤镜可能会导致内存泄漏,这可能是由于粒子移动导致画布尺寸不断变化所致。

尝试在创建画布后添加此行以进行快速修复:

canvas.scrollRect = new Rectangle(0,0,stage.stageWidth,stage.stageHeight);

通过使用scrollRect限制画布的大小,我们将停止此重新分配。

然而,液体不再流到舞台的边缘。这可以在绘图和位图分配例程中解决,增加尺寸和偏移,我会让你去做,但它部分连接到模糊滤镜数量。

我建议将这些想法与安德烈亚斯代码中非常好的想法相适应。

答案 2 :(得分:0)

所以我在Andreas和adamh的帮助下为我的问题提出了一个解决方案。

安德烈亚斯 - 告诉我,我忘了处理bitmapdata(非常好的地方!)

亚当 - 告诉我在画布上定义滚动(大量提升表演!)

但到底是什么让表现变得流畅,这是我身边的一个简单错误。我注意到了particle.as&gt; render(),我只检查粒子是否在两侧(facepalm)超出界限,这是一个愚蠢的错误。当我更改渲染功能以检查剩余的2个边时,它修复了性能问题。

抱歉这个愚蠢的问题:)再次感谢Andreas和adamh

最后的课程:

<强> FluidLayer.as

package  {
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.display.BitmapData;
import flash.filters.BlurFilter;
import flash.display.Bitmap;
import flash.geom.Point;
import flash.geom.ColorTransform;
import flash.display.IBitmapDrawable;
import flash.events.Event;
import flash.display.MovieClip;
import flash.geom.Rectangle;


public class FluidLayer extends MovieClip {

    private var canvas:Sprite;
    private var b:Bitmap;
    private var blur:BlurFilter = new BlurFilter(20,20,3);  
    private var bmd1:BitmapData;

    public function FluidLayer() {
        canvas = new Sprite();
        canvas.scrollRect = new Rectangle(0,0,1010,550);
        for(var i:int = 0;i < 600;i++){

            var p:Particle = new Particle();
            canvas.addChild(p);
            p.x = stage.stageWidth * Math.random();
            p.y = stage.stageHeight* Math.random();
            p.initi(stage);
        }

        canvas.filters = new Array(blur);
        addEventListener(Event.ENTER_FRAME, render);


    }


    private function render(e:Event):void{

        remove();

        b = new Bitmap(makeFluid(canvas),"auto", true);
        b.alpha = 0.7;
        addChild(b);

    }

    private function makeFluid(o:Sprite):BitmapData{

        bmd1 = new BitmapData(stage.stageWidth, stage.stageHeight, true);
        bmd1.draw(o,null,null,null,null,true);
        bmd1.threshold(bmd1, bmd1.rect, new Point(0,0), ">", 0XFF2b2b2b, 0x55FFFF, 0xFFFFFF, false);

        return bmd1;
    }


    private function remove():void{
            if(numChildren > 1)
            removeChildAt(1);

            if(bmd1){ 

                bmd1.dispose();
                bmd1 = null;
            }
    }}}

<强> Particle.as

package  {

import flash.display.MovieClip;
import flash.events.Event;
import flash.display.Stage;


public class Particle extends MovieClip {

    private var speedX:int;
    private var speedY:int;
    private var _s:Stage;       

    public function Particle() {
        this.graphics.beginFill(0x00CCFF);
        this.graphics.drawCircle(0,0,Math.random() * 30);

        speedX = Math.random() * 10 - 5;
        speedY = Math.random() * 10 - 5;

        this.addEventListener(Event.ADDED_TO_STAGE, initi);

    }

    public function initi(s:Stage):void{
        this._s = s;
        addEventListener(Event.ENTER_FRAME, render);
    }       

    private function render(e:Event):void{

        this.x += Math.random()*speedX;
        this.y += Math.random()*speedY;


        if(this.x > _s.stageWidth || this.y > _s.stageHeight || this.x < 0 && this.y < 0){

            this.x =  Math.random()*_s.stageWidth;
            this.y =  Math.random()*_s.stageHeight;

            //removeEventListener(Event.ENTER_FRAME, render);
            //this.parent.removeChild(this);
        }
    }
}}