會講土味情話的煙火怎麼輸?
今天下午,朋友傳給我一段程式碼,剛好我今天也在煩惱要寫什麼。平常很喜歡寫一些創意的程式,這次剛好可以把那段程式碼拿來研究一下,然後加以改造,結果不小心就玩得有點瘋,寫了一個煙火特效!真的是讓我一邊寫一邊感覺超級開心~不過這段程式碼其實也學到了不少東西,所以今天就想和大家分享一下,看看裡面每個 function 的意義,還有我寫這個程式的心得。

let particles = [];
這行定義了一個空的陣列 particles,用來儲存每個煙火粒子。後面會將每個煙火粒子(Particle 物件)加入到這個陣列中,以便在畫布上顯示和更新。
let explosionCenter;
這行定義了一個變數 explosionCenter,用來存儲爆炸的中心位置。這個位置會在用戶點擊滑鼠時設定,並作為煙火粒子從哪個點開始爆炸的基準。
let numParticles = 100;
這行設定了生成的煙火粒子數量,這裡設為 100,意思是在每次爆炸時會生成 100 個粒子,這些粒子會隨機飛散,形成煙火的效果。
let pickupLines = [ ... ];
這是一個陣列,包含了多個土味情話(pickup lines)。每一個情話中都包含了換行符 \n,用來實現顯示多行文本的效果。這些情話會在每次鼠標點擊時隨機顯示,為爆炸效果增添趣味。
let currentLine = '';
這行定義了一個變數 currentLine,用來儲存當前隨機選擇的情話。每次用戶點擊滑鼠,程式會從 pickupLines 陣列中隨機選擇一條情話並存入 currentLine。

createCanvas(400, 400);
這行程式碼創建了一個 400x400 像素的畫布(Canvas),這就是你可以在上面繪製圖形、顯示文本等的區域。這個畫布是程式的顯示區域,所有視覺效果和動畫都會在這個畫布上呈現。
background(0);
這行程式設置畫布的背景顏色為黑色(0 代表黑色,範圍從 0 到 255,這裡的 0 是黑色,255 是白色)。每次 setup() 被調用時,畫布會被填充為黑色。
explosionCenter = createVector(width / 2, height / 2);
這行程式創建了一個 Vector 物件並將其賦值給 explosionCenter 變數。createVector(width / 2, height / 2) 會產生一個向量,這個向量的 x 和 y 座標分別是畫布寬度和高度的一半,也就是畫布的中心位置。這是設置爆炸中心點的位置,後續會在這個位置生成煙火粒子。
textAlign(CENTER, CENTER);
這行設置了文本對齊方式。textAlign(CENTER, CENTER) 使得文本的 x 和 y 方向的對齊方式都設為畫布的中心。這樣顯示的文本就會正中央顯示,而不是從某個角落開始繪製。
textSize(14);
這行設置了文本的大小為 14。這是為了確保顯示的文本大小適中,不會過大或過小。在這個例子中,情話文本會以這個大小顯示在畫布上。

