最近在做一個H5的海戰(zhàn)類小游戲。

地圖很大,船在邊角位置的時候,相對世界坐標(biāo)有絕對運動,也就是說地圖不動船在動。但是在中間位置的時候,地圖和船同時運動,最后的顯示效果,相當(dāng)于船在屏幕中間只轉(zhuǎn)向不運動,而地圖在運動。
實現(xiàn)這樣的效果其實并不難。首先船在海上運動,船的x,y坐標(biāo)是相對地圖(海)的坐標(biāo)。然后根據(jù)船的位置調(diào)整海的坐標(biāo):
_setPos: function (boatX,boatY) {
let x = -boatX;
let y = -boatY;
if (x < -this.xLimit) {
x = -this.xLimit;
} else if (x > this.xLimit) {
x = this.xLimit;
}
if (y < -this.yLimit) {
y = -this.yLimit;
} else if (y > this.yLimit) {
y = this.yLimit;
}
this.node.x = x;
this.node.y = y;
}
其中,xLimit是用地圖的寬度減去設(shè)計分辨率寬度然后除2;yLimit是用地圖的高度減去設(shè)計分辨率高度然后除2。
我們要給船在運動的時候加上水花拖尾的效果,首先想到的就是用粒子系統(tǒng)來實現(xiàn)。然后發(fā)現(xiàn),當(dāng)船在邊角位置移動的時候,有完美的水花拖尾效果,但是船運動到中間以后,由于船相對世界坐標(biāo)沒有運動,拖尾就沒有了。
解決這個問題,在原來cocos的系統(tǒng)中,可以設(shè)置粒子系統(tǒng)里面的positionType,將默認(rèn)的PositionType::FREE設(shè)置為 PositionType::RELATIVE,然而在Creator中我們發(fā)現(xiàn)將positionType由0設(shè)置為1并沒有什么用。
查官方文檔,終于發(fā)現(xiàn)Creator里面并不支持:

怎么辦呢?
自己做一個粒子系統(tǒng)來實現(xiàn)吧。用js實現(xiàn)的效率可能略低,但是總比做不了的好,效率等做出來看了再說。
原理:
每幀在船所在位置下面的海面上貼一個粒子發(fā)射器,每個粒子發(fā)射器發(fā)射3-5個粒子,每個粒子的生命期從0-0.8秒左右隨機,當(dāng)粒子生命期結(jié)束后,主動將自己從發(fā)射器節(jié)點上刪除;當(dāng)發(fā)射器上所有的粒子都已經(jīng)結(jié)束生命期,發(fā)射器主動將自身從父節(jié)點刪除。粒子的位置有一個小范圍隨機,將用作粒子的圖片從0到一個固定的scale進行縮放。數(shù)值可以調(diào)整,生存期長,粒子拖尾會比較長,目標(biāo)scale大,拖尾的尺寸就比較大。具體還可以做一些顏色、透明度等變化。粒子發(fā)射器不跟隨船運動,這樣在船快速運動的時候,就會出現(xiàn)明顯的拖尾。
實戰(zhàn):
首先,自定義一個Prefab做成粒子發(fā)射器,取名為ParticleEmitter。實質(zhì)是一個空節(jié)點,上面掛一個腳本,用于控制粒子發(fā)射。然后在Scene的update里面,每一幀產(chǎn)生一個ParticleEmitter,放到當(dāng)前船所在的位置,貼到地圖上面,船的下面。
update: function (dt) {
let waveNode = cc.instantiate(this.mPrefabWave);
waveNode.parent = this.mSeaNode;
waveNode.x = this.x;
waveNode.y = this.y;
waveNode.setLocalZOrder(10);
},
其中:this.mPrefabWave就是我們的發(fā)射器預(yù)制件ParticleEmitter。this.mSeaNode是海(地圖)所屬的節(jié)點。
ParticleEmitter所掛的腳本代碼如下:
cc.Class({
extends: cc.Component,
properties: {
mPrefabWave: {
default: null,
type: cc.Prefab
}
},
// use this for initialization
onLoad: function () {
this.varX = 10; //粒子X位置可變范圍
this.varY = 5; //粒子Y位置可變范圍
this.avgNum = 4; //每個發(fā)射器發(fā)射的粒子平均數(shù)
let cnt = this.avgNum + cc.MyLib.rand(2) - 1;
//cc.MyLib.rand(2): 在[0,2]區(qū)間產(chǎn)生一個隨機整數(shù)(0,1或者2);
for (let i=0;i<cnt;++i) {
this._createParticleNode();
}
this.particleNum = cnt;
},
_createParticleNode: function() {
let Rand = cc.MyLib.rand;
let particleNode = cc.instantiate(this.mPrefabWave);
particleNode.parent = this.node;
//在當(dāng)前位置附近小范圍內(nèi)隨機一個x,y作為粒子的位置。
let x = Rand(this.varX * 10) / 10 - this.varX / 2;
let y = Rand(this.varY * 10) / 10 - this.varY / 2;
particleNode.setPosition(x,y);
let sc = particleNode.getComponent("ParticleNode");
let attr = {};
attr.pSuper = this;
attr.lifespan = 0.8 * Math.random(); //粒子的生命期
attr.srcR = 64;
attr.srcG = 64;
attr.srcB = 64;
attr.desR = 128;
attr.desG = 128;
attr.desB = 128;
sc._init(attr);
},
//當(dāng)每個粒子生命期結(jié)束的時候,將發(fā)射器的粒子計數(shù)減一。
//當(dāng)計數(shù)減為0的時候,將粒子發(fā)射器自身節(jié)點移除。
_reduceParticleNum: function() {
if (--this.particleNum == 0) {
this.node.removeFromParent();
}
}
});
每個粒子節(jié)點是一個叫做ParticleNode的Prefab, 里面就是一個Sprite,一個靜態(tài)水花的圖做spriteFrame,將Blend設(shè)置為Src:SRC_ALPHA以及Dst:ONE,然后掛上一個控制腳本。

