1. 程式人生 > >Cocos2d-x 2D粒子系統詳解

Cocos2d-x 2D粒子系統詳解

Cocos2d-x 2D粒子系統

宣告:本文使用的是cocos2d-x-3.17的程式碼

文章中的提到的測試程式碼下載地址https://gitee.com/Kyle12/Cocos2dRenderStudy

這裡分析的是Cocos2d中的2d粒子系統,包括粒子系統中的各個引數,以及粒子的更新,粒子的運動。Cocos2d-x中類ParticleSystem實現了對粒子的控制,類ParticleSystemQuad實現了粒子的繪製。在CCParticleExamples中的各種粒子特效都是通過調整ParticleSystem中的各種引數得到的。

粒子系統ParticleSystem

粒子系統類ParticleSystem並本身沒有粒子的繪製,其主要實現了粒子的更新,包括增加新粒子,刪除死亡粒子,更新粒子狀態(粒子的位置、顏色、大小、旋轉等等)。ParticleSystem類結如下:

粒子資料與粒子系統資料

粒子資料

粒子資料是指單獨一個粒子獨有的資料,包括粒子的位置、速度、顏色、大小、旋轉量、生存時間等等,這些資料全部儲存在ParticleSystem:: _particleData中。這些資料會在更新粒子狀態時改變,粒子的更新是有一定規律的,所以粒子資料也是有規律的變化,並不會隨機的改變。另外粒子資料包含了所有的繪製粒子所需要的資料。

粒子系統資料

粒子系統資料是用來生成粒子的資料,這些資料控制了粒子的初始狀態,存放在ParticleSystem類的成員變數中。因為粒子需要一定的隨機性,粒子的更新並不會隨機變化,粒子隨機是隨機設定粒子的初始狀態。粒子初始狀態是由粒子系統資料控制的,所以粒子系統的資料要有一定的變化量。為了實現這個變化量,ParticleSystem類中有很多命名以Var結尾的變數,這些變數就代表了初始粒子資料的隨機變化量。例如:_life/_lifeVar代表粒子的生存時間,如果_life=5,_lifeVar=1,那麼生成的粒子生存時間隨機範圍為[5-1, 5+1]。Cocos2d中的各種粒子特效都是通過調整粒子系統資料得到的,不同的粒子系統資料可以產生不同的粒子效果。

 

粒子數量:粒子系統可以控制粒子的最大數量,粒子系統粒子數量到達最大時則暫時不發射粒子(生成新粒子),如果有粒子死亡導致粒子數量減少則又會發射粒子。可以通過函式ParticleSystem::stopSystem停止發射粒子,但已發射的粒子不受影響會繼續運動,直到耗盡生存時間。

粒子的運動

Cocos2d粒子系統支援兩種粒子運動方式,重力模式(GRAVITY)和放射模式(RADIUS)。兩者的粒子系統資料分別存放在ParticleSystem::modeA和ParticleSystem::modeB,兩者的粒子資料存放在_particleData.modeA和_particleData.modeB。ParticleSystem類繼承值Node類,Node類實現Cocos2d的座標系統,所以粒子系統有自己的座標系,粒子運動位置的更新都是在粒子系統的座標系下完成的。

 

Cocos2d線上粒子編輯器Effecthub,可以編輯粒子設定粒子系統的各個引數。

Effecthub編輯的粒子會在編輯器上有一個位置,這個位置最終成為粒子系統在父節點上的位置。如果繪製通過Effecthub編輯的粒子系統,沒有顯示在螢幕上,有可能是因為這個問題,這時候需要將編輯好的粒子移動的坐下角,也就是座標系0,0處。如下圖:

  

重力模式

粒子系統資料和粒子資料中重力模式引數

 

重力gravity是一個二維向量,對於同一粒子系統中的所有粒子都使用同一個重力加速度,所以粒子資料中不需要儲存重力。

速度speed/speedVar是一個float數,在粒子資料中速度卻是一個二維向量。單一的float是沒有方向的,所以要轉換成二維向量,還需要一個方向,這個方向由ParticleSystem:: _angle/ _angleVar控制。以下程式碼是ParticleSystem::addParticles設定粒子速度的程式碼:

float a = CC_DEGREES_TO_RADIANS_angle + _angleVar * RANDOM_M11(&RANDSEED) );

Vec2 v(cosfa ), sinfa ));

float s = modeA.speed + modeA.speedVar * RANDOM_M11(&RANDSEED);

Vec2 dir = v * s;

_particleData.modeA.dirX[i] = dir.x;//v * s ;

_particleData.modeA.dirY[i] = dir.y;

經向加速度radialAccel/radialAccelVar是一個float值,徑向加速度方向為,粒子位置座標與粒子系統座標原點的連線,正數為遠離原點的方向。

垂直加速度tangentialAccel/tangentialAccelVar,也叫切向加速度,方向和徑向加速度垂直。

經向加速度和垂直加速度方向會隨著粒子位置的改變而改變。

 

重力模式粒子位置更新

粒子狀態的更新是由ParticleSystem::update函式完成,位置更新的程式碼如下:

particle_point tmp, radial = {0.0f, 0.0f}, tangential;
// radial acceleration
if (_particleData.posx[i] || _particleData.posy[i])
{
    normalize_point(_particleData.posx[i], _particleData.posy[i], &radial);
}
tangential = radial;
radial.x *= _particleData.modeA.radialAccel[i];
radial.y *= _particleData.modeA.radialAccel[i];
                
