虽然使用BitmapData来处理粒子可以有比较高的效率,但也有进一步优化的余地。一个最简单的BitmapData粒子类如下所示:
package com.particles{ public class BitmapParticle1{ public var x:Number; public var y:Number; public var vx:Number; public var vy:Number; public function BitmapParticle1(x:Number,y:Number,vx:Number,vy:Number){ this.x = x; this.y = y; this.vx = vx; this.vy = vy; } } }
代码是十分简单,只包含了使粒子运动的属性。为了实现更好的面向对象,在BitmapData粒子中实现update方法,调用它使粒子运动。
package com.particles{ import flash.display.BitmapData; public class BitmapParticle2{ public var x:Number; public var y:Number; public var vx:Number; public var vy:Number; public function BitmapParticle2(x:Number,y:Number,vx:Number,vy:Number){ this.x = x; this.y = y; this.vx = vx; this.vy = vy; } public function update(bitmapData:BitmapData):void{ this.x += vx; this.y += vy; bitmapData.setPixel(this.x,this.y,0xFFFFFF); } } }
对于大多数情况而言,这种做法是十分符合逻辑的,而且也十分易读。和AS3书籍当中提倡的面向对象特性也十分吻合。下面就可以利用上面的代码,实现基本的粒子运动测试。
package{ import com.particles.BitmapParticle2; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; [SWF(width="800",height="600")] public class BitmapParticleTest1 extends Sprite{ private var bmp:Bitmap; private var bmpd:BitmapData; private var particles:Array; public function BitmapParticleTest1(){ bmpd = new BitmapData(800,600,false,0x000000); bmp = new Bitmap(bmpd); addChild(bmp); particles = new Array(); for(var i:int=0;i
效果实现:BitmapParticleTest1
对于以上代码在效率方面主要有以下问题:
1 BitmapData的rect属性并不是静态只读的
bmpd.fillRect(bmpd.rect,0x000000);
这意味着每次查询BitmapData的rect属性,都需要重新实例化一个新的rect。而实例化在AVM2中是比较耗费资源的。
2 数组
用数组来作为保存和访问粒子的方式,可以说是代价作为昂贵的了
particles = new Array();
将粒子保存到无类型的数组中是问题所在。当然可以用Vector对象来保存粒子,使用简单的数据结构,比如链表,可能是更合适的方式。这样修改我们的粒子类如下:
package com.particles{ import flash.display.BitmapData; final public class BitmapParticle3{ public var x:Number; public var y:Number; public var vx:Number; public var vy:Number; public var next:BitmapParticle3; public function BitmapParticle3(x:Number,y:Number,vx:Number,vy:Number){ this.x = x; this.y = y; this.vx = vx; this.vy = vy; } public function update(bitmapData:BitmapData):void{ this.x += vx; this.y += vy; bitmapData.setPixel(this.x,this.y,0xFFFFFF); } } }
注意到我们只是在粒子类中增加了next属性。接下来修改粒子的测试类:
package{ import com.particles.BitmapParticle2; import com.particles.BitmapParticle3; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; [SWF(width="800",height="600")] public class BitmapParticleTest2 extends Sprite{ private var bmp:Bitmap; private var bmpd:BitmapData; private var first:BitmapParticle3; public function BitmapParticleTest2(){ bmpd = new BitmapData(800,600,false,0x000000); bmp = new Bitmap(bmpd); addChild(bmp); var p:BitmapParticle3; var previous:BitmapParticle3; for(var i:int=0;i
使用链表代替数组后的效果:BitmapParticleTest2
3 final修饰符
使用final修饰符可以禁止粒子类被继承,虽然在Flash Player 10.1中性能的提高比之前的版本要少,但也是一处值得优化的地方。
4 inline
注意到测试类的主循环中,使用了面向对象方式来更新粒子的位置。在这里提到它,并不是由于使用面向对象的方式不正确,而是在主循环中调用的是一个函数,而函数调用在AVM2中的代价是昂贵的。而使用inline的方式来实现,由于编译时已经知道具体的代码,所以有性能上的优势。测试类修改如下:
var p : Particle = first; while (p) { p.x += p.vx; p.y += p.velY; bitmapData.setPixel(p.x,p.y,0xFFFFFF); p = p.next; }
5 setPixel()方法
BitmapData的setPixel方法可能是最灵活的方法之一,但使用Vector对象的方式整体更新整张位图,可能是效率最高的方式了。将测试类修改如下:
package{ import __AS3__.vec.Vector; import com.particles.BitmapParticle4; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Sprite; import flash.events.Event; import flash.geom.Rectangle; [SWF(width="800",height="600")] public class BitmapParticleTest3 extends Sprite{ private var bmp:Bitmap; private var bmpd:BitmapData; private var rect:Rectangle; private var first:BitmapParticle4; public function BitmapParticleTest3(){ bmpd = new BitmapData(800,600,false,0x000000); rect = bmpd.rect; bmp = new Bitmap(bmpd); addChild(bmp); var p:BitmapParticle4; var previous:BitmapParticle4; for(var i:int=0;i = bmpd.getVector(rect); var w:int = rect.width; var h:int = rect.height; var p:BitmapParticle4 = first; while(p){ p.x += p.vx; p.y += p.vy; if(p.x > w || p.x < 0){ p.x = p.startX; } if(p.y > h || p.y < 0){ p.y = p.startY; } vec[int(w*int(p.y)+int(p.x))] = 0xFFFFFFFF; p = p.next; } bmpd.setVector(rect,vec); bmpd.unlock(); } } }
40000个粒子的效果如下所示:
BitmapData粒子的基本代码优化完毕。接下来增加两个花样。
首先,使用PerlinNoise生成纹理从而改变粒子的速度。
package{ import __AS3__.vec.Vector; import com.particles.BitmapParticle4; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Shader; import flash.display.Sprite; import flash.events.Event; import flash.filters.ShaderFilter; import flash.geom.Point; import flash.geom.Rectangle; import flash.utils.ByteArray; [SWF(width="800",height="600")] public class BitmapParticleTest6 extends Sprite{ private static const WIDTH:int = 800; private static const HEIGHT:int = 600; [Embed(source="assets/CentralDifference.pbj",mimeType="application/octet-stream")] private static var velocityField:Class; private var velocityVector:Vector.; private var bmp:Bitmap; private var bmpd:BitmapData; private var rect:Rectangle; private var first:BitmapParticle4; public function BitmapParticleTest6(){ var perlinMap:BitmapData = new BitmapData(WIDTH,HEIGHT,false,0x000000); perlinMap.perlinNoise(128,128,6,123456,true,false,7,true); var shader:Shader = new Shader(new velocityField() as ByteArray); var filter:ShaderFilter = new ShaderFilter(shader); var velocityMap:BitmapData = perlinMap.clone(); velocityMap.applyFilter(perlinMap,perlinMap.rect,new Point(),filter); velocityVector = velocityMap.getVector(velocityMap.rect); perlinMap.dispose(); velocityMap.dispose(); bmpd = new BitmapData(WIDTH,HEIGHT,false,0x000000); rect = bmpd.rect; bmp = new Bitmap(bmpd); addChild(bmp); var p:BitmapParticle4; var previous:BitmapParticle4; for(var i:int=0;i = bmpd.getVector(rect); var w:int = rect.width; var h:int = rect.height; var p:BitmapParticle4 = first; var pos:int; while(p){ p.x += p.vx; p.y += p.vy; if(p.x > w || p.x < 0){ p.x = p.startX; } if(p.y > h || p.y < 0){ p.y = p.startY; } pos = int(w*int(p.y)+int(p.x)); vec[pos] = 0xFFFFFFFF; var velocity:int = velocityVector[pos]; var vx:int = ((velocity & 0xFF00) >> 8) - 127.5; var vy:int = (velocity & 0xFF) - 127.5; p.vx += vx * 0.03; p.vy += vy * 0.03; p.vx *= 0.99; p.vy *= 0.99; p = p.next; } bmpd.setVector(rect,vec); bmpd.unlock(); } } }
然后,使用一张位图的颜色值来改变粒子的速度,代码如下:
package{ import __AS3__.vec.Vector; import com.particles.BitmapParticle4; import flash.display.Bitmap; import flash.display.BitmapData; import flash.display.Shader; import flash.display.Sprite; import flash.events.Event; import flash.filters.ShaderFilter; import flash.geom.Point; import flash.geom.Rectangle; import flash.utils.ByteArray; [SWF(width="800",height="600",frameRate="31")] public class BitmapParticleTest7 extends Sprite{ private static const WIDTH:int = 800; private static const HEIGHT:int = 600; [Embed(source="assets/image.jpg")] private static var image:Class; [Embed(source="assets/CentralDifference.pbj",mimeType="application/octet-stream")] private static var velocityField:Class; private var velocityVector:Vector.; private var bmp:Bitmap; private var bmpd:BitmapData; private var rect:Rectangle; private var first:BitmapParticle4; public function BitmapParticleTest7(){ var imageMap:BitmapData = new image().bitmapData; var shader:Shader = new Shader(new velocityField() as ByteArray); var filter:ShaderFilter = new ShaderFilter(shader); var velocityMap:BitmapData = imageMap.clone(); velocityMap.applyFilter(imageMap,imageMap.rect,new Point(),filter); velocityVector = velocityMap.getVector(velocityMap.rect); imageMap.dispose(); velocityMap.dispose(); bmpd = new BitmapData(WIDTH,HEIGHT,false,0x000000); rect = bmpd.rect; bmp = new Bitmap(bmpd); addChild(bmp); var p:BitmapParticle4; var previous:BitmapParticle4; for(var i:int=0;i = bmpd.getVector(rect); var w:int = rect.width; var h:int = rect.height; var p:BitmapParticle4 = first; var pos:int; while(p){ p.x += p.vx; p.y += p.vy; if(p.x > w || p.x < 0){ p.x = p.startX; } if(p.y > h || p.y < 0){ p.y = p.startY; } pos = int(w*int(p.y)+int(p.x)); vec[pos] = 0xFFFFFFFF; var velocity:int = velocityVector[pos]; var vx:int = ((velocity & 0xFF00) >> 8) - 127.5; var vy:int = (velocity & 0xFF) - 127.5; p.vx += vx * 0.03; p.vy += vy * 0.03; p.vx *= 0.99; p.vy *= 0.99; p = p.next; } bmpd.setVector(rect,vec); bmpd.unlock(); } } }
转载请注明:陈童的博客 » BitmapData粒子的优化