CCParticleSystem是用来设置粒子效果的类

1、粒子分为两种模式:重力模式 和 半径模式

重力模式独占属性:

  • gravity 重力方向,Vec2类型,可以分别指定不同方向的重力大小
  • speed 粒子运动的速度
  • radialAccel 向心加速度
  • tangentialAccel 切向加速度
  • rotationIsDir 自转方向

半径模式独占属性:

  • startRadius 开始半径
  • endRadius 结束半径
  • rotatePerSecond 每秒旋转多少角度

两种模式共有属性:

  • angle 粒子发射时的角度
  • duration 发射器的生存时间
  • isActive 发射器活动状态(启用/暂停)
  • life 粒子生存时间
  • emissionRate 粒子的发射率 = _totalParticles / _life
  • emitCounter 每秒发射多少粒子
  • totalParticles 存在的最大粒子数
  • particleCount 目前存在的粒子数量
  • totalParticleCountFactor 影响总粒子数的参数 默认为1.0f
  • allocatedParticles 存在的最大粒子数,在ParticleSystemQuad中设置粒子时使用
  • texture 存储创建粒子的纹理
  • startSize/endSize 粒子开始和结束大小
  • startColor/endColor 粒子开始和结束颜色

所有的var都是用来表示 差异随机数

2、positionType:用来存储粒子的位置模式

粒子位置有三种模式:FREE、RELATIVE、GROUPED

FREE:(完全自由)粒子发射之后,位置相对于世界坐标系,发射器移动不影响已经发射的粒子

RELATIVE:粒子位置相对于发射器,发射器跟随父节点移动时,粒子也会跟着发射器移动;如果手动(触摸/点击)点击改变了发射器的位置,已经发射出去的粒子还会按照原来的路径移动。

GROUPED:粒子位置相对于发射器,发射器跟随父节点移动时,粒子也会跟着发射器移动了;如果手动(触摸/点击)改变了发射器的位置,粒子和发射器会一起移动,也就是说粒子相对于发射器的位置不会变

3、在initWithDictionary中读取了plist文件中的数据,并进行了赋值,最后通过判断调用setTexture方法来设置粒子

4、粒子的实质是通过读取plist文件存储的纹理数据。

放几段代码

