摘要
node
咱們都知道水面的運動形式是極其複雜的。可是在一些二維的小遊戲中,咱們只是想模擬其波動特性,作一些出水入水的效果。如何簡單的實現波動的水面呢?web
正文
看看效果
準備工做
一張背景圖片便可數組
層級結構
Main Camera 攝像機組件的 Background Color 設置白色(不是節點顏色)。Mask 節點掛載 cc.Mask 組件,無需其餘處理。water 節點掛載精靈組件,就是背景圖。正常狀況下,應該是全被遮住。wave 節點掛載 wave.js 腳本便可。微信
點的結構
現實中,水面是由一個個水分子組成。那麼咱們代碼裏就能夠抽象成一個個對象app
水:{ x: 0, y: 0}
性能
flex
this
url
好了,咱們如今有了水面寬度(設計分辨率寬度 720),有了點的對象。那麼咱們放多少個點合適呢?要知道水分子的直徑約爲 0.3 納米,咱們用這麼多對象電腦不炸了。那這個數量就由你決定了,數量越多效果越真實,但性能就會降低。這裏我設置了spa
this.n = 20; // 細分數
能量傳遞
如今,咱們規定了在這 720 寬的水面上有 20 個點。
this.nodeArray = []; // 裝載水面上的點
那麼點與點之間是有能量傳遞的呀,好比最右側的點(this.nodeArray[19])向上運動了,那麼其旁邊的點(this.nodeArray[18])必然受到影響呀。因此咱們應該用一個數組來表徵能量
this.nodeEnergy = []; // 每一個點的能量
用循環的方式表明能量傳遞,循環次數越多就至關於傳遞的越遠。
// 左右點互相影響 2 次, 決定波的傳播快慢for (let k = 0; k < 2; k++) { for (let i = 0; i < this.n; i++) { if (i > 0) { // 0.02 的傳播損失 // 向左傳 this.nodeEnergy[i-1] += 0.98 * (this.nodeArray[i].y - this.nodeArray[i-1].y); } if (i < this.n - 1) { // 向右傳 this.nodeEnergy[i+1] += 0.98 * (this.nodeArray[i].y - this.nodeArray[i+1].y); } }}
速度衰減
能量傳遞寫好了,那麼咱們自身也要有能量損失的。(主要是表面張力與重力)
// 最右側的跳過for (let i = 0; i < this.n - 1; i++) { // 0.02 速度損失 this.nodeEnergy[i] *= 0.98; // 改變位置 this.nodeArray[i].y += this.nodeEnergy[i] * dt;}
遮罩顯示
不知小夥伴們有沒有這樣用過 cc.Mask 組件。
let draw = this.mask._graphics;
遮罩組件是含有 _graphics 這個 cc.Graphics 對象的,咱們用其畫圖就是擦除遮罩的效果。那麼咱們只要根據水面上的點與水底封閉圖形進行擦除就能模擬出水面形狀了。由於是 720 x 1280 找到最下的兩個點(-360,-640)和(360, -640),與水面點鏈接進行圖形封閉。注意不要用 lineTo 要用貝塞爾進行曲線。(由於會出現尖尖的角)
// 利用遮罩原理,把下方顯示showWater () { let draw = this.mask._graphics; draw.clear(); draw.lineWidth = 1; // 畫線顏色與填充顏色不要一致 draw.strokeColor = cc.color(255,0,0); draw.fillColor = cc.color(0,255,0); // 移動到屏幕最左邊,this.h = 200 是我自定義的水面高度。 draw.moveTo(-360, this.h); // 貝塞爾曲線是隔一個點,做爲控制點。 for (let i = 0; i < this.n; i+=2) { // 貝塞爾 draw.quadraticCurveTo(this.nodeArray[i].x, this.nodeArray[i].y, this.nodeArray[i+1].x, this.nodeArray[i+1].y); } // 封閉區域 draw.lineTo(360, -640); draw.lineTo(-360, -640); draw.lineTo(-360, this.h); draw.fill(); draw.stroke();}
賦予動力
在 start 方法下讓最右側的點呈 sin 緩動。(沒看懂的必定要去官方文檔裏看 cc.tween)
// 最右側點緩動let obj = this.nodeArray[this.n-1];let time = 0.5;cc.tween(obj) .repeatForever( cc.tween() .to(time, { y: 40 + this.h}, { easing: 'sineOut'}) .to(time, { y: 0 + this.h}, { easing: 'sineIn'}) .to(time, { y: -40 + this.h}, { easing: 'sineOut'}) .to(time, { y: 0 + this.h}, { easing: 'sineIn'}) )
完整代碼
wave.js
cc.Class({ extends: cc.Component, properties: { mask: cc.Mask }, onLoad () { // 水面高度 this.h = 200; this.n = 20; // 細分數 this.nodeArray = []; // 裝載水面上的點 this.nodeEnergy = []; // 每一個點的能量 // 賦予初始值 for (let i = 0; i < this.n; i++) { this.nodeEnergy[i] = 0; } }, start () { // 建立水面上點 for (let i = 0; i < this.n; i++) { let node = {x: 0, y: 0}; node.y = this.h; node.x = -360 + (i + 1) * 720 / this.n; this.nodeArray[i] = node; } // 最右側點緩動 let obj = this.nodeArray[this.n-1]; let time = 0.5; cc.tween(obj) .repeatForever( cc.tween() .to(time, { y: 40 + this.h}, { easing: 'sineOut'}) .to(time, { y: 0 + this.h}, { easing: 'sineIn'}) .to(time, { y: -40 + this.h}, { easing: 'sineOut'}) .to(time, { y: 0 + this.h}, { easing: 'sineIn'}) ) .start(); }, // 利用遮罩原理,把下方顯示 showWater () { let draw = this.mask._graphics; draw.clear(); draw.lineWidth = 1; draw.strokeColor = cc.color(255,0,0); draw.fillColor = cc.color(0,255,0); draw.moveTo(-360, this.h); for (let i = 0; i < this.n; i+=2) { // 貝塞爾 draw.quadraticCurveTo(this.nodeArray[i].x, this.nodeArray[i].y, this.nodeArray[i+1].x, this.nodeArray[i+1].y); } // 封閉區域 draw.lineTo(360, -640); draw.lineTo(-360, -640); draw.lineTo(-360, this.h); draw.fill(); draw.stroke(); }, update (dt) { // 左右點互相影響 2 次, 決定波的傳播快慢 for (let k = 0; k < 2; k++) { for (let i = 0; i < this.n; i++) { if (i > 0) { // 0.02 的傳播損失 // 向左傳 this.nodeEnergy[i-1] += 0.98 * (this.nodeArray[i].y - this.nodeArray[i-1].y); } if (i < this.n - 1) { // 向右傳 this.nodeEnergy[i+1] += 0.98 * (this.nodeArray[i].y - this.nodeArray[i+1].y); } } } // 最右側的跳過 for (let i = 0; i < this.n - 1; i++) { // 0.02 速度損失 this.nodeEnergy[i] *= 0.98; // 改變位置 this.nodeArray[i].y += this.nodeEnergy[i] * dt; } this.showWater(); },});
結語
想改變水面上某一點的能量或者位置時
// 改變這兩個數組中對應點數據便可this.nodeArraythis.nodeEnergy
工程源碼在如下微信公衆號回覆關鍵詞【水波】便可得到
O(∩_∩)O~~
微信公衆號
>> 更多精彩
本文分享自微信公衆號 - 白玉無冰(lamyoung-com)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。