这次的HGE系列让我们一起来学习一下HGE引擎的粒子系统部分,对于粒子系统不甚了解的朋友可以从这里开始了解。

首先还是让我们从头文件开始:

类名 :hgeParticle

功能 :单个粒子的属性结构

头文件 :hge/hge181/include/hgeParticle.h

实现文件 : 无

struct hgeParticle

{

// 粒子所在的位置

hgeVector       vecLocation;

// 粒子的速度(Velocity)

hgeVector       vecVelocity;

// 粒子的重力

float        fGravity;

// 粒子的线加速度

float        fRadialAccel;

// 粒子的角加速度

float        fTangentialAccel;

// 粒子的旋转角度

float        fSpin;

// 粒子旋转角度偏移量

float        fSpinDelta;

// 粒子的大小

float        fSize;

// 粒子的大小偏移量

float        fSizeDelta;

// 粒子的颜色(带alpha值)

hgeColor colColor;        // + alpha

// 粒子的颜色偏移量

hgeColor colColorDelta;

// 粒子的生存时间

float        fAge;

// 粒子的死亡时间

float        fTerminalAge;

};

仅一个简单的数据集合而已,各中变量相信大家一目了然 :)

  类名 :hgeParticleSystemInfo

功能 :粒子系统的相关信息结构

头文件 :hge/hge181/include/hgeParticle.h

实现文件 : 无

struct hgeParticleSystemInfo

{

// 粒子贴图

hgeSprite*  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;

// alpha变化值

float       fAlphaVar;

};

仍然是一个数据集合而已:)

接着让我们看一看hgeParticleSystem这个类型:

类名 :hgeParticleSystem

功能 :粒子系统

头文件 :hge/hge181/include/hgeParticle.h

实现文件 : hge/hge181/src/helpers/hgeparticle.cpp

class hgeParticleSystem

{

public:

// 用于存储粒子系统的相关信息(详见hgeParticleSystemInfo的定义)

hgeParticleSystemInfo info;

// 构造函数&析构函数

hgeParticleSystem(const char *filename, hgeSprite *sprite);

hgeParticleSystem(hgeParticleSystemInfo *psi);

hgeParticleSystem(const hgeParticleSystem &ps);

~hgeParticleSystem() { hge->Release(); }

// 重载赋值运算符

hgeParticleSystem&      operator= (const hgeParticleSystem &ps);

// 渲染粒子系统

void                      Render();

// 在指定位置启动粒子系统

void                      FireAt(float x, float y);

// 启动粒子系统

void                      Fire();

// 停止粒子系统

void                      Stop(bool bKillParticles=false);

// 更新粒子系统

void                      Update(float fDeltaTime);

// 移动粒子系统的位置

void                      MoveTo(float x, float y, bool bMoveParticles=false);

// 设定粒子系统的位置偏移量

void                      Transpose(float x, float y) { fTx=x; fTy=y; }

// 设置粒子系统的缩放比例

void                      SetScale(float scale) { fScale = scale; }

// 设置是否跟踪粒子系统的边界盒

void                      TrackBoundingBox(bool bTrack) { bUpdateBoundingBox=bTrack; }

// 获取当前粒子系统中活动的粒子个数

int                               GetParticlesAlive() const { return nParticlesAlive; }

// 获得粒子系统的生命值

float                      GetAge() const { return fAge; }

// 获取粒子系统位置

void                      GetPosition(float *x, float *y) const { *x=vecLocation.x; *y=vecLocation.y; }

// 获取位置偏移量

void                      GetTransposition(float *x, float *y) const { *x=fTx; *y=fTy; }

// 获取缩放比例

float                      GetScale() { return fScale; }

// 获取边界盒

hgeRect*               GetBoundingBox(hgeRect *rect) const;

private:

// 私有化默认构造函数,达到屏蔽默认构造的作用

hgeParticleSystem();

// HGE静态指针

static HGE                    *hge;

// 生命值

float                      fAge;

// 剩余未有发射(生成)的粒子数量

float                      fEmissionResidue;

// 前一个位置

hgeVector                     vecPrevLocation;

// 当前位置

hgeVector                     vecLocation;

// 偏移量

float                      fTx, fTy;

// 缩放因子

float                      fScale;

// 活动的粒子个数

int                               nParticlesAlive;

// 粒子系统的边界盒

hgeRect                        rectBoundingBox;

// 是否更新边界盒

bool                      bUpdateBoundingBox;

// hgeParticle数组,用以存储所有的粒子

hgeParticle                   particles[MAX_PARTICLES];

};

