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();
__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, , _totalParticles * sizeof(V3F_C4B_T2F_Quad));
memset(_indices, , _totalParticles * * 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/ ,如果您在非本网址看到此文章,说明网站是爬虫采集抄袭而成。

‎Cocos2d-x 学习笔记(24) ParticleSystem ParticleSystemQuad的更多相关文章

  1. [原创]java WEB学习笔记24:MVC案例完整实践(part 5)---删除操作的设计与实现

    本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...

  2. Linux下汇编语言学习笔记24 ---

    这是17年暑假学习Linux汇编语言的笔记记录,参考书目为清华大学出版社 Jeff Duntemann著 梁晓辉译<汇编语言基于Linux环境>的书,喜欢看原版书的同学可以看<Ass ...

  3. Ext.Net学习笔记24:在ASP.NET MVC中使用Ext.Net

    在前面的笔记中已经介绍了如何在ASP.NET WebForm中使用Ext.Net,由于这个系列一直在WebForm中使用,所以并没有涉及到ASP.NET MVC中的用法. 如果你要在ASP.NET M ...

  4. android学习笔记24——事件处理

    事件处理 android提供了两种事件处理机制: 1.基于回调的事件处理 2.基于监听器的事件处理(通过绑定特定事件监听器) 注意: android对于基于回调的事件处理而言,主要做法就是重写andr ...

  5. C++学习笔记24,方法重写与方法隐藏

    该博文仅用于交流学习.请慎用于不论什么商业用途.本博主保留对该博文的一切权利. 博主博客:http://blog.csdn.net/qq844352155 转载请注明出处: 方法重写.是指在子类中又一 ...

  6. ArcGIS API for JavaScript 4.2学习笔记[24] 【IdentifyTask类】的使用(结合IdentifyParameters类)(第七章完结)

    好吧,我都要吐了. 接连三个例子都是类似的套路,使用某个查询参数类的实例,结合对应的Task类,对返回值进行取值.显示. 这个例子是Identify识别,使用了TileLayer这种图层,数据来自Se ...

  7. Kali学习笔记24:Nikto、Skipfish

    文章的格式也许不是很好看,也没有什么合理的顺序 完全是想到什么写一些什么,但各个方面都涵盖到了 能耐下心看的朋友欢迎一起学习,大牛和杠精们请绕道 实验环境: Kali机器IP:192.168.163. ...

  8. Java学习笔记24(Map集合)

    Map接口: Map接口与Collection接口无继承关系. 区别:Collection中的元素是孤立的,一个一个存进去的. Map作为一个映射集合,每一个元素包含Key-value对(键-值对). ...

  9. PHP全栈学习笔记24

    PHP in_array() 函数 定义和用法 in_array() 函数搜索数组中是否存在指定的值. type 参数被设置为 TRUE,则搜索区分大小写. 语法 in_array(search,ar ...

随机推荐

  1. 补充Java面试记录

    补充Java面试记录 背景:这两天面试遇到的部分问题都分散在了前面两篇文摘中,这里再做一些其他的记录,以备不时之需! 一.谈谈你对SpringBoot的理解? SpringBoot简介:SpringB ...

  2. containerd与kubernetes集成

    kubernetes集群三步安装 概念介绍 cri (Container runtime interface) cri is a containerd plugin implementation of ...

  3. 《SpringCloud docker》读书笔记

    yml配置意义 当Ribbon和Eureka配合使用时,会自动将虚拟主机名映射成微服务的网络地址. yml中info可以展示一些信息 server: port: 8000 # 指定端口 spring: ...

  4. ZDog:简单便捷好玩的的3D设计和动画制作库

    各位老铁,我灰太狼又又又回来了,嘿嘿!!!!最近在忙所以有日子没写博客了,今天带大家看个好玩的东西 这个东西是今天偶尔看到的,是啥呢,难道是漂亮的小姐姐吗?当然是......不可能的了,这个东西其实就 ...

  5. [转载]使用Java操作Mongodb

    HelloWorld程序 学习任何程序的第一步,都是编写HelloWorld程序,我们也不例外,看下如何通过Java编写一个HelloWorld的程序. 首先,要通过Java操作Mongodb,必须先 ...

  6. Spring aop注解失效

    问题 在spring 中使用 @Transactional . @Cacheable 或 自定义 AOP 注解时,对象内部方法中调用该对象的其他使用aop机制的方法会失效. @Transactiona ...

  7. 【KakaJSON手册】04_JSON转Model_04_值过滤

    在KakaJSON手册的第2篇文章中提过:由于JSON格式能表达的数据类型是比较有限的,所以服务器返回的JSON数据有时无法自动转换成客户端想要的数据类型 比如客户端想要的是Date类型,服务器返回的 ...

  8. 前端表格数据导出excel

    使用tableExport.js导出bootstrap-table表格成excel并且支持中文 1. 下载tableExport.js https://github.com/hhurz/tableEx ...

  9. .NET Core C#中级篇2-5 常见实用类

    .NETCore CSharp 中级篇2-5 本节内容为常见实用类和方法的使用 String.Format string.format方法是一个字符串格式化类,它里面的一些写法是对字符串进行指定格式的 ...

  10. Python|队列Queue

    一 前言 本文算是一次队列的学习笔记,Queue 模块实现了三种类型的队列,它们的区别仅仅是队列中元素被取回的顺序.在 FIFO 队列中,先添加的任务先取回.在 LIFO 队列中,最近被添加的元素先取 ...