Processing粒子系統(tǒng)實(shí)戰(zhàn):從零構(gòu)建動(dòng)態(tài)雨景模擬與交互優(yōu)化
1. 從零開(kāi)始為什么選擇Processing來(lái)模擬雨如果你對(duì)創(chuàng)意編程、動(dòng)態(tài)視覺(jué)或者交互藝術(shù)感興趣那么“用Processing做雨”這個(gè)標(biāo)題很可能就是你踏入這個(gè)奇妙世界的第一塊敲門(mén)磚。Processing這個(gè)誕生于麻省理工學(xué)院媒體實(shí)驗(yàn)室的開(kāi)源編程語(yǔ)言和集成開(kāi)發(fā)環(huán)境它的設(shè)計(jì)初衷就是讓藝術(shù)家、設(shè)計(jì)師、教育工作者和編程新手能夠輕松地創(chuàng)作視覺(jué)藝術(shù)和交互媒體。它把復(fù)雜的圖形渲染、數(shù)學(xué)計(jì)算和事件處理封裝成了極其簡(jiǎn)潔的API讓你可以用寥寥幾行代碼就讓屏幕上“下起雨來(lái)”。那么為什么是“雨”呢在視覺(jué)模擬中雨是一個(gè)近乎完美的入門(mén)課題。它看似簡(jiǎn)單——無(wú)非是無(wú)數(shù)個(gè)向下移動(dòng)的點(diǎn)——但其中蘊(yùn)含的物理邏輯、隨機(jī)性控制、視覺(jué)層次和性能優(yōu)化幾乎涵蓋了動(dòng)態(tài)圖形編程的核心要素。通過(guò)模擬雨你可以學(xué)習(xí)到如何管理大量粒子雨滴、如何應(yīng)用簡(jiǎn)單的物理規(guī)則重力、加速度、如何創(chuàng)造視覺(jué)深度近大遠(yuǎn)小、速度差異以及如何通過(guò)參數(shù)調(diào)整來(lái)營(yíng)造不同的氛圍細(xì)雨、暴雨、斜風(fēng)細(xì)雨。更重要的是這個(gè)過(guò)程的成果是即時(shí)、可視且富有美感的它能給你帶來(lái)最直接的成就感驅(qū)動(dòng)你繼續(xù)探索下去。在開(kāi)始之前我們不需要任何復(fù)雜的開(kāi)發(fā)環(huán)境配置。你只需要去Processing的官網(wǎng)下載對(duì)應(yīng)你操作系統(tǒng)的版本安裝然后打開(kāi)它。你會(huì)看到一個(gè)極其簡(jiǎn)潔的界面一個(gè)代碼編輯區(qū)一個(gè)運(yùn)行按鈕和一個(gè)顯示窗口。這就是你的全部畫(huà)布和工具箱。本文的目標(biāo)就是帶你從畫(huà)下第一滴“雨”開(kāi)始逐步構(gòu)建一個(gè)逼真、可交互、甚至帶點(diǎn)詩(shī)意的雨景模擬。我們不僅會(huì)實(shí)現(xiàn)基礎(chǔ)功能還會(huì)深入探討如何優(yōu)化、如何增加細(xì)節(jié)以及如何將你的個(gè)人創(chuàng)意融入其中。無(wú)論你是完全的編程新手還是有一定基礎(chǔ)想探索創(chuàng)意編程的開(kāi)發(fā)者都能從這里找到樂(lè)趣和啟發(fā)。2. 第一滴雨基礎(chǔ)粒子系統(tǒng)的構(gòu)建與核心原理萬(wàn)事開(kāi)頭難但用Processing開(kāi)頭真的不難。我們的第一步是在屏幕上創(chuàng)造一個(gè)移動(dòng)的點(diǎn)也就是一滴最基本的“雨”。2.1 初始化舞臺(tái)與繪制循環(huán)打開(kāi)Processing你會(huì)看到一段預(yù)設(shè)的代碼框架void setup() { size(800, 600); } void draw() { background(0); }這段代碼定義了兩個(gè)核心函數(shù)。setup()函數(shù)只在程序啟動(dòng)時(shí)運(yùn)行一次通常用于初始化設(shè)置比如這里我們用size(800, 600)創(chuàng)建了一個(gè)800像素寬、600像素高的顯示窗口。draw()函數(shù)則是Processing的心臟它每秒會(huì)默認(rèn)調(diào)用60次幀率60fps窗口中的所有動(dòng)畫(huà)和交互都發(fā)生在這個(gè)循環(huán)里。background(0)則將每一幀的背景色設(shè)置為黑色0代表純黑相當(dāng)于在畫(huà)新圖前先清空畫(huà)布?,F(xiàn)在讓我們創(chuàng)造雨滴。在編程中這種大量存在、行為相似的微小對(duì)象我們通常用“類(lèi)”Class來(lái)定義。這就像先設(shè)計(jì)好一滴雨滴的藍(lán)圖。class Raindrop { float x, y; // 雨滴的位置 float speed; // 下落速度 float length; // 雨滴長(zhǎng)度 // 構(gòu)造函數(shù)創(chuàng)建雨滴對(duì)象時(shí)初始化屬性 Raindrop() { x random(width); // 在窗口寬度范圍內(nèi)隨機(jī)一個(gè)橫坐標(biāo) y random(-500, -50); // 從屏幕上方之外開(kāi)始制造陸續(xù)進(jìn)入的效果 speed random(5, 15); // 隨機(jī)速度讓雨滴有快有慢 length random(10, 30); // 隨機(jī)長(zhǎng)度 } // 更新雨滴狀態(tài) void fall() { y speed; // 根據(jù)速度更新縱坐標(biāo)實(shí)現(xiàn)下落 // 如果雨滴落出屏幕底部就重置到頂部 if (y height) { y random(-200, -50); x random(width); } } // 顯示雨滴 void display() { stroke(138, 180, 248); // 設(shè)置線(xiàn)條顏色為一種淺藍(lán)色 strokeWeight(2); // 設(shè)置線(xiàn)條粗細(xì) line(x, y, x, y length); // 用一條線(xiàn)段繪制雨滴 } }讓我們拆解這個(gè)“藍(lán)圖”屬性x, y定義了雨滴頂端的位置。speed是它每幀下落的像素距離。length是雨滴的長(zhǎng)度。使用float浮點(diǎn)數(shù)類(lèi)型是為了更精細(xì)的控制。構(gòu)造函數(shù)Raindrop()當(dāng)我們?cè)谥鞒绦蛑袑?xiě)下new Raindrop()時(shí)就會(huì)調(diào)用它。它用random()函數(shù)為每個(gè)屬性賦予一個(gè)隨機(jī)初始值。y初始值為負(fù)是為了讓雨滴從屏幕外落下而不是突然全部出現(xiàn)在頂部這樣更自然。方法fall()方法負(fù)責(zé)更新邏輯每幀讓雨滴的y坐標(biāo)增加speed實(shí)現(xiàn)下落。當(dāng)雨滴完全離開(kāi)屏幕y height就將其重置到屏幕上方隨機(jī)位置實(shí)現(xiàn)循環(huán)降雨。display()方法負(fù)責(zé)繪制用line()函數(shù)畫(huà)一條從(x, y)到(x, ylength)的豎線(xiàn)模擬雨絲。注意random(a, b)函數(shù)生成一個(gè)介于a包含和b不包含之間的隨機(jī)浮點(diǎn)數(shù)。width和height是Processing的內(nèi)置變量分別代表窗口的寬和高。2.2 從一滴雨到一場(chǎng)雨數(shù)組與集合管理有了雨滴的藍(lán)圖我們需要?jiǎng)?chuàng)建并管理成千上萬(wàn)個(gè)雨滴實(shí)例。這里我們使用ArrayList它是比普通數(shù)組更靈活的動(dòng)態(tài)數(shù)組。在主程序setup和draw之外聲明一個(gè)全局的ArrayListArrayListRaindrop rain new ArrayListRaindrop(); int totalDrops 500; // 初始雨滴數(shù)量在setup()中初始化這些雨滴void setup() { size(800, 600); for (int i 0; i totalDrops; i) { rain.add(new Raindrop()); // 創(chuàng)建500個(gè)雨滴對(duì)象并加入列表 } }最后在每幀都執(zhí)行的draw()循環(huán)中更新和繪制所有雨滴void draw() { background(0); // 用黑色清屏產(chǎn)生雨滴移動(dòng)的軌跡效果 // 遍歷ArrayList中的每一個(gè)雨滴 for (Raindrop drop : rain) { drop.fall(); // 更新位置 drop.display(); // 繪制 } }運(yùn)行這段代碼你應(yīng)該能看到一場(chǎng)簡(jiǎn)單的、由500條藍(lán)色線(xiàn)段構(gòu)成的“雨”開(kāi)始落下。雖然簡(jiǎn)陋但你已經(jīng)實(shí)現(xiàn)了一個(gè)最基礎(chǔ)的粒子系統(tǒng)所有的動(dòng)態(tài)圖形、游戲特效其底層邏輯都與此類(lèi)似定義對(duì)象 - 在循環(huán)中更新對(duì)象狀態(tài) - 繪制對(duì)象。3. 賦予生命物理模擬、視覺(jué)優(yōu)化與交互設(shè)計(jì)基礎(chǔ)雨景有了但它看起來(lái)還像是一些單調(diào)的線(xiàn)條。接下來(lái)我們要為這場(chǎng)雨注入“生命”讓它更逼真、更美觀甚至能與你互動(dòng)。3.1 引入風(fēng)力和加速度真實(shí)的雨滴下落并非勻速且會(huì)受到風(fēng)的影響。我們修改Raindrop類(lèi)增加風(fēng)速和加速度。class Raindrop { float x, y; float speed; float length; float wind; // 風(fēng)力影響水平方向移動(dòng) Raindrop() { x random(width); y random(-500, -50); speed random(5, 15); length map(speed, 5, 15, 8, 25); // 根據(jù)速度映射長(zhǎng)度快的雨滴更長(zhǎng) wind random(-0.5, 0.5); // 隨機(jī)微風(fēng)可正可負(fù) } void fall() { speed 0.05; // 模擬重力加速度越落越快 y speed; x wind; // 水平方向受風(fēng)力影響 // 重置邏輯也需要考慮風(fēng)力導(dǎo)致的水平偏移 if (y height) { reset(); } // 如果被風(fēng)吹出屏幕左右兩側(cè)也重置 if (x -10 || x width 10) { reset(); } } void reset() { y random(-200, -50); x random(width); speed random(5, 10); length map(speed, 5, 10, 8, 20); wind random(-0.5, 0.5); } void display() { // 根據(jù)速度微調(diào)顏色快的雨滴更亮更白 float alpha map(speed, 5, 15, 150, 255); stroke(160, 200, 255, alpha); strokeWeight(map(speed, 5, 15, 1, 2.5)); // 快的雨滴更粗 line(x, y, x wind*2, y length); // 繪制時(shí)加入輕微傾斜與風(fēng)向一致 } }關(guān)鍵點(diǎn)解析加速度speed 0.05;這行代碼是物理模擬的靈魂。它讓雨滴每一幀的下落速度都增加一點(diǎn)點(diǎn)模擬重力持續(xù)作用的效果。你會(huì)發(fā)現(xiàn)雨滴越落越快更加真實(shí)。風(fēng)力wind變量在每幀更新位置時(shí)作用于x坐標(biāo)讓雨滴產(chǎn)生水平漂移。重置時(shí)也重新隨機(jī)wind模擬風(fēng)向的變化。視覺(jué)映射這里大量使用了map()函數(shù)它是Processing中極其有用的工具。map(value, start1, stop1, start2, stop2)能將value從第一個(gè)區(qū)間線(xiàn)性映射到第二個(gè)區(qū)間。例如length map(speed, 5, 15, 8, 25)意味著當(dāng)speed為最小值5時(shí)length為8當(dāng)speed為15時(shí)length為25中間值按比例計(jì)算。這建立了速度與長(zhǎng)度、粗細(xì)、透明度之間的視覺(jué)關(guān)聯(lián)讓快的雨滴看起來(lái)更長(zhǎng)、更粗、更亮極大地增強(qiáng)了視覺(jué)層次感和真實(shí)感。傾斜繪制line(x, y, x wind*2, y length)讓雨滴的末端根據(jù)風(fēng)向進(jìn)行水平偏移繪制出一條斜線(xiàn)比純粹的豎線(xiàn)更有動(dòng)感。3.2 營(yíng)造景深多層雨幕與模糊效果在現(xiàn)實(shí)中遠(yuǎn)處的雨看起來(lái)密集、細(xì)小、移動(dòng)慢近處的雨則稀疏、粗大、移動(dòng)快。我們可以通過(guò)創(chuàng)建多個(gè)具有不同屬性的雨滴層來(lái)模擬這種景深。首先我們不再使用一個(gè)ArrayList而是創(chuàng)建兩個(gè)代表遠(yuǎn)近兩層ArrayListRaindrop rainBack new ArrayListRaindrop(); // 遠(yuǎn)景雨 ArrayListRaindrop rainFront new ArrayListRaindrop(); // 近景雨在setup()中分別初始化它們賦予不同的特性void setup() { size(800, 600); // 遠(yuǎn)景雨數(shù)量多速度慢細(xì)顏色淡 for (int i 0; i 800; i) { rainBack.add(new Raindrop(true)); } // 近景雨數(shù)量少速度快粗顏色實(shí) for (int i 0; i 200; i) { rainFront.add(new Raindrop(false)); } }我們需要修改Raindrop類(lèi)的構(gòu)造函數(shù)接受一個(gè)布爾參數(shù)isBack來(lái)判斷屬于哪一層class Raindrop { ... boolean isBackground; Raindrop(boolean isBack) { isBackground isBack; if (isBack) { // 遠(yuǎn)景雨屬性 x random(width); y random(-100, height); // 甚至可以初始就在屏幕內(nèi)填滿(mǎn)空間 speed random(2, 6); length random(5, 15); wind random(-0.2, 0.2); } else { // 近景雨屬性 x random(width); y random(-500, -100); speed random(8, 20); length random(15, 40); wind random(-1, 1); } } ... void display() { if (isBackground) { stroke(180, 210, 255, 80); // 遠(yuǎn)景雨更淡、更透明 strokeWeight(1); } else { float alpha map(speed, 8, 20, 180, 255); stroke(150, 190, 255, alpha); // 近景雨更實(shí) strokeWeight(map(speed, 8, 20, 1.5, 3)); } line(x, y, x wind*2, y length); } }在draw()中必須先繪制遠(yuǎn)景層再繪制近景層這是計(jì)算機(jī)圖形學(xué)中基本的圖層疊加原理void draw() { background(0); // 先畫(huà)遠(yuǎn)景 for (Raindrop drop : rainBack) { drop.fall(); drop.display(); } // 再畫(huà)近景 for (Raindrop drop : rainFront) { drop.fall(); drop.display(); } }此外我們可以為遠(yuǎn)景層添加運(yùn)動(dòng)模糊效果讓快速移動(dòng)的近景雨更清晰而遠(yuǎn)景更朦朧進(jìn)一步強(qiáng)化速度感。一個(gè)簡(jiǎn)單的方法是不再用純黑色background(0)清屏而是用半透明的黑色矩形覆蓋void draw() { // 用半透明黑色填充產(chǎn)生拖尾模糊效果 fill(0, 25); // 黑色透明度25范圍0-255 noStroke(); rect(0, 0, width, height); // ... 然后繪制雨滴 }這樣每一幀都會(huì)在上一幀的畫(huà)面上疊加一層半透明的黑色之前的畫(huà)面會(huì)慢慢淡去雨滴的運(yùn)動(dòng)軌跡就會(huì)留下淡淡的拖尾形成自然的運(yùn)動(dòng)模糊。這個(gè)效果的透明度值這里的25需要仔細(xì)調(diào)試值太小拖尾太長(zhǎng)會(huì)糊成一片值太大則模糊效果不明顯。3.3 添加交互讓雨景響應(yīng)你的操作靜態(tài)的雨景很美但交互能讓它變成你的作品。Processing可以輕松捕獲鼠標(biāo)和鍵盤(pán)事件。鼠標(biāo)交互 - 雨隨指動(dòng) 我們可以讓雨滴受到鼠標(biāo)吸引或排斥。在Raindrop類(lèi)的fall()方法中加入與鼠標(biāo)位置的互動(dòng)void fall() { ... // 計(jì)算雨滴到鼠標(biāo)的距離 float distanceToMouse dist(x, y, mouseX, mouseY); if (distanceToMouse 100) { // 如果距離小于100像素 // 計(jì)算遠(yuǎn)離鼠標(biāo)的方向向量 float dx x - mouseX; float dy y - mouseY; // 歸一化并施加一個(gè)排斥力 float force 50 / distanceToMouse; // 距離越近力越大 x dx * 0.01 * force; y dy * 0.01 * force; } ... }這里用到了dist()函數(shù)計(jì)算兩點(diǎn)距離以及基本的向量運(yùn)算來(lái)產(chǎn)生一個(gè)遠(yuǎn)離鼠標(biāo)的力。你可以把100和0.01這些參數(shù)改成變量用鼠標(biāo)位置或按鍵來(lái)控制創(chuàng)造出“撥開(kāi)雨幕”的效果。鍵盤(pán)交互 - 控制天氣 通過(guò)鍵盤(pán)按鍵來(lái)動(dòng)態(tài)改變雨的參數(shù)比如雨量大小。我們?cè)诔绦蝽敳柯暶饕粋€(gè)控制雨滴數(shù)量的變量int totalDropsFront 200; int totalDropsBack 800;在draw()循環(huán)中實(shí)時(shí)檢查按鍵void draw() { ... // 檢查按鍵動(dòng)態(tài)增加或減少雨滴 if (keyPressed) { if (key || key ) { // 增加近景雨 rainFront.add(new Raindrop(false)); totalDropsFront; } else if (key - || key _) { // 減少近景雨確保列表不為空 if (rainFront.size() 10) { rainFront.remove(rainFront.size() - 1); totalDropsFront--; } } else if (key r || key R) { // 重置所有雨滴 rainFront.clear(); rainBack.clear(); for (int i 0; i totalDropsBack; i) rainBack.add(new Raindrop(true)); for (int i 0; i totalDropsFront; i) rainFront.add(new Raindrop(false)); } } ... }同時(shí)可以在屏幕上顯示當(dāng)前狀態(tài)讓交互反饋更直觀void draw() { ... // 顯示狀態(tài)信息 fill(255); noStroke(); textSize(16); text(雨滴數(shù)量 (近景): totalDropsFront, 20, 30); text(按 /- 鍵增減雨量按 R 重置, 20, 55); }4. 高級(jí)潤(rùn)色與性能考量從模擬到作品當(dāng)基礎(chǔ)功能都實(shí)現(xiàn)后我們可以從藝術(shù)性和技術(shù)性?xún)蓚€(gè)層面進(jìn)行深度優(yōu)化讓你的雨景從“一個(gè)程序”變成“一件作品”。4.1 視覺(jué)藝術(shù)化處理背景與氛圍純黑色的背景雖然經(jīng)典但略顯單調(diào)。我們可以繪制一個(gè)簡(jiǎn)單的漸變背景來(lái)模擬陰雨天的天空。void draw() { // 繪制從上到下、由深灰到黑的線(xiàn)性漸變背景 for (int i 0; i height; i) { float inter map(i, 0, height, 0, 1); color c lerpColor(color(50, 60, 80), color(0, 10, 20), inter); stroke(c); line(0, i, width, i); } noStroke(); // ... 然后繪制半透明黑色矩形用于模糊和雨滴 fill(0, 20); rect(0, 0, width, height); ... }lerpColor()函數(shù)用于在兩個(gè)顏色之間進(jìn)行線(xiàn)性插值map()函數(shù)確定當(dāng)前行的插值比例。這創(chuàng)造了一個(gè)深邃的天空背景。雨滴形態(tài)多樣化并非所有雨滴都是直線(xiàn)。我們可以引入一個(gè)很小的概率讓部分雨滴在落地前“碎裂”模擬雨滴撞擊地面或其他物體濺起的小水花。這需要?jiǎng)?chuàng)建一個(gè)新的Splash濺射粒子類(lèi)其生命周期短向隨機(jī)方向擴(kuò)散然后消失。由于篇幅限制這里不展開(kāi)完整代碼但思路是在Raindrop的fall()方法中當(dāng)雨滴重置即“落地”時(shí)有一定概率在當(dāng)前位置生成一組Splash粒子這些粒子擁有很小的初始向上速度然后受“重力”下落并迅速淡出。光影與反射在潮濕的地面上雨滴會(huì)產(chǎn)生漣漪和反光。這是一個(gè)更高級(jí)的主題涉及到著色器Shader或像素級(jí)操作。一個(gè)取巧的方法是在底層繪制一個(gè)模糊的、上下顛倒的雨景副本并降低其透明度模擬地面上的倒影這能立刻增加場(chǎng)景的濕潤(rùn)感和深度。4.2 性能優(yōu)化實(shí)戰(zhàn)當(dāng)你把雨滴數(shù)量增加到數(shù)千甚至上萬(wàn)時(shí)可能會(huì)感到幀率下降。優(yōu)化粒子系統(tǒng)是創(chuàng)意編程的必修課。1. 離屏粒子回收與對(duì)象池 在當(dāng)前的代碼中雨滴落出屏幕后我們調(diào)用reset()方法重新初始化它。這沒(méi)有問(wèn)題。但更高效的做法是使用“對(duì)象池”O(jiān)bject Pool。我們預(yù)先創(chuàng)建好一個(gè)足夠大的雨滴對(duì)象數(shù)組循環(huán)使用。當(dāng)一滴雨“死亡”出屏我們并不刪除它只是將其狀態(tài)標(biāo)記為“可用”并將其屬性重置放回池中需要新雨滴時(shí)從池中取一個(gè)“可用”的對(duì)象來(lái)重新激活。這避免了頻繁的對(duì)象創(chuàng)建和垃圾回收對(duì)性能提升巨大尤其是在JavaScriptP5.js或移動(dòng)端等環(huán)境中。2. 簡(jiǎn)化計(jì)算與繪制距離計(jì)算優(yōu)化在鼠標(biāo)交互的排斥力計(jì)算中dist()函數(shù)內(nèi)部進(jìn)行了開(kāi)方運(yùn)算比較耗性能。一個(gè)常見(jiàn)的優(yōu)化是計(jì)算距離的平方與半徑的平方進(jìn)行比較if ((dx*dx dy*dy) 10000) { // 100*100 }這樣可以避免開(kāi)方。繪制調(diào)用優(yōu)化stroke()和strokeWeight()是相對(duì)耗時(shí)的調(diào)用。如果可能將屬性相同的雨滴批量繪制。例如在display()方法中遠(yuǎn)景雨的顏色和粗細(xì)是統(tǒng)一的我們可以先在循環(huán)外設(shè)置好一次畫(huà)筆屬性然后在一個(gè)循環(huán)內(nèi)用line()繪制所有遠(yuǎn)景雨再切換屬性繪制近景雨。但這需要將位置數(shù)據(jù)存儲(chǔ)起來(lái)稍微增加了代碼復(fù)雜度。Processing的shape()或beginShape()/endShape()函數(shù)可以更高效地批量繪制大量頂點(diǎn)適合繪制數(shù)萬(wàn)級(jí)別的粒子。3. 使用PGraphics離屏緩沖 對(duì)于非常復(fù)雜的、靜態(tài)或變化緩慢的背景如我們的漸變天空不需要每幀都重新計(jì)算和繪制。我們可以使用PGraphics這個(gè)類(lèi)在setup()中將其繪制好然后在draw()中直接將其作為圖像貼到屏幕上這稱(chēng)為離屏渲染。PGraphics sky; void setup() { size(800, 600); sky createGraphics(width, height); sky.beginDraw(); // 在sky這個(gè)PGraphics對(duì)象上繪制漸變背景 for (int i 0; i sky.height; i) { float inter map(i, 0, sky.height, 0, 1); color c lerpColor(color(50, 60, 80), color(0, 10, 20), inter); sky.stroke(c); sky.line(0, i, sky.width, i); } sky.endDraw(); // ... 初始化雨滴 } void draw() { image(sky, 0, 0); // 直接繪制預(yù)先渲染好的背景極快 // ... 后續(xù)的半透明模糊和雨滴繪制 }4.3 擴(kuò)展思路從雨到更多自然現(xiàn)象掌握了雨的核心——粒子系統(tǒng)、物理模擬、視覺(jué)分層——你就可以舉一反三模擬許多其他自然現(xiàn)象雪將雨滴的直線(xiàn)下落改為緩慢的、帶有隨機(jī)水平擺動(dòng)的下落x random(-0.5, 0.5)將線(xiàn)條繪制改為繪制小的圓形或雪花狀多邊形并取消加速度讓雪花緩緩飄落?;鹧媪W泳哂邢蛏系某跛俣鹊艿健爸亓Α毕蛳吕瑫r(shí)速度會(huì)逐漸衰減。顏色從亮黃高溫過(guò)渡到暗紅低溫再到消失粒子大小也隨生命周期變化。星空/流星粒子基本靜止星星少數(shù)粒子具有極快的速度并留下軌跡流星。背景為深色粒子為亮色。煙霧/云朵粒子具有強(qiáng)烈的擴(kuò)散行為隨機(jī)運(yùn)動(dòng)并且相互之間可以有非常微弱的排斥或吸引力形成不斷變化的有機(jī)形態(tài)。關(guān)鍵在于調(diào)整粒子的初始屬性位置、速度、大小、顏色、更新規(guī)則受力情況和繪制方式。你可以創(chuàng)建一個(gè)更通用的Particle類(lèi)然后通過(guò)不同的參數(shù)和規(guī)則來(lái)實(shí)例化出雨、雪、火等不同系統(tǒng)。最后我想分享一點(diǎn)個(gè)人在創(chuàng)作這類(lèi)動(dòng)態(tài)模擬時(shí)的體會(huì)參數(shù)調(diào)試的過(guò)程往往比寫(xiě)代碼本身花費(fèi)更多時(shí)間但也更有樂(lè)趣。沒(méi)有一個(gè)“正確”的參數(shù)值只有“看起來(lái)對(duì)”的感覺(jué)。不要害怕去瘋狂調(diào)整那些數(shù)字——重力系數(shù)、風(fēng)力范圍、透明度、映射區(qū)間——這個(gè)過(guò)程本身就是一種藝術(shù)創(chuàng)作。試著在深夜運(yùn)行你的程序配上一點(diǎn)環(huán)境音樂(lè)觀察屏幕上數(shù)字的舞蹈如何轉(zhuǎn)化為一種情緒。這才是Processing這類(lèi)工具帶給我們的超越技術(shù)本身的價(jià)值。

相關(guān)新聞

知識(shí)庫(kù)與AI寫(xiě)作融合提升公文寫(xiě)作效率

知識(shí)庫(kù)與AI寫(xiě)作融合提升公文寫(xiě)作效率

1. 知識(shí)庫(kù)與AI寫(xiě)作的融合價(jià)值公文寫(xiě)作作為組織內(nèi)部信息傳遞的核心載體,其規(guī)范性和效率直接影響行政效能。傳統(tǒng)模式下,寫(xiě)作者需要反復(fù)查閱規(guī)章制度、歷史文檔和案例庫(kù),這個(gè)過(guò)程往往耗費(fèi)30%以上的創(chuàng)作時(shí)間。而知識(shí)庫(kù)與AI寫(xiě)作技術(shù)的結(jié)合&#xf…

2026/7/29 6:56:07 閱讀更多
AI Agent工程經(jīng)理:開(kāi)發(fā)者從編碼到智能體管理的轉(zhuǎn)型指南

AI Agent工程經(jīng)理:開(kāi)發(fā)者從編碼到智能體管理的轉(zhuǎn)型指南

這次我們來(lái)看一個(gè)正在發(fā)生的重要趨勢(shì):開(kāi)發(fā)者角色正在向AI Agent工程經(jīng)理轉(zhuǎn)型。隨著AI Agent技術(shù)的快速發(fā)展,傳統(tǒng)的編碼工作方式正在被更智能的自動(dòng)化流程取代,開(kāi)發(fā)者需要掌握新的技能組合來(lái)管理這些"數(shù)字員工"。AI Agent不再是簡(jiǎn)單…

2026/7/29 6:56:07 閱讀更多
EntityX事件模塊:C++ ECS框架中的觀察者模式實(shí)踐與性能優(yōu)化

EntityX事件模塊:C++ ECS框架中的觀察者模式實(shí)踐與性能優(yōu)化

1. 項(xiàng)目概述:為什么我們需要EntityX的事件模塊?如果你用C寫(xiě)過(guò)游戲,或者任何需要處理大量動(dòng)態(tài)交互的復(fù)雜應(yīng)用,肯定對(duì)“事件驅(qū)動(dòng)”這個(gè)詞不陌生。想象一下,你的游戲里有成百上千個(gè)實(shí)體(Entity)&am…

2026/7/29 6:56:07 閱讀更多
基于金稅四期的財(cái)稅風(fēng)控規(guī)則引擎與業(yè)財(cái)一體化架構(gòu)實(shí)戰(zhàn)

基于金稅四期的財(cái)稅風(fēng)控規(guī)則引擎與業(yè)財(cái)一體化架構(gòu)實(shí)戰(zhàn)

隨著金稅四期全面上線(xiàn),傳統(tǒng)財(cái)稅系統(tǒng)在面對(duì)海量高頻風(fēng)險(xiǎn)預(yù)警指標(biāo)時(shí),常因數(shù)據(jù)孤島和規(guī)則硬編碼導(dǎo)致合規(guī)響應(yīng)滯后。企業(yè)在進(jìn)行IPO財(cái)務(wù)規(guī)范或高企申報(bào)時(shí),業(yè)財(cái)數(shù)據(jù)不一致往往成為致命瓶頸。本文將結(jié)合高頓咨詢(xún)?cè)贐端財(cái)稅數(shù)字化領(lǐng)域的工程實(shí)踐&#…

