当前做粒子效果的主流思想就是bitmapData的setPixel方法,
这个方法可以在一个位置设置此位置像素点的颜色。它的前两个参数供我们设置位置的x,y坐标,
第三个参数是欲设置像素的颜色值。让我们边看代码边讲会更有feel一些:
package
{
public
class PixelVO
{
public
var xpos:Number;
public
var ypos:Number;
public
var speedX:Number;
public
var speedY:Number;
}
}
OK,接下来建立我们的主类,请允许我省略一些列的import语句:
[SWF(width=
"
500
",height=
"
450
")]
public
class MyColorPicker extends Sprite
{
private
var pixelNum:
int =
500;
private
var bm:Bitmap;
private
var bmd:BitmapData;
private
var container:Sprite;
private
var pixelList:Array =
new Array();
private
var gravity:Number =
0.1;
public function MyColorPicker()
{
init();
}
private function init():
void{
container =
new Sprite();
container.x =
50;
container.y =
0;
addChild(container);
bmd =
new BitmapData(
400,
400,
true,
0xff000000);
bm =
new Bitmap( bmd );
container.addChild( bm );
container.addEventListener(MouseEvent.CLICK, onClick);
}
private function onClick(
event:MouseEvent):
void{
fire(container.mouseX, container.mouseY);
}
private function fire( toX:Number, toY:Number ):
void{
for(
var i:
int=
0; i<pixelNum; i++)
{
var vo:PixelVO =
new PixelVO();
vo.xpos = toX;
vo.ypos = toY;
vo.speedX =
2 - Math.random() *
4;
vo.speedY =
1 - Math.random() *
4;
pixelList.push(vo);
}
if( !
this.hasEventListener(Event.ENTER_FRAME) )
{
this.addEventListener(Event.ENTER_FRAME, onEF);
}
}
private function onEF(
event:Event):
void{
bmd.
lock();
for (
var i:
int=
0; i<pixelList.length; i++ )
{
var item:PixelVO = pixelList[i]
as PixelVO;
item.xpos += item.speedX;
item.ypos += item.speedY;
item.speedY += gravity;
bmd.setPixel( item.xpos, item.ypos,
0xffffff );
if( item.ypos >
400 ){
pixelList.splice( i,
1 );
i--;
item =
null;
}
}
bmd.unlock()
}
在init()函数中我本来想直接放一个bitmap到舞台上去的,无奈bitmap不能监听鼠标点击事件,
只能在它外面加一层Sprite的容器,此容器不需要设置它的宽与高,因为Sprite可以根据其内部的子对象大小来调整自己的长与宽。
我之后创建了一个用于显示烟花的bitmap,它的bitmapData在new的时候我设置了它的全部四个参数,前两个是它的大小,
第三个是设置指定位图图像是否支持每个像素具有不同的透明度,保持默认值true,第四个是用于填充位图图像区域的 32 位 ARGB 颜色值,
即类似0xffffffff格式,一般我们用的颜色值在0x后面都只有6位,这里多了2位Alpha值用来设置透明度,这里我设置的值是0xff000000,
表示透明度为最大,即完全不透明的黑色。
之后我们设置了点击后执行fire函数,fire函数就是用来产生烟花的,一听函数名字就知道很有杀气,里面的代码也很容易理解,
创建了pixelNum个烟花粒子并给每个粒子设置了初始位置为鼠标点击点和随机的一个速度,这个速度可上可下可左可右。
最后我们来看,主要的难点就在于enterFrame处理函数中。对于
bmd.
lock();
bmd.unlock();
这样的标签对的使用会直接影响运行效率,在对粒子进行数据处理之前先锁定,待数据处理完成后再进行渲染。
为了提高运行效率,当别人问你为什么要用这两句时你可以果断回答之“不解释”。
在标签对中我们遍历粒子数组对每个粒子进行数据处理,更新粒子位置并让y轴上的速度受到重力加速度的影响。
之后再执行setPixel()方法设置粒子对应位置的颜色,这里我统一设成了白色。
这样的话每一帧到来时粒子位置的变化都会显式地表现出来。最后我们把掉落出烟花容器底部的粒子从数组里移去。
运行一下,我们发现烟花是放出了,但是每个粒子移动过的路径都还留着:
这可不是我们想要的效果,不过仔细想想,之前我们用setPixel在设置过一个像素点的颜色后就没再设置他回原先的颜色了,
出现这种情况是理所当然的。那咋办呢?我们找到一个类叫做ColorTransform,它可以设置一个对象的颜色值,看看他的参数先:
ColorTransform(redMultiplier:Number = 1.0, greenMultiplier:Number = 1.0, blueMultiplier:Number = 1.0, alphaMultiplier:Number = 1.0, redOffset:Number = 0, greenOffset:Number = 0, blueOffset:Number = 0, alphaOffset:Number = 0)
靠,这参数真TMD的多,不过我们一般这里只用到前三个,它们分别代表红色、绿色以及蓝色乘数的值,在 0 到 1 范围内。
设置了这三个参数之后可以把需要设置颜色的对象的红、绿、蓝颜色值乘以这三个乘数形成一个新的颜色。
那我们为了让每一帧到来时削减粒子运动路径上的颜色值,就把这三个参数设置成小于1的一个小数。
在声明语句中加上一句:
private
var ctf:ColorTransform =
new ColorTransform(
0.9,
0.96,
0.96);
之后去onEF函数中在bmd.lock之后加上:
bmd.
lock();
bmd.colorTransform( bmd.rect, ctf );
在bitMapData中的colorTransform属性可以让我们使用 ColorTransform 对象调整位图图像的指定区域中的颜色值。
我们每一帧都将一个红绿蓝三色颜色乘数都小于1的ColorTransform 对象传值给bmd后就可以达到每一帧削减bmd范围
内所有像素的颜色为原来的0.9, 0.96, 0.96,这样的话我们就能够看到粒子当前所在位置是正常的颜色,
因为他的颜色是新设置的,而它运动过的路径颜色就在不断变淡,最后和背景色融为一体,因为他们都被设置成了黑色0x000000。
为了视觉效果更加美观,再加上个模糊滤镜:
全部代码
private
var bf:BlurFilter =
new BlurFilter(
6,
6,
2);
……
private function onEF(
event:Event):
void{
bmd.
lock();
bmd.colorTransform( bmd.rect, ctf );
bmd.applyFilter(bmd, bmd.rect,
new Point(), bf);
……