简介
粒子系统主要用来解决由大量按一定规则运动(变化)的微小物质组成的大物质
在计算机上的生成与显示的问题。经常使用粒子系统模拟的现象有火、爆炸、烟、
水流、火花、落叶、云、雾、雪、尘、流星尾迹或者象发光轨迹这样的抽象视觉
效果等等。
粒子系统的实现:
- 每个粒子是一个带纹理的三角形/多边形,粒子系统由多个粒子组成;
- 粒子按一定规则运动;
- 每个粒子有自己的属性,如位置、速度、质量、尺寸、年龄、运动轨迹斜率、颜色等;
- 粒子系统更新循环划分为两个不同的阶段:参数更新/模拟阶段以及渲染阶段。
- 参数更新/模拟阶段根据粒子系统信息更新每个粒子的速度、位置、尺寸、颜
色等信息;
- 渲染阶段根据粒子属性重新绘制粒子;
- 每个粒子都有自己的生命,年龄超过最大年龄的粒子会被销毁,系统会生成
新的粒子。
JGE 游戏引擎提供了一个粒子系统,基于该引擎的很多游戏的爆炸效果就是使用
粒子系统实现的。该粒子系统从 HGE 游戏引擎移植而来,下面将对其做深入的剖析。
粒子属性
每个粒子包括如下属性:
- vecLocation:粒子的位置;
- vecVelocity:粒子速度,记录粒子下一个位置相对于当前位置的偏移(包括 x 和 y 方向);
- fGravity:粒子质量,决定粒子受重力影响(自由落体)的程度,值越大,粒子自由落体越远;
- fRadialAccel:决定粒子往四周的发散程度,值越大发射出去的越远;
- fTangentialAccel:决定粒子的运动轨迹的斜率,斜率越大运动轨迹弯曲程度越大,斜率越小,轨迹越趋向于直线。
- fSpin:
- fSpinDelta:
- fSize:粒子的大小;
- fSizeDelta:粒子大小的变化;
- colColor:粒子的颜色;
- colColorDelta:颜色的变化;
- fAge:粒子的年龄,当粒子超过死亡年龄后死亡,被系统销毁;
- fTerminalAge:粒子死亡年龄;
粒子的速度受到 fDeltaTime, fRadialAccel, fTangentialAccel, fGravity 等
参数的影响,求粒子下一 frame 的速度如下所示:
vecAccel = par->vecLocation-vecLocation;
vecAccel.Normalize();
vecAccel2 = vecAccel;
vecAccel *= par->fRadialAccel;
// vecAccel2.Rotate(M_PI_2);
// the following is faster
ang = vecAccel2.x;
vecAccel2.x = -vecAccel2.y;
vecAccel2.y = ang;
vecAccel2 *= par->fTangentialAccel;
par->vecVelocity += (vecAccel+vecAccel2)*fDeltaTime;
par->vecVelocity.y += par->fGravity*fDeltaTime;
粒子的位置与速度有关,求粒子下一 frame 处的位置如下所示:
par->vecLocation += par->vecVelocity;
粒子参数 fSpin, fSize, colColor 受 fDeltaTime 影响,求粒子下一 frame
处的 spin, size 和 color 如下所示:
par->fSpin += par->fSpinDelta*fDeltaTime;
par->fSize += par->fSizeDelta*fDeltaTime;
par->colColor += par->colColorDelta*fDeltaTime;
粒子系统属性
粒子系统具有如下属性:
- hgeParticle particles[MAX_PARTICLES]:粒子数组,保存所有粒子;
- hgeParticleSystemInfo info:粒子系统信息,此数据从 psi 文件读取得到;
- fAge: 粒子系统年龄;
- fEmissionResidue:
- vecPrevLocation:上一 frame 中粒子系统的中心点位置;
- vecLocation:当前 frame 中粒子系统的中心位置;
- fTx, fTy:
- nParticlesAlive:活着的粒子个数;
- rectBoundingBox:粒子系统外包矩形。
- mTimer:
粒子系统数据文件 psi
粒子系统数据文件以 psi 作为后缀,该文件记录了粒子系统的所有信息。
粒子系统信息通常使用粒子系统编辑工具生成。利用粒子系统编辑工具可以以所
见即所得的方式对粒子系统参数进行编辑,并可以实时地看到效果。这里介绍的
psi 文件就是使用 HGE 游戏引擎的粒子系统工具 particleed 生成的。
粒子系统具体信息包括:
- nEmission:每秒产生的粒子数;
- fLifetime
- fParticleLifeMin:粒子最短生命周期;
- fParticleLifeMax:粒子最长生命周期;
- fDirection:
- fSpread;粒子发散程度;
- bRelative;
- fSpeedMin:最小速度;
- fSpeedMax:最大速度;
- fGravityMin:最小重量;
- fGravityMax:最大重量;
- fRadialAccelMin:最小发射速度;
- fRadialAccelMax:最大发射速度;
- fTangentialAccelMin:粒子运动轨迹最小斜率;
- fTangentialAccelMax:粒子运动轨迹最大斜率;
- fSizeStart:粒子初始尺寸;
- fSizeEnd:粒子最后尺寸;
- fSizeVar:尺寸变化;
- fSpinStart
- fSpinEnd;
- fSpinVar;
- colColorStart: 粒子初始颜色;
- colColorEnd:粒子最后颜色;
- fColorVar:粒子颜色变化;
- fAlphaVar:粒子 Alpha 值变化。
粒子系统信息数据结构如下所示:
struct hgeParticleSystemInfo
{
JQuad* sprite; // texture + blend mode
int nEmission; // particles per sec
float fLifetime;
float fParticleLifeMin;
float fParticleLifeMax;
float fDirection;
float fSpread;
bool bRelative;
float fSpeedMin;
float fSpeedMax;
float fGravityMin;
float fGravityMax;
float fRadialAccelMin;
float fRadialAccelMax;
float fTangentialAccelMin;
float fTangentialAccelMax;
float fSizeStart;
float fSizeEnd;
float fSizeVar;
float fSpinStart;
float fSpinEnd;
float fSpinVar;
hgeColor colColorStart; // + alpha
hgeColor colColorEnd;
float fColorVar;
float fAlphaVar;
};
粒子系统类图
粒子系统主要由两个结构和两个类组成:
结构 hgeParticle 封装了粒子的所有属性,如位置、速度、尺寸、颜色等。
结构 hgeParticleSystemInfo 封装了粒子系统的所有信息。
类 hgeParticleSystem 封装了粒子系统的所有属性和操作。所有粒子保存在数
组 particles[MAX_PARTICLES] 中。Fire() 方法启动粒子系统;MoveTo() 方法移
动粒子系统的中心点;Update() 方法更新粒子的各个属性值;Render() 方法将
各个粒子绘制出来。
类 hgeParticleManager 提供了一个管理器,实现了对多个粒子系统的管理和操作。

