當前做粒子效果的主流思想就是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);
……