2026/7/29 9:26:11 閱讀更多
Flexx桌面應(yīng)用安全加固實(shí)戰(zhàn):從代碼到部署的全面防護(hù)指南

Flexx桌面應(yīng)用安全加固實(shí)戰(zhàn):從代碼到部署的全面防護(hù)指南

1. 項(xiàng)目概述:為什么Flexx應(yīng)用需要特別的安全關(guān)注? 最近在社區(qū)里看到不少朋友開(kāi)始用Flexx來(lái)開(kāi)發(fā)桌面應(yīng)用,尤其是那些想把Web應(yīng)用打包成獨(dú)立桌面程序的項(xiàng)目。Flexx這個(gè)框架確實(shí)挺有意思,它讓你能用純Python寫(xiě)前端界面,然…

2026/7/29 9:26:11 閱讀更多
Go與C語(yǔ)言面向?qū)ο缶幊虒?duì)比:結(jié)構(gòu)體、方法接收者與函數(shù)指針模擬類(lèi)

Go與C語(yǔ)言面向?qū)ο缶幊虒?duì)比:結(jié)構(gòu)體、方法接收者與函數(shù)指針模擬類(lèi)

1. 項(xiàng)目概述:當(dāng)Go遇上C,兩種“類(lèi)”思維的碰撞在編程語(yǔ)言的演進(jìn)長(zhǎng)河中,面向?qū)ο缶幊?amp;#xff08;OOP)無(wú)疑是一座重要的里程碑。當(dāng)我們談?wù)摗邦?lèi)”時(shí),腦海中首先浮現(xiàn)的可能是Java、C這類(lèi)以類(lèi)為第一公民的語(yǔ)言。但今天&…