// tangential acceleration
std::swap(tangential.x, tangential.y); // 交換後x變數乘以-1得到徑向加速度的垂直方向
tangential.x *= - _particleData.modeA.tangentialAccel[i];
tangential.y *= _particleData.modeA.tangentialAccel[i];
                
// (gravity + radial + tangential) * dt
tmp.x = radial.x + tangential.x + modeA.gravity.x;
tmp.y = radial.y + tangential.y + modeA.gravity.y;
tmp.x *= dt; tmp.y *= dt;
//先更新粒子速度
_particleData.modeA.dirX[i] += tmp.x;
_particleData.modeA.dirY[i] += tmp.y;
  
// 更新粒子位置
tmp.x = _particleData.modeA.dirX[i] * dt * _yCoordFlipped;
tmp.y = _particleData.modeA.dirY[i] * dt * _yCoordFlipped;
_particleData.posx[i] += tmp.x;
_particleData.posy[i] += tmp.y;

 

放射模式

粒子系統資料和粒子資料放射模式引數

 

放射模式下可以把粒子資料中的radius和angle看著一個極座標,兩者分別代表極經和極角。放射模式下會有兩個圓,一個有起始半徑控制,一個由最終半徑控制,點的極經會在生存時間內從起始半徑變化到最終半徑,粒子的極角每秒會變化量由rotatePerSecond/rotatePerSecondVar控制。

粒子的初始角度(極角)是由ParticleSystem:: _angle/ _angleVar控制。發射模式粒子運動圖如下:

 

放射模式粒子位置更新

//更新機座標系,使用 += 
for (int i = 0; i < _particleCount; ++i)
{
    _particleData.modeB.angle[i] += _particleData.modeB.degreesPerSecond[i] * dt;
}
for (int i = 0; i < _particleCount; ++i)
{
    _particleData.modeB.radius[i] += _particleData.modeB.deltaRadius[i] * dt;
}
//將極座標轉換為直角座標,使用 = 
for (int i = 0; i < _particleCount; ++i)
{
   _particleData.posx[i]=- cosf(_particleData.modeB.angle[i]) * _particleData.modeB.radius[i];
}
for (int i = 0; i < _particleCount; ++i)
{
    _particleData.posy[i] = - sinf(_particleData.modeB.angle[i]) * _particleData.modeB.radius[i] * _yCoordFlipped;
}

 

粒子的位置

粒子位置與粒子系統位置

粒子系統ParticleSystem類繼承值Node類,Node類實現Cocos2d的座標系統,所以粒子系統有自己的位置(粒子系統位置),也有自己的座標系,粒子運動位置的更新都是在粒子系統的座標系下完成的。粒子系統位置指的是粒子系統在其父節點上的位置,粒子位置是粒子在粒子系統座標系下的位置。

粒子資料中的位置座標_particleData.posx/posy記錄的是粒子在粒子系統座標系下的位置。當粒子發射後,粒子位置座標_particleData.posx/posy會按照重力模式或者放射模式更新,更新的時候並不會與粒子系統位置有關,也就是說粒子系統位置的改變不會影響粒子的位置資料的更新。

位置模式

粒子系統支援三種位置模式,三種位置模式,FREE自由模式、RELATIVE相對模式、GROUPED獨立模式。

FREE自由模式,粒子系統可以保證發射後的粒子相對於世界座標系下移動,不會因為粒子系統位置的改變而改變。

RELATIVE相對模式,粒子系統可以保證發射後的粒子相對於粒子系統父節座標系下移動,當粒子系統父節點移動時,粒子跟著移動,但粒子系統的移動不影響粒子位置。

GROUPED獨立模式,粒子系統可以保證發射後的粒子當粒子系統移動是粒子跟著移動。

 

實際上位置模式並不會影響_particleData.posx/posy的更新,位置模式會影響粒子位置初始偏移_particleData. startPosX/startPosY,具體影響如下:

在繪製粒子時可以通過粒子的初始位置偏移量,以及繪製時粒子系統為位置偏移來計算粒子最終的偏移。

粒子位置模式測試程式,完整程式碼ParticlePositionScene.cpp/ParticlePositionScene.h對應主程式選單:“"Test Particle System”->“Position Type Test”。

 

 

 

粒子繪製ParticleSystemQuad

粒子系統類ParticleSystem本身並沒有實現粒子的繪製,其主要是提供繪製粒子的資料,cocos2d中實現粒子繪製的類為ParticleSystemQuad。ParticleSystemQuad類繼承至ParticleSystem,會根據粒子系統類中的資料繪製粒子。

 

ParticleSystemQuad結構如下:

 

正如類名中的Quad,ParticleSystemQuad類繪製粒子的方式是繪製正方形,使用的繪製QuadCommand命令。QuadCommad繼承至TrianglesCommand,Cocos2d的Render類會把QuadCommand當作TrianglesCommand處理。TrianglesCommand正是精靈類Sprite的處理命令,也就是說繪製時每個粒子相當於一個精靈Sprite,一個粒子需要繪製六個點兩個三角形。這種繪製效率並不高,實際上OpenGL中可以使用點精靈來繪製粒子,這種情況下每個粒子只相當於一個點。Cocos2d中完全可以通過重寫ParticleSystem類來實現用點精靈繪製的粒子系統。