//初始化,这个方法可以配合着cocos2dx DEMO中自带的plist文件阅读
bool ParticleSystem::initWithDictionary(ValueMap& dictionary, const std::string& dirname)
{
bool ret = false;
unsigned char *buffer = nullptr;
unsigned char *deflated = nullptr;
Image *image = nullptr;
do
{
int maxParticles = dictionary["maxParticles"].asInt(); //获取文件中设置的最大粒子个数
// self, not super
//通过粒子个数初始化粒子
if(this->initWithTotalParticles(maxParticles))
{
// Emitter name in particle designer 2.0
_configName = dictionary["configName"].asString(); // angle
_angle = dictionary["angle"].asFloat();
_angleVar = dictionary["angleVariance"].asFloat(); // duration
//发射器的生存时间
_duration = dictionary["duration"].asFloat(); // blend function
if (!_configName.empty())
{
_blendFunc.src = dictionary["blendFuncSource"].asFloat();
}
else
{
_blendFunc.src = dictionary["blendFuncSource"].asInt();
}
_blendFunc.dst = dictionary["blendFuncDestination"].asInt(); // color
//开始颜色
_startColor.r = dictionary["startColorRed"].asFloat();
_startColor.g = dictionary["startColorGreen"].asFloat();
_startColor.b = dictionary["startColorBlue"].asFloat();
_startColor.a = dictionary["startColorAlpha"].asFloat(); //颜色方差
_startColorVar.r = dictionary["startColorVarianceRed"].asFloat();
_startColorVar.g = dictionary["startColorVarianceGreen"].asFloat();
_startColorVar.b = dictionary["startColorVarianceBlue"].asFloat();
_startColorVar.a = dictionary["startColorVarianceAlpha"].asFloat(); //结束颜色
_endColor.r = dictionary["finishColorRed"].asFloat();
_endColor.g = dictionary["finishColorGreen"].asFloat();
_endColor.b = dictionary["finishColorBlue"].asFloat();
_endColor.a = dictionary["finishColorAlpha"].asFloat(); //颜色方差
_endColorVar.r = dictionary["finishColorVarianceRed"].asFloat();
_endColorVar.g = dictionary["finishColorVarianceGreen"].asFloat();
_endColorVar.b = dictionary["finishColorVarianceBlue"].asFloat();
_endColorVar.a = dictionary["finishColorVarianceAlpha"].asFloat(); // particle size
// 粒子 开始和结束 的 大小和方差
_startSize = dictionary["startParticleSize"].asFloat();
_startSizeVar = dictionary["startParticleSizeVariance"].asFloat();
_endSize = dictionary["finishParticleSize"].asFloat();
_endSizeVar = dictionary["finishParticleSizeVariance"].asFloat(); // position
float x = dictionary["sourcePositionx"].asFloat();
float y = dictionary["sourcePositiony"].asFloat();
if(!_sourcePositionCompatible) {
this->setSourcePosition(Vec2(x, y));
}
else {
this->setPosition(Vec2(x, y));
}
_posVar.x = dictionary["sourcePositionVariancex"].asFloat();
_posVar.y = dictionary["sourcePositionVariancey"].asFloat(); // Spinning 旋转
_startSpin = dictionary["rotationStart"].asFloat();
_startSpinVar = dictionary["rotationStartVariance"].asFloat();
_endSpin= dictionary["rotationEnd"].asFloat();
_endSpinVar= dictionary["rotationEndVariance"].asFloat(); _emitterMode = (Mode) dictionary["emitterType"].asInt(); // Mode A: Gravity + tangential accel + radial accel
// 模式A是重力模式
if (_emitterMode == Mode::GRAVITY)
{
// gravity 重力方向
modeA.gravity.x = dictionary["gravityx"].asFloat();
modeA.gravity.y = dictionary["gravityy"].asFloat(); // speed 重力速度
modeA.speed = dictionary["speed"].asFloat();
modeA.speedVar = dictionary["speedVariance"].asFloat(); // radial acceleration 径向加速度
modeA.radialAccel = dictionary["radialAcceleration"].asFloat();
modeA.radialAccelVar = dictionary["radialAccelVariance"].asFloat(); // tangential acceleration 切向加速度
modeA.tangentialAccel = dictionary["tangentialAcceleration"].asFloat();
modeA.tangentialAccelVar = dictionary["tangentialAccelVariance"].asFloat(); // rotation is dir 旋转方向
modeA.rotationIsDir = dictionary["rotationIsDir"].asBool();
} // or Mode B: radius movement 半径运动
// 模式B是半径模式
else if (_emitterMode == Mode::RADIUS)
{
if (!_configName.empty())
{
modeB.startRadius = dictionary["maxRadius"].asInt();
}
else
{
modeB.startRadius = dictionary["maxRadius"].asFloat();
}
modeB.startRadiusVar = dictionary["maxRadiusVariance"].asFloat();
if (!_configName.empty())
{
modeB.endRadius = dictionary["minRadius"].asInt();
}
else
{
modeB.endRadius = dictionary["minRadius"].asFloat();
} if (dictionary.find("minRadiusVariance") != dictionary.end())
{
modeB.endRadiusVar = dictionary["minRadiusVariance"].asFloat();
}
else
{
modeB.endRadiusVar = 0.0f;
} if (!_configName.empty())
{
modeB.rotatePerSecond = dictionary["rotatePerSecond"].asInt();
}
else
{
modeB.rotatePerSecond = dictionary["rotatePerSecond"].asFloat();
}
modeB.rotatePerSecondVar = dictionary["rotatePerSecondVariance"].asFloat(); } else {
CCASSERT( false, "Invalid emitterType in config file");
CC_BREAK_IF(true);
} // life span 粒子生存时间
_life = dictionary["particleLifespan"].asFloat();
_lifeVar = dictionary["particleLifespanVariance"].asFloat(); // emission Rate 发射率 = 粒子个数 / 粒子生存时间 每秒钟发射多少粒子
_emissionRate = _totalParticles / _life; //don't get the internal texture if a batchNode is used
//如果使用batchNode,不要获取内部纹理?
if (!_batchNode)
{
// Set a compatible default for the alpha transfer
_opacityModifyRGB = false; // texture
// Try to get the texture from the cache
// 尝试获取文件中提供的纹理文件名
std::string textureName = dictionary["textureFileName"].asString(); // 对纹理文件名从后往前找'/' ,正常来说找不到,文件名没有'/' 如:"fire.png"
size_t rPos = textureName.rfind('/'); //如果找到了 (找不到的... 所以不会进去,可以在下面设断点试一下
if (rPos != string::npos)
{
//截取包括'/'的纹理文件夹地址
string textureDir = textureName.substr(0, rPos + 1); if (!dirname.empty() && textureDir != dirname)
{
textureName = textureName.substr(rPos+1); //获取纹理名称
textureName = dirname + textureName; //拼接完整文件路径
}
}
//一般会进这个判断
else if (!dirname.empty() && !textureName.empty())
{
textureName = dirname + textureName; //获取纹理的路径+文件名
} Texture2D *tex = nullptr; if (!textureName.empty())
{
// set not pop-up message box when load image failed
// 设置加载图像失败时不弹出消息框
bool notify = FileUtils::getInstance()->isPopupNotify();
FileUtils::getInstance()->setPopupNotify(false); //通过纹理名获取纹理
tex = Director::getInstance()->getTextureCache()->addImage(textureName);
// reset the value of UIImage notify
FileUtils::getInstance()->setPopupNotify(notify);
} //如果找到了纹理,就通过找到的纹理设置粒子 //这个判断进不来
if (tex)
{
setTexture(tex);
}
//在plist的最后有一大串字符,那就是纹理数据了
else if( dictionary.find("textureImageData") != dictionary.end() )
{
std::string textureData = dictionary.at("textureImageData").asString();
CCASSERT(!textureData.empty(), "textureData can't be empty!"); auto dataLen = textureData.size();
if (dataLen != 0)
{
// if it fails, try to get it from the base64-gzipped data
int decodeLen = base64Decode((unsigned char*)textureData.c_str(), (unsigned int)dataLen, &buffer);
CCASSERT( buffer != nullptr, "CCParticleSystem: error decoding textureImageData");
CC_BREAK_IF(!buffer); ssize_t deflatedLen = ZipUtils::inflateMemory(buffer, decodeLen, &deflated);
CCASSERT( deflated != nullptr, "CCParticleSystem: error ungzipping textureImageData");
CC_BREAK_IF(!deflated); // For android, we should retain it in VolatileTexture::addImage which invoked in Director::getInstance()->getTextureCache()->addUIImage()
image = new (std::nothrow) Image();
//使用文件中的图片数据初始化image
bool isOK = image->initWithImageData(deflated, deflatedLen);
CCASSERT(isOK, "CCParticleSystem: error init image with Data");
CC_BREAK_IF(!isOK); //初始化失败就跳出if //使用获取的image初始化纹理
setTexture(Director::getInstance()->getTextureCache()->addImage(image, _plistFile + textureName)); //初始化纹理后可以释放image
image->release();
}
} _yCoordFlipped = dictionary.find("yCoordFlipped") == dictionary.end() ? 1 : dictionary.at("yCoordFlipped").asInt(); if( !this->_texture)
CCLOGWARN("cocos2d: Warning: ParticleSystemQuad system without a texture");
}
ret = true;
}
} while (0);
free(buffer);
free(deflated);
return ret;
}
//停止粒子系统
void ParticleSystem::stopSystem()
{
_isActive = false; //不活动
_elapsed = _duration; //直接把度过的时间设置为最终要执行的时间,间接停止了
_emitCounter = 0; //每秒发射粒子设为0
}
//每帧刷新
void ParticleSystem::update(float dt)
{
CC_PROFILER_START_CATEGORY(kProfilerCategoryParticles , "CCParticleSystem - update"); //是活动的、有发射率的
if (_isActive && _emissionRate)
{
//发射速率的倒数
float rate = 1.0f / _emissionRate; //__totalParticleCountFactor影响粒子总数,可以设置,默认是1.0
int totalParticles = static_cast<int>(_totalParticles * __totalParticleCountFactor); //issue #1201, prevent bursts of particles, due to too high emitCounter
// 判断粒子个数是否小于设定的个数
if (_particleCount < totalParticles)
{
_emitCounter += dt; //用于下面计算粒子个数
if (_emitCounter < 0.f) //这里的判断可能是emitCounter是否已经超过了float的最大值
_emitCounter = 0.f;
} //这里的emitCount可能会获得两种值
//1、粒子的发射速率 < 设定的还未发射的粒子数,emitCount=粒子发射率*度过的时间
//2、粒子的发射速率 > 设定的还未发射的粒子数,emitCount=设定的还未发射的粒子数
//这里确保了粒子的最低发射数量,如果还未发射的粒子数小一些,那么发射器是有能力发射这么多的,
//但是现在不用发射率的那么多粒子,只需要把还未发射的粒子补齐就可以了。
//如果发射速率小一些,那就需要全力的发射,保障粒子发射。
int emitCount = MIN(totalParticles - _particleCount, _emitCounter / rate);
addParticles(emitCount);
_emitCounter -= rate * emitCount; //去掉已经发射的 _elapsed += dt;//记录度过的时间
if (_elapsed < 0.f)
_elapsed = 0.f;
if (_duration != DURATION_INFINITY && _duration < _elapsed)
{
this->stopSystem();
}
} {
//更新每个粒子存在的时间
for (int i = 0; i < _particleCount; ++i)
{
_particleData.timeToLive[i] -= dt;
} //更新存活粒子数量
for (int i = 0; i < _particleCount; ++i)
{
if (_particleData.timeToLive[i] <= 0.0f)
{
int j = _particleCount - 1;
while (j > 0 && _particleData.timeToLive[j] <= 0)
{
_particleCount--;
j--;
}
_particleData.copyParticle(i, _particleCount - 1);
if (_batchNode)
{
//disable the switched particle
int currentIndex = _particleData.atlasIndex[i];
_batchNode->disableParticle(_atlasIndex + currentIndex);
//switch indexes
_particleData.atlasIndex[_particleCount - 1] = currentIndex;
}
--_particleCount;
if( _particleCount == 0 && _isAutoRemoveOnFinish )
{
this->unscheduleUpdate();
_parent->removeChild(this, true);
return;
}
}
} //按照不同的模式,根据时间刷新参数
if (_emitterMode == Mode::GRAVITY)
{
for (int i = 0 ; i < _particleCount; ++i)
{
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);
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; // this is cocos2d-x v3.0
// if (_configName.length()>0 && _yCoordFlipped != -1) // this is cocos2d-x v3.0
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;
}
}
else
{
//Why use so many for-loop separately instead of putting them together?
//When the processor needs to read from or write to a location in memory,
//it first checks whether a copy of that data is in the cache.
//And every property's memory of the particle system is continuous,
//for the purpose of improving cache hit rate, we should process only one property in one for-loop AFAP.
//It was proved to be effective especially for low-end machine.
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;
}
} //color r,g,b,a
//刷新颜色
for (int i = 0 ; i < _particleCount; ++i)
{
_particleData.colorR[i] += _particleData.deltaColorR[i] * dt;
} for (int i = 0 ; i < _particleCount; ++i)
{
_particleData.colorG[i] += _particleData.deltaColorG[i] * dt;
} for (int i = 0 ; i < _particleCount; ++i)
{
_particleData.colorB[i] += _particleData.deltaColorB[i] * dt;
} for (int i = 0 ; i < _particleCount; ++i)
{
_particleData.colorA[i] += _particleData.deltaColorA[i] * dt;
}
//size
//刷新大小
for (int i = 0 ; i < _particleCount; ++i)
{
_particleData.size[i] += (_particleData.deltaSize[i] * dt);
_particleData.size[i] = MAX(0, _particleData.size[i]);
}
//angle
//刷新角度
for (int i = 0 ; i < _particleCount; ++i)
{
_particleData.rotation[i] += _particleData.deltaRotation[i] * dt;
} //刷新粒子矩阵
updateParticleQuads();
//刷新完成,脏标记设为false
_transformSystemDirty = false;
} // only update gl buffer when visible
// 刷新GL缓冲区
if (_visible && ! _batchNode)
{
postStep();
} CC_PROFILER_STOP_CATEGORY(kProfilerCategoryParticles , "CCParticleSystem - update");
}