2026/7/29 9:26:11 閱讀更多
Arduino模擬信號(hào)與PWM控制:從電位器到LED亮度調(diào)節(jié)的完整實(shí)現(xiàn)

Arduino模擬信號(hào)與PWM控制:從電位器到LED亮度調(diào)節(jié)的完整實(shí)現(xiàn)

1. 從旋鈕到光暈:一個(gè)燈光調(diào)節(jié)器的誕生 最近在整理工作室的舊物,翻出來(lái)一塊吃灰已久的Arduino Edison開(kāi)發(fā)板。看著它,我忽然想起很多朋友,包括當(dāng)年的我自己,在入門(mén)嵌入式開(kāi)發(fā)時(shí),常常會(huì)卡在一個(gè)看似簡(jiǎn)單卻至…

2026/7/29 9:26:11 閱讀更多
國(guó)內(nèi)專(zhuān)業(yè)網(wǎng)站建設(shè)公司盤(pán)點(diǎn),2026 精選十家高口碑網(wǎng)站設(shè)計(jì)公司全方位梳理

國(guó)內(nèi)專(zhuān)業(yè)網(wǎng)站建設(shè)公司盤(pán)點(diǎn),2026 精選十家高口碑網(wǎng)站設(shè)計(jì)公司全方位梳理