总的来说,hgeParticleSystem封装了一个相对简洁的粒子系统的各项功能,其头文件声明的数据及函数成员并未有十分费解的地方,下面就让我们看一看其的实现源码:

首先自然是他的构造函数:

hgeParticleSystem::hgeParticleSystem(const char *filename, hgeSprite *sprite)

{

void *psi;

// 创建HGE,以获得其提供的底层接口

hge=hgeCreate(HGE_VERSION);

// 使用Resource_Load加载文件

psi=hge->Resource_Load(filename);

if(!psi) return;

// 将文件中的内容拷贝至info(hgeParticleSystemInfo)中

memcpy(&info, psi, sizeof(hgeParticleSystemInfo));

// 释放资源

hge->Resource_Free(psi);

// 设置粒子精灵类

info.sprite=sprite;

// 初始化粒子系统的位置

vecLocation.x=vecPrevLocation.x=0.0f;

vecLocation.y=vecPrevLocation.y=0.0f;

// 初始化粒子系统的偏移位置

fTx=fTy=0;

// 初始化缩放因子,1.0f代表不做缩放

fScale = 1.0f;

// 初始化未发射(生成)粒子数量

fEmissionResidue=0.0f;

// 初始化活动粒子数

nParticlesAlive=0;

// 初始化粒子生命值

fAge=-2.0;

// 初始化边界盒

rectBoundingBox.Clear();

// 默认不更新边界盒

bUpdateBoundingBox=false;

}

各项操作还是相当易解的,剩下的两个构造函数如出一辙,在此不再赘述,只是就其中的复制构造函数,在这里我想说上一说:

源代码中,关于复制构造函数的实现很简单:

hgeParticleSystem::hgeParticleSystem(const hgeParticleSystem &ps)

{

memcpy(this, &ps, sizeof(hgeParticleSystem));

hge=hgeCreate(HGE_VERSION);

}

由于hgeParticleSystem并不涉及资源的管理(关于HGE的资源管理,可以看看这篇),所以结构内容基本可以按照POD数据的方式进行复制,这也是该函数中大胆使用memcpy的一个原因,但是令我比较意外的是,虽然hgeParticleSystem的头文件中声明了重载赋值运算符函数:

/ / 重载赋值运算符

hgeParticleSystem&      operator= (const hgeParticleSystem &ps);

但是却没有加以实现,所以当使用时必然引起链接错误,如果这是作者的初衷的话,我想将其变为私有(private)将更为妥当,而且按照C++中公认的编程规范,一旦你定义了复制构造函数,那么你也应该定义赋值构造函数。对于这个类别,我想比较妥当的定义方法也许可以是这个样子:

hgeParticleSystem::hgeParticleSystem(const hgeParticleSystem &ps)

{

memcpy(this, &ps, sizeof(hgeParticleSystem));

hge=hgeCreate(HGE_VERSION);

}

hgeParticleSystem&      hgeParticleSystem::operator= (const hgeParticleSystem &ps)

{

if( this == &ps )

return;

memcpy(this, &ps, sizeof(hgeParticleSystem));

}

另外一提的便是fAge这个成员变量,在构造函数中他被设置成了-2.0:

// 初始化粒子生命值

fAge=-2.0;

