天花無數月中開,五采祥雲繞絳台。墮地忽驚星彩散,飛空旋作雨聲來。怒撞玉鬥翻晴雪,勇踏金輪起疾雷。更漏已深人漸散,鬧竿挑得彩燈回。
——明·瞿佑·《煙火戲》
記得每年過春節的那段時間,除了欣賞隆冬的景色,剩下的就是欣賞天空中美麗的煙花了。
成都的冬天,天空中總是灰濛濛的,像是織了一層薄薄的輕紗,把陽光擋走了一部分。路邊的楓樹上,沒有了夏日整天“知了”,“知了”的小傢伙,是否有鳥兒,我卻也忘了。樹上的楓葉寥寥無幾,可能是在某一時候,悄無聲息地飄落了;有時路過一棵年歲已高的樹,也不知是地球引力還是什麼的原應,一片葉子會自然地落下來,不留神的話就會掉到你頭頂上,頂著它走幾裡路了,還沒發現。
大冷的天,躲在屋裡,有時大風掛起,看著不停散落的楓葉,都開始害怕他們會凍成冰。有時敲代碼不知不覺就到了傍晚,天更灰了,回身看看窗外的楓樹,只見一片片的紅雲飄逸在眼前,隨著風起而凋零。
除夕夜裡,一般是在親戚家過的,吃完了飯,一家人便一邊欣賞提前放飛的煙花,一邊聊天。屋裡暖暖的,想到還在外面勞作的人們,不覺得會為他們打個寒戰。
有時我和父母還有哥哥會提前回家,告別了眾親戚,便往屋裡趕,希望還能趕上春節聯歡晚會的開始(雖然現在很多人討厭看春節聯歡晚會,但我想這是一種傳統,搞得不好可以提意見,但是好的傳統應該繼續傳承下去,就像類的繼承一樣)。父親是個不愛花冤枉錢的人,去某個地方從來不打車,要不自己走,要不就自己騎車,或者自己開車。因此在走回去的路上,我又可以觀賞這隆冬的華麗夜景了。
回家後,第一件事就是把年貨拿出來,不停地吃,父親一向不許我吃零食,但是過節的時候從來不阻止。記得有一年,父親叫我和哥哥把所有吃剩的瓜子殼,橘子皮都扔在地上,我說難道不難收拾嗎?父親則說到時候掃一下就可以了,仍在地上才有節日的氣氛。到現在來想想,過節不就是要氣氛嗎?沒了氣氛,也就沒了溫馨,沒了溫馨,這節日還有什麼好過的呢?
當鐘上的時針和分針都指在12點的位置上的時候,就是該觀看煙花的時候了。各種各樣的煙花被撒如天幕,綻放出各種五顏六色,奇形怪狀的煙花粒子。有的升入雲霄,忽地不見了,過一眨眼的功夫,便突然呈現為一點金色的小花,像夜幕中的星星一般。有的就給子彈一樣,不停地連發出去,飛到一定高度也消失了,有人說這就是傳說中的“衝天炮”,我覺得與其叫“衝天炮”,還不如叫火箭“喀秋莎”算了。煙花的聲音可不是一般的混亂,轟隆隆的聲響裡,總少不了嗖嗖地煙花升天的聲音。這些聲音倒也吵人,倘若靠近聲源處,旁邊的人說話是聽不見的。
可惜最近幾年迷上了編程式,煙花也懶得看了,幾年沒欣賞煙花了,心裡也惦記起來了。最近用html5做了一個拖尾效果,想到用拖尾可以做一個煙花效果,也就嘗試做了一下,沒想到不做不知道,原來實現起來這麼簡單。上面瞎扯了一大堆,大家見諒一下,接下來就給大家介紹一下是如何?的。
先看一下遊戲:
測試地址:http://www.cnblogs.com/yorhom/articles/3244140.html
用支援html5的瀏覽器開啟就可以。
本次開發和上次一樣,用到了開源引擎lufylegend,詳細資料如下
lufylegend官方網站:http://lufylegend.com/lufylegend
lufylegend API文檔:http://lufylegend.com/lufylegend/api
※注意:在瞭解了引擎lufylegend的前提下閱讀本文方可沒有障礙。
接下來是實現過程。
一,改進拖尾類
在上一節《『HTML5夢幻之旅』-炫麗的流星雨效果》中,我們講解了Smearing這個類,這個類主要用於現實拖尾等效果。上一節中,我們講到了Smearing.to()方法裡的時候,談到如果對象移動完畢時,自動將自己的mode設定為"complete"。實現這個方面的時候,我們直接將緩動的資料的onComplete變更,以達到效果。後來發現這樣做不好,因此改進了一下:想將原資料的onComplete儲存到一個變數中,再更改onComplete裡的資料,更改為先調用先前儲存的onComplete,然後再將mode改為"complete",這樣的話,使用者就可以自己設定onComplete裡的內容了。Smearing.to()方法裡的代碼改為如下:
Smearing.prototype.to = function($duration,$vars){var self = this;var customFunc = $vars.onComplete || function(){};$vars.onComplete = function(){customFunc();self.mode = "complete";}LTweenLite.to(self.originalSprite,$duration,$vars);};
代碼清單1
二,煙花類Fireworks
為了實現煙花效果,我們封裝一個叫Fireworks的類,類的構造器如下:
function Fireworks(x,y,color){var self = this;base(self,LSprite,[]);self.fireworksX = x;self.fireworksY = y;self.angle = 20;self.count = 18;self.smearingColor = color;self._showFireworks();}
代碼清單2
這段代碼其實不難理解,首先和拖尾類一樣,繼承自LSprite,然後把前三個參數儲存進自身屬性裡面,中間有一段代碼,如下:
self.angle = 20;self.count = 18;
代碼清單3
這兩行代碼看似平凡,卻十分重要。由於我們的煙花是一圈一圈的,所以,我們在現實煙花噴出來的粒子的時候,要計算出每個煙花的位置。要確定位置,就要確定每個煙花的角度,以及所有煙花粒子的個數,為了實現這個,我們設定這兩個屬性。angle屬性是當前煙花粒子要到達的位置與圓心的連線和上一個煙花粒子要到達的位置與圓心的連線的夾角。count屬性是所有煙花粒子的個數。可以看到,如果angle和count想乘,積是360,也就是一圈的度數。
我們每個粒子要到達的位置圖示如下:
這一點想通了就不難了,最後我們調用成員函數_showFireworks(),通過掉用這個函數實現顯示煙花。具體代碼如代碼清單4:
Fireworks.prototype._showFireworks = function(){var self= this;var kaku;for(var i=0; i<self.count; i++){kaku = i*self.angle;var toX = 100*Math.sin(kaku * Math.PI / 180);var toY = 100*Math.cos(kaku * Math.PI / 180);var smearingLayer = new LGraphics();smearingLayer.drawArc(0,"",[0,0,5,0,2*Math.PI],true,self.smearingColor);var spreadingSmearing = new Smearing(smearingLayer);spreadingSmearing.x = self.fireworksX;spreadingSmearing.y = self.fireworksY;spreadingSmearing.to(1,{x: toX,y: toY,onComplete:function(){self.mode = "complete";}});self.addChild(spreadingSmearing);}};
代碼清單4
前兩行代碼跳過,因為js程式員都應該知道。然後我們跳到最重要的迴圈環節,整理後代碼如下:
for(var i=0; i<self.count; i++){kaku = i*self.angle;var toX = 100*Math.sin(kaku * Math.PI / 180);var toY = 100*Math.cos(kaku * Math.PI / 180);var smearingLayer = new LGraphics();smearingLayer.drawArc(0,"",[0,0,5,0,2*Math.PI],true,self.smearingColor);var spreadingSmearing = new Smearing(smearingLayer);spreadingSmearing.x = self.fireworksX;spreadingSmearing.y = self.fireworksY;spreadingSmearing.to(1,{x: toX,y: toY,onComplete:function(){self.mode = "complete";}});self.addChild(spreadingSmearing);}
代碼清單5
首先我們為了達到粒子環形圍繞效果,我們通過迴圈,取出每個點的角度,並且通過Math.sin()和Math.cos()兩個函數確定該粒子的位置。然後我們畫出一個圓形,顏色是我們執行個體化時給的color參數的值,上面我們已經把它儲存起來了,所以這裡直接用。畫好之後,我們給這個粒子建立拖尾效果。該粒子拖尾的位置是我們儲存好的x,y參數。接著通過to方法讓改拖尾移動到我們指定的位置。當移動到位後,我們就通過onComplete加入完成時的函數,將該煙花類的mode設定為"complete",因為我們在上面更改了to方法,所以可以自訂onComplete裡的東西了。
煙花類就Over了,接下來是有了這些封裝之後,如何?的方法。
三,濺入空中的煙花
先把所有代碼放在下面:
init(10,"mylegend",500,500,main);var backLayer,fireworksLayer;var back;//煙花顏色集var colorArray = new Array("yellow","orangered","red","pink");//加入煙花最大數量var maxFrame = 4;//當前加入煙花數量var frameIndex = 0;var sound;function main(){LStage.setDebug(true);//加入音樂sound = new LSound("http://stream20.qqmusic.qq.com/34962638.mp3");//加入底板層backLayer = new LSprite();addChild(backLayer);//加入煙花層fireworksLayer = new LSprite();addChild(fireworksLayer);//畫一個黑色矩形作為背景back = new LGraphics();back.drawRect(0,"",[0,0,LStage.width,LStage.height],true,"black");backLayer.addChild(back);//加入時間軸事件backLayer.addEventListener(LEvent.ENTER_FRAME,onframe)}function addFireworks(){var toY = Math.floor(Math.random() * (-350 + 250) - 250);var colorIndex = Math.floor(Math.random() * 4)//畫一個黃色矩形作為一顆升天的煙花var fireworks = new LSprite();fireworks.x = Math.floor(Math.random() * (480 - 20) + 20);fireworks.y = 500;fireworks.graphics.drawRect(0,"",[0,0,10,10],true,colorArray[colorIndex]);//為升起的煙花添加一個拖尾var smearing = new Smearing(fireworks,50);//移動煙花smearing.to(1,{x: 0,y: toY,//-300onComplete:function(){//添加擴散開的煙花var spreading = new Fireworks(fireworks.x,fireworks.y+toY,colorArray[colorIndex]);fireworksLayer.addChild(spreading);}});fireworksLayer.addChild(smearing);}function onframe(){//加入煙花if(frameIndex < maxFrame){frameIndex ++;addFireworks();}//播放音樂if(sound.playing == false){sound.play();}//移除煙花for(var key in fireworksLayer.childList){if(fireworksLayer.childList[key].mode == "complete"){//通過緩動更改煙花透明度LTweenLite.to(fireworksLayer.childList[key],0.3,{alpha:0,onComplete:function(o){//移除對象fireworksLayer.removeChild(o);//如果介面上沒有煙花,將已經加入數量設為0if(fireworksLayer.childList.length == 0){frameIndex = 0;}}});}}}
代碼清單6
代碼都加了注釋,我只講一下設計方案:因為我們的煙花是五顏六色的,所以我們將要有的顏色全部放入colorArray中,然後我們在main中初始化層,並加入背景,音樂,時間軸事件。在時間軸事件觸發的onframe函數中,我們通過addFireworks來加煙花。在addFireworks中,我們首先取出一個煙花的顏色,並以這種顏色畫出相應的煙花,然後通過拖尾效果中的to方法送這個煙花上天,然後在上天動作完成的時候,我們加入散開的粒子效果,如果我們反覆調用addFireworks就會反覆出現煙花。由於我們的介面上不能出現太多的煙花,因為一方面會顯得擁擠看不清楚效果,另一方面就是會導致介面很卡。為瞭解決這個問題,我們通過,maxFrame和frameIndex兩個變數來控制。當onframe每觸發一次,我們就將frameIndex加一,然後判斷是否小於maxFrame,如果是,就調用addFireworks(),因為我們在上面定義的maxFrame為4,所以最多加4個煙花到介面上。但是如果我們不把frameIndex重新設定為0的話,且不移除煙花的話,那4個煙花就會一直在螢幕上,一不會消失,而不會重新加入。為了實現這些,我們加入迴圈,在迴圈裡通過判斷mode是否為"complete"來移除對象,通過判斷fireworksLayer裡的對象是否被移除完了來判斷是否把frameIndex重新設定為0。通過這一個系統,就會在畫面上不斷地出現煙花並消失。這個效果就搞定了~~~~
最後運行出來,連我都感歎道:“這不是久違不見的煙花君嗎?”
lufy長者也曰到:“效果很漂亮啊。”
嘿嘿,既然專家都說漂亮了,大家不趕快試試?
最後奉上原始碼:http://files.cnblogs.com/yorhom/fireworks.rar
話說這煙花效果效率不高,先截了一次放20個煙花的:
最後我冒著卡死的風險搞了一次放1000個煙花的效果,居然還可以運行,如下:
如果大家要測試效率的話,用把變數maxFrame調大些就可以了。
本篇文章就到此結束了。文章如有什麼地方寫得不妥,歡迎提出。另外,如果有任何不解的地方,可以在部落格下方留言或者新浪微博@Yorhom,當然也可以發郵件,郵箱:wangyuehao1999@gmail.com,我會盡我所能幫你解決。
支援就是最大的鼓勵!
----------------------------------------------------------------
歡迎大家轉載我的文章。
轉載請註明:轉自Yorhom's Game Box
http://blog.csdn.net/yorhomwang
歡迎繼續關注我的部落格