一、2026 網(wǎng)站建設(shè)行業(yè)現(xiàn)狀深度解析生成式 AI、GEO 搜索優(yōu)化、llms 協(xié)議規(guī)范、多系統(tǒng)數(shù)據(jù)互通等新技術(shù)落地,市場(chǎng)對(duì)網(wǎng)站建設(shè)服務(wù)商的能力要求發(fā)生根本性分層。中大型企業(yè)、上市公司、出海品牌更青睞兼具行業(yè)深耕、定制開(kāi)發(fā)、AI 營(yíng)銷(xiāo)配套、長(zhǎng)期運(yùn)維迭代能力的綜合服務(wù)…

2026/7/29 9:26:11 閱讀更多
Agent 平臺(tái)化思考:從定制化開(kāi)發(fā)到通用 Agent 平臺(tái)的架構(gòu)演進(jìn)

Agent 平臺(tái)化思考:從定制化開(kāi)發(fā)到通用 Agent 平臺(tái)的架構(gòu)演進(jìn)

Agent 平臺(tái)化思考:從定制化開(kāi)發(fā)到通用 Agent 平臺(tái)的架構(gòu)演進(jìn) 一、從"一個(gè) Agent 一個(gè)項(xiàng)目"到"Agent 平臺(tái)":工程化必經(jīng)之路 2026 年初,某 SaaS 公司面臨一個(gè)困境:過(guò)去一年,他們?yōu)椴煌蛻?hù)需求開(kāi)發(fā)了…