background(0, 50);
這行程式設置背景顏色並使其略微透明。0 代表黑色,而 50 代表透明度(alpha 值)。這樣設置背景會在每一幀更新時給畫面一層半透明的黑色效果,創造出煙火粒子留下拖影的感覺(稱為「trail」效果)。
for (let i = particles.length - 1; i >= 0; i--) {
這是一個倒序循環,從 particles 陣列的最後一個元素開始,逐個遍歷所有粒子。倒序遍歷是為了在移除粒子時不會影響到陣列的索引順序。
let p = particles[i];
這行程式將當前粒子(位於 particles 陣列中的第 i 個元素)賦值給變數 p,方便在接下來的程式中進行操作。
p.update();
這行調用 Particle 類別中定義的 update() 方法,更新當前粒子的狀態,比如位置、速度等。每次迴圈都會呼叫一次 update(),使粒子按照預定規律移動。
p.show();
這行調用 Particle 類別中的 show() 方法,負責繪製當前粒子。通常這會在畫布上顯示出粒子的形狀,這樣粒子在每一幀中就會有動態效果。
if (p.alpha <= 0) { particles.splice(i, 1); }
這是一個條件語句,檢查當前粒子的透明度 alpha 是否小於或等於 0。當粒子完全消失後,alpha 值會變為 0。此時,使用 splice() 方法將這個粒子從 particles 陣列中移除,這樣粒子就不再被更新和繪製,從而達到粒子消失的效果。
displayRainbowText(currentLine, explosionCenter.x, explosionCenter.y);
這行程式會在每一幀中顯示隨機的情話文本(從 currentLine 變數中獲取),並將其顯示在爆炸中心(explosionCenter.x 和 explosionCenter.y 位置)。這樣文本會在每次觸發爆炸時出現,並在畫布上顯示彩虹漸層效果。

explosionCenter = createVector(mouseX, mouseY);
當使用者點擊滑鼠時,這行程式會將滑鼠的當前位置(mouseX 和 mouseY)轉換成一個 p5.Vector 物件,並將其賦值給 explosionCenter 變數。這樣,爆炸的中心位置就會根據滑鼠點擊的座標來設置。
currentLine = random(pickupLines);
這行程式會從 pickupLines 陣列中隨機選擇一條情話並將其賦值給 currentLine 變數。pickupLines 是事先定義的一組情話,這樣每次觸發爆炸時,顯示的情話會隨機變化。
for (let i = 0; i < numParticles; i++) {
這是一個循環,會執行 numParticles 次,numParticles 的值是事先設定的粒子數量(在這個程式中是 100)。每一次迴圈,會創建一個新的粒子並將其加入到 particles 陣列中。
particles.push(new Particle(explosionCenter));
這行程式會創建一個新的 Particle 物件,並將其加入到 particles 陣列中。這裡的 new Particle(explosionCenter) 會創建一個新的粒子,並將 explosionCenter 作為粒子的初始位置。每個粒子會在爆炸中心發射並進行運動。

陣列 rainbowColors 包含了代表彩虹顏色的 color() 函數,每個顏色是以 RGB 格式定義的。這裡包括了紅色、橙色、黃色、綠色、藍色、靛藍色和紫色。
shuffle() 函數隨機打亂 rainbowColors 陣列中的顏色順序。這樣,顏色顯示的順序每次都會不同,帶來更多的視覺變化。
pickupText(情話文本)可以根據換行符 \n 進行分割,將多行文本轉換為一個陣列 lines,這樣每一行就可以單獨處理並顯示。
所以這裡是在逐行顯示文本,並為每個字符設定顏色。
然後 textWidth(c) 函數計算當前字符的寬度,並將其加到 lineLength 變數上,以確保下個字符會正確地顯示在上一個字符之後。

constructor(position)
this.pos: 設定粒子的初始位置,position.copy() 是從傳入的 position 位置複製一個新的 p5.Vector 對象。這是粒子出現的起始位置。
this.vel: 使用 p5.Vector.random2D() 生成一個隨機的 2D 向量,表示粒子的隨機方向。
this.vel.mult(random(2, 5)): 隨機縮放這個速度向量,使粒子的初速度在 2 到 5 之間。
this.size: 為粒子隨機賦予一個大小,範圍在 4 到 10 之間。
this.alpha: 設定粒子的初始透明度為 255,即完全不透明。
this.color: 隨機生成一個顏色,用 color(random(100, 255), random(100, 255), random(100, 255)) 隨機產生紅、綠、藍三個顏色值,顏色範圍在 100 到 255 之間,讓顏色不會過於暗淡。
update()
this.pos.add(this.vel): 根據當前速度向量更新粒子的位置。
this.vel.mult(0.96): 每次更新時,將速度乘以 0.96,這代表粒子受到一個輕微的阻力(減速),使其運動逐漸減慢。
this.alpha -= 4: 讓粒子的透明度逐漸減少,達到粒子消失的效果。
show()
noStroke(): 禁止繪製粒子的邊框。
fill(red(this.color), green(this.color), blue(this.color), this.alpha): 設定粒子的顏色,使用 red(), green(), blue() 函數從 this.color 中提取紅、綠、藍三個分量,並將透明度 this.alpha 加入,實現粒子顏色隨著時間逐漸變透明的效果。
ellipse(this.pos.x, this.pos.y, this.size): 用 ellipse() 函數在畫布上繪製粒子,位置是 this.pos,大小是 this.size。
寫完這段程式之後,真的超想去看煙火!如果大家也喜歡煙火或者想玩這段程式碼,歡迎一起試試!也許你會發現,寫程式也能像放煙火一樣,帶來許多意外的驚喜~
ps. 因為這堂課剛好在教 ai 結合 coding,所以這篇網誌除了這段話以外都是 ai,謝謝 ai^^
喜欢我的作品吗?别忘了给予支持与赞赏,让我知道在创作的路上有你陪伴,一起延续这份热忱!