cacheAsBitmap在Egret中的妙用

很多人对cacheAsBitmap不理解,或者理解的不够透彻。 其实很多东西都非常简单,只要你能够以全局的角度来看到Egret,那么你就会发现原来很多东西如此简单。

在上一篇文章《Egret性能优化之优化渲染》中,我曾经提到过一个优化的东西叫做cacheAsBitmap的优化技巧。由于篇幅的关心,和我不想啰嗦太多,导致讲解的不够细致。cacheAsBitmap很多时候用处非常大。在大型项目中,带来的性能提升也会非常明显。

首先我需要你回忆一下Egret中的Main Loop。这个东西和cacheAsBitmap息息相关。如果你还不太明白这个东西,请回到《Egret性能优化之优化渲染》重新阅读。

我们先来看一段代码,并且看看运行后的效果。(只贴出了关键的代码片段)

private context:egret.Sprite = new egret.Sprite();
private createGameScene(): void {
    egret.Profiler.getInstance().run();
    for( var i:number=0; i<12;i++ )
    {
        for(var t:number=0;t<5;t++)
        {
            var bit:egret.Bitmap = new egret.Bitmap();
            bit.texture = RES.getRes("image_png");
            bit.x = 75t+41(i%2);
            bit.y = 48*i;
            bit.scaleX = 0.5;
            bit.scaleY = 0.5;
            this.context.addChild(bit);
        }
    }
    this.addChild(this.context);
}
注意,我在代码中创建了60个位图,并且有序的排列。将60个Bitmap放到一个Sprite中。运行后看到的效果如下: 屏幕快照 2015-03-25 下午5.21.15

此时你会发现,当前draw的数量为60。也就是说,在Main Loop中,每帧执行60次draw操作(Main Loop第四步)

我现在将代码简单的修改一下。

private context:egret.Sprite = new egret.Sprite();
private createGameScene(): void {
    egret.Profiler.getInstance().run();
    for( var i:number=0; i<12;i++ )
    {
        for(var t:number=0;t<5;t++)
        {
            var bit:egret.Bitmap = new egret.Bitmap();
            bit.texture = RES.getRes("image_png");
            bit.x = 75t+41(i%2);
            bit.y = 48*i;
            bit.scaleX = 0.5;
            bit.scaleY = 0.5;
            this.context.addChild(bit);
        }
    }
    this.addChild(this.context);
    this.context.cacheAsBitmap = true;
}
在运行一下,效果如图:

屏幕快照 2015-03-25 下午5.21.43

此时,draw变成了1。为什么会这样?到底在哪里会因为我将cacheAsBitmap设置为了true之后,draw变成了1呢?很简单,此时Sprite内部的60个位图被缓存为了1张位图。所以从60变成了1。

这个用法有个需要注意的地方,使用了cacheAsBitmap后,内部的元素将无法进行图形修改,除非你把cacheAsBitmap再设置为false。

为了解释这个问题,我们将代码稍微修改一下。

private context:egret.Sprite = new egret.Sprite();
private createGameScene(): void {
    egret.Profiler.getInstance().run();
    for( var i:number=0; i<12;i++ )
    {
        for(var t:number=0;t<5;t++)
        {
            var bit:egret.Bitmap = new egret.Bitmap();
            bit.texture = RES.getRes("image_png");
            bit.x = 75t+41(i%2);
            bit.y = 48*i;
            bit.scaleX = 0.5;
            bit.scaleY = 0.5;
            this.context.addChild(bit);
        }
    }
    this.addChild(this.context);
    this.context.cacheAsBitmap = true;
    this.context.width = egret.MainContext.instance.stage.stageWidth;
    this.context.height = egret.MainContext.instance.stage.stageHeight;
    this.context.touchEnabled = true;
    this.context.addEventListener(egret.TouchEvent.TOUCH_BEGIN,this.click,this);
}

private click(evt:egret.TouchEvent):void { this.context.getChildAt(0).scaleX = 2; console.log("click"); this.context.x += 10; }

当我点击了这个Sprite之后,我希望第一张位图,能够横向拉伸2倍。但是,通过下面的截图你会发现,图像并没有修改。为了方便查看效果,我特意在点击事件中输出了click字符。

屏幕快照 2015-03-26 上午2.16.35

这说名了一个问题,cacheAsBitmap被启用后,其内部的可视化对象不在参与第三步与第四步操作。这里所谓的操作是指,当Main Loop遍历显示列表中元素的时候,认为Sprite内部就是一个Bitmap,而非60个。而这一个Bitmap是有引擎为你缓存出来的图像。

我们稍微修改一下代码,把设置cacheAsBitmap这行注释掉。你再来看一下效果。

private context:egret.Sprite = new egret.Sprite();
private createGameScene(): void {
    egret.Profiler.getInstance().run();
    for( var i:number=0; i<12;i++ )
    {
        for(var t:number=0;t<5;t++)
        {
            var bit:egret.Bitmap = new egret.Bitmap();
            bit.texture = RES.getRes("image_png");
            bit.x = 75t+41(i%2);
            bit.y = 48*i;
            bit.scaleX = 0.5;
            bit.scaleY = 0.5;
            this.context.addChild(bit);
        }
    }
    this.addChild(this.context);
    //this.context.cacheAsBitmap = true;
    this.context.width = egret.MainContext.instance.stage.stageWidth;
    this.context.height = egret.MainContext.instance.stage.stageHeight;
    this.context.touchEnabled = true;
    this.context.addEventListener(egret.TouchEvent.TOUCH_BEGIN,this.click,this);
}

private click(evt:egret.TouchEvent):void { this.context.getChildAt(0).scaleX = 2; console.log("click"); this.context.x += 10; }

屏幕快照 2015-03-26 上午2.17.50

非常明显的区别。

最后留一个问题,如果我像下面这样编写代码,最后看到效果如图,为什么会这样?

private context:egret.Sprite = new egret.Sprite();
private createGameScene(): void {
    egret.Profiler.getInstance().run();
    for( var i:number=0; i<12;i++ )
    {
        for(var t:number=0;t<5;t++)
        {
            var bit:egret.Bitmap = new egret.Bitmap();
            bit.texture = RES.getRes("image_png");
            bit.x = 75t+41(i%2);
            bit.y = 48*i;
            bit.scaleX = 0.5;
            bit.scaleY = 0.5;
            this.context.addChild(bit);
        }
    }
    this.addChild(this.context);
    this.context.cacheAsBitmap = true;
    this.context.getChildAt(0).scaleX = 2;
    console.log("click");
    this.context.x += 10;
}
屏幕快照 2015-03-26 上午2.20.16

欢迎大家来Egret社区讨论,bbs.egret-labs.org