相信不少朋友对于这个-2.0的由来应该比较奇怪,其实他是一个“魔数”,代表着粒子系统尚未启动(使用FireAt或者Fire来启动粒子系统),不过这种编程技巧我个人是比较不赞同的,我的建议是至少使用枚举之类明显的变量值进行代替,最低限度也可以使用粗糙的宏定义来简单的包装一下,譬如:

#define PARTICLE_SYSTEM_NOT_START (-2.0)

好了,闲话少叙,接着让我们来看看其他的成员函数:

// 移动粒子系统的位置

void hgeParticleSystem::MoveTo(float x, float y, bool bMoveParticles)

{

int i;

float dx,dy;

// 如果移动所有活动粒子的话

if(bMoveParticles)

{

// 计算目标坐标与当前坐标的偏移量

dx=x-vecLocation.x;

dy=y-vecLocation.y;

// 重新设置所有活动粒子的位置

for(i=0;i<nParticlesAlive;i++)

{

particles[i].vecLocation.x += dx;

particles[i].vecLocation.y += dy;

}

// 重新计算先前所在坐标的位置

vecPrevLocation.x=vecPrevLocation.x + dx;

vecPrevLocation.y=vecPrevLocation.y + dy;

}

// 否则仅移动即将发射的粒子

else

{

// 如果粒子系统尚未启动

if(fAge==-2.0) { vecPrevLocation.x=x; vecPrevLocation.y=y; }

else { vecPrevLocation.x=vecLocation.x;     vecPrevLocation.y=vecLocation.y; }

}

// 设置当前位置坐标

vecLocation.x=x;

vecLocation.y=y;

}

可以看到,相关的代码并不复杂,值得注意一下的是代码中对于vecPrevLocation的设置 :)

接着是FireAt、Fire以及对应的Stop:

// 在指定位置启动粒子系统

void hgeParticleSystem::FireAt(float x, float y)

{

// 简单三部曲:首先停止,接着移动,最后启动

Stop();

MoveTo(x,y);

Fire();

}

// 启动粒子系统

void hgeParticleSystem::Fire()

{

// 注意fAge的设置

if(info.fLifetime==-1.0f) fAge=-1.0f;

else fAge=0.0f;

}

// 停止粒子系统

void hgeParticleSystem::Stop(bool bKillParticles)

{

// 注意fAge的设置

fAge=-2.0f;

// 如果需要清除粒子的话

if(bKillParticles)

{

// 重新设置活动粒子数量为0

nParticlesAlive=0;

// 重置粒子系统的边界盒

rectBoundingBox.Clear();

}

}

再者让我们看看如何渲染粒子系统:

// 渲染粒子系统

void hgeParticleSystem::Render()

{

int i;

DWORD col;

hgeParticle *par=particles;

// 获取粒子精灵的颜色

col=info.sprite->GetColor();

// 对于每一个活动的粒子

for(i=0; i<nParticlesAlive; i++)

{

// 设置粒子精灵的颜色

if(info.colColorStart.r < 0)

info.sprite->SetColor(SETA(info.sprite->GetColor(),par->colColor.a*255));

else

info.sprite->SetColor(par->colColor.GetHWColor());

// 渲染粒子精灵,注意各个参数的设置

info.sprite->RenderEx(par->vecLocation.x*fScale+fTx, par->vecLocation.y*fScale+fTy, par->fSpin*par->fAge, par->fSize*fScale);

// 移动到下一活动粒子

par++;

}

// 还原先前的粒子精灵颜色

info.sprite->SetColor(col);

}

边界盒的获取也并不复杂:

hgeRect *hgeParticleSystem::GetBoundingBox(hgeRect *rect) const

{

*rect = rectBoundingBox;

// 根据缩放因子缩放坐标

rect->x1 *= fScale;

rect->y1 *= fScale;

rect->x2 *= fScale;

rect->y2 *= fScale;

return rect;

}

最后,让我们来看一看稍有些复杂的Update函数:

void hgeParticleSystem::Update(float fDeltaTime)