2026/7/29 9:16:11 閱讀更多
面試官大笑:“一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,不比 1 個(gè)快 5 倍?“我搖頭:“快不了,還可能更慢“

面試官大笑:“一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,不比 1 個(gè)快 5 倍?“我搖頭:“快不了,還可能更慢“

前兩個(gè)月,我在重構(gòu) AlgoMooc 網(wǎng)站過(guò)程中,發(fā)現(xiàn)一個(gè)問(wèn)題:在 Claude Code 里把一個(gè)任務(wù)拆給 5 個(gè) Subagent 并行跑,結(jié)果可能比 1 個(gè) agent 從頭干到尾還慢? 大多數(shù)人的第一反應(yīng)是反過(guò)來(lái)的:活是并行干的&#…

2026/7/29 0:15:24 閱讀更多
# 鴻蒙 HarmonyOS 應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號(hào)與動(dòng)畫(huà)渲染精講

# 鴻蒙 HarmonyOS 應(yīng)用開(kāi)發(fā)實(shí)戰(zhàn)(第25期)|骰子(Dice Roller)— Unicode 符號(hào)與動(dòng)畫(huà)渲染精講

一、應(yīng)用概述 骰子(Dice Roller) 是一款經(jīng)典的休閑娛樂(lè)應(yīng)用,模擬了真實(shí)擲骰子的過(guò)程。應(yīng)用投擲兩個(gè)骰子(六面標(biāo)準(zhǔn)骰),使用 Unicode 骰面符號(hào)直觀展示每個(gè)骰子的點(diǎn)數(shù),并伴有快速滾動(dòng)的動(dòng)畫(huà)效果?!?/p>

2026/7/29 0:15:24 閱讀更多