大家好,歡迎來到粒子引擎教程第二季。如果你還沒讀過第一季,請先讀上一季。在上一季裡我們已經為粒子引擎打下了基礎。生活需要多姿多彩,現在就來為引擎新增視覺效果吧。
See the Pen Particle engine tutorial Part 1 by suffick (@suffick) on CodePen.
色彩
到目前為止,我們只有一種顏色,那就是我們定義在配置裡的顏色。顯然這有點寒摻, 我們希望擁有豐富以及炫目的效果。所以我們要打造一個色彩變換系統。所有的顏色都由四部分組成:紅,綠,藍以及alpha值。我們把顏色值拆分到這些單項裡更便於我們進行單獨操控。從一種顏色漸變到另一種顏色還不夠騷,更騷的是從隨機的某一顏色變到另一隨機的顏色。
下面我們在setting物件裡定義初始顏色和目標顏色陣列
1 2 3 4 5 6 7 8 |
'start_colours': [ [130, 196, 245, 1], [69, 152, 212, 1] ], 'end_colours': [ [130, 196, 245, 0], [69, 152, 212, 0] ] |
這些陣列裡的值就是上面提到的顏色值。紅,綠,藍的值域為0到255, alpha值為0到1。我們會從初始顏色陣列裡隨機選擇一種顏色,在粒子的生命週期內漸變到另一隨機的目標顏色。要實現這個目標首先我們要給粒子的建構函式增加兩個變數,start_colour和colour_step, 分別代表每秒鐘顏色改變的度量。
1 2 3 4 5 6 7 8 9 |
var Particle = function(..., start_colour, colour_step) { ... /* the particle's colour values */ this.colour = start_colour; this.colour_step = colour_step; }; |
現在我們需要修改建立新粒子的程式碼部分(在噴射器的update方法裡)。我們需要獲取初始顏色和目標顏色,並計算每秒鐘需要改變多少顏色量,以便粒子在生命週期內能夠完成顏色轉換。為了在陣列中隨機選擇元素,我們可以在0和陣列長度之間產生一個隨機數,並用Math.floor()進行取整。
我們需要在產生新粒子之前獲得初始顏色和目標顏色。
1 2 3 4 5 |
var start_colour = this.settings.start_colours[Math.floor(this.settings.start_colours.length * Math.random())]; var end_colour = this.settings.end_colours[Math.floor(this.settings.end_colours.length * Math.random())]; |
或者你可以將初始顏色和目標顏色進行繫結,比如你可以只產生一個隨機數,從兩個陣列裡都取這個隨機數位置的元素。
接下來要計算每秒鐘的顏色轉換量。首先我們得知道粒子的生命值,目前這個值是在建構函式裡傳入的,所以我們需要在建立粒子前將他賦給一個變數。然後通過目標顏色減去初始顏色的值我們可以知道初始顏色和目標顏色的差值, 最後除以生命值就能得到每秒鐘需要的顏色轉換量。需要特別注意的是這裡粒子的顏色值應該是start_colour陣列的拷貝而不能是原陣列的引用,否則所有采用這個顏色的粒子都會被改變。我們可以用Array.prototype.slice()來實現。
現在我們的建立粒子的程式碼應該像下面這樣:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
var start_colour = this.settings.start_colours[Math.floor(this.settings.start_colours.length * Math.random())]; var end_colour = this.settings.end_colours[Math.floor(this.settings.end_colours.length * Math.random())]; var life = this.settings.min_life + Math.random() * this.settings.life_range; var colour_step = [ (end_colour[0] - start_colour[0]) / life, /* red */ (end_colour[1] - start_colour[1]) / life, /* green */ (end_colour[2] - start_colour[2]) / life, /* blue */ (end_colour[3] - start_colour[3]) / life /* alpha */ ]; this.particles.push( new Particle( 0, 0, this.settings.min_angle + Math.random() * this.settings.angle_range, this.settings.min_speed + Math.random() * this.settings.speed_range, life, this.settings.min_size + Math.random() * this.settings.size_range, start_colour.slice(), colour_step ) ); |
我們只需要把colour_step值新增到粒子的當前顏色然後將fillStyle設定為new Colour就行了。還需要將colour_step乘以經歷的時間才能進行正確轉換。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
ctx.fillStyle = this.settings.colour; /* now becomes... */ particle.colour[0] += particle.colour_step[0] * dt; particle.colour[1] += particle.colour_step[1] * dt; particle.colour[2] += particle.colour_step[2] * dt; particle.colour[3] += particle.colour_step[3] * dt; ctx.fillStyle = 'rgba(' + Math.round(particle.colour[0]) + ',' + Math.round(particle.colour[1]) + ',' + Math.round(particle.colour[2]) + ',' + particle.colour[3] + ')'; |
先來看一下效果:
See the Pen Particle engine tutorial part 2 [a] by suffick (@suffick) on CodePen.
太騷了,已經很不錯了。
重力
對於很多效果來說重力都是非常重要的元素。重力設定有兩個值, gravity.x影響X軸, gravity.y影響Y軸。 兩個值都可以為正(右邊和下邊)也可以為負(左邊和上邊)還可以為0(沒有重力)。 單位為畫素每秒。
新增重力物件到settings。
1 2 3 4 |
'gravity': { x: 0, y: 80 } |
現在需要做的就是將這些值乘以與上次更新粒子速率的時間差再加到當前速率。請看下面程式碼:
1 2 |
particle.vel.x += this.settings.gravity.x * dt; particle.vel.y += this.settings.gravity.y * dt; |
很簡單吧,來看看效果:
See the Pen Particle engine tutorial part 2 [a] by suffick (@suffick) on CodePen.
現在看上去更加自然形象,更加具有動態。 嘗試一下改變顏色,重力,角度,大小和其他設定吧,你會發現現在我們已經能建立大量的效果了。現階段我們的粒子系統差不多可以用到遊戲中了,但還差了一點兒東西。
位置變化
如果粒子相對於噴射器的初始位置能夠發生變化,這將為我們開啟更多效果的大門,比如說雨模擬。 要想模擬雨點效果,粒子的初始位置需要沿整個X軸隨機分佈,我們需要增加兩個設定來實現, min_position和 position_range。 不說你也應該知道這兩個值幹啥用,他們定義粒子散開的範圍。
先加上這兩個設定:
1 2 3 4 5 6 7 8 |
'min_position': { x: 0, y: 0 }, 'position_range': { x: 100, y: 100 } |
我希望這些設定為可選項,所以我們在噴射器的建構函式里加上預設值。
1 2 3 |
this.position_vary = this.settings.position_range || false; this.min_position = this.settings.min_position || {x: 0, y: 0}; |
預設情況下,粒子初始位置為0,0跟以前一樣。如果設定了位置,我們在範圍內選取一個隨機值而不是直接設定把X和Y的值設定為0。
1 2 3 4 5 6 7 8 9 10 |
this.particles.push( new Particle( this.min_position.x + (this.position_vary ? Math.random() * this.position_vary.x : 0), this.min_position.y + (this.position_vary ? Math.random() * this.position_vary.y : 0), ... ) ); |
收工!下面是一些基本的效果的演示。
See the Pen Particle engine tutorial part 2 [b] by suffick (@suffick) on CodePen.
下一步?
現在我們的粒子系統已經比較完善了,但還沒有完工。第三季我會講解渲染效果讓它更逼真,還有實現噴射器的生命週期以及其他效果。
感謝關注, 希望你喜歡並採用。 第三季待續。。。