{

int i;

float ang;

hgeParticle *par;

hgeVector vecAccel, vecAccel2;

// 如果粒子系统生命值大于0

if(fAge >= 0)

{

// 累计生命值

fAge += fDeltaTime;

// 如果超过系统生命周期时间,则重新设置为未启动

if(fAge >= info.fLifetime) fAge = -2.0f;

}

// 更新所有活动粒子

// 更新粒子系统边界盒

if(bUpdateBoundingBox) rectBoundingBox.Clear();

par=particles;

// 对于每一个活动粒子

for(i=0; i<nParticlesAlive; i++)

{

// 更新生命值

par->fAge += fDeltaTime;

// 如果达到死亡时间

if(par->fAge >= par->fTerminalAge)

{

// 递减活动粒子数目

nParticlesAlive--;

// 将最后一个活动粒子拷贝至此处

memcpy(par, &particles[nParticlesAlive], sizeof(hgeParticle));

// 递减计数值,以达到下次继续处理该位置粒子的目的

i--;

continue;

}

// 计算粒子的线加速度

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;

// 计算粒子的位置

par->vecLocation += par->vecVelocity*fDeltaTime;

// 计算粒子的旋转角度

par->fSpin += par->fSpinDelta*fDeltaTime;

// 计算粒子的大小

par->fSize += par->fSizeDelta*fDeltaTime;

// 计算粒子的颜色

par->colColor += par->colColorDelta*fDeltaTime;

// 更新粒子边界盒

if(bUpdateBoundingBox) rectBoundingBox.Encapsulate(par->vecLocation.x, par->vecLocation.y);

// 移动到下一个粒子

par++;

}

// 生成新的粒子

// 如果粒子系统已经启动的话

if(fAge != -2.0f)

{

// 计算需要生成的粒子数

float fParticlesNeeded = info.nEmission*fDeltaTime + fEmissionResidue;

// 强制装换为无符号整数

int nParticlesCreated = (unsigned int)fParticlesNeeded;

// 计算遗留下来未有生成的粒子,等待下一次更新生成

fEmissionResidue=fParticlesNeeded-nParticlesCreated;

par=&particles[nParticlesAlive];

// 对于每一个需要生成的粒子

for(i=0; i<nParticlesCreated; i++)

{

// 如果活动粒子已经超过上界,则不在生成

if(nParticlesAlive>=MAX_PARTICLES) break;

// 设置粒子的各项属性

// 生命值

par->fAge = 0.0f;

par->fTerminalAge = hge->Random_Float(info.fParticleLifeMin, info.fParticleLifeMax);

// 设置粒子的位置

par->vecLocation = vecPrevLocation+(vecLocation-vecPrevLocation)*hge->Random_Float(0.0f, 1.0f);

par->vecLocation.x += hge->Random_Float(-2.0f, 2.0f);

par->vecLocation.y += hge->Random_Float(-2.0f, 2.0f);

// 设置粒子的速度值,注意角度的计算             ang=info.fDirection-M_PI_2+hge->Random_Float(0,info.fSpread)-info.fSpread/2.0f;

if(info.bRelative) ang += (vecPrevLocation-vecLocation).Angle()+M_PI_2;

par->vecVelocity.x = cosf(ang);

par->vecVelocity.y = sinf(ang);

par->vecVelocity *= hge->Random_Float(info.fSpeedMin, info.fSpeedMax);

// 设置粒子的重力、线角速度以及角加速度

par->fGravity = hge->Random_Float(info.fGravityMin, info.fGravityMax);

par->fRadialAccel = hge->Random_Float(info.fRadialAccelMin, info.fRadialAccelMax);

par->fTangentialAccel = hge->Random_Float(info.fTangentialAccelMin, info.fTangentialAccelMax);

// 设置粒子的大小及偏移量

par->fSize = hge->Random_Float(info.fSizeStart, info.fSizeStart+(info.fSizeEnd-info.fSizeStart)*info.fSizeVar);

par->fSizeDelta = (info.fSizeEnd-par->fSize) / par->fTerminalAge;

// 设置粒子的旋转角度及偏移量

par->fSpin = hge->Random_Float(info.fSpinStart, info.fSpinStart+(info.fSpinEnd-info.fSpinStart)*info.fSpinVar);

par->fSpinDelta = (info.fSpinEnd-par->fSpin) / par->fTerminalAge;

// 设置粒子颜色

par->colColor.r = hge->Random_Float(info.colColorStart.r, info.colColorStart.r+(info.colColorEnd.r-info.colColorStart.r)*info.fColorVar);

par->colColor.g = hge->Random_Float(info.colColorStart.g, info.colColorStart.g+(info.colColorEnd.g-info.colColorStart.g)*info.fColorVar);

par->colColor.b = hge->Random_Float(info.colColorStart.b, info.colColorStart.b+(info.colColorEnd.b-info.colColorStart.b)*info.fColorVar);

par->colColor.a = hge->Random_Float(info.colColorStart.a, info.colColorStart.a+(info.colColorEnd.a-info.colColorStart.a)*info.fAlphaVar);

// 设置粒子颜色偏移量

par->colColorDelta.r = (info.colColorEnd.r-par->colColor.r) / par->fTerminalAge;

par->colColorDelta.g = (info.colColorEnd.g-par->colColor.g) / par->fTerminalAge;

par->colColorDelta.b = (info.colColorEnd.b-par->colColor.b) / par->fTerminalAge;

par->colColorDelta.a = (info.colColorEnd.a-par->colColor.a) / par->fTerminalAge;

// 更新粒子系统边界盒

if(bUpdateBoundingBox) rectBoundingBox.Encapsulate(par->vecLocation.x, par->vecLocation.y);

// 递增活动粒子数目

nParticlesAlive++;

// 移动到下一个待生成粒子

par++;

}

}

// 设置先前粒子系统位置

vecPrevLocation=vecLocation;

}