从零开始のcocos2dx生活(七)ParticleSystem的更多相关文章

  1. 从零开始のcocos2dx生活(二)Node

    节点 Node 文章目录 节点 Node 前言 变量初始化 创建一个节点对象 获取节点依赖的计数器 获取节点的描述(获取节点的Tag) 节点的局部层顺序值(LocalZOrder) 设置节点的Loca ...

  2. 从零开始のcocos2dx生活(十一)TableView

    目录 简述 主要变量 主要方法 setVerticalFillOrder reloadData cellAtIndex updateCellAtIndex insertCellAtIndex remo ...

  3. 从零开始のcocos2dx生活(十)ScrollView

    目录 简介 基础变量 ScrollViewDelegate Direction _dragging _container _touchMoved _bounceable _touchLength 方法 ...

  4. 从零开始のcocos2dx生活(九)CCBReader

    NodeLoaderLibrary是用来存储节点加载器类型的类,通过registerDefaultNodeLoaders()可以注册所有默认类型的加载器 在CocosBuilder的使用手册中: 1. ...

  5. 从零开始のcocos2dx生活(八)ParticleSystemQuad

    https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/#_1 写的真的非常好-最近没时间拜读,只看 ...

  6. 从零开始のcocos2dx生活(六)EventDispatcher

    EventDispatcher可能是所有的里面比较不容易理解也不容易看的 我说自己的理解可能会误导到你们-[索了你们看不下去>< 我写了几乎所有的代码的注释,有的是废话跳过就好 主要的代码 ...

  7. 从零开始のcocos2dx生活(一)内存管理

    cocos中所有的对象都是继承自Ref基类,Ref的职责就是对对象进行引用计数管理 内存管理中最重要的是三个方法retain().release().autorelease() 在cocos中创建对象 ...

  8. 从零开始のcocos2dx生活(五)ActionEase

    文章目录 sineEaseIn sineEaseOut sineEaseInOut expoEaseIn expoEaseOut expoEaseInOut easeIn easeOut easeIn ...

  9. 从零开始のcocos2dx生活(四)ActionManager

    文章目录 初始化构造函数 析构函数 删除哈希元素 分配存放动作对象的空间 通过索引移除动作 暂停动作 恢复动作 暂停所有的动作 恢复所有的动作 添加动作 移除所有的动作 移除target中的所有动作 ...

随机推荐

  1. git错误——Eclipse git commit错误;Committing changes has encountered a problem An Internal error occured

    背景 在使用eclipse时,使用git commit 提交代码时,出项如下错误 解决方法 在工程目录下找到 .git 文件夹 ,找到里面的 index.lock 文件,然后删掉这个文件就可以了,如下 ...

  2. 【iOS知识学习】_int、NSInteger、NSUInteger、NSNumber的差别和联系

    版权声明:本文为博主原创文章.未经博主同意不得转载. https://blog.csdn.net/weasleyqi/article/details/33396809 1.首先先了解下NSNumber ...

  3. 2019-8-31-dotnet-通过-WMI-拿到显卡信息

    title author date CreateTime categories dotnet 通过 WMI 拿到显卡信息 lindexi 2019-08-31 16:55:58 +0800 2019- ...

  4. Data Flow-File Read-详细过程

  5. UITableView 刷新问题

    遇到的问题: 在程序里异步请求服务器后回调函数中处理数据和界面数据的刷新,但是更新UITableView的时候总是很慢才更新完,打印TableView的代理方法也都很快打印. 解决办法就是: [sel ...

  6. day2_python之数据类型常用操作方法

    一.什么可变数据类型和不可变数据类型 可变数据类型:value值改变,id值不变:不可变数据类型:value值改变,id值也随之改变. 如何确定一种数据类型是可变的还是不可变的: 根据可变数据类型与不 ...

  7. Remove Extra One 权值线段树

    最近疯狂练习线段树... 这道题题意很简单,在1-n中,找寻一个数,使得去掉这个数后,对于每个位置中满足 1<=j<i && a[ j ]<a[ i ] 的位置尽可能 ...

  8. g++ 编译单个文件和多个文件

    转载:https://www.cnblogs.com/battlescars/p/cpp_linux_gcc.html 1.单个源文件生成可执行程序 下面是一个保存在文件 helloworld.cpp ...

  9. SuperSocket根据条件获取 Session

    如果你有一个自定义的属性 "CompanyId" 在你的 AppSession 类之中,如果你想要获取这个属性等于某值的 的所有 Session, 你可以使用 AppServer ...

  10. HTML静态网页---标签

    一. 创建HTML: (一) body的属性: bgcolor 页面背景色 background   背景壁纸.图片 text   文字颜色 topmargin   上边距 leftmargin    ...