精华内容
下载资源
问答
  • route add -net x.x.x.x netmask x.x.x.x dev eht0

    千次阅读 2016-11-28 10:19:42
    在Linux系统,设置路由通常是 为了解决以下问题:该Linux系统在一个局域网,局域网有一个网关,能够让机器访问Internet,那么就需要将这台机器的IP地址设置为 Linux机器的默认路由。要注意的是,直接在命令行...

    route命令用于显示和操作IP路由表。要实现两个不同的子网之间的通信,需要一台连接两个网络的路由器,或者同时位于两个网络的网关来实现。在Linux系统中,设置路由通常是 为了解决以下问题:该Linux系统在一个局域网中,局域网中有一个网关,能够让机器访问Internet,那么就需要将这台机器的IP地址设置为 Linux机器的默认路由。要注意的是,直接在命令行下执行route命令来添加路由,不会永久保存,当网卡重启或者机器重启之后,该路由就失效了;要想永久保存,有如下方法:


    1.在/etc/rc.local里添加
    2.在/etc/sysconfig/network里添加到末尾
    3./etc/sysconfig/static-router :
    any net x.x.x.x/24 gw y.y.y.y


    格式:route 
    格式:/sbin/route 
    用于打印路由表(display the current routing table)。

    在非root用户使用时需要使用完整路径执行route命令。


    命令参数

    [root@linux ~]# route [-nee]
    [root@linux ~]# route add [-net|-host] [网域或主机] netmask [mask] [gw|dev]
    [root@linux ~]# route del [-net|-host] [网域或主机] netmask [mask] [gw|dev]
    观察的参数:
       -n  :不要使用通讯协定或主机名称,直接使用 IP 或 port number
       -ee :使用更详细的资讯来显示
    增加 (add) 与删除 (del) 路由的相关参数:
       -net    :表示后面接的路由为一个网域;
       -host   :表示后面接的为连接到单部主机的路由;
       netmask :与网域有关,可以设定 netmask 决定网域的大小;
       gw      gateway 的简写,后续接的是 IP 的数值喔,与 dev 不同;
       dev     :如果只是要指定由那一块网路卡连线出去,则使用这个设定,后面接 eth0 


    格式:route -n
    格式:/sbin/route -n
    用于打印路由表,加上-n参数就是在输出的信息中不打印主机名而直接打印ip地址。

    格式:route add default gw {IP-ADDRESS} {INTERFACE-NAME}
    用于设置默认路由,其中,
    参数{IP-ADDRESS): 用于指定路由器(网关)的IP地址;
    参数{INTERFACE-NAME}: 用于指定接口名称,如eth0。使用/sbin/ifconfig -a可以显示所有接口信息。

    例:route add default gw mango

    格式:route add -net {NETWORK-ADDRESS} netmask {NETMASK} dev {INTERFACE-NAME}
    添加到指定网络的路由规则,其中
    参数{NETWORK-ADDRESS}: 用于指定网络地址
    参数{NETMASK}: 用于指定子网掩码
    参数{INTERFACE-NAME}: 用于指定接口名称,如eth0。

    例1:route add -net 192.56.76.0 netmask 255.255.255.0 dev eth0
    例2:route add -net 224.0.0.0 netmask 240.0.0.0 dev eth0

    格式:route add -net {NETWORK-ADDRESS} netmask {NETMASK} reject
    设置到指定网络为不可达,避免在连接到这个网络的地址时程序过长时间的等待,直接就知道该网络不可达。

    例:route add -net 10.0.0.0 netmask 255.0.0.0 reject

    格式:route del -net {NETWORK-ADDRESS} netmask {NETMASK} dev {INTERFACE-NAME}
    格式:route del -net {NETWORK-ADDRESS} netmask {NETMASK} reject
    用于删除路由设置。参数指定的方式与route add相似。


    输出详解

    route命令输出的路由表字段含义如下:
        Destination 目标
              The destination network or destination host. 目标网络或目标主机。

        Gateway 网关
              The gateway address or '*' if none set. 网关地址,如果没有就显示星号。

        Genmask 网络掩码
              The  netmask  for  the  destination net; '255.255.255.255' for a
              host destination and '0.0.0.0' for the default route.

         Flags:总共有多个旗标,代表的意义如下:                        

             o U (route is up):该路由是启动的;                       

             o H (target is a host):目标是一部主机 (IP) 而非网域;                       

             o G (use gateway):需要透过外部的主机 (gateway) 来转递封包;                       

             o R (reinstate route for dynamic routing):使用动态路由时,恢复路由资讯的旗标;                       

             o D (dynamically installed by daemon or redirect):已经由服务或转 port 功能设定为动态路由                       

             o M (modified from routing daemon or redirect):路由已经被修改了;                       

             o !  (reject route):这个路由将不会被接受(用来抵挡不安全的网域!)

             o A (installed by addrconf)

     

             o C (cache entry)

        Metric 距离、跳数。暂无用。
              The 'distance' to the target (usually counted in  hops).  It  is
              not  used  by  recent kernels, but may be needed by routing dae-
              mons.

        Ref   不用管,恒为0。
              Number of references to this route. (Not used in the Linux  ker-nel.)

        Use    该路由被使用的次数,可以粗略估计通向指定网络地址的网络流量。
              Count  of lookups for the route.  Depending on the use of -F and
              -C this will be either route cache misses (-F) or hits (-C).

        Iface 接口,即eth0,eth0等网络接口名
              Interface to which packets for this route will be sent.



    范例一

    单纯的观察路由状态


    [root@linux ~]# route -n
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    192.168.10.0    0.0.0.0         255.255.255.0   U     0      0        0 eth0
    169.254.0.0     0.0.0.0         255.255.0.0     U     0      0        0 eth0
    0.0.0.0         192.168.10.30   0.0.0.0         UG    0      0        0 eth0
    [root@linux ~]# route
    Kernel IP routing table
    Destination     Gateway         Genmask         Flags Metric Ref    Use Iface
    192.168.10.0    *               255.255.255.0   U     0      0        0 eth0
    169.254.0.0     *               255.255.0.0     U     0      0        0 eth0
    default         server.cluster     0.0.0.0         UG    0      0        0 eth0

     

    此外,观察一下上面的路由排列顺序喔,依序是由小网域(192.168.10.0/24 是 Class C),逐渐到大网域(169.254.0.0/16 Class B) 最后则是预设路由 (0.0.0.0/0.0.0.0)。然后当我们要判断某个网路封包应该如何传送的时候,该封包会经由这个路由的过程来判断喔!举例来说,我上头仅有三个路由,若我有一个传往 192.168.10.20 的封包要传递,那首先会找 192.168.10.0/24 这个网域的路由,找到了!所以直接由 eth0 传送出去;如果是传送到 Yahoo 的主机呢? Yahoo 的主机 IP 是 202.43.195.52,我通过判断 

    1)不是 192.168.10.0/24
          2)不是 169.254.0.0/16 结果到达 

    3)0/0  时, OK !传出去了,透过  eth0  将封包传给  192.168.10.30 那部  gateway  主机啊!所以说,路由是有顺序的。因此当你重复设定多个同样的路由时,例如在你的主机上的两张网路卡设定为相同网域的  IP 时,会出现什么情况?会出现如下的情况: 
    Kernel  IP  routing  table 
    Destination          Gateway                  Genmask                  Flags  Metric  Ref        Use  Iface 
    192.168.10.0        0.0.0.0                  255.255.255.0      U          0            0                0  eth0 
    192.168.10.0        0.0.0.0                  255.255.255.0      U          0            0                0  eth1 
    也就是说,由于路由是依照顺序来排列与传送的,所以不论封包是由那个介面  (eth0, eth1)  所接收,都会由上述的  eth0  传送出去,所以,在一部主机上面设定两个相同网域的  IP  本身没有什么意义!有点多此一举就是了。除非是类似虚拟主机  (Xen, VMware  等软体  所架设的多主机时,才会有这个必要~


    范例二

    [root@linux ~]# route del -net 169.254.0.0 netmask 255.255.0.0 dev eth0
    上面这个动作可以删除掉 169.254.0.0/16 这个网域!
    请注意,在删除的时候,需要将路由表上面出现的资讯都写入
    包括  netmask , dev 等等参数喔!注意注意
    [root@linux ~]# route add -net 192.168.100.0 netmask 255.255.255.0 dev eth0
    透过 route add 来增加一个路由!请注意,这个路由必须要能够与你互通。


    举例来说,如果我下达底下的指令就会显示错误:
     route add -net 192.168.200.0 netmask 255.255.255.0 gw 192.168.200.254
    因为我的环境内仅有 192.168.10.100 这个 IP ,所以不能与 192.168.200.254这个网段直接使用 MAC 互通!这样说,可以理解喔!?
    [root@linux ~]# route add default gw 192.168.10.30
    增加预设路由的方法!请注意,只要有一个预设路由就够了喔!
    在这个地方如果您随便设定后,记得使用底下的指令重新设定你的网路
      /etc/init.d/network restart
         如果是要进行路由的删除与增加,那就得要参考上面的例子了,其实,使用 man route 里面的资料就很丰富了!仔细查阅一下囉!你只要记得,当出现『SIOCADDRT: Network is unreachable』这个错误时,肯定是由于 gw 后面接的 IP 无法直接与您的网域沟通 (Gateway 并不在你的网域内)



    WINDOWS下的route命令

     

    简单的的操作如下,

    查看路由状态:routeprint

    只查看ipv4(ipv6)路由状态:route print-4(-6)

    添加路由:route add 目的网络 mask 子网掩码 网关——重启机器或网卡失效

    route add 192.168.20.0 mask 255.255.255.0192.168.10.1

    添加永久:route -p add 目的网络 mask子网掩码网关

    route -p add 192.168.20.0 mask 255.255.255.0192.168.10.1

    删除路由:route delete 目的网络 mask 子网掩码

    route delete 192.168.20.0 mask255.255.255.0


    展开全文
  • 用HBuilder X编辑器打开的网页出现中文乱码一、问题描述二、尝试解决1.修改文件打开的指定编码方式(文件——以指定编码重新打开)2.网上有说在头部加上标签meta和属性 charset="utf-8"的,也还是不行。三、问题解决...



    一、问题描述

    编辑区域代码显示正常,但是自带的web浏览器显示中文乱码。
    此时看到右下角显示的是GB18030编码方式。



    二、尝试解决

    1.修改文件打开的指定编码方式(文件——以指定编码重新打开)

    换了好几个编码方式,web浏览器一直显示中文乱码,换成utf-8后,甚至连编辑区代码也乱码了;再切换编码方式最后会搞得恢复不了,好在能撤销。


    在这里插入图片描述

    2.网上有说在头部加上标签meta和属性 charset="utf-8"的,也还是不行。
    	<head>
    		<meta charset="utf-8"/>
    	</head>
    



    三、问题解决

    总体上就是上面两个办法结合一下,再注意下小细节。

    1.首先,在头部里加上代码< meta charset=“utf-8”/>,保存,如上图。

    2.然后,修改编辑器的编码方式(文件——以指定编码重新打开),如上上图。

    补充:我安装的是新版HBuilder X 2.4.2.20191115,编码方式的修改就这个办法,有些文档写着打开工具——选项,但新版没有这一项了,另外工具——设置上也是不能修改编码方式的。

    3.正如上面所说,会发现编辑区的代码也乱码了,别急,ctrl+z撤销一下就行了。

    怎么确定撤销到哪一步呢?我这里是一次就行了。你看到代码中< meta charset=“utf-8”/>存在右下角显示的编码方式也是utf-8,同时编码区域不出现乱码保存一次。可能此时右边的web浏览器显示的中文还是乱码,刷新一下就行 。


    四、原因分析及问题规避

    原因1:一开始写网页代码的时候没有设定好编码方式,并且用的是记事本写的话,默认的代码就是ANSI,直接用浏览器打开没有问题,但是用编辑器打开,编辑器就会按照它内部认为可行的编码方式打开。同时,一旦出现中文乱码了,还继续用会中文乱码的编码方式一直打开就会破坏数据,最后可能恢复不了。

    原因2:(叨叨:尽管最后的解决和搜索出来的答案很接近了,但没有最后的撤销,我想这又得卡住了。)
    主要是思考问题的时候太片面了,没有全局观,没有抓住解决问题的关键。中文乱码了说明编码有问题,那么解决的关键就是设定好编码方式,这是必须的。至于编辑器还是乱码,说明它还不懂事,那就让它懂,设置它或者关了再打开,总体上是要朝着相同的编码方式走的。两部基础的都设置好了,最后就等效果了。这也是上面三个加亮部分的内容。

    问题规避:
    (内容上)写网页就加上编码方式吧。
    (思想上)解决问题思路:
    ——什么问题?涉及哪个知识点?问题出现在哪里(可用排除法)?
    ——能做的都做了还是出错?那考虑一下背后运行机制,它究竟是怎么出错的?(知己知彼,百战百胜)

    同样的,其他中文乱码的问题也可这么解决。

    展开全文
  • Cocos2d-x中3D相机的使用

    千次阅读 2014-12-12 13:38:10
    本教程是讲Cocos2d-x中相机的使用,在Cocos2d-x 3.2版本加入了相机这个类,这个类在3D游戏是必不可少的,因为3D游戏是一个立体的世界,在3D游戏的摄像机就相当于是我们的眼睛,通过它我们可以观察整个的游戏...

    转载自:http://cn.cocos2d-x.org/tutorial/show?id=2019

    本教程是讲Cocos2d-x中相机的使用,在Cocos2d-x 3.2版本中加入了相机这个类,这个类在3D游戏中是必不可少的,因为3D游戏是一个立体的世界,在3D游戏中的摄像机就相当于是我们的眼睛,通过它我们可以观察整个的游戏世界。相机相当于人的眼睛,人在用眼睛看物体的时候,可以从理论上的任意位置,以任意的角度观察物体,所以物体的形状会受到相机的位置、旋转角度的影响而影响。

     

    1.jpg

    上图就是在3D游戏中使用相机的原理,当然这是一个透视投影的相机,我们在游戏中的相机就是根据图中的原理实现的。在游戏中一般有两种类型的相机:一种就是上图中介绍的透视相机,它在3D游戏中是很常见的。还有一种是正交投影相机,它没有像透视投影一样的近大远小的效果而是在相机内任何位置的物体的大小都是一样的,通常这种类型的相机都用在角色属性面板中显示的角色模型。说完了相机的两种类型,下面我们来看看在Cocos2d-x中我们是如何来创建相机的:

    01.jpg

    上面的代码就是创建了一个透视投影的相机,下面我来说明下参数的意义:第一个参数是FOV,即视场角(field of view),它可以理解为你的视线左右能看多宽(以角度计)第二个就是上述所有的宽高比,最后两个是相机的近裁面和远裁面,这个也很好理解,距离相机比近裁面还要近的,比远裁面还要远的,都不会被渲染到。

     

    2.jpg

    上图是正交相机的原理。

    02.jpg

    上面的代码就是创建了一个正交投影的相机,下面我来说明下参数的意义:第一个参数是相机的宽度,第二个就是相机的高度,最后两个是相机的近裁面和远裁面,这个也很好理解,距离相机比近裁面还要近的,比远裁面还要远的,都不会被渲染到。这个和透视相机是一样的。接下来,我们需要对相机设置一个标记位(FLAG),这样可以让相机与其他的相机区分开来--在一些游戏的应用中,通常不仅仅只有一个相机,如果有多个相机的话,那么我们要标记一个物体,到底是要被哪一个相机所"看到",这时候,我们就需要设置它的CameraMask来与相机的Flag对应:

    03.jpg

    04.jpg

    如果同时存在多个相机,怎么标记某个物体被那些相机看到呢?


    注意到Camera中有个_cameraFlag属性,为枚举类型,定义如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    enum class CameraFlag
    {
        DEFAULT = 1,
        USER1 = 1 << 1,
        USER2 = 1 << 2,
        USER3 = 1 << 3,
        USER4 = 1 << 4,
        USER5 = 1 << 5,
        USER6 = 1 << 6,
        USER7 = 1 << 7,
        USER8 = 1 << 8,
    };

    Node中有个_cameraMask的属性,当相机的_cameraFlag & _cameraMask为true时,该Node可以被该相机看到。所以在上述相机的创建代码中,camera的CameraFlag设置为CameraFlag::USER1,并且该layer的CameraMask为2,则表示该layer只能被CameraFlag::USER1相机看到。如果你设置的精灵的cameraMask是3的话,它也是能被cameraFlag为CameraFlag::USER1和CameraFlag::USER2的相机看到的。我们还要注意如果你的精灵是在layer设置cameraMask之后添加的,它是不会被看到的,还需要手动再设置精灵的cameraMask。不要以为这样就可以了,最后我们还要把相机添加到场景中,不然我们还是看不到效果的,一定要记住呀,下图就是把相机加到场景中的代码:

    3.jpg

    这样一来我们就算是创建好相机啦,这只还是第一部哟,下面我来说下相机在游戏中是如何使用的。


    一般来说在3D游戏中相机有三种使用方式:

    • 第一种就是自由相机

    • 第二种是第一人称相机

    • 第三种是第三人称相机


    下面我们先来说第一种自由相机,这种类型的相机一般都多用在即时战略类型的游戏中,比如魔兽争霸3用的就是自由相机,这类相机可以随着鼠标的移动而移动,能够看到游戏场景中的任何位置,下图就是自由相机在Cocos2d-x中的使用方法:

    1418094963609635.jpg

    怎么样是不是很简单,我们其实只要在移动函数中把在屏幕移动的距离赋值给相机,让相机跟随移动就可以了,需要注意的是我们在屏幕上上下移动相对于相机是前后移动,所以我们要把在屏幕移动的y值赋给相机的z,而且方向是反的因为我们向下滑屏幕相机是向前移动下上滑相机是向后移动。那么大家会问,我们既然是3D相机那么y轴怎么移动呐?下图就是相机拉近和拉远的实现:

    5.jpg

    6.jpg

    第一张图是拉近,第二张图是拉远。这样我们也可以用Cocos2d-x来制作魔兽争霸3这样的游戏啦。


    接下来我们来看第二种类型的相机,那就是第一人称的相机,这种相机其实很好理解,它就相当于是我们的眼睛看到的东西,这种相机多用在FPS类型的游戏中,像是使命的召唤就是这类相机,那么它能不能在Cocos2d-x中实现呐?答案是肯定的,我们来看下图:

    7.png

    其实就是把相机的位置绑在你在游戏中控制的角色上就可以啦,我们使用sprite3d的移动来代替在屏幕上滑动,也就是说我们在屏幕上滑动不是控制相机而是控制sprite3d,再把sprite3d的移动赋给相机,大家可能会问FPS游戏还可以旋转视角呀?咱们的相机可以实现,下面让我们来看代码:

    8.jpg

    这个是向左旋转。

    9.jpg

    这个是向右旋转。

    通过这样的设置我们就能实现FPS游戏的效果了。


    最后再来介绍下第三人称的相机,这类的相机多应用在MMORPG类型游戏中,这种类型的游戏是我们最最常见的游戏类型,现在大多数的网游都是这样的相机,它其实就是把第一人称的相机的位置设定在我们控制的角色身后一定距离的位置上,以便我们能看清自己操控的游戏角色,在Cocos2d-x中的实现和第一人称相机类似,相同的部分我们就不再说明了就是在得到角色位置赋值给相机后再把相机的位置加上个偏移量就可以了,偏移量我们用offset表示,如下图:

    10.jpg

    其他的像移动和旋转和第一人称相机是一样的,我就不在这里赘述了,这样就可以实现第三人称的相机了。


    好了,今天的教程就先讲到这里,通过这篇教程,我们就可以在Cocos2d-x中创建属于我们自己的相机啦,通过创建相机来制作我们自己的3D游戏,以上讲的三种相机的实现以及使用方法都是非常简单的入门级介绍,可能大家觉得不太完整,没关系大家可以参考CppTest中的Camera Test这个代码,我的教程就是基于它写的,在Camera Test中有详细的代码实现供大家参考,希望大家能过制作出比肩大作的游戏,谢谢!


    本文主要汇总Cocos引擎中文站以及Cocos2d-x论坛中的关于Cocos2d-x 3D相关的教程,帮助大家更快地了解Cocos2d-x中的3D功能。


    看了这么多篇的3D教程,是否对Cocos2d-x的3D功能有了更多更好的了解了呢?也把你的开发经验或者实战项目分享给大家吧!

    http://blog.csdn.net/minsenwu/article/details/17120495

    展开全文
  • [Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier] 红孩儿Cocos2d-X学习园地QQ2群:44208467加群写:...Cocos2d-x2.0 粒子系统深入分析三曲(一) 另:本章所用Coco

    [Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]

    红孩儿Cocos2d-X学习园地QQ2群:44208467加群写:Cocos2d-x
    红孩儿Cocos2d-X学习园地QQ群:249941957加群写:Cocos2d-x

    Cocos2d-x2.0 粒子系统深入分析三部曲(一)

    另:本章所用Cocos2d-x版本为:

    cocos2d-2.0-x-2.0.2@ Aug 30 2012

    http://cn.cocos2d-x.org/download


                 大家好,今天我们来学习一下Cocos2d-x 2.0 的粒子系统,所谓粒子系统,即:“具有相同运动物理特性的一定数量级的有生命周期的个体,通过对其的控制来表现一些特定的现象,如火、爆炸、烟、水流、火花、落叶、云、雾、雪、尘、流星尾迹或者象发光轨迹这样的抽象视觉效果等等”。




     

              在几乎所有的游戏引擎中,都有专属的类来进行生成粒子系统,其中主要有两个功能类:

    (1)。粒子:具有大量属性(如生命,方向,速度,加速度等)的结构体或类。表现为一个粒子个体。

    (2)。粒子发射器:用来进行生成,控制,回收粒子的管理器类。


              在进行粒子系统的创建时,首先把粒子发射器放到某个位置,然后设定要生成的粒子的数量及粒子起始属性(往往设定生命值为随机),然后在开始创建出粒子并不断的更新粒子粒子在更新运动状态的同时生命值会被不断的消耗直至死亡。死亡后的粒子被发射器记录回收,为了保证同一时间内有固定数量的粒子在存活中,发射器会在合适的时间重新初始化并运行回收的粒子

            

             回到Cocos2d-x,打开ParticleTest.h:

    //演示粒子系统的场景
    class ParticleTestScene : public TestScene
    {
    public:
        virtual void runThisTest();
    };
    //由纯色层派生的显示粒子系统的层,做为后面多种形式粒子系统演示所在层的基类。
    class ParticleDemo : public CCLayerColor
    {
    protected:
    	//粒子发射器
        CCParticleSystem*    m_emitter;
    	//背景图精灵
        CCSprite*            m_background;
    
    public:
    	//析构
        ~ParticleDemo(void);
    	//当前层加载时调用的函数。
        virtual void onEnter(void);
    	//取得当前层的标题。
        virtual std::string title();
    	//取得当前层的副标题。
        virtual std::string subtitle();
    	//响应菜单按钮的回调函数。
    	//重新启动当前演示。
        void restartCallback(CCObject* pSender);
    	//启动下一个演示。
        void nextCallback(CCObject* pSender);
    	//启动上一个演示。
        void backCallback(CCObject* pSender);
    	//点击文字选项时切换粒子运动模式的响应函数。
        void toggleCallback(CCObject* pSender);
    	//注册相应的触屏消息处理。
        virtual void registerWithTouchDispatcher();
    	//触屏消息处理
    	//触屏(按下时调用)
        virtual bool ccTouchBegan(CCTouch* touch, CCEvent* event);
    	//触屏(按下并移动时调用)
        virtual void ccTouchMoved(CCTouch* touch, CCEvent* event);
    	//触屏(松开时调用)
        virtual void ccTouchEnded(CCTouch* touch, CCEvent* event);
    	//更新
        virtual void update(float dt);
    	//设置粒子发射器的位置。
        void setEmitterPosition();
    };
    
    //模拟火的粒子系统演示。
    class DemoFirework : public ParticleDemo
    {
    public:
        virtual void onEnter();
        virtual std::string title();
    };
    

    后面是更多的粒子系统演示,不再一一重复列出。

     

    上面定义了一些比较常见的粒子系统的演示,我们来看一下它们是怎么实现的,打开CPP文件:

    //加载当前演示Layer时调用的函数。
    void DemoFirework::onEnter()
    {
    	//先调用基类的相应函数。
        ParticleDemo::onEnter();
    	//创建一个Fireworks类型的粒子发射器。
    m_emitter = CCParticleFireworks::create();
    //对其引用计数器加一。
    m_emitter->retain();
    //将发射器放入背景图精灵。
        m_background->addChild(m_emitter, 10);
    //设置发射器的纹理。    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_stars1) );
        //设置发射器的位置。
        setEmitterPosition();
    }
    //取得当前演示的标题。
    std::string DemoFirework::title()
    {
        return "ParticleFireworks";
    }
    

    后面也是一大堆相似的代码,也不一一列出了。

        重点是:所有的演示都需要用到一个粒子系统对象实例,所有的粒子系统都是由CCParticleSystem派生出来的。我们只需要好好的掌握它和学会派生出更多需要的粒子系统类就可以了。

        在学习粒子系统基类之前,我们要先了解一下CCParticleBatchNode。这个类名称中有“Batch”这个词,说明它与粒子的批次优化有一定关系。在“Cocos2d-x中图字原理之深入分析”一文中,我有讲解过CCSpriteBatchNode这个类。如果您是跟随本博一起学习Cocos2d-x的话,那么会很容易看懂CCParticleBatchNode这个类。

    CCParticleBatchNode是为了将使用相同纹理的粒子并合到一个渲染批次中,可以大大的提升渲染的效率。

    CCParticleBatchNode.h:

    #ifndef __CCPARTICLEBATCHNODE_H__
    #define __CCPARTICLEBATCHNODE_H__
    
    #include "base_nodes/CCNode.h"
    #include "CCProtocols.h"
    //使用Cocos2d命名空间
    NS_CC_BEGIN
    //使用相应的类。
    class CCTexture2D;
    class CCTextureAtlas;
    class CCParticleSystem;
    
    //默认粒子系统中粒子的容器的容量。
    #define kCCParticleDefaultCapacity 500
    //粒子的批次结点。
    class CC_DLL CCParticleBatchNode : public CCNode, public CCTextureProtocol
    {
    public:
    	//构造函数。
    CCParticleBatchNode();
    //析构函数。
        virtual ~CCParticleBatchNode();
    
        //创建粒子系统的批次结点,参数一为对应的纹理,参数二为创建批次结点所能容纳的最大粒子数量。内部调用create实现。
        CC_DEPRECATED_ATTRIBUTE static CCParticleBatchNode* batchNodeWithTexture(CCTexture2D *tex, unsigned int capacity = kCCParticleDefaultCapacity);
    
        //创建粒子系统的批次结点,参数一为图片文件名称,参数二为创建批次结点所能容纳的最大粒子数量。内部调用create实现。
        CC_DEPRECATED_ATTRIBUTE static CCParticleBatchNode* batchNodeWithFile(const char* fileImage, unsigned int capacity = kCCParticleDefaultCapacity);
    
        //第一个创建函数的create实现。
        static CCParticleBatchNode* createWithTexture(CCTexture2D *tex, unsigned int capacity = kCCParticleDefaultCapacity);
    
        //第二个创建函数的create实现。
        static CCParticleBatchNode* create(const char* fileImage, unsigned int capacity = kCCParticleDefaultCapacity);
    
        //初始化粒子系统。
        bool initWithTexture(CCTexture2D *tex, unsigned int capacity);
        bool initWithFile(const char* fileImage, unsigned int capacity);
    
        //将一个粒子系统做为子结点加入批次管理结点。
        virtual void addChild(CCNode * child);
        virtual void addChild(CCNode * child, int zOrder);
        virtual void addChild(CCNode * child, int zOrder, int tag);
    
         //将一个粒子系统做为子结点插入批次管理结点的相应位置。
        void insertChild(CCParticleSystem* pSystem, unsigned int index);
    
        //将一个粒子系统子结点从批次管理结点中移除。
        virtual void removeChild(CCNode* child, bool cleanup);
    	//重新排序粒子系统子结点.
        virtual void reorderChild(CCNode * child, int zOrder);
        //将一个指定索引位置的粒子系统子结点从批次管理结点中移除。
        void removeChildAtIndex(unsigned int index, bool doCleanup);
        //将所有粒子系统子结点从批次管理结点中移除。
        void removeAllChildrenWithCleanup(bool doCleanup);
        //设置某个粒子失效不显示。
        void disableParticle(unsigned int particleIndex);
    	//绘制当前管理的所有粒子系统。
        virtual void draw(void);
        // 返回所用的纹理。
        virtual CCTexture2D* getTexture(void);
        // 设置所用的纹理。
        virtual void setTexture(CCTexture2D *texture);
    	//设置ALPHA混合方案。
        virtual void setBlendFunc(ccBlendFunc blendFunc);
        //取得ALPHA混合方案。
        virtual ccBlendFunc getBlendFunc(void);
    	//结点树遍历当前结点时调用的函数。
        void visit();
    private:
    	//更新所有粒子的矩形顶点缓冲信息块的索引。.
        void updateAllAtlasIndexes();
    	//扩大矩形顶点缓冲信息块的数组的容量。
        void increaseAtlasCapacityTo(unsigned int quantity);
    	//通过Z值取得矩形顶点缓冲信息块的索引。
        unsigned int searchNewPositionInChildrenForZ(int z);
    	//取得子结点在设置Z排序中z值前的位置和设置后的位置
        void getCurrentIndex(unsigned int* oldIndex, unsigned int* newIndex, CCNode* child, int z);
    	//将一个粒子系统做为子结点加入当前批次结点。
        unsigned int addChildHelper(CCParticleSystem* child, int z, int aTag);
    	//更新ALPHA混合方案。
        void updateBlendFunc(void);
        // CCTextureAtlas类是用来对当前粒子的批次结点中所有粒子所使用的顶点缓冲区进行管理的类,它以数组的方式保存了显示所有粒子所需的四边形的顶点结构信息。在“Cocos2d-x中图字原理之深入分析”一文中有详细源码分析。我们可称之为“同纹理的顶点缓冲区管理器”.
        CC_SYNTHESIZE(CCTextureAtlas*, m_pTextureAtlas, TextureAtlas);
    private:
        //ALPHA混合方案。
        ccBlendFunc m_tBlendFunc;
    };
    
    NS_CC_END
    

    对应的CPP:


    #include "CCParticleBatchNode.h"
    #include "textures/CCTextureCache.h"
    #include "textures/CCTextureAtlas.h"
    #include "ccConfig.h"
    #include "ccMacros.h"
    #include "effects/CCGrid.h"
    #include "support/CCPointExtension.h"
    #include "CCParticleSystem.h"
    #include "shaders/CCShaderCache.h"
    #include "shaders/CCGLProgram.h"
    #include "shaders/ccGLStateCache.h"
    #include "support/base64.h"
    #include "support/zip_support/ZipUtils.h"
    #include "platform/CCFileUtils.h"
    #include "kazmath/GL/matrix.h"
    //Cocos2d命名空间
    NS_CC_BEGIN
    //构造
    CCParticleBatchNode::CCParticleBatchNode()
    : m_pTextureAtlas(NULL)
    {
    
    }
    //析构
    CCParticleBatchNode::~CCParticleBatchNode()
    {
    	//释放“同纹理的顶点缓冲区管理器”。
        CC_SAFE_RELEASE(m_pTextureAtlas);
    }
    //创建粒子系统的批次结点,参数一为对应的纹理,参数二为创建批次结点所能容纳的最大粒子数量。内部调用create实现。
    CCParticleBatchNode* CCParticleBatchNode::batchNodeWithTexture(CCTexture2D *tex, unsigned int capacity/* = kCCParticleDefaultCapacity*/)
    {
        return CCParticleBatchNode::createWithTexture(tex, capacity);
    }
    //上面的创建函数的create实现。
    CCParticleBatchNode* CCParticleBatchNode::createWithTexture(CCTexture2D *tex, unsigned int capacity/* = kCCParticleDefaultCapacity*/)
    {
    	//先new出实例对象,然后调用初始化函数,之后交由内存管理器进行引用计数器的管理。
        CCParticleBatchNode * p = new CCParticleBatchNode();
        if( p && p->initWithTexture(tex, capacity))
        {
            p->autorelease();
            return p;
        }
    	//如果失败,释放并置空。
        CC_SAFE_DELETE(p);
        return NULL;
    }
    
    //创建粒子系统的批次结点,参数一为图片文件名称,参数二为创建批次结点所能容纳的最大粒子数量。内部调用create实现。
    CCParticleBatchNode* CCParticleBatchNode::batchNodeWithFile(const char* imageFile, unsigned int capacity/* = kCCParticleDefaultCapacity*/)
    {
        return CCParticleBatchNode::create(imageFile, capacity);
    }
    //上面的创建函数的create实现。
    CCParticleBatchNode* CCParticleBatchNode::create(const char* imageFile, unsigned int capacity/* = kCCParticleDefaultCapacity*/)
    {
    	//先new出实例对象,然后调用初始化函数,之后交由内存管理器进行引用计数器的管理。
        CCParticleBatchNode * p = new CCParticleBatchNode();
        if( p && p->initWithFile(imageFile, capacity))
        {
            p->autorelease();
            return p;
        }
    	//如果失败,释放并置空。
        CC_SAFE_DELETE(p);
        return NULL;
    }
    
    //初始化函数。
    bool CCParticleBatchNode::initWithTexture(CCTexture2D *tex, unsigned int capacity)
    {
    	//实例化一个“同纹理的顶点缓冲区管理器”。
        m_pTextureAtlas = new CCTextureAtlas();
    	//使用纹理对象和容量大小初始化这个管理器。
        m_pTextureAtlas->initWithTexture(tex, capacity);
    
        // 创建一个CCArray,初始化容量大小,用于存放所有的粒子结点。
        m_pChildren = new CCArray();
        m_pChildren->initWithCapacity(capacity);
    	// 初始化ALPHA混合方案。
        m_tBlendFunc.src = CC_BLEND_SRC;
        m_tBlendFunc.dst = CC_BLEND_DST;
    
        //设置所用的Shader代码片段 setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));
        
        return true;
    }
    
    //与上面不同参数的初始化函数。
    bool CCParticleBatchNode::initWithFile(const char* fileImage, unsigned int capacity)
    {
    	//先由图片名称创建出对应的纹理对象,并调用初始化函数。
        CCTexture2D *tex = CCTextureCache::sharedTextureCache()->addImage(fileImage);
        return initWithTexture(tex, capacity);
    }
    
    //系统在遍历结点时调用的函数。
    void CCParticleBatchNode::visit()
    {
        //如果不显示,直接返回。
        if (!m_bIsVisible)
        {
            return;
        }
    	//将当前程序所用的矩阵先压栈保存。
        kmGLPushMatrix();
    	//如果m_pGrid有值被激活则开启渲染到纹理(此节可参看本博“Cocos2d-x 2.0 网格动画深入分析”一文。
        if ( m_pGrid && m_pGrid->isActive())
        {
            m_pGrid->beforeDraw();
            transformAncestors();
        }
    	//进行矩阵的变换。
        transform();
    	//绘制。
        draw();
    	//如果m_pGrid有值并被激活,则关闭渲染到纹理。这样当前结点上所有绘制的图像都被输出到m_pGrid对应的纹理中了。
        if ( m_pGrid && m_pGrid->isActive())
        {
            m_pGrid->afterDraw(this);
        }
    	//将之前压栈的矩阵恢复成当前所用的矩阵。
        kmGLPopMatrix();
    }
    
    //重载基类的相应函数。
    //加入一个粒子系统子结点。
    void CCParticleBatchNode::addChild(CCNode * child)
    {
        CCNode::addChild(child);
    }
    //加入一个粒子系统子结点。
    void CCParticleBatchNode::addChild(CCNode * child, int zOrder)
    {
        CCNode::addChild(child, zOrder);
    }
    //加入一个粒子系统子结点。
    void CCParticleBatchNode::addChild(CCNode * child, int zOrder, int tag)
    {
    	//一大堆有效性判断。
        CCAssert( child != NULL, "Argument must be non-NULL");
        CCAssert( dynamic_cast<CCParticleSystem*>(child) != NULL, "CCParticleBatchNode only supports CCQuadParticleSystems as children");
        CCParticleSystem* pChild = (CCParticleSystem*)child;
        CCAssert( pChild->getTexture()->getName() == m_pTextureAtlas->getTexture()->getName(), "CCParticleSystem is not using the same texture id");
        // 如果当前粒子的批次优化结点中存储的粒子数量为0,则设置相应的ALPHA混合方案。
        if( m_pChildren->count() == 0 ) 
        {
            setBlendFunc(pChild->getBlendFunc());
        }
    	//有效性判断。
        CCAssert( m_tBlendFunc.src  == pChild->getBlendFunc().src && m_tBlendFunc.dst  == pChild->getBlendFunc().dst, "Can't add a PaticleSystem that uses a differnt blending function");
    
        //将粒子系统做为当前批次结点的相应索引位置的子结点。
        unsigned int pos = addChildHelper(pChild,zOrder,tag);
    
        //定义一个临时变量存储当前批次结点中“同纹理的顶点缓冲区管理器”中对应矩形顶点缓冲块的索引。
        unsigned int atlasIndex = 0;
    	//如果不是第一个。
        if (pos != 0) 
        {
    		//取得对应的粒子系统。
            CCParticleSystem* p = (CCParticleSystem*)m_pChildren->objectAtIndex(pos-1);
    		//取得当前粒子系统的“同纹理的顶点缓冲区管理器”中对应矩形顶点缓冲块的索引加上粒子的总数量存入atlasIndex。
            atlasIndex = p->getAtlasIndex() + p->getTotalParticles();
        }
        else
        {
    	    //如果是第一个,就直接设置atlasIndex为0。
            atlasIndex = 0;
        }
    	//将粒子系统与atlasIndex做为Z排序值插入相应的容器。
        insertChild(pChild, atlasIndex);
    
        // 设置粒子系统使用当前的批次结点。
        pChild->setBatchNode(this);
    }
    //将一个粒子系统做为当前批次结点的子结点。
    unsigned int CCParticleBatchNode::addChildHelper(CCParticleSystem* child, int z, int aTag)
    {
    	//有效性判断。
        CCAssert( child != NULL, "Argument must be non-nil");
        CCAssert( child->getParent() == NULL, "child already added. It can't be added again");
    	//如果存储子结点的容器指针为空,则使用new创建出对应的容器,并初始化容量为4。
        if( ! m_pChildren ) 
        {
            m_pChildren = new CCArray();
            m_pChildren->initWithCapacity(4);
        }
        //取出子结点容器中z位置在Z排序中的索引位置。
        unsigned int pos = searchNewPositionInChildrenForZ(z);
    	//将子结点放入容器的相应位置。
        m_pChildren->insertObject(child, pos);
    	//设置子结点的tag值。
        child->setTag(aTag);
    	//设置Z排序值。
        child->_setZOrder(z);
    	//设置父结点为当前批次结点。
        child->setParent(this);
    	//如果在运行中,调用相应的初始化函数。
        if( m_bIsRunning ) 
        {
            child->onEnter();
            child->onEnterTransitionDidFinish();
        }
        return pos;
    }
    
    //对指定的粒子系统重新排序。
    void CCParticleBatchNode::reorderChild(CCNode * child, int zOrder)
    {
    	//有效性判断。
        CCAssert( child != NULL, "Child must be non-NULL");
        CCAssert( dynamic_cast<CCParticleSystem*>(child) != NULL, "CCParticleBatchNode only supports CCQuadParticleSystems as children");
        CCAssert( m_pChildren->containsObject(child), "Child doesn't belong to batch" );
    	//转换为粒子系统。
        CCParticleSystem* pChild = (CCParticleSystem*)(child);
    	//如果本身顺序就与指定排序索引相同,直接返回。
        if( zOrder == child->getZOrder() ) 
        {
            return;
        }
    
        // 如果只有一个子结点,就不用排序了。
        if( m_pChildren->count() > 1)
        {
    	   //定义临时变量,用于存储指定结点插入前的索引和插入后的索引。
            unsigned int newIndex = 0, oldIndex = 0;
    	   //取得这两个索引值。
            getCurrentIndex(&oldIndex, &newIndex, pChild, zOrder);
    	   //如果索引有改动。
            if( oldIndex != newIndex )
            {
                //子结点的引用计数器加一。
                pChild->retain();
    		   //将旧的索引位置的结点从容器中删除。
                m_pChildren->removeObjectAtIndex(oldIndex);
    		   //将子结点以新的索引位置放入容器中。
                m_pChildren->insertObject(pChild, newIndex);
    		   //子结点的引用计数器减一。
                pChild->release();
    
                // 将粒子系统的“同纹理的顶点缓冲区管理器”中对应矩形顶点缓冲块的索引保存到oldAtlasIndex中。
                unsigned int oldAtlasIndex = pChild->getAtlasIndex();
    
                // 更新所有的索引位置。
                updateAllAtlasIndexes();
    
                //定义临时变量用于取出新的相应粒子系统的“同纹理的顶点缓冲区管理器”中对应矩形顶点缓冲块的索引。
                unsigned int newAtlasIndex = 0;
                for( unsigned int i=0;i < m_pChildren->count();i++)
                {
                    CCParticleSystem* pNode = (CCParticleSystem*)m_pChildren->objectAtIndex(i);
                    if( pNode == pChild ) 
                    {
                        newAtlasIndex = pChild->getAtlasIndex();
                        break;
                    }
                }
    
                //将oldAtlasIndex位置的数据拷到newAtlasIndex位置保持数组中信息连续有效。
                m_pTextureAtlas->moveQuadsFromIndex(oldAtlasIndex, pChild->getTotalParticles(), newAtlasIndex);
    		   //更新一下粒子系统。
                pChild->updateWithNoTime();
            }
        }
        //重新设置Z排序值。
        pChild->_setZOrder(zOrder);
    }
    //取得粒子系统子结点在Z排序的结点数组中的位置和设置排序值z后的位置
    void CCParticleBatchNode::getCurrentIndex(unsigned int* oldIndex, unsigned int* newIndex, CCNode* child, int z)
    {
        bool foundCurrentIdx = false;
        bool foundNewIdx = false;
    
        int  minusOne = 0;
        unsigned int count = m_pChildren->count();
    	//遍历所有的子结点。
        for( unsigned int i=0; i < count; i++ ) 
        {
    		//取出相应的结点。
            CCNode* pNode = (CCNode *)m_pChildren->objectAtIndex(i);
    
            // 如果Z值大于z了,就记录索引返回给新的索引参数。
            if( pNode->getZOrder() > z &&  ! foundNewIdx ) 
            {
                *newIndex = i;
                foundNewIdx = true;
    			//新索引和旧索引都找到时break。
                if( foundCurrentIdx && foundNewIdx )
                {
                    break;
                }
            }
    
            //如果结点相同。就记录索引返回给旧的索引参数。
            if( child == pNode ) 
            {
                *oldIndex = i;
                foundCurrentIdx = true;
    		   //如果未找到新的索引,则将minuseOne值由0改为-1。
                if( ! foundNewIdx )
                {
                    minusOne = -1;
                }
    		    //新索引和旧索引都找到时break。
                if( foundCurrentIdx && foundNewIdx )
                {	
                    break;
                }
            }
    
        }
    	//如果未找到新的索引,则将新的索引参数的值填为最大值。
        if( ! foundNewIdx )
        {
            *newIndex = count;
        }
    	//
        *newIndex += minusOne;
    }
    //通过z值来取得结点在Z排序的结点数组中的索引。
    unsigned int CCParticleBatchNode::searchNewPositionInChildrenForZ(int z)
    {
    	//取得子结点的数量。
        unsigned int count = m_pChildren->count();
    	//遍历子结点,找到Z结点在Z排序的结点数组中大于z值的第一个结点的索引。
        for( unsigned int i=0; i < count; i++ ) 
        {
            CCNode *child = (CCNode *)m_pChildren->objectAtIndex(i);
            if (child->getZOrder() > z)
            {
                return i;
            }
        }
    	//如果找不到,返回最大值。
        return count;
    }
    
    //将粒子系统子结点从批次结点中移除。
    void  CCParticleBatchNode::removeChild(CCNode* child, bool cleanup)
    {
        // 无效返回.
        if (child == NULL)
        {
            return;
        }
        //有效性判断.
        CCAssert( dynamic_cast<CCParticleSystem*>(child) != NULL, "CCParticleBatchNode only supports CCQuadParticleSystems as children");
        CCAssert(m_pChildren->containsObject(child), "CCParticleBatchNode doesn't contain the sprite. Can't remove it");
    	//将对应的子结点指针转化为粒子系统指针.
        CCParticleSystem* pChild = (CCParticleSystem*)child;
    	//移除对应的子结点.
        CCNode::removeChild(pChild, cleanup);
    
        // 从“同纹理的顶点缓冲区管理器”中找到相应的矩形顶点缓冲块的索引位置然后移除相应粒子数量的矩形顶点缓冲信息块.并使用memmove将后面的数据拷到删除位置保持矩形顶点缓冲信息块的数组信息连续有效。
        m_pTextureAtlas->removeQuadsAtIndex(pChild->getAtlasIndex(), pChild->getTotalParticles());
    
        //在“同纹理的顶点缓冲区管理器”中从参数一指定位置后相应粒子数量的矩形顶点缓冲信息块都置零。这里即将上面数组中更新的矩形顶点缓冲信息块数量之后的内存置零。
        m_pTextureAtlas->fillWithEmptyQuadsFromIndex(m_pTextureAtlas->getTotalQuads(), pChild->getTotalParticles());
    
        // 设置被移除的粒子系统不使用批次结点.
        pChild->setBatchNode(NULL);
    	//重建“同纹理的顶点缓冲区管理器”中所有的矩形顶点缓冲信息块的索引.
        updateAllAtlasIndexes();
    }
    //通过索引移除相应的粒子系统。
    void CCParticleBatchNode::removeChildAtIndex(unsigned int index, bool doCleanup)
    {
        removeChild((CCParticleSystem *)m_pChildren->objectAtIndex(index),doCleanup);
    }
    //清空所有使用当前批次结点的粒子系统。
    void CCParticleBatchNode::removeAllChildrenWithCleanup(bool doCleanup)
    {
    	//遍历所有的粒子系统调用setBatchNode函数,参数为NULL。即将所有使用当前批次结点的粒子系统都设置不使用批次结点。
        arrayMakeObjectsPerformSelectorWithObject(m_pChildren, setBatchNode, NULL, CCParticleSystem*);
    	//清空所有的子结点。
        CCNode::removeAllChildrenWithCleanup(doCleanup);
    	//清空“同纹理的顶点缓冲区管理器”中所有的矩形顶点缓冲信息块。
        m_pTextureAtlas->removeAllQuads();
    }
    //绘制批次结点。
    void CCParticleBatchNode::draw(void)
    {
        CC_PROFILER_STOP("CCParticleBatchNode - draw");
    	//如果矩形顶点缓冲信息块的数量为0直接返回。
        if( m_pTextureAtlas->getTotalQuads() == 0 )
        {
            return;
        }
    	//开始使用Shader进行绘制结点。
        CC_NODE_DRAW_SETUP();
    	//设置Opengl采用相应的ALPHA混合方案。
        ccGLBlendFunc( m_tBlendFunc.src, m_tBlendFunc.dst );
    	//调用drawQuads来绘制所有矩形顶点缓冲构成的图形。
        m_pTextureAtlas->drawQuads();
        CC_PROFILER_STOP("CCParticleBatchNode - draw");
    }
    //扩增矩形顶点缓冲信息块组数的容量。
    void CCParticleBatchNode::increaseAtlasCapacityTo(unsigned int quantity)
    {
    	//打印日志。
        CCLOG("cocos2d: CCParticleBatchNode: resizing TextureAtlas capacity from [%lu] to [%lu].",
              (long)m_pTextureAtlas->getCapacity(),
              (long)quantity);
    	//重新调整矩形顶点缓冲信息块数组的大小。
        if( ! m_pTextureAtlas->resizeCapacity(quantity) ) {
            // serious problems
            CCLOGWARN("cocos2d: WARNING: Not enough memory to resize the atlas");
            CCAssert(false,"XXX: CCParticleBatchNode #increaseAtlasCapacity SHALL handle this assert");
        }
    }
    
    //设置对应索引的粒子无效。
    void CCParticleBatchNode::disableParticle(unsigned int particleIndex)
    {
    	//取出对应索引的矩形顶点缓冲信息块并将大小置0.
        ccV3F_C4B_T2F_Quad* quad = &((m_pTextureAtlas->getQuads())[particleIndex]);
        quad->br.vertices.x = quad->br.vertices.y = quad->tr.vertices.x = quad->tr.vertices.y = quad->tl.vertices.x = quad->tl.vertices.y = quad->bl.vertices.x = quad->bl.vertices.y = 0.0f;
    }
    
    // 将一个粒子系统按照指定的索引位置做为子结点加入批次结点。
    void CCParticleBatchNode::insertChild(CCParticleSystem* pSystem, unsigned int index)
    {
    	//用index做为粒子系统使用的矩形顶点缓冲信息块索引。
        pSystem->setAtlasIndex(index);
    	//如果“同纹理的顶点缓冲区管理器”中所有的矩形顶点缓冲信息块的数量在直接加入粒子系统的相应数量之后超过了容量,需要扩增容量。
        if(m_pTextureAtlas->getTotalQuads() + pSystem->getTotalParticles() > m_pTextureAtlas->getCapacity())
        {
            //扩增一下“同纹理的顶点缓冲区管理器”中所有的矩形顶点缓冲信息块的数组容量。
            increaseAtlasCapacityTo(m_pTextureAtlas->getTotalQuads() + pSystem->getTotalParticles());
    
            //将数组中新增的尾部粒子数量大小的矩形顶点缓冲信息块内存数据清零。
            m_pTextureAtlas->fillWithEmptyQuadsFromIndex(m_pTextureAtlas->getCapacity() - pSystem->getTotalParticles(), pSystem->getTotalParticles());
        }
    
        // 粒子系统不是最后一个结点。则需要将index位置粒子数量的矩形顶点缓冲信息移到插入新的信息之后的位置,这样对应位置的矩形顶点缓冲信息块就空出来了。
        if (pSystem->getAtlasIndex() + pSystem->getTotalParticles() != m_pTextureAtlas->getTotalQuads())
        {
            m_pTextureAtlas->moveQuadsFromIndex(index, index+pSystem->getTotalParticles());
        }
    
        //按照粒子系统的总的粒子数量增加矩形顶点缓冲信息块的数量记数值。
        m_pTextureAtlas->increaseTotalQuadsWith(pSystem->getTotalParticles());
    	//重建所有的粒子的“同纹理的顶点缓冲区管理器”的矩形顶点缓冲信息块的索引。
        updateAllAtlasIndexes();
    }
    
    //重建所有的粒子的“同纹理的顶点缓冲区管理器”的矩形顶点缓冲信息块的索引。
    void CCParticleBatchNode::updateAllAtlasIndexes()
    {
    	//定义临时变量用于循环取值.
        CCObject *pObj = NULL;
        unsigned int index = 0;
    	//遍历所有的子结点容器,取出每个元素存入到pObj中.
        CCARRAY_FOREACH(m_pChildren,pObj)
        {
    		//取出每个元素,转换成为粒子系统指针.
            CCParticleSystem* child = (CCParticleSystem*)pObj;
    	    //设置对应的粒子系统所对应的“同纹理的顶点缓冲区管理器”的矩形顶点缓冲信息块的索引.
            child->setAtlasIndex(index);
    	   //索引递增相应粒子数量.
            index += child->getTotalParticles();
        }
    }
    
    // 更新ALPHA混合状态方案.
    void CCParticleBatchNode::updateBlendFunc(void)
    {
    	//如果纹理有ALPHA通道,设置其相应的ALPHA混合方案.
        if( ! m_pTextureAtlas->getTexture()->hasPremultipliedAlpha()) {
            m_tBlendFunc.src = GL_SRC_ALPHA;
            m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
        }
    }
    //设置粒子的批次结点所使用的纹理对象。
    void CCParticleBatchNode::setTexture(CCTexture2D* texture)
    {
    	//设置当前“同纹理的顶点缓冲区管理器”的对应纹理图。
        m_pTextureAtlas->setTexture(texture);
    
        //如果纹理没有ALPHA通道并且ALPHA混合方案与指定方案相同,则重新设置相应的ALPHA混合方案。
        if( texture && ! texture->hasPremultipliedAlpha() && ( m_tBlendFunc.src == CC_BLEND_SRC && m_tBlendFunc.dst == CC_BLEND_DST ) )
        {
                m_tBlendFunc.src = GL_SRC_ALPHA;
                m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
        }
    }
    
    //取得当前粒子的批次结点所用的纹理对象指针。
    CCTexture2D* CCParticleBatchNode::getTexture(void)
    {
        return m_pTextureAtlas->getTexture();
    }
    //设置ALPHA混合方案。
    void CCParticleBatchNode::setBlendFunc(ccBlendFunc blendFunc)
    {
        m_tBlendFunc = blendFunc;
    }
    //取得ALPHA混合方案。
    ccBlendFunc CCParticleBatchNode::getBlendFunc(void)
    {
        return m_tBlendFunc;
    }
    
    NS_CC_END
    

    粒子的批次结点讲完了,我们来总结一下:

             批次结点通过m_pTextureAtlas来对使用相同纹理的多个粒子系统中的粒子进行矩形顶点缓冲区的管理,这些粒子系统以子结点的形式按照Z排序存放在m_pChildren中。批次结点可以根据设置的纹理是否有ALPHA通道来设置透明度混合和高亮两种混合方案,并在渲染时使用。渲染时通过m_pTextureAtlas来进行绘制处理,一个批次内将使用相同纹理的所有粒子系统的所有粒子渲染出来。大大优化了渲染状态和纹理的切换开销,提升了渲染效率。


           但是,如何指定粒子系统放在相应的批次结点中呢?这个还需要后面我们继续分析研究。

     

            下面我们继续学习粒子系统,打开CCParicleSystem.h:


    #ifndef __CCPARTICLE_SYSTEM_H__
    #define __CCPARTICLE_SYSTEM_H__
    
    #include "CCProtocols.h"
    #include "base_nodes/CCNode.h"
    #include "cocoa/CCDictionary.h"
    #include "cocoa/CCString.h"
    
    //使用Cocos2d命名空间
    NS_CC_BEGIN
    
    //.h中要用到CCParticleBatchNode类。
    
    class CCParticleBatchNode;
    
    //一些发射器的状态类型设置枚举。
    enum {
        //发射器永远激活
        kCCParticleDurationInfinity = -1,
    
        //开始与结束时粒子数量相等
        kCCParticleStartSizeEqualToEndSize = -1,
    
        //开始与结束时粒子的旋转角度相等。
        kCCParticleStartRadiusEqualToEndRadius = -1,
    
        //上面枚举的一些别名
        kParticleStartSizeEqualToEndSize = kCCParticleStartSizeEqualToEndSize,
        kParticleDurationInfinity = kCCParticleDurationInfinity,
    };
    
    //粒子发射器的两种模式
    enum {
        //重力模式 [让粒子向一个中心点移动或离开一个中心点,比如火,烟]
        kCCParticleModeGravity,
    
        //环型模式[让粒子沿着一个圆形旋转,比如螺旋]
        kCCParticleModeRadius,    
    };
    
    
    //粒子的两种运动方式
    typedef enum {
        //自由方式:粒子的运动是自由的无拘无束的,移动不受任何结点的影响。
        kCCPositionTypeFree,
    
        //相对方式:粒子的运动是相对于父结点的坐标系。比如可以将粒子关联到一个精灵上,让粒子跟随精力一起移动。
        kCCPositionTypeRelative,
    
        //集群方式:这种方式粒子跟随发射器移动。
        kCCPositionTypeGrouped,
    }tCCPositionType;
    
    // 兼容之前的版本的对应枚举值。
    enum {
        kPositionTypeFree = kCCPositionTypeFree,
        kPositionTypeGrouped = kCCPositionTypeGrouped,
    }; 
    
    //这里就是包含粒子信息的粒子结构体了。我们来深入的看一下它的属性。
    typedef struct sCCParticle {
    
        CCPoint     pos;//位置
        CCPoint     startPos;//起始位置
    
        ccColor4F    color;//颜色
        ccColor4F    deltaColor;//颜色变化量
    
        float        size;//大小
        float        deltaSize;//大小变化量
    
        float        rotation;//旋转角度
        float        deltaRotation;//旋转角度变化量。
    
        float        timeToLive;//生命值。
    
        unsigned int    atlasIndex;//对应批次结点中的矩形顶点缓冲块的索引。
    
        //描述变化方式的成员结构 (重力加速度模式)
        struct {
            CCPoint      dir;				//运动方向
            float        radialAccel;		//旋转加速度
            float        tangentialAccel;	//切线加速度
        } modeA;
    
        //描述变化方式的成员结构(环型模式)
        struct {
            float        angle;				//起始旋转角度
            float        degreesPerSecond;// 每秒的旋转弧度
            float        radius;			// 起始旋转角度
            float        deltaRadius;	    // 每次更新时旋转角度变化量。
        } modeB;
    
    }tCCParticle;
    
    需要用到纹理对象,这里声明一下。
    
    class CCTexture2D;
    
    //粒子系统类的定义,一个粒子系统是由结点类和纹理接口类派生。
    class CC_DLL CCParticleSystem : public CCNode, public CCTextureProtocol
    {    
    protected:
    	//对应的PLIST文件
        std::string m_sPlistFile;
        //开始到当前的运行秒数
        float m_fElapsed;
    
        // 不同变化模式的信息结构
        //重力加速度模式
        struct {
            //重力加速度
            CCPoint gravity;
            //速度值
            float speed;
            //速度值的变化量
            float speedVar;
            //相关加速值
            float tangentialAccel;
            //相关加速值变化量
            float tangentialAccelVar;
            //旋转加速值
            float radialAccel;
            //旋转加速值变化量
            float radialAccelVar;
        } modeA;
    
        //环型移动模式
        struct {
            //起始角度值
            float startRadius;
            //起始角度值变化量
            float startRadiusVar;
            //结束角度值
            float endRadius;
            //结束角度值变化量
            float endRadiusVar;            
            //每秒旋转角度值
            float rotatePerSecond;
            //每秒旋转角度值变化量
            float rotatePerSecondVar;
        } modeB;
    
        //粒子的动态数组
        tCCParticle *m_pParticles;
    
        //色彩调整
        //    BOOL colorModulate;
    
        //每秒的粒子发射数
        float m_fEmitCounter;
    
        //!  particle idx
        unsigned int m_uParticleIdx;
    
        // 优化选项
        //CC_UPDATE_PARTICLE_IMP    updateParticleImp;
        //SEL                        updateParticleSel;
    
        //渲染精灵所用的粒子批次结点动态数组
        CC_PROPERTY(CCParticleBatchNode*, m_pBatchNode, BatchNode);
    
        // 当前系统在批次结点数组中的索引
        CC_SYNTHESIZE(unsigned int, m_uAtlasIndex, AtlasIndex);
    
        //是否缩放和旋转。
        bool m_bTransformSystemDirty;
        //已经创建的粒子数量
        unsigned int m_uAllocatedParticles;
    
        //发射器是否被激活。
        bool m_bIsActive;
        //当前已经发射的粒子数量。
        CC_PROPERTY_READONLY(unsigned int, m_uParticleCount, ParticleCount)
        //发射器将运行的时间。
        CC_PROPERTY(float, m_fDuration, Duration)
        //发射器的原位置点。
        CC_PROPERTY_PASS_BY_REF(CCPoint, m_tSourcePosition, SourcePosition)
        //当前发射器的位置变量。
        CC_PROPERTY_PASS_BY_REF(CCPoint, m_tPosVar, PosVar)
        //初始时的粒子生命值。
        CC_PROPERTY(float, m_fLife, Life)
        //粒子生命值的变化量。
        CC_PROPERTY(float, m_fLifeVar, LifeVar)
        //粒子的初始角度。
        CC_PROPERTY(float, m_fAngle, Angle)
        //粒子的角度变化量。
        CC_PROPERTY(float, m_fAngleVar, AngleVar)
    
    //相关公共函数。
    public:
        //重力加速度模式的变量值的存取函数。
        virtual const CCPoint& getGravity();
        virtual void setGravity(const CCPoint& g);
        virtual float getSpeed();
        virtual void setSpeed(float speed);
        virtual float getSpeedVar();
        virtual void setSpeedVar(float speed);
        virtual float getTangentialAccel();
        virtual void setTangentialAccel(float t);
        virtual float getTangentialAccelVar();
        virtual void setTangentialAccelVar(float t);
        virtual float getRadialAccel();
        virtual void setRadialAccel(float t);
        virtual float getRadialAccelVar();
        virtual void setRadialAccelVar(float t);
        //环型移动模式的变量值的存取函数。
        virtual float getStartRadius();
        virtual void setStartRadius(float startRadius);
        virtual float getStartRadiusVar();
        virtual void setStartRadiusVar(float startRadiusVar);
        virtual float getEndRadius();
        virtual void setEndRadius(float endRadius);
        virtual float getEndRadiusVar();
        virtual void setEndRadiusVar(float endRadiusVar);
        virtual float getRotatePerSecond();
        virtual void setRotatePerSecond(float degrees);
        virtual float getRotatePerSecondVar();
        virtual void setRotatePerSecondVar(float degrees);
    
    	//设置缩放
        virtual void setScale(float s);
    	//设置旋转
        virtual void setRotation(float newRotation);
    	//设置单方向轴的缩放
        virtual void setScaleX(float newScaleX);
        virtual void setScaleY(float newScaleY);
        //是否被激活
        virtual bool isActive();
    	//混合状态是否为亮模式
        virtual bool isBlendAdditive();
    	//设置是否高亮模式
        virtual void setBlendAdditive(bool value);
        //粒子的开始大小的属性和存取访问接口
        CC_PROPERTY(float, m_fStartSize, StartSize)
        //粒子的开始大小的变化值属性和存取访问接口
        CC_PROPERTY(float, m_fStartSizeVar, StartSizeVar)
        //粒子的结束大小的属性和存取访问接口
        CC_PROPERTY(float, m_fEndSize, EndSize)
        //粒子的结束大小的变化值属性和存取访问接口
        CC_PROPERTY(float, m_fEndSizeVar, EndSizeVar)
        //粒子的起始颜色的属性和存取访问接口
        CC_PROPERTY_PASS_BY_REF(ccColor4F, m_tStartColor, StartColor)
        //粒子的起始颜色的变化值属性和存取访问接口
        CC_PROPERTY_PASS_BY_REF(ccColor4F, m_tStartColorVar, StartColorVar)
        //粒子的结束颜色的属性和存取访问接口
        CC_PROPERTY_PASS_BY_REF(ccColor4F, m_tEndColor, EndColor)
        //粒子的结束颜色的变化值属性和存取访问接口
        CC_PROPERTY_PASS_BY_REF(ccColor4F, m_tEndColorVar, EndColorVar)
        //粒子的初始化角度值属性和存取访问接口
        CC_PROPERTY(float, m_fStartSpin, StartSpin)
        //粒子的初始化角度变化值属性和存取访问接口
        CC_PROPERTY(float, m_fStartSpinVar, StartSpinVar)
        //粒子的结束角度值属性和存取访问接口
        CC_PROPERTY(float, m_fEndSpin, EndSpin)
        //粒子的结束角度变化值属性和存取访问接口
        CC_PROPERTY(float, m_fEndSpinVar, EndSpinVar)
        //发射器的发射速度值属性和存取访问接口
        CC_PROPERTY(float, m_fEmissionRate, EmissionRate)
        //最大粒子数量值属性和存取访问接口
        CC_PROPERTY(unsigned int, m_uTotalParticles, TotalParticles)
        //粒子所用的纹理对象实例指针和存取访问接口
        CC_PROPERTY(CCTexture2D*, m_pTexture, Texture)
        //粒子渲染所用的混合状态方案和存取访问接口
        CC_PROPERTY(ccBlendFunc, m_tBlendFunc, BlendFunc)
        //是否使用透晨度来影响修改颜色值和存取访问接口
        CC_PROPERTY(bool, m_bOpacityModifyRGB, OpacityModifyRGB)
    
    	//混合状态是否是使用源混合状态 = GL_SRC_ALPHA,目标混合状态 = GL_ONE 的混合叠加方案
        bool m_bIsBlendAdditive;
        //粒子的移动方式:1,自由方式   2,集群方式
        CC_PROPERTY(tCCPositionType, m_ePositionType, PositionType)
    protected:
    	//在运行结束后是否被自动移除。
        bool m_bIsAutoRemoveOnFinish;
    public:
    	//取得运行结束后是否被自动移除。
        virtual bool isAutoRemoveOnFinish();
    	//设置运行结束后是否被自动移除。
        virtual void setAutoRemoveOnFinish(bool var);
    
    	//发射器模式:1,重力加速度模式 2 环型模式
        CC_PROPERTY(int, m_nEmitterMode, EmitterMode)
    
    public:
    	//构造函数。
        CCParticleSystem();
    	//析构函数。
        virtual ~CCParticleSystem();
        //静态的由PLIST文件创建粒子系统的函数,参数为PLIST文件名,内部调用create来进行实现。
        CC_DEPRECATED_ATTRIBUTE static CCParticleSystem * particleWithFile(const char *plistFile);
    
        //上面的create实现。
        static CCParticleSystem * create(const char *plistFile);
    
        //粒子系统的初始化。
        bool init();
        //从一个PLIST文件中初始化粒子系统。
        bool initWithFile(const char *plistFile);
    
        //从一个词典中初始化粒子系统。
        bool initWithDictionary(CCDictionary *dictionary);
    
        //初始化粒子总数量。
        virtual bool initWithTotalParticles(unsigned int numberOfParticles);
        //为发射器增加一个粒子。
        bool addParticle();
        //初始化一个粒子。
        void initParticle(tCCParticle* particle);
        //暂停当前粒子系统。
        void stopSystem();
        //重置当前粒子系统
        void resetSystem();
        //当前粒子系统是否已经将所有粒子发射。
        bool isFull();
    
        //用于被派生类重载的接口。
        virtual void updateQuadWithParticle(tCCParticle* particle, const CCPoint& newPosition);
        //用于被派生类重载的接口。
        virtual void postStep();
    
    	//更新粒子系统。
        virtual void update(float dt);
    	//不需要时间流逝的更新粒子系统。
        virtual void updateWithNoTime(void);
    
    protected:
    	//更新混合状态方案
        virtual void updateBlendFunc();
    };
    

    CPP文件:

    #include "CCParticleSystem.h"
    #include "CCParticleBatchNode.h"
    #include "ccTypes.h"
    #include "textures/CCTextureCache.h"
    #include "textures/CCTextureAtlas.h"
    #include "support/base64.h"
    #include "support/CCPointExtension.h"
    #include "platform/CCFileUtils.h"
    #include "platform/CCImage.h"
    #include "platform/platform.h"
    #include "support/zip_support/ZipUtils.h"
    #include "CCDirector.h"
    #include "support/CCProfiling.h"
    // opengl
    #include "CCGL.h"
    
    //使用Cocos2d-x命名空间。
    NS_CC_BEGIN
    
    //粒子系统的构造函数。
    CCParticleSystem::CCParticleSystem()
        :m_sPlistFile("")
        ,m_fElapsed(0)
        ,m_pParticles(NULL)
        ,m_fEmitCounter(0)
        ,m_uParticleIdx(0)
        ,m_bIsActive(true)
        ,m_uParticleCount(0)
        ,m_fDuration(0)
        ,m_tSourcePosition(CCPointZero)
        ,m_tPosVar(CCPointZero)
        ,m_fLife(0)
        ,m_fLifeVar(0)
        ,m_fAngle(0)
        ,m_fAngleVar(0)
        ,m_fStartSize(0)
        ,m_fStartSizeVar(0)
        ,m_fEndSize(0)
        ,m_fEndSizeVar(0)
        ,m_fStartSpin(0)
        ,m_fStartSpinVar(0)
        ,m_fEndSpin(0)
        ,m_fEndSpinVar(0)
        ,m_fEmissionRate(0)
        ,m_uTotalParticles(0)
        ,m_pTexture(NULL)
        ,m_bOpacityModifyRGB(false)
        ,m_bIsBlendAdditive(false)
        ,m_ePositionType(kCCPositionTypeFree)
        ,m_bIsAutoRemoveOnFinish(false)
        ,m_nEmitterMode(kCCParticleModeGravity)
        ,m_pBatchNode(NULL)
        ,m_uAtlasIndex(0)
        ,m_bTransformSystemDirty(false)
        ,m_uAllocatedParticles(0)
    {
    	//大量的数据初始化工作。
        modeA.gravity = CCPointZero;
        modeA.speed = 0;
        modeA.speedVar = 0;
        modeA.tangentialAccel = 0;
        modeA.tangentialAccelVar = 0;
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 0;
        modeB.startRadius = 0;
        modeB.startRadiusVar = 0;
        modeB.endRadius = 0;
        modeB.endRadiusVar = 0;            
        modeB.rotatePerSecond = 0;
        modeB.rotatePerSecondVar = 0;
        m_tBlendFunc.src = CC_BLEND_SRC;
        m_tBlendFunc.dst = CC_BLEND_DST;
    }
    // 从PLIST文件中创建粒子系统。内部调用create进行实现。
    CCParticleSystem * CCParticleSystem::particleWithFile(const char *plistFile)
    {
        return CCParticleSystem::create(plistFile);
    }
    //从PLIST文件中创建粒子系统的具体实现。
    CCParticleSystem * CCParticleSystem::create(const char *plistFile)
    {
    	//创建一个粒子系统,由PLIST文件进行初始化并交由内存管理器进行引用计数器的管理。
        CCParticleSystem *pRet = new CCParticleSystem();
        if (pRet && pRet->initWithFile(plistFile))
        {
            pRet->autorelease();
            return pRet;
        }
    	//如果失败,释放并置空。
        CC_SAFE_DELETE(pRet);
        return pRet;
    }
    //粒子系统的初始化。
    bool CCParticleSystem::init()
    {
    	//初始化粒子的总数为150个。
        return initWithTotalParticles(150);
    }
    //从PLIST文件中初始化粒子系统。
    bool CCParticleSystem::initWithFile(const char *plistFile)
    {
        bool bRet = false;
    	//取得PLIST的全路径。
        m_sPlistFile = CCFileUtils::sharedFileUtils()->fullPathFromRelativePath(plistFile);
    	//从PLIST文件中创建出数据项与数据值对应关系表的词典。
        CCDictionary *dict = CCDictionary::createWithContentsOfFileThreadSafe(m_sPlistFile.c_str());
    	//有效性判断。
        CCAssert( dict != NULL, "Particles: file not found");
    	//由词典中的所有数据来进行初始化当前粒子系统。
        bRet = this->initWithDictionary(dict);
    	//释放词典。
        dict->release();
    
        return bRet;
    }
    //由词典来初始化当前的粒子系统。
    bool CCParticleSystem::initWithDictionary(CCDictionary *dictionary)
    {
    	//定义临时变量。
        bool bRet = false;
        unsigned char *buffer = NULL;
        unsigned char *deflated = NULL;
        CCImage *image = NULL;
    	//
        do 
        {
    		//从词典中取得粒子的最大数量值。
            int maxParticles = dictionary->valueForKey("maxParticles")->intValue();
            //初始化粒子容量。
            if(this->initWithTotalParticles(maxParticles))
            {
                // 从词典中取得角度及角度变化量的值。
                m_fAngle = dictionary->valueForKey("angle")->floatValue();
                m_fAngleVar = dictionary->valueForKey("angleVariance")->floatValue();
    
                //从词典中取得动画时长的值。
                m_fDuration = dictionary->valueForKey("duration")->floatValue();
    
                //从词典中取得混合方案
                m_tBlendFunc.src = dictionary->valueForKey("blendFuncSource")->intValue();
                m_tBlendFunc.dst = dictionary->valueForKey("blendFuncDestination")->intValue();
    
                //从词典中取得起始颜色
                m_tStartColor.r = dictionary->valueForKey("startColorRed")->floatValue();
                m_tStartColor.g = dictionary->valueForKey("startColorGreen")->floatValue();
                m_tStartColor.b = dictionary->valueForKey("startColorBlue")->floatValue();
                m_tStartColor.a = dictionary->valueForKey("startColorAlpha")->floatValue();
    			//从词典中取得起始颜色变化量
                m_tStartColorVar.r = dictionary->valueForKey("startColorVarianceRed")->floatValue();
                m_tStartColorVar.g = dictionary->valueForKey("startColorVarianceGreen")->floatValue();
                m_tStartColorVar.b = dictionary->valueForKey("startColorVarianceBlue")->floatValue();
                m_tStartColorVar.a = dictionary->valueForKey("startColorVarianceAlpha")->floatValue();
    			//从词典中取得结束的颜色值。
                m_tEndColor.r = dictionary->valueForKey("finishColorRed")->floatValue();
                m_tEndColor.g = dictionary->valueForKey("finishColorGreen")->floatValue();
                m_tEndColor.b = dictionary->valueForKey("finishColorBlue")->floatValue();
                m_tEndColor.a = dictionary->valueForKey("finishColorAlpha")->floatValue();
    			//从词典中取得结束的颜色变化量值。
                m_tEndColorVar.r = dictionary->valueForKey("finishColorVarianceRed")->floatValue();
                m_tEndColorVar.g = dictionary->valueForKey("finishColorVarianceGreen")->floatValue();
                m_tEndColorVar.b = dictionary->valueForKey("finishColorVarianceBlue")->floatValue();
                m_tEndColorVar.a = dictionary->valueForKey("finishColorVarianceAlpha")->floatValue();
    
                //从词典中取得粒子的起始大小。
                m_fStartSize = dictionary->valueForKey("startParticleSize")->floatValue();
                m_fStartSizeVar = dictionary->valueForKey("startParticleSizeVariance")->floatValue();
    
                //从词典中取得粒子的终止大小。
                m_fEndSize = dictionary->valueForKey("finishParticleSize")->floatValue();
                m_fEndSizeVar = dictionary->valueForKey("finishParticleSizeVariance")->floatValue();
    
                //从词典中取得源位置。
                float x = dictionary->valueForKey("sourcePositionx")->floatValue();
                float y = dictionary->valueForKey("sourcePositiony")->floatValue();
                this->setPosition( ccp(x,y) );
                //从词典中取得源位置变化量。            
                m_tPosVar.x = dictionary->valueForKey("sourcePositionVariancex")->floatValue();
                m_tPosVar.y = dictionary->valueForKey("sourcePositionVariancey")->floatValue();
    
                //从词典中取得粒子的起始旋转角度。
                m_fStartSpin = dictionary->valueForKey("rotationStart")->floatValue();
    			 //从词典中取得粒子的起始旋转角度变化值。
                m_fStartSpinVar = dictionary->valueForKey("rotationStartVariance")->floatValue();
                //从词典中取得粒子的终止旋转角度。
                m_fEndSpin= dictionary->valueForKey("rotationEnd")->floatValue();
                //从词典中取得粒子的终止旋转角度变化值。
                m_fEndSpinVar= dictionary->valueForKey("rotationEndVariance")->floatValue();
                //从词典中取得粒子的发射器类型。
                m_nEmitterMode = dictionary->valueForKey("emitterType")->intValue();
    
                //重力加速度模式
                if( m_nEmitterMode == kCCParticleModeGravity ) 
                {
                    //从词典中取得粒子的重力加速度。
                    modeA.gravity.x = dictionary->valueForKey("gravityx")->floatValue();
                    modeA.gravity.y = dictionary->valueForKey("gravityy")->floatValue();
    
                    //从词典中取得粒子的速度及变化量。
                    modeA.speed = dictionary->valueForKey("speed")->floatValue();
                    modeA.speedVar = dictionary->valueForKey("speedVariance")->floatValue();
    
                    //从词典中取得粒子的旋转加速度及变化量。
                    modeA.radialAccel = dictionary->valueForKey("radialAcceleration")->floatValue();
                    modeA.radialAccelVar = dictionary->valueForKey("radialAccelVariance")->floatValue();
    
                    //从词典中取得粒子的切线加速度及变化量。
                    modeA.tangentialAccel = dictionary->valueForKey("tangentialAcceleration")->floatValue();
                    modeA.tangentialAccelVar = dictionary->valueForKey("tangentialAccelVariance")->floatValue();
                }
    
                // 环型模式
                else if( m_nEmitterMode == kCCParticleModeRadius ) 
                {
    				  //从词典中取得粒子的最大角度。
                    modeB.startRadius = dictionary->valueForKey("maxRadius")->floatValue();
    				 //从词典中取得粒子的最大角度变化值。
                    modeB.startRadiusVar = dictionary->valueForKey("maxRadiusVariance")->floatValue();
    				 //从词典中取得粒子的最小角度。
                    modeB.endRadius = dictionary->valueForKey("minRadius")->floatValue();
    				 //竟然没有从词典中取得粒子的最小角度变化值而是直接设为0。好吧!
                    modeB.endRadiusVar = 0.0f;
    				 //从词典中取得粒子的每秒旋转圈数。
    
                    modeB.rotatePerSecond = dictionary->valueForKey("rotatePerSecond")->floatValue();
    				//从词典中取得粒子的每秒旋转圈数变化量。
                    modeB.rotatePerSecondVar = dictionary->valueForKey("rotatePerSecondVariance")->floatValue();
    
                } else {//类型无效的错误提示。
                    CCAssert( false, "Invalid emitterType in config file");
                    CC_BREAK_IF(true);
                }
    
                // 从词典中取得粒子的生命值及变化量。
                m_fLife = dictionary->valueForKey("particleLifespan")->floatValue();
                m_fLifeVar = dictionary->valueForKey("particleLifespanVariance")->floatValue();
    
                //计数出发射器的发射速度。
                m_fEmissionRate = m_uTotalParticles / m_fLife;
    
    			 //如果一个批次结点被使用着,则不能直接取得内部的纹理对象。
                if (!m_pBatchNode)
                {
    				  // 设置修改透明度不影响RGB值。
                    m_bOpacityModifyRGB = false;
    
                    // 从词典中取得所用的纹理图片名称。
                    const char* textureName = dictionary->valueForKey("textureFileName")->getCString();
    				  // 通过文件管理器查找到相应的全路径字符串。
                    std::string fullpath = CCFileUtils::sharedFileUtils()->fullPathFromRelativeFile(textureName, m_sPlistFile.c_str());
                    //定义纹理对象指针并置空。
                    CCTexture2D *tex = NULL;
                    //如果纹理名称有效。
                    if (strlen(textureName) > 0)
                    {
                        // 先取得当前文件管理器是否进行相关提示。
                        bool bNotify = CCFileUtils::sharedFileUtils()->isPopupNotify();
                        // 在这里指定不需要弹出文件读取出错的提示对话框。为什么呢?
    CCFileUtils::sharedFileUtils()->setPopupNotify(false);
    					  // 将文件加载到纹理管理器中并返回纹理对象指针。
                        tex = CCTextureCache::sharedTextureCache()->addImage(fullpath.c_str());
                        
                        //重置文件读取出错的提示对话框。
    CCFileUtils::sharedFileUtils()->setPopupNotify(bNotify);
                    }
                    //如果纹理对象指针有效,设置为当前粒子系统所用的纹理贴图。
                    if (tex)
                    {
                        setTexture(tex);
                    }
                    else
                    {   
    					 //如果无效,则不是从文件中加载,而是直接从数据中创建纹理。
    					  //从词典中取得所用的纹理图片数据返回给textureData。
                        const char *textureData = dictionary->valueForKey("textureImageData")->getCString();
    					  //数据的有效性判断。
                        CCAssert(textureData, "");
                        //取得数据的长度。
                        int dataLen = strlen(textureData);
                        if(dataLen != 0)
                        {
                            //注意base64Decode提供了Base64解码过程。返回解码后的数据到buffer,返回长度值decodeLen。
                            int decodeLen = base64Decode((unsigned char*)textureData, (unsigned int)dataLen, &buffer);
    							//有效性判断。
                            CCAssert( buffer != NULL, "CCParticleSystem: error decoding textureImageData");
                            CC_BREAK_IF(!buffer);
                            // ZipUtils::ccInflateMemory 提供了gzip解压的过程。返回解压后的数据到deflated,返回长度值decodeLen。
                            int deflatedLen = ZipUtils::ccInflateMemory(buffer, decodeLen, &deflated);
    							//有效性判断。
                            CCAssert( deflated != NULL, "CCParticleSystem: error ungzipping textureImageData");
                            CC_BREAK_IF(!deflated);
                            
                            //创建一个新的CCImage,返回其实例对象指针给image。
                            image = new CCImage();
    						   //通过解压后的数据来填充CCImage的图像数据。
                            bool isOK = image->initWithImageData(deflated, deflatedLen);
    							//判断是否成功。
                            CCAssert(isOK, "CCParticleSystem: error init image with Data");
                            CC_BREAK_IF(!isOK);
                            
                            //将这个image加入到纹理管理器中,并设置给当前粒子系统使用。
    setTexture(CCTextureCache::sharedTextureCache()->addUIImage(image, fullpath.c_str()));
    							//image的释放处理。
                            image->release();
                        }
                    }
    				  //纹理的有效性判断。
                    CCAssert( this->m_pTexture != NULL, "CCParticleSystem: error loading the texture");
                }
                bRet = true;
            }
        } while (0);
    	//释放buffer和deflated。
        CC_SAFE_DELETE_ARRAY(buffer);
        CC_SAFE_DELETE_ARRAY(deflated);
        return bRet;
    }
    //通过粒子的最大数量来初始化粒子系统。
    bool CCParticleSystem::initWithTotalParticles(unsigned int numberOfParticles)
    {
    	//记录最大数量。
        m_uTotalParticles = numberOfParticles;
    	//先释放原来的粒子数组信息。
        CC_SAFE_FREE(m_pParticles);
        //重新申请粒子数组对应的相应内存。
        m_pParticles = (tCCParticle*)calloc(m_uTotalParticles, sizeof(tCCParticle));
    	//如果申请内存失败,提示出错,释放并返回false。
        if( ! m_pParticles )
        {
            CCLOG("Particle system: not enough memory");
            this->release();
            return false;
        }
    	//记录当前数组中的粒子的数量。
        m_uAllocatedParticles = numberOfParticles;
    	//如果对应的粒子的批次结点有效。
        if (m_pBatchNode)
        {
    		//遍历并填充相应粒子对应的批次结点中矩形顶点缓冲信息块的索引。
            for (unsigned int i = 0; i < m_uTotalParticles; i++)
            {
                m_pParticles[i].atlasIndex=i;
            }
        }
        // 默认激活。
        m_bIsActive = true;
    
        // 默认的ALPHA混合方案。
        m_tBlendFunc.src = CC_BLEND_SRC;
        m_tBlendFunc.dst = CC_BLEND_DST;
    
        // 默认的移动类型。
        m_ePositionType = kCCPositionTypeFree;
    
        // 默认的运动模式。
        m_nEmitterMode = kCCParticleModeGravity;
    
        //默认在结束后不自动释放当前粒子系统。
        m_bIsAutoRemoveOnFinish = false;
    
        //设置不使用矩阵转换,更新更快一些,
        m_bTransformSystemDirty = false;
        // 在动画作束后更新。
        this->scheduleUpdateWithPriority(1);
    
        return true;
    }
    //析构函数。
    CCParticleSystem::~CCParticleSystem()
    {
        unscheduleUpdate();
    	//释放粒子数组。
        CC_SAFE_FREE(m_pParticles);
    	//释放纹理。
        CC_SAFE_RELEASE(m_pTexture);
    }
    //增加一个新的粒子。
    bool CCParticleSystem::addParticle()
    {
    	//如果当前粒子系统已经达到最大量则返回。
        if (this->isFull())
        {
            return false;
        }
    	//否则获取数组中当前数量对应的索引位置取出来相应的粒子并初始化。
        tCCParticle * particle = &m_pParticles[ m_uParticleCount ];
        this->initParticle(particle);
        ++m_uParticleCount;
    
        return true;
    }
    
    //初始化粒子。
    void CCParticleSystem::initParticle(tCCParticle* particle)
    {
        // 初始化生命时间。CCRANDOM_MINUS1_1()是取一个(-1,1)之间的随机值。
        particle->timeToLive = m_fLife + m_fLifeVar * CCRANDOM_MINUS1_1();
    	// 将随机值限定大于0。
        particle->timeToLive = MAX(0, particle->timeToLive);
    
        // 初始化一个以m_tSourcePosition位置为中心的随机位置,最大变化范围为m_tPosVar。
        particle->pos.x = m_tSourcePosition.x + m_tPosVar.x * CCRANDOM_MINUS1_1();
        particle->pos.y = m_tSourcePosition.y + m_tPosVar.y * CCRANDOM_MINUS1_1();
    
    
        // 初始化一个以m_tStartColor为基本色的随机色,最大变化范围为m_ tStartColorVar。
        ccColor4F start;
        start.r = clampf(m_tStartColor.r + m_tStartColorVar.r * CCRANDOM_MINUS1_1(), 0, 1);
        start.g = clampf(m_tStartColor.g + m_tStartColorVar.g * CCRANDOM_MINUS1_1(), 0, 1);
        start.b = clampf(m_tStartColor.b + m_tStartColorVar.b * CCRANDOM_MINUS1_1(), 0, 1);
    start.a = clampf(m_tStartColor.a + m_tStartColorVar.a * CCRANDOM_MINUS1_1(), 0, 1);
    
        // 初始化一个以m_tEndColor为基本色的随机色,最大范围为m_ tEndColorVar。
        ccColor4F end;
        end.r = clampf(m_tEndColor.r + m_tEndColorVar.r * CCRANDOM_MINUS1_1(), 0, 1);
        end.g = clampf(m_tEndColor.g + m_tEndColorVar.g * CCRANDOM_MINUS1_1(), 0, 1);
        end.b = clampf(m_tEndColor.b + m_tEndColorVar.b * CCRANDOM_MINUS1_1(), 0, 1);
        end.a = clampf(m_tEndColor.a + m_tEndColorVar.a * CCRANDOM_MINUS1_1(), 0, 1);
    
        // 初始化粒子的颜色和每次更新时的变化量。
        particle->color = start;
        particle->deltaColor.r = (end.r - start.r) / particle->timeToLive;
        particle->deltaColor.g = (end.g - start.g) / particle->timeToLive;
        particle->deltaColor.b = (end.b - start.b) / particle->timeToLive;
        particle->deltaColor.a = (end.a - start.a) / particle->timeToLive;
    
    // 初始化一个以m_fStartSize为基本大小的随机大小值,最大变化范围为m_fStartSizeVar。
        float startS = m_fStartSize + m_fStartSizeVar * CCRANDOM_MINUS1_1();
    	//限定大小。
        startS = MAX(0, startS); 
    	//设置大小。
        particle->size = startS;
    	//如果终止大小为设定的值-1,即代表大小不随更新变化。
        if( m_fEndSize == kCCParticleStartSizeEqualToEndSize )
        {
            particle->deltaSize = 0;
        }
        else
        {
    		//否则计算出大小每次更新时的变化量。
            float endS = m_fEndSize + m_fEndSizeVar * CCRANDOM_MINUS1_1();
            endS = MAX(0, endS); // No negative values
            particle->deltaSize = (endS - startS) / particle->timeToLive;
        }
    
        // 随机的旋转起始和终止角度。
        float startA = m_fStartSpin + m_fStartSpinVar * CCRANDOM_MINUS1_1();
        float endA = m_fEndSpin + m_fEndSpinVar * CCRANDOM_MINUS1_1();
    	//保存起始角度,并计算每次更新的旋转角度。
        particle->rotation = startA;
        particle->deltaRotation = (endA - startA) / particle->timeToLive;
    
        //如果是自由方式的位置设置方式,初始化起点为0,0点。
        if( m_ePositionType == kCCPositionTypeFree )
        {	
    		//将当前坐标系中零零点转换为世界坐标系中的位置。
            currentPosition = this->convertToWorldSpace(CCPointZero);
            particle->startPos = this->convertToWorldSpace(CCPointZero);
        }
        else if ( m_ePositionType == kCCPositionTypeRelative )
        {
    		//如果是相对方式的位置设置方式,初始化起点位置为当前粒子系统坐标系的位置。
            particle->startPos = m_tPosition;
        }
    
        // 设置方向值,是一个弧度,由以m_fAngle为基础的随机范围为m_fAngleVar所生成的角度转化而成。
        float a = CC_DEGREES_TO_RADIANS( m_fAngle + m_fAngleVar * CCRANDOM_MINUS1_1() );    
    
        // 如果是重力加速度模式。
        if (m_nEmitterMode == kCCParticleModeGravity) 
        {
    		 //由方向值计算出X,Y的分量的角度。
            CCPoint v(cosf( a ), sinf( a ));
    		// s 为此模式下的速度,是以modeA.speed为基础的随机范围为modeA.speedVar的随机值。
            float s = modeA.speed + modeA.speedVar * CCRANDOM_MINUS1_1();
    
            // 将上面的信息相乘的结果做为方向保存到粒子的modeA的方向值中。
            particle->modeA.dir = ccpMult( v, s );
    
            // 旋转加速度的值为modeA.radialAccel为基础的随机范围为modeA.radialAccelVar的值。
            particle->modeA.radialAccel = modeA.radialAccel + modeA.radialAccelVar * CCRANDOM_MINUS1_1();
     
    
            // 切线加速度的值为modeA.tangentialAccel为基础的随机范围为modeA.tangentialAccelVar的值。
            particle->modeA.tangentialAccel = modeA.tangentialAccel + modeA.tangentialAccelVar * CCRANDOM_MINUS1_1();
    
        }
    
        // 如果是环形模式。
        else 
        {
            // 设置起始弧度和结束弧度。也是以相应值为基础进行随机计算。
            float startRadius = modeB.startRadius + modeB.startRadiusVar * CCRANDOM_MINUS1_1();
            float endRadius = modeB.endRadius + modeB.endRadiusVar * CCRANDOM_MINUS1_1();
    
    		 //填充到粒子的数据值中。
            particle->modeB.radius = startRadius;
    		 //如果结束弧度值为指定的值,则在更新过程中不进行旋转变化。
            if(modeB.endRadius == kCCParticleStartRadiusEqualToEndRadius)
            {
                particle->modeB.deltaRadius = 0;
            }
            else
            {
    			  //否则计算每次更新的弧度变化量。
                particle->modeB.deltaRadius = (endRadius - startRadius) / particle->timeToLive;
            }
    		 //保存方向值弧度。并计算每秒的旋转弧度。
            particle->modeB.angle = a;
            particle->modeB.degreesPerSecond = CC_DEGREES_TO_RADIANS(modeB.rotatePerSecond + modeB.rotatePerSecondVar * CCRANDOM_MINUS1_1());
        }    
    }
    //停止粒子系统。
    void CCParticleSystem::stopSystem()
    {
        m_bIsActive = false;
        m_fElapsed = m_fDuration;
        m_fEmitCounter = 0;
    }
    //重新启动粒子系统。
    void CCParticleSystem::resetSystem()
    {
        m_bIsActive = true;
        m_fElapsed = 0;
    	//遍历每个粒子,设置生命值为0。
        for (m_uParticleIdx = 0; m_uParticleIdx < m_uParticleCount; ++m_uParticleIdx)
        {
            tCCParticle *p = &m_pParticles[m_uParticleIdx];
            p->timeToLive = 0;
        }
    }
    //粒子系统是否已经填满。
    bool CCParticleSystem::isFull()
    {
        return (m_uParticleCount == m_uTotalParticles);
    }
    
    // 粒子系统的更新函数。
    void CCParticleSystem::update(float dt)
    {
    	//
        CC_PROFILER_START_CATEGORY(kCCProfilerCategoryParticles , "CCParticleSystem - update");
    	//如果是激活状态,并且发射速度大于0
        if (m_bIsActive && m_fEmissionRate)
        {
    		//计算出产生每个粒子的平均时间间隔。
            float rate = 1.0f / m_fEmissionRate;
    		//如果当前粒子数量仍小于最大数量。对发射器的时间计数器加上当前帧的时间间隔值dt。
            if (m_uParticleCount < m_uTotalParticles)
            {
                m_fEmitCounter += dt;
            }
            //Whle循环,看当前积累的时间间隔积足够创建多少粒子就创建多少粒子。
            while (m_uParticleCount < m_uTotalParticles && m_fEmitCounter > rate) 
            {
                this->addParticle();
                m_fEmitCounter -= rate;
            }
    		//记录时间间隔。
            m_fElapsed += dt;
    		//如时总的动画时长为-1并且
            if (m_fDuration != -1 && m_fDuration < m_fElapsed)
            {
                this->stopSystem();
            }
        }
    	//设置粒子的索引值。
        m_uParticleIdx = 0;
    	//定义一个临时结构变量存储当前位置,初始化为零零点。
        CCPoint currentPosition = CCPointZero;
    	//如果自由模式
        if (m_ePositionType == kCCPositionTypeFree)
        {
    		//将当前坐标系中零零点转换为世界坐标系中的位置。
            currentPosition = this->convertToWorldSpace(CCPointZero);
        }
        else if (m_ePositionType == kCCPositionTypeRelative)
        {
    		//如果是相对模式,保存当前坐标系的位置。
            currentPosition = m_tPosition;
        }
    	//如果显示标记为true
        if (m_bIsVisible)
        {
    		//遍历每一个粒子,进行更新.
            while (m_uParticleIdx < m_uParticleCount)
            {
    			 //取得对应的粒子.
                tCCParticle *p = &m_pParticles[m_uParticleIdx];
    
                // 生命值的减少.
                p->timeToLive -= dt;
    
    			//如果生命值大于0.则进行更新.
                if (p->timeToLive > 0) 
                {
                    //如果是重力加速度模式
                    if (m_nEmitterMode == kCCParticleModeGravity) 
                    {
    					   //定义临时变量存储位置,旋转角度,切角
                        CCPoint tmp, radial, tangential;
    					  //初始化角度值为零零点.
                        radial = CCPointZero;
                        // 旋转角度的计算.
                        if (p->pos.x || p->pos.y)
                        {
    						   //向量归一化.
                            radial = ccpNormalize(p->pos);
                        }
    					  //设置切角度为当前角度。
                        tangential = radial;
    					  // 旋转角度的乘以速度值.
                        radial = ccpMult(radial, p->modeA.radialAccel);
    
                        // 切角度变化计算.
                        float newy = tangential.x;
                        tangential.x = -tangential.y;
                        tangential.y = newy;
    					 //
                        tangential = ccpMult(tangential, p->modeA.tangentialAccel);
    
                        // 位置的计算.
                        tmp = ccpAdd( ccpAdd( radial, tangential), modeA.gravity);
                        tmp = ccpMult( tmp, dt);
                        p->modeA.dir = ccpAdd( p->modeA.dir, tmp);
                        tmp = ccpMult(p->modeA.dir, dt);
                        p->pos = ccpAdd( p->pos, tmp );
                    }
    
                    //
                    else 
                    {
                        // 如果是环形模式。
                        // 更新粒子的角度。
                        p->modeB.angle += p->modeB.degreesPerSecond * dt;
                        p->modeB.radius += p->modeB.deltaRadius * dt;
                        // 计算粒子的位置。
                        p->pos.x = - cosf(p->modeB.angle) * p->modeB.radius;
                        p->pos.y = - sinf(p->modeB.angle) * p->modeB.radius;
                    }
    
                    // 颜色的变化计算。
                    p->color.r += (p->deltaColor.r * dt);
                    p->color.g += (p->deltaColor.g * dt);
                    p->color.b += (p->deltaColor.b * dt);
                    p->color.a += (p->deltaColor.a * dt);
    
                    // 大小的变化计算。
                    p->size += (p->deltaSize * dt);
                    p->size = MAX( 0, p->size );
    
                    // 角度。
                    p->rotation += (p->deltaRotation * dt);
    
    				  //
                    CCPoint    newPos;
    				  //如果是自由模式或者相对模式
                    if (m_ePositionType == kCCPositionTypeFree || m_ePositionType == kCCPositionTypeRelative) 
                    {
    					   //
                        CCPoint diff = ccpSub( currentPosition, p->startPos );
                        newPos = ccpSub(p->pos, diff);
                    } 
                    else
                    {
                        newPos = p->pos;
                    }
                    //如果使用批次结点,加上当前粒子系统坐标系的位置。为什么呢?因为矩阵转换不会影响批次结点。
                    if (m_pBatchNode)
                    {
                        newPos.x+=m_tPosition.x;
                        newPos.y+=m_tPosition.y;
                    }
    				  //更新粒子对应批次结点中的矩形顶点缓冲信息。
                    updateQuadWithParticle(p, newPos);
                    
                    // 更新粒子计数器。
                    ++m_uParticleIdx;
                } 
                else 
                {
                    // 如果生命值小于0.
    				  // 
                    int currentIndex = p->atlasIndex;
                    if( m_uParticleIdx != m_uParticleCount-1 )
                    {
                        m_pParticles[m_uParticleIdx] = m_pParticles[m_uParticleCount-1];
                    }
    				  //如果使用批次结点,通过设置对应的矩形顶点缓冲中大小为0达到不显示指定粒子的目的。
                    if (m_pBatchNode)
                    {
                        //disable the switched particle
                        m_pBatchNode->disableParticle(m_uAtlasIndex+currentIndex);
    
                        //将最后一个粒子信息中的
                        m_pParticles[m_uParticleCount-1].atlasIndex = currentIndex;
                    }
    				  //粒子数量减一。
                    --m_uParticleCount;
    				  //如果粒子数量减为0并且设置自动删除粒子系统。
                    if( m_uParticleCount == 0 && m_bIsAutoRemoveOnFinish )
                    {
    					   //停止更新并从父结点中删除自已。
                        this->unscheduleUpdate();
                        m_pParent->removeChild(this, true);
                        return;
                    }
                }
            } 
    	     //
            m_bTransformSystemDirty = false;
        }
    	//如果不使用批次结点进行优化,则调用用户重载的虚函数postStep进行更新处理。
        if (! m_pBatchNode)
        {
            postStep();
        }
    
        CC_PROFILER_STOP_CATEGORY(kCCProfilerCategoryParticles , "CCParticleSystem - update");
    }
    
    //不增加时间变化的更新
    void CCParticleSystem::updateWithNoTime(void)
    {
        this->update(0.0f);
    }
    
    //需要被重载的更新粒子的对应矩形顶点缓冲信息块的虚函数。
    void CCParticleSystem::updateQuadWithParticle(tCCParticle* particle, const CCPoint& newPosition)
    {
        CC_UNUSED_PARAM(particle);
        CC_UNUSED_PARAM(newPosition);
        // should be overriden
    }
    //需要被重载的粒子更新处理函数。
    void CCParticleSystem::postStep()
    {
        // should be overriden
    }
    
    //设置当前粒子系统所用的纹理对象。
    void CCParticleSystem::setTexture(CCTexture2D* var)
    {
    	//如果纹理对象不同,则释放旧的纹理对象,设置为新的纹理对象,并更新ALPHA混合方案。
        if (m_pTexture != var)
        {
            CC_SAFE_RETAIN(var);
            CC_SAFE_RELEASE(m_pTexture);
            m_pTexture = var;
            updateBlendFunc();
        }
    }
    
    //更新ALPHA混合方案。为了方便理解,先了解一下常用的混合状态值的意义:
    GL_ZERO: 表示使用0.0作为因子,实际上相当于不使用这种颜色参与混合运算。
    GL_ONE: 表示使用1.0作为因子,实际上相当于完全的使用了这种颜色参与混合运算。
    GL_SRC_ALPHA:表示使用源颜色的alpha值来作为因子。
    GL_DST_ALPHA:表示使用目标颜色的alpha值来作为因子。
    GL_ONE_MINUS_SRC_ALPHA:表示用1.0减去源颜色的alpha值来作为因子。
    GL_ONE_MINUS_DST_ALPHA:表示用1.0减去目标颜色的alpha值来作为因子。 除此以外,还有GL_SRC_COLOR(把源颜色的四个分量分别作为因子的四个分量)、GL_ONE_MINUS_SRC_COLOR、 GL_DST_COLOR、GL_ONE_MINUS_DST_COLOR等,前两个在OpenGL旧版本中只能用于设置目标因子,后两个在OpenGL 旧版本中只能用于设置源因子。新版本的OpenGL则没有这个限制,并且支持新的GL_CONST_COLOR(设定一种常数颜色,将其四个分量分别作为 因子的四个分量)、GL_ONE_MINUS_CONST_COLOR、GL_CONST_ALPHA、 GL_ONE_MINUS_CONST_ALPHA。另外还有GL_SRC_ALPHA_SATURATE。新版本的OpenGL还允许颜色的alpha 值和RGB值采用不同的混合因子。
    
    void CCParticleSystem::updateBlendFunc()
    {
    	//有效性判断。
        CCAssert(! m_pBatchNode, "Can't change blending functions when the particle is being batched");
    	//如果纹理对象指针有效,取出其是否有ALPHA通道,并将有ALPHA通道的设为ALPHA混合方案,
        if(m_pTexture)
        {
            bool premultiplied = m_pTexture->hasPremultipliedAlpha();
            
            m_bOpacityModifyRGB = false;
            
            if( m_pTexture && ( m_tBlendFunc.src == CC_BLEND_SRC && m_tBlendFunc.dst == CC_BLEND_DST ) )
            {
    			  //如果有ALPHA通道,设置标记m_bOpacityModifyRGB为true.
                if( premultiplied )
                {
                    m_bOpacityModifyRGB = true;
                }
                else
                {
                   //如果没有ALPHA通道,使用glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);,则表示源颜色乘以自身的alpha 值,目标颜色乘以1.0减去源颜色的alpha值,这样一来,源颜色的alpha值越大,则产生的新颜色中源颜色所占比例就越大,而目标颜色所占比例则减 小。这种情况下,我们可以简单的将源颜色的alpha值理解为“不透明度”。这也是混合时最常用的方式。
                    m_tBlendFunc.src = GL_SRC_ALPHA;
                    m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
                }
            }
        }
    }
    //取得当前粒子系统的纹理对象.
    CCTexture2D * CCParticleSystem::getTexture()
    {
        return m_pTexture;
    }
    
    // 设置粒子的混合方案是否采用高亮模式
    void CCParticleSystem::setBlendAdditive(bool additive)
    {
    	//如果是高亮模式
        if( additive )
        {
    		 //设置混合方案的源和目标状态值
            m_tBlendFunc.src = GL_SRC_ALPHA;
            m_tBlendFunc.dst = GL_ONE;
        }
        else
        {
    		//如果不是高亮模式,则根据是否有ALPHA通道来进行相应的方案设置.
            if( m_pTexture && ! m_pTexture->hasPremultipliedAlpha() )
            {
                m_tBlendFunc.src = GL_SRC_ALPHA;
                m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
            } 
            else 
            {
    			 //下面的两个渲染状态值为别是对应GL_ONE和GL_ONE_MINUS_SRC_ALPHA.
                m_tBlendFunc.src = CC_BLEND_SRC;
                m_tBlendFunc.dst = CC_BLEND_DST;
            }
        }
    }
    //取得是否采用了高亮方式.
    bool CCParticleSystem::isBlendAdditive()
    {
        return( m_tBlendFunc.src == GL_SRC_ALPHA && m_tBlendFunc.dst == GL_ONE);
    }
    
    // 后面是一大堆属性设置函数.不贴了,大家都看得懂.
    

     总结一下:

        粒子系统提供了对于粒子信息结构体tCCParticle的数组控制管理,通过tCCParticle中有限的数据属性来摸拟自然界粒子的存在与运动规律。从其中需要重载的函数我们知道,Cocos2d-x的一些粒子系统的具体演示都是由这个基础的粒子系统类进行派生和相应函数重载实现的。所以重点是理解属性的意义和掌握将来我们如何进行扩展。

    在粒子系统中有函数setBatchNode来设置所用的批次结点。我们将在二部曲中再具体分析。


    课后作业

    (1)理解批次优化的概念,提高游戏研发的思维习惯。

    (2)参考DX或Opengl的一般粒子系统的实现,实做一个小的粒子系统。

    (3)找到不同的ALPHA混合方案并进行测试和理解,或者找到相应的博客资源进行学习。

     

           这篇博客时间跨度较大,近期红孩儿除了写博之外,也在每天深夜坚持写新的工具-“红孩儿游戏工具箱”,一个巨无霸的游戏开发工具箱。包括切图,拼图,帧动画与骨骼动画,粒子系统编辑,界面编辑,场景编辑,字体编辑等多项功能。以立图将游戏开发形成一套形之有效的工具化流程。



    #分享视频# http://url.cn/BUYo0v “红孩儿游戏工具箱”之切图编辑

     


    #分享视频# http://url.cn/A4hkpf 红孩儿游戏工具箱之图片合并编辑

     


    #分享视频# http://url.cn/CXe3w3 新做的“红孩儿游戏工具箱”之关键帧动画演示



    #分享视频# http://url.cn/9fUJt9 新做的“红孩儿游戏工具箱”之融合动画演示


    #分享视频# http://url.cn/DngRC8 红孩儿游戏工具箱-骨骼动画编辑演示



     #分享视频# http://url.cn/E36O0O 红孩儿工具箱之:骨骼动画树做为结点的挂接编辑



    #分享视频# http://url.cn/Bc0iq6 红孩儿工具箱之基础层场景编辑


    #分享视频# http://url.cn/BX9uAk 红孩儿游戏工具箱-场景阻挡格编辑:不是对某个格子编辑,而是对一个图块进行阻挡编辑。



    #分享视频# http://url.cn/B1DwtD 红孩儿游戏工具箱-界面编辑器之面板与按钮编辑




    欢迎大家到我的微博上关注我随时更新发布的视频。再次感谢大家的支撑!


    新浪微博: http://weibo.com/u/1834515945

    腾讯微博:http://t.qq.com/honghaier_2005


    展开全文
  • Hadoop1.X 与 Hadoop2.X比较

    千次阅读 2016-03-12 11:43:03
    0.20.x版本最后演化成了现在的1.0.x版本 0.23.x版本最后演化成了现在的2.x版本 hadoop 1.0 指的是1.x(0.20.x),0.21,0.22 hadoop 2.0 指的是2.x,0.23.x CDH3,CDH4分别对应了hadoop1.0 h
  • X,X11,Xorg,XServer,XClient,Xlib

    千次阅读 2018-06-23 11:50:27
    1.X 就是 X Window System ,1984年由MIT研发,它的设计哲学之一是:提供机制,而非策略。 主要特点:1,Server/Client网络模型。2,通过扩展使它保持”与时俱进”。 Linux内核是没有GUI图形显示的,X Window的...
  • 移动开发之浅析cocos2d-x的中文支持问题

    万次阅读 热门讨论 2012-04-04 22:35:01
    移动开发之浅析cocos2d-x的中文支持问题  题记:这阵子一直在... 使用引擎的过程遇到的问题很多,中文支持便是一例,虽然问题本身并不复杂,但是网上的相关资料都比较简单零散,自己捣鼓了几下,觉得有必要的整
  • [Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier] ... 红孩儿Cocos2d-X学习园地QQ2群:44208467加群写:Cocos2d-x  红孩儿Cocos2d-X学习园地QQ群:249941957加群写:
  • Nexus 5X的Android 8.0 Oreo刷机步骤 2017年12月14日 首先,下载最新版的驱动:platform-tools-latest-windows.zip (Qiang) 解压,win+R输入cmd,进入命令行,使用cd命令进入刚解压的文件夹,...
  • 记录下学习过程遇到的问题:将中文关键字提取后...\xe7\xbb\x87\xe7\x89\xa9 \xe7\xbb\x93\xe5\x90\x88\xe9\x83\xa8 \xe7\xbb\x93\xe6\x9e\x84 \xe5\x86\x85\xe7\xae\xa1 \xe5 \x88\x86\xe9\x9a\x94 \xe8\x86\xa8\...
  • 敲代码的时候十分的不方便,一小心就把后面的代码替代了 非常的麻烦 找到了一个快速解决的办法 记录一下 按下键盘上的inster键即可! 这样就方便很多啦 如果一不小心变成了这样,就用这个键解决吧! ...
  • 我们项目用cocos2d-x开发的游戏在份模拟器上面会闪退,闪退时的log留下一句 Fatal signal 11 (SIGSEGV) at 0x00000000 (code=128) //注意这里面的code是128,不是1。 闪退的地方是随机的,通常是在游戏初始化时...
  • 前言 在实际项目,用户ip的获取很重要。通过报障用户的ip来快速定位用户的请求日志,还可以通过ip访问频率来进行防盗链...都是通过HeaderX-Forward-For、X-Real-IP或Remote addr等属性获取,但是如果确保获...
  • 内容可能有不到之处,欢迎交流。...**在很多POST请求,经常会加入一些字段来控制会话或者数据的表单提交。**例如,在京东的模拟登陆,并不是你在Java或者python程序,直接输入用户名密码就能登陆成...
  • highcharts柱形图中x轴数据的类型

    千次阅读 2014-07-15 16:03:41
    这两天在用highcharts做统计图表,x轴的数据和对应的标识都准备好了,可是图表却只有标识,没有数据: 郁闷了,从浏览器的控制台输出了一下,数据看不出有什么问题,有18个标识,对应的18个值,...
  • C-V2X 技术介绍

    万次阅读 多人点赞 2020-12-17 18:00:32
    缩略词 C-ITS(Cooperative Intelligent Transportation System,协作式智能交通系统) C-V2X(Cellular-V2X,...D2D(Device to Device,设备到设备)是指物联网设备直连通信技术。 DSRC(Dedicated Short Range Com
  • X窗口系统

    千次阅读 2012-10-17 14:19:38
    X窗口系统(X Window System,也常称为X11或X)是一种以位图方式显示的软件窗口系统。最初是1984年麻省理工学院的研究,之后变成UNIX、类UNIX、以及OpenVMS等操作系统所一致适用的标准化软件工具包及显示架构的运作...
  • x window配置

    千次阅读 2013-06-28 10:35:40
    关闭x window:init 3  生成配置文件:X -configure 然后就可以生成xorg.conf. 1. 编译内核时选中内核驱动: drivers/char/agpgart/VIA chipset drivers/char/drm/ATI Radeon 2. 安装ati xserver驱动: apt-
  • X Window系统

    千次阅读 2012-06-19 11:33:15
    X Window系统(也常称为X11或X X的工作站)是一种以位图方式显示的软件视窗系统。最初是1984年麻省理工学院的研究,之后变成UNIX、类UNIX、以及OpenVMS等操作系统所一致适用的标准化软件工具套件及显示架构的运作...
  • 黑群晖教程:(二)黑群晖安装DSM6.1x(图文)

    万次阅读 多人点赞 2019-01-14 22:51:45
    只需要注意DSM 6.x的引导需要支持UEFI引导的主板,如果主板仅支持Legacy请安装DSM5.x(这篇教程不适合DSM5.x)。 另外,博客所说的 NAS、黑群晖、群晖 都指的是你要做黑群晖的旧设备。 准备U盘一个,容量512M足矣...
  • /* 说明: **1.本次游戏实例是《cocos2d-x游戏开发之旅》上的最后一个游戏...这里用cocos2d-x 3.0版本重写,很多地方不同,但是从重写过程也很好的学习了cocos2d-x */ ***每一步对应的所有代码以及用到的资
  • 编写代码,以给定值x为基准将链表分割为两部分,所有小于x的结点将排在大于或等于x的结点之前。 /*传入链表的首结点,以及作为链表分割基准的值*/ #include using namespace std; typedef struct node { ...
  • VMware NSX for vSphere 6.x的许可版本以及与VMware NSX for vSphere 6.2.x,6.3.x和6.4.x的各种许可版本相关的功能列表。 适用于vSphere 6.4.1+的VMware NSX Data Center的许可版本,以及适用于vSphere 6.4.1+的...
  • “在汽车V2X市场预计车到基础设施通信系统(V2I)的增长速率最快”图1、V2I系统处于整个集成车载安全系统的核心汽车V2I预计将以最高的速度增长。 V2I通信的好处是例如通过向司机通报车辆与安装在道路上的传感器...
  • Python3.x+迅雷x 自动下载高分电影

    千次阅读 多人点赞 2020-01-11 15:07:33
    这是个Python GUI开发的库,图这个简陋的可怜的界面就是基于TK开发的,不想要界面也可以去掉,丝毫不影响爬电影,加上用户界面可以显得屌一点,当然最主要的是我想学习一点新知识 静态网页的分析技巧  相对于...
  • X Window System介绍

    千次阅读 2017-06-28 16:08:27
    1、概述  X Window System是1984年由麻省理工学院(MIT)和DEC公司共同开发研究的,是运行在UNIX系统上的视窗系统。严格地说,X Window System并不是一个软件,...能满足此协议及符合X协会其他规范的系统便可称为X
  • 初级X编程

    千次阅读 2009-03-18 13:49:00
    绪论本教程是有关X窗口编程的"would-be"系列教程的第一。单方面来说,这个教程是没用的,因为一个真正的X窗口程序员通常会使用抽象级更高的库,例如Modif(或者是它的自由版本lesstiff),GTK,QT或者其它类似的库...
  • Windows下虚拟机安装Mac OS X ----- VM12安装Mac OS X 10.11

    万次阅读 多人点赞 2016-05-07 01:53:29
    Windows下虚拟机安装Mac OS X VM12安装Mac OS X 10.11随着Iphone在国内大行其道,越来越多的开发者涌入IOS开发大军 ,但都苦于没有苹果机,本文即将介绍WIN虚拟MAC的教程。一、工具: Mac OS X 10.11 镜像文件...
  • HBuilderX连接夜神模拟器步骤

    千次阅读 2020-01-04 15:05:37
    首先您的电脑上需要安装 HBuilderX 与 夜神模拟器...第二步:在系统变量找到Path 将上面新建的HBuilderX的路径配置名称(%HBuilderXPath%)拼接在path 内容 第三:找到模拟器bin下debugReport.but运行查看模...
  • 今天本来打算使用BOX2D物理引擎的, 想到前段时间听说cocos2d-X在3.0版本封装了一个自己的物理引擎Physics, 听名字就很霸气额, 用起来确实是比BOX2D简单很多( 毕竟是基于BOX2D封装的嘛), 好了, 现在我们来看看如何...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 197,080
精华内容 78,832
关键字:

中x部