好了,至此,我们完全可以使用hgeParticleSystem来生成不错的粒子特效了,不过在这之前,让我们继续来看一下HGE对于hgeParticleSystem的更进一步封装:hgeParticleManager

类名 :hgeParticleManager

功能 :粒子系统管理类

头文件 :hge/hge181/include/hgeParticle.h

实现文件 : hge/hge181/src/helpers/hgepmanager.cpp

头文件如下:

class hgeParticleManager

{

public:

// 构造函数&析构函数

hgeParticleManager();

~hgeParticleManager();

// 更新函数

void                      Update(float dt);

// 渲染函数

void                      Render();

// 生成粒子系统

hgeParticleSystem*       SpawnPS(hgeParticleSystemInfo *psi, float x, float y);

// 粒子系统是否活动

bool                      IsPSAlive(hgeParticleSystem *ps) const;

// 设置粒子系统偏移

void                      Transpose(float x, float y);

// 获取粒子系统位移

void                      GetTransposition(float *dx, float *dy) const {*dx=tX; *dy=tY;}

// 销毁指定的粒子系统

void                      KillPS(hgeParticleSystem *ps);

// 销毁所有粒子系统

void                      KillAll();

private:

// 私有化复制构造函数及赋值操作符函数,已达到屏蔽作用

hgeParticleManager(const hgeParticleManager &);

hgeParticleManager&    operator= (const hgeParticleManager &);

// 粒子系统数量

int                               nPS;

// 偏移位置坐标

float                      tX;

float                      tY;

// 粒子系统指针数组

hgeParticleSystem*       psList[MAX_PSYSTEMS];

};

hgeParticleManager看来还是相对简单的 :)

首先自然是构造函数和析构函数:

hgeParticleManager::hgeParticleManager()

{

// 设定初始值,使用成员列表更好一些

nPS=0;

tX=tY=0.0f;

}

hgeParticleManager::~hgeParticleManager()

{

// 遍历指针数组依次删除

int i;

for(i=0;i<nPS;i++) delete psList[i];

}

没有什么奇特的地方,那么接着让我们来看一看SpawnPS:

