1. ParticleSystem
ParticleData是存儲粒子數據的類,ParticleSystem會關聯一個ParticleData對象。
ParticleSystem直接繼承了Node、TextureProtocol(紋理)、PlayableProtocol(start stop方法)。
ParticleSystem定義了粒子的相關屬性。
粒子從ParticleSystem的位置發射。
屬性
- float _elapsed
運行時間。
粒子相關屬性
- float _startSize, _startSizeVar, _endSize, _endSizeVar
粒子大小及浮動值。
- Color4F _startColor, _startColorVar, _endColor, _endColorVar
粒子顏色值及浮動值。
- float _startSpin, _startSpinVar, _endSpin, _endSpinVar
粒子自身旋轉的角度值及浮動值。
- PositionType _positionType
粒子位置模式,有3種:FREE(粒子在世界坐標系移動)、RELATIVE(粒子相對父節點坐標系移動)、GROUPED(粒子跟隨發射點移動)。
- float _life, _lifeVar
粒子生存時間及浮動值。
發射相關屬性
- float _angle, _angleVar
粒子發射的角度及浮動值。
- Mode _emitterMode
發射器模式。發射有2種模式,用枚舉Mode表示:
Gravity:重力模式,Mode A,屬性:重力加速度(向量表示)、速度和浮動值(粒子的初速度)、徑向加速度和浮動值(與速度方向平行)、切向加速度和浮動值(與速度方向垂直)。
Radius:徑向模式,Mode B,屬性:起始半徑和浮動值(粒子出生時和圓心距離)、結束半徑和浮動值(粒子死亡時和圓心距離)、每秒旋轉的角度和浮動值。
- float _emissionRate
發射器每秒發射的粒子數。
- int _totalParticles
生存的粒子最大數量。
- int _particleCount
當前生存的粒子數。
- float _duration
發射器工作時長,-1為永遠發射。
- Vec2 _sourcePosition, _posVar
發射位置及發射位置的浮動值。
_sourcePosition與bool _sourcePositionCompatible有關,布爾值默認為true表明兼容,位置將被設置到node的位置變量;為false位置將被設置到_sourcePosition變量。
兩個靜態屬性
- static Vector<ParticleSystem*> __allInstances
存儲粒子系統的容器。
- static float __totalParticleCountFactor
該屬性默認為1。在update方法中,最大粒子數_totalParticles乘該系數,得到最大粒子數。
1. 創建
粒子對象的創建可以分為這3種方法:
- 通過代碼創建ParticleSystemQuad,並設置屬性
- 通過plist文件創建
- 直接通過代碼使用現成的粒子子類實現特效
plist文件會被轉成ValueMap,從而對plist的各項數據進行解析,相關屬性值賦給ParticleSystem屬性。
2. onEnter() onExit()方法
在執行addChild方法將粒子節點加到父節點時,會調用節點的onEnter方法。
ParticleSystem的onEnter方法中有這兩行:
this->scheduleUpdateWithPriority(1); __allInstances.pushBack(this);
先設置update方法將在每幀執行。再將當前粒子類加入靜態容器__allInstances中。
ParticleSystem的onExit方法會執行unscheduleUpdate,停止每幀執行update(dt),並從靜態容器__allInstances中刪除粒子類。
3. update(dt)方法
該方法被調度器Scheduler每幀觸發,粒子的更新離不開這個重要的方法。
4. 粒子添加與粒子到期后刪除
ParticleSystem成員_particleCount表示當前存在屏幕上的粒子數(當前已生成沒銷毀的粒子數)。
在update(dt)中,每幀會調用粒子添加的方法addParticles(int count)。
添加數量為count的粒子,實際上是把粒子的數據被添加到ParticleSystem成員中_particleData。
新增的各個粒子各項數據是被修改到從對應的_particleData數組成員的“最末尾”的下標位置開始,要添加多少個粒子,就修改多少個指針。“最末尾”的下標指的是根據_particleCount得出。
粒子到期后刪除時,也是在update(dt)方法中刪除,但是沒有真正執行“刪除”操作,而是將_particleData最末尾(數組成員最末尾)的數據復制到當前粒子的位置上,並對_particleCount減1。
我們知道,在update方法中是把_particleData每個數組成員前_particleCount項作為當前存在的粒子的數據並進行更新。所以,“被復制”的數據項位置實際上在之后的update中被“忽略”了,所以該項的數據只會被新增的粒子覆蓋,而前面被刪除的粒子的數據位置被這個“被復制”的數據項覆蓋,保證了數組成員前_particleCount項始終代表着當前存在的粒子的數據。
總結ParticleSystem
ParticleSystem關聯一個ParticleData。
每個粒子當前的屬性值狀態信息等,被保存在ParticleData中。而ParticleSystem中的粒子信息是我們直接設置的,例如浮動值只存儲在ParticleSystem成員變量中。
每一次update時,新建的粒子和當前粒子狀態各項信息通過計算后,都被保存在ParticleData中。
盡管ParticleData是一個類,我們可以把它理解成是一個“容器”。粒子系統ParticleSystem主要是提供對粒子當前信息ParticleData“容器”的管理,例如對“容器”修改、更新、添加等。所以說,粒子系統ParticleSystem是所有粒子的基類,只負責粒子最基礎的創建和存儲功能。
2. ParticleSystemQuad
ParticleSystemQuad直接繼承了ParticleSystem。所有粒子特效類都是ParticleSystemQuad的子類。
該類在父類的基礎上增加了對粒子的繪制功能。
1. 頂點緩存與索引緩存
ParticleSystemQuad有兩個重要變量:頂點緩存和索引緩存。
- V3F_C4B_T2F_Quad *_quads
頂點緩存:包含多個頂點數據的一塊內存,一個四邊形的4個頂點信息作為一個單位進行存儲。每一個V3F_C4B_T2F_Quad結構體存儲了4個頂點的信息(4個V3F_C4B_T2F結構體對象),每個頂點的信息包括三維頂點坐標、顏色和透明度、紋理UV坐標。
- GLushort *_indices
索引緩存:每6個索引對應一個四邊形,OpenGL是通過三角形來繪制四邊形的,所以一個四邊形需要兩個三角形的頂點數據,也就是6個索引對應1個V3F_C4B_T2F_Quad了。
在所有粒子效果類的create方法中,都會調用ParticleSystemQuad的initWithTotalParticles方法進行初始化,該方法會調用allocMemory分配粒子效果類需要的頂點緩存和索引緩存的內存區域。
內存的個數為我們設置的最大粒子數。
對於頂點緩存,每塊內存的大小為V3F_C4B_T2F_Quad結構體的大小。對於索引緩存,每塊內存為索引的6倍,也就是說把一個四邊形需要的所有6個索引作為一個內存區域存儲。大致邏輯是:
memset(_quads, 0, _totalParticles * sizeof(V3F_C4B_T2F_Quad)); memset(_indices, 0, _totalParticles * 6 * sizeof(GLushort));
索引緩存是對頂點緩存的映射,這樣可以對於重復的頂點使用一個內存進行存儲。
在剛才說的initWithTotalParticles方法進行兩個緩存內存的分配之后,對索引緩存設置索引值,設置索引指向的頂點位置。具體步驟如下:
for(int i = 0; i < _totalParticles; ++i) { const unsigned int i6 = i*6; const unsigned int i4 = i*4; _indices[i6+0] = (GLushort) i4+0; _indices[i6+1] = (GLushort) i4+1; _indices[i6+2] = (GLushort) i4+2; _indices[i6+5] = (GLushort) i4+1; _indices[i6+4] = (GLushort) i4+2; _indices[i6+3] = (GLushort) i4+3; }
另外,在setTotalParticles(int tp)方法中,根據參數最大粒子數,對頂點緩存_quads和索引緩存_indices大小更新並初始化,使用的也是類似上面的思路。
2. initTexCoordsWithRect(rect)
initTexCoordsWithRect(rect)方法,通過紋理更新每個粒子在的UV坐標。
該方法2個使用位置:
設置粒子的紋理時,使用setTexture方法,需要重新設置新紋理的UV坐標。
重新設置粒子最大數時,使用setTotalParticles方法,因為粒子總數發生改變,需要重新初始化頂點緩存和索引緩存,需要重新設置頂點緩存內的UV坐標。
該方法要求傳入的參數rect是根據紋理坐標系(Texture coordinates,OpenGL坐標系)表示的,而不能是像素坐標系(Pixel coordinates)。
紋理坐標系(OpenGL坐標系)左下角為原點(0,0);像素坐標系是圖片數據原始的坐標系,左上角為原點(0,0)。
接下來通過計算,得出矩形四個頂點的UV坐標。此時的UV坐標需要存入頂點緩存中,而頂點緩存是直接面向GPU的,所以不采用紋理OpenGL坐標系,而是像素坐標系。使用像素坐標系的結果是頂部的坐標值為0,與底部的坐標值交換了。
該方法最終向頂點緩存中存入一個矩形的4個頂點像素坐標系下的UV坐標:
for(unsigned int i=start; i<end; i++) { // bottom-left vertex: quads[i].bl.texCoords.u = left; quads[i].bl.texCoords.v = bottom; // bottom-right vertex: quads[i].br.texCoords.u = right; quads[i].br.texCoords.v = bottom; // top-left vertex: quads[i].tl.texCoords.u = left; quads[i].tl.texCoords.v = top; // top-right vertex: quads[i].tr.texCoords.u = right; quads[i].tr.texCoords.v = top; }
3. updateParticleQuads()
該方法在ParticleSystem的每幀update最后執行。
在ParticleSystem的中,該方法為空,調用的是ParticleSystemQuad的該方法。
update方法在updateParticleQuads之前是把本幀的粒子數據修改到ParticleData中,之后執行該方法,用來把ParticleData中生存的粒子數據寫到頂點緩存中,是對位置和顏色分別在頂點緩存進行更新。
把頂點坐標寫入頂點緩存前,根據3種粒子位置類型計算當前粒子位置,之后執行updatePosWithParticle方法寫入頂點緩存。該方法通過位置坐標、旋轉角度、矩形大小,計算出矩形4個頂點坐標,存入頂點緩存中。
存入頂點緩存的坐標是像素坐標系的坐標,所以updatePosWithParticle方法會將旋轉角度取反。
4. 其它
ParticleSystemQuad重寫了Node的draw方法,使用了頂點緩存和索引緩存。
initWithTotalParticles方法調用setupVBO或setupVBOandVAO方法加載VBO VAO。
本文原創地址:https://www.cnblogs.com/deepcho/ ,如果您在非本網址看到此文章,說明網站是爬蟲采集抄襲而成。