粒子系统使用样例
下面是基于上述粒子系统模块实现一个粒子系统的具体例子,该例子来自 JGE 引擎的教程 11 (hgeparticles),它在屏幕中央放了一个粒子系统。
class GameApp: public JApp
{
public:
virtual void Create();
virtual void Update();
virtual void Render();
void LoadNewParticleSys();
... ...
private:
hgeParticleSystem* mParticleSys;
JQuad *mParticles[16];
JTexture *mTexture;
... ...
};
void GameApp::LoadNewParticleSys()
{
if (mParticleSys)
{
delete mParticleSys;
mParticleSys = NULL;
}
char s[80];
sprintf(s, "particle%d.psi", (mCurrParticleSys+1));
mParticleSys = new hgeParticleSystem(s, mParticles[mCurrParticle]);
mParticleSys->MoveTo(SCREEN_WIDTH_F/2, SCREEN_HEIGHT_F/2);
mParticleSys->Fire();
}
void GameApp::Create()
{
mTexture = JRenderer::GetInstance()->LoadTexture("particles.png", TEX_TYPE_USE_VRAM);
// load all the particle images
int n = 0;
for (int i=0;i<4;i++)
{
for (int j=0;j<4;j++)
{
mParticles[n] = new JQuad(mTexture, j*32, i*32, 32, 32);
mParticles[n]->SetHotSpot(16,16);
n++;
}
}
LoadNewParticleSys();
... ...
}
void GameApp::Update()
{
... ...
float dt = engine->GetDelta(); // Get time elapsed since last update.
... ...
mParticleSys->Update(dt);
}
void GameApp::Render()
{
// get JRenderer instance
JRenderer* renderer = JRenderer::GetInstance();
// clear screen to black
renderer->ClearScreen(ARGB(0,0,0,0));
... ...
mParticleSys->Render();
... ...
}
void GameApp::Destroy()
{
for (int i=0;i<16;i++)
SAFE_DELETE(mParticles[i]);
SAFE_DELETE(mParticleSys);
SAFE_DELETE(mTexture);
... ...
}
Reference
|