hgeParticleSystem* hgeParticleManager::SpawnPS(hgeParticleSystemInfo *psi, float x, float y)

{

// 如果粒子系统超过上界,则返回

if(nPS==MAX_PSYSTEMS) return 0;

// 生成粒子系统

psList[nPS]=new hgeParticleSystem(psi);

// 启动

psList[nPS]->FireAt(x,y);

// 设置偏移

psList[nPS]->Transpose(tX,tY);

// 递增粒子系统数目

nPS++;

return psList[nPS-1];

}

函数很简单,生成粒子系统而已 :)

接下来的几个成员函数也并不困难:

bool hgeParticleManager::IsPSAlive(hgeParticleSystem *ps) const

{

// 依次遍历数组查找指定的粒子系统

int i;

for(i=0;i<nPS;i++) if(psList[i]==ps) return true;

return false;

}

void hgeParticleManager::Transpose(float x, float y)

{

// 依次遍历并设置粒子系统偏移

int i;

for(i=0;i<nPS;i++) psList[i]->Transpose(x,y);

// 更新tX和tY

tX=x; tY=y;

}

void hgeParticleManager::KillPS(hgeParticleSystem *ps)

{

int i;

// 依次遍历指针数组

for(i=0;i<nPS;i++)

{

// 如果找到

if(psList[i]==ps)

{

// 删除这个粒子系统

delete psList[i];

// 并将其指向最后一个活动的粒子系统

psList[i]=psList[nPS-1];

// 递减粒子系统数目

nPS--;

return;

}

}

}

void hgeParticleManager::KillAll()

{

// 遍历并删除所有粒子系统

int i;

for(i=0;i<nPS;i++) delete psList[i];

nPS=0;

}

最后,让我们来看一看Update和Render:

void hgeParticleManager::Render()

{

// 依次遍历并渲染每个粒子系统

int i;

for(i=0;i<nPS;i++) psList[i]->Render();

}

Render相当简单,那么Update如何呢:

void hgeParticleManager::Update(float dt)

{

int i;

// 对于每个存在的粒子系统

for(i=0;i<nPS;i++)

{

// 更新该粒子系统

psList[i]->Update(dt);

// 如果该粒子系统尚未启动并且活动粒子数目为0

if(psList[i]->GetAge()==-2.0f && psList[i]->GetParticlesAlive()==0)

{

// 删除这个粒子系统

delete psList[i];

// 将其指向最后一个活动粒子系统

psList[i]=psList[nPS-1];

// 更新粒子系统数目

nPS--;

// 递减计数值,已达到下次循环继续处理该位置粒子系统的目的

i--;

}

}

}

呼,至此,HGE的粒子系统终算泛泛的讲解完毕了,虽然篇幅不短,但也还算简单,平心而论,HGE的粒子系统虽说并不十分复杂,但也提供了非常不错的显示效果和可扩展性,对于我们关于粒子系统的学习还是很有助益的,有兴趣的朋友远可以进一步使用或者扩展,说不定可以搞出令人惊奇的东西(到时务必告知一下,以便观摩学习)!好了好了,我想我废话也够多了,是时候说ByeBye了,那么,下次再见吧 :)