ParticleNode控制腳本的內(nèi)容:
cc.Class({
extends: cc.Component,
properties: {
},
// use this for initialization
onLoad: function () {
this.totalDt = 0;
this.srcScale = 0;
this.node.scale = this.srcScale;
this.desScale = 0.8;
this.srcOpacity = 255;
},
_init: function(attr) {
this.pSuper = attr.pSuper;
this.life = attr.lifespan;
this.srcR = attr.srcR;
this.srcG = attr.srcG;
this.srcB = attr.srcG;
this.desR = attr.desR;
this.desG = attr.desG;
this.desB = attr.desB;
this.node.color = cc.color(this.srcR,this.srcG,this.srcB,this.srcOpacity);
},
// called every frame, uncomment this function to activate update callback
update: function (dt) {
this.totalDt += dt;
if (this.totalDt <= this.life/2) {
let ratio = 2 * this.totalDt / this.life;
let scale = this.srcScale + (this.desScale-this.srcScale) * ratio;
this.node.scale = scale;
let r = this.srcR + (this.desR-this.srcR) * ratio;
let g = this.srcG + (this.desG-this.srcG) * ratio;
let b = this.srcB + (this.desB-this.srcB) * ratio;
this.node.color = cc.color(r,g,b,this.srcOpacity);
} else if (this.totalDt <= this.life) {
let ratio = (1 - this.totalDt / this.life) * 2;
let opacity = this.srcOpacity * ratio;
this.node.setOpacity(opacity);
} else {
this.node.removeFromParent();
this.pSuper._reduceParticleNum();
}
},
});
update里面,一半生命期粒子的尺寸從0一直增大到目標(biāo)scale,因為圖片略大,這里的目標(biāo)scale定為0.8,同時顏色值從開始的RGB不斷變化到目標(biāo)RGB。另外一半生命期,將粒子的透明度從當(dāng)前透明度減少到0,實現(xiàn)淡出效果。生命期結(jié)束后,將粒子自身節(jié)點移除,并且在它所屬的發(fā)射器上將粒子計數(shù)減少1。
最后的效果還不錯,同時效率也還可以,看一下效果圖:



后話:
雖然自己實現(xiàn)了相對運動的粒子效果,但是如果有現(xiàn)成的粒子系統(tǒng)可以用的話,誰愿意重復(fù)造輪子???希望Creator能盡快完善吧。