HGE引擎的粒子系统相关推荐

  1. uinty粒子系统子物体变大_Unity的粒子系统(一)基础篇

    简介 闲来无事,仔细的学习一下粒子系统,也当是给自己做个笔记方便之后进行回顾. 引擎版本:Unity2018.3 创建一个ParticleSystem 创建方式: 1.Hierarchy-->E ...

  2. [VC] 【游戏编程】构架游戏中的粒子系统 图文教程

    转载自:http://www.52pojie.cn/thread-165772-1-1.html Expression GameEngine Particle System的实现效果   动画原图: ...

  3. C++多小球非对心弹性碰撞(HGE引擎)

    程序是一个月前完成的,之前一直没正儿八经的来整理下这个程序,感觉比较简单,不过即使简单的东西也要跟大家分享下. 源码下载:http://download.csdn.net/detail/y851716 ...

  4. unity2017.1.0f3与旧的粒子系统不兼容

    unity2017.1.0f3与旧的粒子系统不兼容 在测试旧版本插件unistorm时用unity2017.1.0f3打开后其它天气效果显示正常,雨点看不到,再用unity5.52打开后,所有效果都可 ...

  5. D3D中的粒子系统(4)

    14.3具体的粒子系统:雪.火.粒子枪 现在让我们用cParticleSystem类开始一个具体的粒子系统,为了说明用意,这些系统的设计很简单,没有用到cParticleSystem类所提供的所有灵活 ...

  6. 基于CUDA的粒子系统的实现

    基于CUDA的粒子系统的实现 用途: 这篇文章作为代码实现的先导手册,以全局的方式概览一下粒子系统的实现大纲. 科普: 对粒子进行模拟有两种基本方法: Eulerian(grid-based) met ...

  7. 基于GPU的粒子系统

    粒子系统通常要随着时间的推移来发射和销毁粒子.从表面上看,这一工作应该用动态顶点缓冲区来实现,并在CPU上生成和销毁粒子.然后,用当前存活的粒子来填充顶点缓冲区,并对其进行绘制.不过,我们在上一节讲过 ...

  8. HGE引擎写的俄罗斯方块程序(附vc源码)[r]

    使用HGE引擎写了个俄罗斯方块游戏,for study 执行文件 源代码 运行效果图 PS: 开源!!! 转载于:https://www.cnblogs.com/dotLive/archive/200 ...

  9. 详解Unity中的粒子系统Particle System (十二 | 终)

    前言 终于来到了最后一篇,粒子系统宣告终结!这十来篇博客删删改改写了半个多月,真是离谱.今天该讲案例与粒子系统的应用,那么我们就进入正题吧! 目录 前言 本系列提要 一.如何做出效果 二.案例演示 1 ...

最新文章

  1. Transformer再度出手!low-level多个任务榜首被占领,
  2. 2 获取对象 IDbDataAdapter 用于填充 DataSet 和更新数据源
  3. 中国制鞋机械行业市场“十四五”规划模式及项目投资分析报告2022-2028年版
  4. 【Canal源码分析】TableMetaTSDB
  5. sap可配置BOM的主要流程
  6. android第三方应用,Android 第三方应用接入微信平台研究情况分享(一)
  7. 面试必问系列之在浏览器中输入URL后到网页显示 其间发生了什么?
  8. 设计模式----2(简单工厂模式的概念,简单工厂模式的实现,简单工厂模式的优缺点)
  9. 关于discuz 不能全文搜索的问题
  10. mysql词法分析antlr4_sharding-jdbc之ANTLR4 SQL解析
  11. BaseServlet 继承 httpServlet
  12. 网页内容变化监控提醒
  13. NATAPP内网穿透使用
  14. 二次函数顶点式计算机,二次函数公式:顶点式、交点式、两根式
  15. 没有银弹-软件工程中的根本和次要问题
  16. unity简单小球下落
  17. 【魔方攻略】五魔方教程(原创)
  18. APP注册登录那点事
  19. stm32 ADXL345传感器
  20. google账号已停用,此账号的使用方式似乎违反了Google的政策

热门文章

  1. linux 冷启动命令,鸟哥的 Linux 私房菜7 -- 首次开机关机与基本指令执行
  2. ubuntu开机总是报xorg no devices detected no screens found,而且睡眠后不能启动了
  3. db2 mysql 代码_db2 修改数据库字符集
  4. GitHub标星9,flutter实时刷新
  5. Java基础阶段综合练习
  6. python文件运行一段时间后失效_机器人运行一段时间后停止运行,报如下错误
  7. idea提交git报错Failed with error: Authentication failed for ‘http://gitlab.
  8. 用5G太费电?美国最大5G运营商发推特,号召用户关5G打开4G以省电
  9. 解决Windows Media Player播放无声或声音过快问题
  10. 批量把excel文件转为csv格式