存档

‘cocos2d-x’ 分类的存档

cocos2dx 网络编程(CCHttpRequest和CURL两个方式)

2014年7月23日 没有评论

在使用之前需要设置一些参数:参考:http://blog.csdn.net/wangbin_jxust/article/details/9632771

在完成上面的操作后,还需要在链接器的输入里面添加一个参数pthreadVCE2.lib;

使用CCHttpRequest方法实现:(异步连接)

  1. void HallView::Qudian(){//网络异步连接方法  
  2.     cocos2d::extension::CCHttpRequest* postRequest=new cocos2d::extension::CCHttpRequest();  
  3.     postRequest->setRequestType(cocos2d::extension::CCHttpRequest::kHttpPost);//设置发送类型  
  4.     postRequest->setUrl("");//设置网址  
  5.     postRequest->setResponseCallback(this,callfuncND_selector(HallView::onHttpRequestCompleted));//回调函数,处理接收到的信息  
  6.   
  7.     string caozuo="";  
  8.     CCString *data=CCString::stringWithString(caozuo);  
  9.     postRequest->setRequestData(data->getCString(),data->length());//这里的代码会接在网络地址后面,一起发送。  
  10.   
  11.     cocos2d::extension::CCHttpClient* httpClient=cocos2d::extension::CCHttpClient::getInstance();  
  12.     httpClient->setTimeoutForConnect(10);<span style="font-family: Arial, Helvetica, sans-serif;">//设置连接超时时间</span>  
  13.     httpClient->setTimeoutForRead(10);//设置发送超时时间  
  14.     httpClient->send(postRequest);//设置接收数据类型  
  15.     postRequest->release();//释放  
  16. }  


添加一个回调方法。

  1. void HallView::onHttpRequestCompleted(cocos2d::CCNode *sender ,void *data){  
  2.     cocos2d::extension::CCHttpResponse* response=(cocos2d::extension::CCHttpResponse*)data;  
  3.     if(!response) {CCLOG("Log:response =null,plase check it."); return;}  
  4.       
  5.      //请求失败  
  6.     if(!response->isSucceed())  
  7.     {  
  8.         this->removeChildByTag(Animate_loading,true);  
  9.         CCDictionary* pDict = CCDictionary::createWithContentsOfFile("chines.xml");  
  10.         platform::showMsg(((CCString*)pDict->objectForKey("networking"))->getCString());  
  11.         CCLOG("ERROR BUFFER:%s",response->getErrorBuffer());  
  12.         return;  
  13.     }     
  14.   
  15.     int codeIndex=response->getResponseCode();  
  16.     const char* tag=response->getHttpRequest()->getTag();  
  17.       
  18.     //请求成功  
  19.     std::vector<char>* buffer=response->getResponseData();  
  20.     std::string temp(buffer->begin(),buffer->end());  
  21.     CCString* responseData=CCString::create(temp);  
  22.     Json::Reader reader;//json解析    
  23.     Json::Value value;//表示一个json格式的对象    
  24.     if(reader.parse(responseData->getCString(),value))//解析出json放到json中区    
  25.     {    
  26.           //这里就可以对返回来的信息做处理  
  27.         }  
  28.           
  29. }  





使用异步连接,程序和联网的方法将互相不干扰,联网方法将为一个独立的线程。

使用CURL方法实现:(同步连接)

第一个方法

需要加入 头文件#include "curl/curl.h"

  1. void HallView::denglu(){    //登陆游戏  
  2.     CURL *curl;    
  3.     CURLcode res;    
  4.     string cc;   
  5.     curl=curl_easy_init();    
  6.     if(curl)    
  7.     {   
  8.         curl_easy_setopt(curl, CURLOPT_URL, ""); //设置请求的地址    
  9.         curl_easy_setopt(curl, CURLOPT_POST, true); //设置数据类型  
  10.         string caozuo="";  
  11.         curl_easy_setopt(curl, CURLOPT_POSTFIELDS,caozuo.c_str()); //将操作代码,和连接的网站组合,一起发送!   
  12.         curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);  
  13.         curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,HallView::writehtml); //数据处理回调函数    
  14.         curl_easy_setopt(curl, CURLOPT_WRITEDATA, &cc);//缓冲的内存    
  15.         curl_easy_setopt(curl,CURLOPT_TIMEOUT_MS,5000); //设置连接超时时间    
  16.         res=curl_easy_perform(curl);    
  17.         if(res!=CURLE_OK)    
  18.         {    
  19.             CCDictionary* pDict = CCDictionary::createWithContentsOfFile("chines.xml");  
  20.             string mes=((CCString*)pDict->objectForKey("networking"))->getCString();  
  21.             platform::showMsg(mes);  
  22.         }    
  23.         curl_easy_cleanup(curl);    
  24.     }    
  25.     else    
  26.     {    
  27.         CCLog("curl is null");    
  28.     }    
  29. }  




在定义回调函数:这个方法为静态方法,如果里面要引用其他变量,需要为静态变量。

  1. size_t HallView::writehtml(uint8_t* ptr,size_t size,size_t number,void *stream)      
  2. {       
  3.     CCString* a=CCString::createWithFormat("%s",ptr);    
  4.     std::string str1=a->getCString();  
  5.     Json::Reader reader;//json解析    
  6.     Json::Value value;//表示一个json格式的对象    
  7.     if(reader.parse(str1,value))//解析出json放到json中区    
  8.     {    
  9.         string out=value["gameId"].asString();      
  10.         gameda->gameId=out;  
  11.         out=value["newIMSI"].asString();  
  12.         gameda->newIMSI=out;  
  13.     }    
  14.     return size*number;//这里一定要返回实际返回的字节数      
  15. }  




在.h中定义:

static size_t writehtml(uint8_t* ptr,size_t size,size_t number,void *stream); 
使用同步连接,联网方法的启动就直接阻塞游戏主进程的运行,直到获取到返回值为止。


curl_easy_setopt::属性
curlopt_url//URL地址值
curlopt_writefunction//将得到的数据传递相应的函数
curlopt_writeddata//将函数传递给相应的第四个参数里
curlopt_header//如果设置为1,可以返回http头的值;如果设置为非0值,则可以把一个头包含在输出中
CURLOPT_TIMEOUT_MS //设置cURL允许执行的最长毫秒数。
curlopt_low_speed_limit//设置一个长整型。控制传送多少字节
curlopt_cookie//传递一个包含httpcookie的头连接
curlopt_flie//传送到输出文件
curlopt_infile//传送过来的输出文件
curlopt_writeheader//输出头部分
curlopt_proxyuserpwd//传递一个形如[username]:[password]格式的字符串去连接http代理
curlopt_postfields//传递一个作为httppost操作的所有数据的字符串
curlopt_referer //在http请求中包含一个referer头的字符串
curlopt_useragent//在http请求中包含一个user-agent 头的字符串
curlpot_ftpport 传递一个包含被ftppost指令使用的IP地址
使用格式curl_easy_setopt( curl, CURLOPT_FOLLOWLOCATION, 1L); //第一个参数实例化的curl,第二个数属性,第三个为属性值

如果,获取的返回值是josn格式,我的博客中有方法非常方便提取指定的值。

分类: cocos2d, cocos2d-x, 未分类 标签:

cocos2d-x-3.2alpha0 如何创建新工程

2014年7月7日 没有评论

一直在用2.x所以对3.x接触的比较晚。直接上手cocos2d-x-3.2alpha0

1.在cocos2d-x-3.2alpha0/运行python命令

python setup.py

//它的作用是将下面这些路径加入到你的用户环境变量中,当然你也可以不添加

COCOS_CONSOLE_ROOT = ‘COCOS_CONSOLE_ROOT’
NDK_ROOT = ‘NDK_ROOT’
ANDROID_SDK_ROOT = ‘ANDROID_SDK_ROOT’
ANT_ROOT = ‘ANT_ROOT’

//如果你已经将COCOS_CONSOLE_ROOT = "COCOS_CONSOLE_ROOT’加入到了你的环境变量中,也就是说你运行了setup.py

2.那么直接运行下面命令

cocos new MyGame -p com.MyCompany.MyGame -l cpp -d e:/MyCompany

MyGame: name of your project
-p com.MyCompany.MyGame: package name for android
-l cpp: programming language used for the project, valid value is cppand lua
-d e:/MyCompany: directory to hold your project 

cocos在cocos2d-x-3.2alpha0/tools/cocos2d-console/bin下

好了,恭喜你新的项目创建成功了!你会发现这个项目很大,编译后有0.98G,因为它把引擎也拷贝过来了,我想它这样做的好处是简化项目配置环境,也就是说不管谁拿到这个项目都可以跑起来,不需要重新去改项目的配置了,不过也有缺点,就是每次新建一个项目,就得把cocos2d库重新编译下,不过在编译过后,可以直接copy政企项目后改项目名和名改名即可。

分类: cocos2d, cocos2d-x 标签:

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

2013年10月29日 没有评论

      大家好,我是红孩儿.上一节我们学习了使用《红孩儿工具箱》开发《打地鼠》游戏.这一节我们继续学习使用《红孩儿工具箱》来开发《坦克大战》游戏.

《坦克大战》这个游戏大家小时候都玩过,它给了我们儿时一个实现英雄梦的机会.现在每每想起和儿时的小伙伴们一起玩FC时的情景,都会感觉无限美好.唉,人一长大,各种烦闷都来了,不说这个了,下面来介绍一下本次《坦克大战》的设计规划.

我们这是个单机游戏,玩家可以控制一辆坦克在游戏中对敌方进攻的坦克进行回击,保卫自已的大本营.将敌方坦克都消灭了,当前关卡就胜利结束了.如果被敌方坦克消灭,那当前关卡就失败了.

首先,我们要制做一个开始界面,有一个背景图和两个按钮-开始游戏和退出游戏.这个界面的作用是给玩家一个宣明的主题,并让玩家选择是否游戏.需要使用工具箱的界面编辑器.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

 

之后,我们做一个战争的剧情动画,做这个是为了让玩家能够迅速的进入到主题,有一种身临其境的感受.为了更好的把战争的气氛渲染出来,我们这里使用打字机效果来说明剧情背景故事并配备一个老照片的动画更好的对人的心理进行一个触动.这里需要使用工具箱的字体编辑器来生成字图,以及使用动画编辑器来制做动画.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

然后我们进入到关卡界面,本次我们设计了十五个关卡,另外要求下次玩时要保存已玩过的关卡.这样就不必每次都从第一关开始玩了.这里当然还是要使用工具箱的界面编辑器.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

再继续就是游戏的主场景了,核心的玩法都在这里面了,场景由工具箱的地图编辑器进行制作,玩法逻辑则在代码中实现.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

打完一仗后有一个关卡结束的界面,对胜利,失败和击毁的坦克有一个结算处理.让玩家稍适休息,继续战斗.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

所有十五个关卡都结束会进入到通关界面,通过这个界面中的动画将战争结束的激动心情更好的烘托出来.玩到这里,相信很多小伙伴会把悬着的一颗心放下,感叹一下幸好没生活在战争时代,幸好,幸好.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

好了,我们现在开始第一步:开始界面制作.

一.开始界面制作


 首先我们需要从www.game2z.com  网站,在左上角的 下载红孩儿工具箱最新版 链接处下载最新的0.2.3版本。下载后我们将其解压到自定义的目录。打开后我们可以看到工具箱EXE程序和一个dx9的dll.还有一个资源目录Resource,一个测试设备图片存放目录Dev以及一个配套的SDK目录.红孩儿工具箱只需要这个EXE和对应的这个dll就可以运行在Windows操作系统了.本次的更新重点是多了一个工具箱配套库目录-HHR,这里面有两个子目录include和Lib
,分别为包含头文件和静态Lib库.同时又分为基础库HHRLib和高级特效库EffectLib.
 
在工具箱所在目录的资源目录Resource中有一个《坦克大战》的资源目录TankWar。为了方便学习和使用,我把《坦克大战》所需的资源和做好的场景地图,动画源文件都放在这里了.

启动工具箱,这时我们可以看到红孩儿工具箱的登录界面,我们一般查看可以选择离线登录,但离线登录不能导出动画和场景,主要是用于查看和编辑。如果是想导出,那就最好到www.game2z.com论坛注册一个正式用户的账号。在这里我们输入注册账号和密码后,点击“登录”,稍等片刻完成验证后即可进入工具箱主界面。

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      我们首先要做的,是先设置一下我们的工程资源目录,点击“设置工程资源目录”按钮,选择我们的资源目录即可,一旦设置完成,工具箱就会开始遍历和登记工程目录下所有的文件资源,这样我们就可以使用工程资源目录下的图片了.下次再使用工具箱时也会自动的设置好资源目录.

      进入工具箱后,我们来作做一下开始界面.选择界面编辑选项卡,进入到界面编辑器.当前版本的界面编辑仍是未完全版,有很多高级控件还未实现,不过对于一些简单的界面也基本够用了.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

 

      界面编辑器分为四大区域,顶部主要是界面文件打开,保存,导出按钮与控件选取区域,左边是控件关系树.右边是属性值设置区域,中间是界面的主视图区域.

      我们要制作一个界面,首先要考虑这个界面都需要哪些控件.在我们这个开始界面中,主要是有一个背景图和两个按钮,所以我们至少需要两种控件,分别为面板控件和按钮控件.我们现在来做一下.

      在左边的控件树的Root项下我们右键单击,在弹出的菜单中选择增加子结点-面板”.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      单击后,我们可以看到在中央视窗区域会生成一个面板.同时在右边的属性编辑区域出现相应的属性值.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      我们看到此时这个界面是处于被选中状态的,因为它的顶点和边界中点有拖动块,我们可以通过鼠标直接点击这个面板并移动它,也可以通过鼠标点击小拖动块来改变他的大小.如果觉得不好控制,还可以直接在右边的属性框中输入数据值来进行修改.另外我们可以通过鼠标中健或滚轮按下后移动鼠标来方便我们改变编辑时我们观察点.

      我们希望这个面板填充背景图片,这时我们来看一下面板控件的属性都有什么.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

       Visible:是否可见

      Enable:是否有效,无效的话就不能响应屏幕触屏控制.

      PosX,PosY,PosZ,具体的位置X,Y,Z

      Width,Height:控件宽高.

      Normal_Image:所要填充的图片

      ImageShowType:图片缩放方式,有按控件大小,按图片大小和平铺填充.

    在这里我们点击Normal_Image编辑框会有一个按钮.点击后弹出选择显示资源的对话框.这时我们可以选择控件要显示的图片或动画.

     使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      如果当前选择显示资源的对话框中没有我们需要的素材,我们可以点击“导入资源文件到当前资源库中”.并查找相应的图片或动画文件加入到这里.

    使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

   

      找到start.png后,我们点击"打开",这时start.png就被加入到资源库中了.我们点击左边对应的树项,这里可以在右边看到它,这里显示会有点大,我们可以点击"限制大小"来进行界面大小重置.

    使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      看着没什么问题,我们就可以点击"应用此图片"了.之后我们的面板就会填充相应的图片.然后再点击"关闭选择窗口"还进入到界面编辑器.

 

      这时候我们的面板位置,大小还不对,我们需要修改一下属性,既然图片是800×600大小,我们这里就让控件大小按照图片大小就可以了。选择ImageShowType为图片大小,之后将PosY设为-600。因为在Cocos2d-x中使用的是Opengl的坐标系,所以这里调整为-600。基准点00点以上显示界面。

    使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

   

      这样背景图就做好了,我们继续来做一下两个按钮。我们在左边的控件树的面板控件树项上右键单击,在弹出菜单中点选“增加子控件-按钮”,这时候在面板的左上角会产生出一个按钮控件。

    使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

    
然后我们把创建出来的按钮控件放到合适的地方,然后看一下属性编辑区域。

    使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

  

      这里除了和面板一件的基本属性外,又多了一些控件特有的属性

MoveUp_Image:这里是说鼠标移上时的图片资源,适用于PC游戏的开发,如果是触屏游戏就没这个需求了。

LButtonDown_Image:鼠标按下或触屏响应时的图片资源。

      按下响应函数:这个是对应界面的Lua响应处理函数名称。在界面编辑器保存导出后,会生成一个对应的Lua文件,如果是使用Lua来处理界面逻辑,则可以在这里进行函数名的设置,当然,工具箱本身已经给他默认了一个基于控件名称的响应函数.

      

      我们点击Normal_Image,MoveUp_Image,LButtonDown_Image分别在选择图片资源对话框中将"开始游戏"图片加入.

    使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

   

    使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

    使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

   

将这几种状态的图片资源都选中之后我们再设置控件也按图片大小进行显示,并拖放到合适的位置,这时候我们把鼠标移上或点击时就可以看到相应状态时的图片了.

   使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

  

      "开始游戏"的按钮做好了,我们在左边它对应树项上右键单击,在弹出菜单中点击"复制当前控件",然后在面板控件项上右键再单击,在弹出菜单中点击"粘贴到此结点下"这样就会又复制出一个新的按钮.我们用鼠标可以将它拖到合适的位置,并按照刚才设置
开始游戏"按钮的办法,将另外的"退出游戏"的按钮也做出来.

   使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

   使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

  

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

我相信大家一定可以自已做到,这里我就不再赘述了.

这个界面基本功能控件算做完了,下面我们保存一下界面start.xml

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

 这样界面只有静态图构成,稍单调了一点,现在我们来为他增加一些动画元素.在标题处增加两个旋转闪动的星星.我们点击工具箱的编辑器选项页的“动画编辑”,进入到动画编辑器.

在动画编辑器左边的结点树区域,在根结点Root上右键,在弹出的菜单中点击"增加图片结点".

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

 

 

      我们在图片上双击,会弹出选择图片资源的对话框.然后我们将事先准备好的两个星星的图片加载进来.

   使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

   使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

  

  应用此图片后,原来显示Cocos2d-x图标的结点会变成星星图片.

  使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

 

        我们在帧显示区域的60帧处右键单击,会弹出一个菜单,点击"加入关键帧".

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

 

之后我们可以看到帧显示区域在60帧处出现了关键帧的标记.然后我们在图片结点上点击右键.在弹出菜单里找到"调整自身旋转".

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

  

      

      点击之后,会弹出一个"调整图片自身的旋转"输入对话框.在这时我们希望图片绕Z轴旋转一圈.所以我们在z处输入角度360.即旋转一周.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

确定后我们可以再在30帧处增加一个新的关键帧.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      然后我们点击右上角的"属性编辑"对话框.这时候我们可以看到结点的属性编辑面板会显示出来.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

在这个编辑面板里.我们可以调整四个顶点的透明度都调整为0.这样可以让图片完全透明.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

这样这个动画就做好了,我们点击"播放动画"可以来查看一下.

  

 使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

   

最后我们保存一下动画.这里需要在弹出的"请设置动画名称"中输入动作名称.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      现在动画文件star.ani就做好了.我们继续把另一个星星的闪烁旋转的动画做好并保存之后,我们再加到界面编辑器中,在面板上新加入两个子控件面板.并设置其图片资源为我们刚才做好的两个动画文件.

      使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

       选中相应的动画后,应用为面板所对应的动画资源即可.这样我们就把界面上的动画元素做好了.

  

  使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

  保存一下,然后导出ui_start.ui待用.

二.剧情动画制作

这里我们要做一个打字机效果来显示剧情文字.并在结束后显示一个老照片动画.我们这里可以使用工具箱的文字字图编辑器来制作字图.我们点击工具箱的"字体编辑"按钮进入到字体编辑器.

首先我们将所用到的文字都放到一个新建的txt文档中.

      

     使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

然后我们在字体编辑器中加载相应的文档文件.之后在文字编辑框中会显示出文字.

  

  使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

  然后我们点击"选择字体",在弹出的字体对话框中选择一种字体使用.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

   

然后我们点击"开始生成",在等待几秒后,字图被生成出来.

  

  使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

点击"编辑文字效果"后可以进入到文字效果编辑面板.这时我们可以对颜色,描边以及纹理做一些调整.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      调好了之后就可以点击"导出字图"来将文字导出fntpng了.这样我们的剧情介绍字图就制作完成了.

三.剧情动画制作

在剧情文字介绍的打字机效果动画结束后,我们还需要再做一个老照片动画,现在我们点击“动画编辑”进入到动画编辑器.

  首先我们在左边的结点树控件区域右健,增加一个图片结点,然后我们按照之前的方法设置其对应的图片为老照片.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

之后我们在下部帧显示区域第50帧区间内右键单击,在弹出菜单里选择新增一个关键帧,同样也在51帧区间内增加一个关键帧,然后我们点击"属性编辑"

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

在展开的属性编辑面板里我们点击"隐藏精灵"复选框.使照片不显示.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用同样的方法,我们在53,55,57,58帧上增加关键帧,并设置显示或隐藏精灵,以达到动画播放时的快速照片闪现动画.

 

58帧显示设置之后,我们在下部的帧显示区域滑动条"当前显示起始帧"处调整一下帧显示区域的起始帧,这样我们可以设置区域显示不到的帧信息.我们在130帧处再增加一个关键帧.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

130帧处我们通过属性设置来设计照片的四个顶点颜色改变.这样就可以实现变色动画了.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

然后我们保存为aboutwar.ani并导出aboutwar.gan.以供使用.

四.选择关卡界面制作

            在看完战争的剧情动画后,我们将会进入到关卡界面,关卡界面主要是为了显示可以玩的关卡,它主要由一些按钮组成.在我们学会了制作开始界面后,这个关卡界面就很容易了.仍然是一样的方法.通过创建面板增加背景图,然后在上面加上一系列按钮并为它们选择相应状态图.在这里就不再赘述了.相信大家可以通过自已的实践来完成它.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

五.游戏场景制作

 

             游戏场景是游戏中最重要的部分了,我们终于开始进入到游戏场景的制作了,相信大家都很期待.我们现在就开始.

      点击"场景编辑"后进入到场景编辑器.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      在场景编辑器的左上角位置,点击按钮"创建",在弹出的对话框中输入场景的宽780,高度600,点击确定后在场景显示区域出现红色区域方框,即场景的大小.

      我们用鼠标点击场景编辑器的中间分隔部分向右拖动,调整到合适位置以方便我们观察和编辑.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

我们在右下部分的"场景层元素区域"中右键单击,在弹出的菜单中点击"增加新图层".这是一个背景图的格子图层,用于显示地表.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

  

  

我们在弹出的"创建新图层"对话框中输入图层名称,格子大小.之后确定.

  使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

        确定之后,会在层元素显示区域出现一个树控件,这样这一层就建好了,我们可以在这一层里加入一些元素.在这里面的空白区域右键单击,在弹出菜单上点选"创建新图类",输入BK后点击"确定".

  使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      这样这个图类就创建好了.我们在相应的图类树项上右健点击,在弹出菜单中点选"增加新图片",然后在弹出的文件对话框中将地表图片加进来.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

这样我们就完成了地表格子元素的导入.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      点击左上角的"网络显示"后,我们可以看到场景区域会出现网格,并吸选中的图片元素会自动吸咐在网格中.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

这时候我们就可以进行场景的第一层绘制了,在鼠标按下的状态下可以在场景中刷格子,如果按着Ctrl从左上向下拖动,则对角线所在矩形区域都会被刷上相同的元素.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

然后我们来创建第二层,在第二层中,我们要进行建筑的一些布局和事件点的设置.我们在图层显示区域BK选项卡右边空白处右键,在弹出的菜单中点击"增加新图层".

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      在弹出的对话框中输入与第一层基本相同的设置.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

 

在这一层中我们导入一些砖墙,树,金属墙,木桶等图片元素.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      待元素增加完之后,我们现在也可以开始尝试在场景中刷一些建筑

  使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      是不是很犀利?现在我们还要为这些元素设置一下相应的阻挡属性,我们在砖墙,金属墙,木桶等有阻挡的图片树项上右键点击,在弹出的菜单中点击"修改图片属性",在弹出的对话框中点选"是否阻挡".之后确定.

  使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

设置完相应的阻挡后,场景会有红色圈来标识出物体所在的阻挡格子.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

我们用动画编辑器再制作一个鹰的变色动画放在场景的基地中.这个动画的制作很简单,学到这一步大家应该都会了,就是做关键帧,并在相应关键帧上调整元件的顶点色.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

做好后保存为home.ani供场景使用.并导出home.ganCocos2d-x中调用.然后我们加到场景编辑器.在第二层中按之前同样的方法增加图片资源,只是在文件类型中选择动画文件ani.找到home.ani加入进来.并选中它放在基地中间.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

 

  

      越来越像回事了,不是么?下面我们来为场景中增加一些坦克出生点,这个可以用事件格来做.我们在图层显示区域的Build树项上右键,在弹出菜单中点击“增加空事件格”,这时就可以看到在当前层分类下有一个新的子项“空事件格”。

   使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

我们选中它后在场景中的当前层相应格子中点击,就可以增加事件格了。

   使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      这些事件格都有一个ID值,默认为-1,我们可以自行设置它的ID,并在编写代码时进行访问和区分事件格,这里我们设置敌方坦克出生点的事件为0,我方坦克出生点主位为1,副位为2。我们只要在场景显示区域中事件点右键,点击弹出菜单的“设置当前的事件点ID”项,就可以为事件点设置ID了。

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

在这个场景中,我们将上边的事件点设置为0,基本左右方的事件点设置为12

  

   使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

这样这个场景就做好了。我们保存为第一关即可。

      用同样的方法,我们可以迅速的做出十五个关卡.

  使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

  

六.角色动作管理

      工具箱提供了对于分类角色的动作管理,通过对于角色动作的分类管理,我们可以更加清晰方便的在Cocos2d-x中对于角色的动作进行播放,而不必在代码中手动编写相应的动画文件名称或PLIST

   使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

 

 

      点击“角色编辑”面板,我们可以看到与动画编辑器类似的界面,在最左边的区域是分类角色与动作的一个树控件。我们在这里对游戏中的所有角色和动作进行统一管理。下面我们来做一下。

 

首先我们要确定都有哪些角色.

列举一下,应该有5个角色。

<1>我方主坦克

<2>敌方普通坦克

<3>敌方快速战车

<4>敌方重型坦克

<5>子弹

      为什么把子弹也作为一个角色了呢?因为在这个游戏里,子弹是在场景中运动的,另外它有两种,普通子弹和穿甲弹。我们可以把这两种子弹作为子弹角色的两个动作。当然,在没有好的美工辅助的情况下,我只能用单帧图片来做这样的动作。

 

我们在动画编辑器中创建出相应的图片结点并一一为其设置相应的图片资源后保存为各自的动作文件player.ani,enemy1,an,enemy2.ani,enem3.ani,bullet.ani,bullet3.ani

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

在弹出的对话框里输入类名称为“Tank”,然后确定,这样就有了一个新分类,我们在其对应树项上右键单击,在弹出菜单中点选“增加新角色”。

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

之后在弹出的对话框里输入角色名称“Player”,点击确定,这样在其树项下就又生成出一个角色树项,继续~,我们为这个角色导入动画player.ani

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

按照这种办法,我们继续将所有的角色和动作文件一一导入,只是子弹导入bullet.anibullet3.ani两个动作文件。

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      这样我们保存Tank.rol并导出Tank.gro。在Cocos2d-x中只需要根据角色名称就可以创建相应角色,通过动作名称就可以播放相应动画了。虽然这里只是一帧的单图。

八.爆炸粒子效果制作

             在这个游戏中,爆炸的粒子效果有两种,一种是基础粒子效果,在工具箱的粒子编辑面板里制作,另一种是高级粒子效果,在工具箱的效果编辑面板里制作。我们先来看一下基础粒子效果的制作,点击“粒子编辑”切换到粒子编辑器,从粒子模版库中选择“Fire”,然后更换图片为fire.png。之后设置一些属性,主要是角度要设成360度随机。

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

之后就可以看到我们需要的爆炸粒子特效了,

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

  保存为explode.plist供游戏中使用。

      然后我们来看一下高级爆炸效果的制作。点击“效果编辑”进入到效果编辑器。

  使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      在主视窗区域右键单击,在弹出菜单里选择“添加粒子系统效果”,然后在材质属性上替换图片。

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      点击粒子发射器参数,修改粒子总数目,每秒发射粒子数目和修改Z轴发散角度为180,注意这个Z轴发散角度的意思是说(-180~180)。并修改生命变化0.5速度变化50宽度10高度10宽度变化5高度变化5,经过粒子参数的设置,这个时候粒子的参数增加了很多随机性。      

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      点击粒子宽高缩放关键帧,这时会弹出关键帧编辑界面

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

在生命周期时间帧上起始帧,0.4生命周期帧,和结束帧上都双击,做一些大小的调整。

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      比如在开始帧处宽高为1.00.4生命周期处宽高为2.0,结束时宽高为3.0

      按同样的方法我们修改粒子颜色关健帧。

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

并修改粒子速度缩放关键帧,修改初始帧和结束帧的速度缩放值.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      这样这个粒子运动速度就实现了由慢变快的效果.最后,我们修改下粒子系统的喷射时间,点击基础属性,修改生命时间为0.16,修改名字为爆炸火星.

  使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      我们做好这个爆炸粒子效果后,按照同样的方式增加几个其它的粒子.

闪动火焰:修改纹理名字为huangguang.png,粒子总数目为1,每秒喷射粒子数目为1,生命变化为0,宽度为64,宽度变化为0,高度64,高度变化为0,粒子重力为0,速度为0.速度变化为0.设置粒子宽高缩放关键帧为

  使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

设置粒子颜色关键帧为

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

粒子速度缩放关键帧不设置.

 

 扩散光圈:纹理名字为qilang_mogu.png,粒子生命时间为0.12,总数目为1,每秒喷射粒子数目为1,生命为0.5,宽度为256,宽度变化为0,高度256,高度变化为0,粒子重力为0,速度为0.速度变化为0,设置粒子宽高缩放关键帧为

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

设置粒子颜色关键帧为

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

粒子速度缩放关键帧不设置

扩散光圈2:纹理名字为ring_huangquan.png,粒子生命时间为1,总数目为1,每秒喷射粒子数目为1,生命为1.0,宽度为256,宽度变化为0,高度256,高度变化为0,粒子重力为0,速度为0.速度变化为0,设置粒子宽高缩放关键帧为:

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

设置粒子颜色关键帧为

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

粒子速度缩放关键帧不设置.

最后我们点击下左测的效果元素列表,

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

将效果保存.

七.代码编写

             现在终于进入到代码编写的阶段了,是不是手早就痒痒了。我们将HelloCpp拷出一份进行改造就可以了,将其工程名改为TankWar

 

             现在我们要进行一下设计规划。首先我们再确定一下游戏的流程。

 

             首先是初始化HelloWorld层时加载开始界面和预载入一些音声文件。开始界面ui_start.ui中的按钮可以通过Lua来进行界面显示隐藏的逻辑处理,并通过回调函数来进行对于游戏界面切换时的一些逻辑进行处理。

 

             点击“开始游戏”按钮后,在回调函数中创建打字机效果来展示剧情文字。在所有的文字展示完之后播放一个老照片的显示动画。在动画结束帧处增加回调函数,进入到选关卡界面ui_chosemap.ui

 

             ui_chosemap.ui创建后根据存档信息来设置相应的关卡按钮可以被点选及其相应的回调函数,在这个回调函数中创建出场景并初始化游戏敌我双方的坦克设置。

 

             为场景中的敌我双方的坦克做逻辑处理。这个可以通过重载场景库中的现有NPC类来做到。通过其NPC私有属性结构的重载来进行私有属性的设计,在其每帧update函数的重载中做一些逻辑判断。比如键盘的按下响应或位置的更新,在这里我们按下攻击键时,要产生出一个子弹。子弹也可以通过重载场景库中的现有NPC类来做到,在重载其每帧的update函数中进行与坦克和阻挡物的碰撞判断,如果与坦克碰撞上,减少坦克的HP,如果与阻挡物碰撞上,击毁阻挡物并同时创建粒子爆炸效果。

             

             在坦克被击中减血到0时,统计击中的坦克数量,如果所有坦克被击毁了,那就释放场景。并显示胜利统计界面。当然,如果我方坦克或基地之鹰被击毁,那则显示失败统计界面。在点击Go!!!按钮后会如果是胜利就开始下一关卡,如果是失败就重新开始当前关卡。

 

 

             所有场景都结束之后,要显示一下通关的老照片动画。游戏回到开始界面。

 

             上面的流程确定了,下面我们来认识一下工具箱的配套库。

 

             工具箱的配套库暂时由两个模块来构成,一个是基础模块HHRLib,一个是高级特效模块EffectLib.基础模块HHRLib中包括动画解析与播放,界面解析与播放,场景解析与播放,角色列表解析,资源加密解密等功能。高级特效模块EffectLib中主要是特效与技能的解析与播放。当然,这也都只是当前版本的功能,未来肯定还会不断的扩展和强大。

             我们将这两个目录拷到坦克大战的目录下,并在TankWar工程设置里加入包含目录与库目录。

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

如上图示,将HHRLib_d.lib,EffectLib_d.lib放在debug模式下工程设置的引用库中。如果是Release模式,则换成HHRLib.libEffectLib.lib

 

      现在工具箱的配套库和头文件就引入到我们的工程中了,我们就可以解析和显示工具箱导出的界面和场景等内容了。

 

      这个游戏规模很小,我们的所有游戏界面处理主要放在HelloWorldScene所在的中就行了。下面我们开始流程的第一步:加载和显示开始界面。

      首先我们这个游戏画面大小约定为800×600像素,我们修改一下main.cpp中的大小设置。

USING_NS_CC;

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // create the application instance
    AppDelegate app;
    CCEGLView* eglView = CCEGLView::sharedOpenGLView();
    eglView->setViewName("HelloCpp");
    eglView->setFrameSize(800, 600);
    // The resolution of ipad3 is very large. In general, PC's resolution is smaller than it.
    // So we need to invoke 'setFrameZoomFactor'(only valid on desktop(win32, mac, linux)) to make the window smaller.
    eglView->setFrameZoomFactor(1.0f);
    return CCApplication::sharedApplication()->run();
}

      然后我们打开AppMacros.h,将ipad的大小修改成800×600,注意:这样做只针对于win32平台的这个DEMO,如果你是真的为ipad写游戏,那还是要根据相应的设备大小进行游戏的开发。

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

在完成了WIN32模式下窗口大小的修改后,我们在HelloWorld.h中定义一个枚举,用来标识所要用到的界面ID。这是一个好的习惯,方便后面的ID获取,如果用数字的话,往往容易忘记。

enumNodeID

{

   NID_UI_START
= 1,      
//开始界面

   NID_ANI_WAR
= 2,       
//战争介绍动画

   NID_UI_CHOSEMAP
= 3,   
//选关卡界面

   NID_UI_FXP
= 4,        
//方向与攻击控制界面

   NID_UI_FINISH
= 5,     
//关卡结束界面

   NID_UI_WIN
= 6         
//通关界面

};

然后我们可以在HelloWorldinit函数中加入创建开始界面的代码:

//开始界面
	CGameUI*		pNewUI = new CGameUI();
	pNewUI->autorelease();
	pNewUI->LoadUITree("ui_start.ui");
	//设置界面的位置
	pNewUI->setOffset(0, 0);
	this->addChild(pNewUI,1,NID_UI_START);

这样我们把做好的ui_start.ui和相应的图片资源拷到工程的Resource下的ipad目录中。我们就可以运行一下,看到相应的界面了,但是这个界面还不能响应点击,我们可以来处理一下:

      首先我们把ui_start.lua也拷到ipad中。并修改其中代码:

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      我们在“开始游戏”的按钮对应的LUA函数中加入了对于开始界面的隐藏处理。

      在“结束游戏”的按钮对应的LUA函数中加入了退出程序的处理。

      在完成后,大家可以运行下测试一下。在点击“开始游戏”的按钮后,我们还希望能够启动后面的剧情文字介绍。这时候我们用LUA做就没必要了,可以直接对按钮增加回调函数:

//通过控件名称获取到相应的控件
	CUICtrl*	pUICtrl = pNewUI->QueryUICtrl("UICtrl2_Button");
	if(pUICtrl)
	{
		//转化为按钮
		CUIButton*	pUIButton = dynamic_cast<CUIButton*>(pUICtrl);
		if(pUIButton)
		{
			//设置在按下时响应的回调函数
			pUIButton->setClickEndCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::CreateAboutUI), (void*)0xbebabeba));
		}
	}

      在这个CreateAboutUI的回调函数中,我们将创建相应的文字行并设定一个定时回调的函数CreateAboutPrintAni函数来显示打字机动画。因为文字是通过我们工具箱创建的字图来生成的,所以我们要创建出每一行的CCLabelBMFont打字机效果的实现通过每次调用定时回调函数时对这一行的文字要显示的字数加一就可以实现了,这样我们就需要统计出当前行的字数和总共的字数,我们在HelloWorld类中增加相应的成员。

private:

	//第一行
	int				m_nPrintCharSize1_Total;
	int				m_nPrintCharSize1_Curr;
	string			m_strPrintText1;
	CCLabelBMFont*  m_pAboutTextLable1;

	//第二行
	int				m_nPrintCharSize2_Total;
	int				m_nPrintCharSize2_Curr;
	string			m_strPrintText2;
	CCLabelBMFont*  m_pAboutTextLable2;

	//第三行
	int				m_nPrintCharSize3_Total;
	int				m_nPrintCharSize3_Curr;
	string			m_strPrintText3;
	CCLabelBMFont*  m_pAboutTextLable3;

	//第四行
	int				m_nPrintCharSize4_Total;
	int				m_nPrintCharSize4_Curr;
	string			m_strPrintText4;
	CCLabelBMFont*  m_pAboutTextLable4;

	//第五行
	int				m_nPrintCharSize5_Total;
	int				m_nPrintCharSize5_Curr;
	string			m_strPrintText5;
	CCLabelBMFont*  m_pAboutTextLable5;

因为我们使用到中文,所以我们按照之前做《打地鼠》的经验,可以将这些文字放到PLIST中再读取。

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

并在CPP中增加CreateAboutUI函数:

	//开始介绍战争
void HelloWorld::CreateAboutUI(CCNode* pSender, void* data)
{
	if(m_pAboutTextLable1)
	{
		CGameUI*		pNewUI
= dynamic_cast<CGameUI*>(getChildByTag(NID_UI_START));
		if(pNewUI)
		{
			pNewUI->setVisible(false);
		}
		return ;
	}
	//因为我们要显示的文字是汉字,所以为了避免乱码,我们在这里将文字存入到XML中,然后在Cocos2d-x中读取。
	CCDictionary *strings = CCDictionary::createWithContentsOfFile("string.plist"); 
	//第一行
	m_strPrintText1 = ((CCString*)strings->objectForKey("line1"))->m_sString; 
	m_nPrintCharSize1_Total = m_strPrintText1.size();
	m_nPrintCharSize1_Curr = 0;
	//第二行
	m_strPrintText2 = ((CCString*)strings->objectForKey("line2"))->m_sString; 
	m_nPrintCharSize2_Total = m_strPrintText2.size();
	m_nPrintCharSize2_Curr = 0;
	//第三行
	m_strPrintText3 = ((CCString*)strings->objectForKey("line3"))->m_sString; 
	m_nPrintCharSize3_Total = m_strPrintText3.size();
	m_nPrintCharSize3_Curr = 0;
	//第四行
	m_strPrintText4 = ((CCString*)strings->objectForKey("line4"))->m_sString; 
	m_nPrintCharSize4_Total = m_strPrintText4.size();
	m_nPrintCharSize4_Curr = 0;
	//第五行
	m_strPrintText5 = ((CCString*)strings->objectForKey("line5"))->m_sString; 
	m_nPrintCharSize5_Total = m_strPrintText5.size();
	m_nPrintCharSize5_Curr = 0;

	//创建相应的文字标签
	CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
	m_pAboutTextLable1 = CCLabelBMFont::create("", "card_0_text.fnt");
    m_pAboutTextLable1->setPosition(ccp(20, visibleSize.height - 100));
	m_pAboutTextLable1->setAlignment(kCCTextAlignmentLeft);
	m_pAboutTextLable1->setAnchorPoint(ccp(0,0));
    this->addChild(m_pAboutTextLable1,3);

	m_pAboutTextLable2 = CCLabelBMFont::create("", "card_0_text.fnt");
    m_pAboutTextLable2->setPosition(ccp(20, visibleSize.height - 150));
	m_pAboutTextLable2->setAlignment(kCCTextAlignmentLeft);
	m_pAboutTextLable2->setAnchorPoint(ccp(0,0));
    this->addChild(m_pAboutTextLable2,3);

	m_pAboutTextLable3 = CCLabelBMFont::create("", "card_0_text.fnt");
    m_pAboutTextLable3->setPosition(ccp(20, visibleSize.height - 200));
	m_pAboutTextLable3->setAlignment(kCCTextAlignmentLeft);
	m_pAboutTextLable3->setAnchorPoint(ccp(0,0));
    this->addChild(m_pAboutTextLable3,3);

	m_pAboutTextLable4 = CCLabelBMFont::create("", "card_0_text.fnt");
    m_pAboutTextLable4->setPosition(ccp(20, visibleSize.height - 250));
	m_pAboutTextLable4->setAlignment(kCCTextAlignmentLeft);
	m_pAboutTextLable4->setAnchorPoint(ccp(0,0));
    this->addChild(m_pAboutTextLable4,3);

	m_pAboutTextLable5 = CCLabelBMFont::create("", "card_0_text.fnt");
    m_pAboutTextLable5->setPosition(ccp(20, visibleSize.height - 300));
	m_pAboutTextLable5->setAlignment(kCCTextAlignmentLeft);
	m_pAboutTextLable5->setAnchorPoint(ccp(0,0));
    this->addChild(m_pAboutTextLable5,3);
	//每0.06秒出现一个字
	schedule(schedule_selector(HelloWorld::CreateAboutPrintAni), 0.06f);
	//播放打字机的音效
m_nSoundEffectID = SimpleAudioEngine::sharedEngine()->playEffect("1943.mp3");
	m_bShowAboutTextAndAni = true;
	m_nAwardTotal = 0;

}

      在这个函数里只是初始化了每一行的文字标签,真正的打字机效果定时更新文字是在每0.06秒定时调用的CreateAboutPrintAni函数中。

//开始战争介绍-显示打字机动画效果
void HelloWorld::CreateAboutPrintAni(float dt)
{		
	char	tTemp[1024];
	memset(tTemp,0,sizeof(tTemp));
	m_nPrintCharSize1_Curr++;
	if(m_nPrintCharSize1_Curr >= m_nPrintCharSize1_Total)
	{
		m_nPrintCharSize2_Curr++;

		if(m_nPrintCharSize2_Curr >= m_nPrintCharSize2_Total)
		{
			m_nPrintCharSize3_Curr++;

			if(m_nPrintCharSize3_Curr >= m_nPrintCharSize3_Total)
			{
				m_nPrintCharSize4_Curr++;

				if(m_nPrintCharSize4_Curr >= m_nPrintCharSize4_Total)
				{
					m_nPrintCharSize5_Curr++;

					if(m_nPrintCharSize5_Curr >= m_nPrintCharSize5_Total)
					{
						m_nPrintCharSize5_Curr++;
								unschedule(schedule_selector(HelloWorld::CreateAboutPrintAni));		
						//停止音效ID
						SimpleAudioEngine::sharedEngine()->pauseEffect(m_nSoundEffectID);
						//文字隐藏
						removeChild(m_pAboutTextLable1);
						removeChild(m_pAboutTextLable2);
						removeChild(m_pAboutTextLable3);
						removeChild(m_pAboutTextLable4);
						removeChild(m_pAboutTextLable5);

						//播放动画
scheduleOnce(schedule_selector(HelloWorld:: CreateAboutPictureAni), 1.0f);

					}
					else
					{
						strcpy(tTemp,m_strPrintText5.c_str());
						memset(tTemp+m_nPrintCharSize5_Curr,0,1024-m_nPrintCharSize5_Curr);
						m_pAboutTextLable5->setCString(tTemp);
					}
				}
				else
				{
					strcpy(tTemp,m_strPrintText4.c_str());
					memset(tTemp+m_nPrintCharSize4_Curr,0,1024-m_nPrintCharSize4_Curr);
					m_pAboutTextLable4->setCString(tTemp);
				}

			}
			else
			{
				strcpy(tTemp,m_strPrintText3.c_str());
				memset(tTemp+m_nPrintCharSize3_Curr,0,1024-m_nPrintCharSize3_Curr);
				m_pAboutTextLable3->setCString(tTemp);
			}

		}
		else
		{
			strcpy(tTemp,m_strPrintText2.c_str());
			memset(tTemp+m_nPrintCharSize2_Curr,0,1024-m_nPrintCharSize2_Curr);
			m_pAboutTextLable2->setCString(tTemp);
		}

	}
	else
	{
		strcpy(tTemp,m_strPrintText1.c_str());
		memset(tTemp+m_nPrintCharSize1_Curr,0,1024-m_nPrintCharSize1_Curr);
		m_pAboutTextLable1->setCString(tTemp);
	}
}

      这样就实现了带感的打字机介绍剧情的动画效果。在所有文字都介绍完之后,我们调用了停止音效的函数并注销了定时回调函数,并调用一个CreateAboutPictureAni函数来显示老照片动画。这个函数很简单,加载工具箱制做的老照片动画并播放就OK了。

//显示老照片动画
void HelloWorld::CreateAboutPictureAni(float dt)
{
		//创建动画
	C2DSkinAni*		pNewSkinAni = new C2DSkinAni();
	if(pNewSkinAni)
	{
		if(true == pNewSkinAni->ReadAllSubBodyFromBin("card_0_about.gan"))
		{
			CCSize tBoundingBox = pNewSkinAni->GetBoundingBoxSize();
			pNewSkinAni->autorelease();
			pNewSkinAni->Play(1);
			this->addChild(pNewSkinAni,4,NID_ANI_WAR);
			CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
			pNewSkinAni->SetOffset(visibleSize.width/2,visibleSize.height/2 );
			//最后一帧加一个函数调用
			pNewSkinAni->SetEndFrameCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::AboutUIEndFrameCallBack), (void*)0xbebabeba));
		}
	}

      我们在动画的最后一帧加入了一个函数调用AboutUIEndFrameCallBack。我们将在这个函数里做一下稍后调用显示选关卡界面的处理:

//老照片动画显示结束的回调
void HelloWorld::AboutUIEndFrameCallBack(CCNode* pSender, void* data)
{
	//停留一秒后调用显示选关卡函数ShowCardUI。
	scheduleOnce(schedule_selector(HelloWorld::ShowCardUI), 1.0f);
}

增加一个ShowCardUI函数,在这个函数里,我们将加载选关卡的界面,并为相应的函数增加回调函数:

//选择关卡界面
void HelloWorld::ShowCardUI(float dt)
{
	//显示剧情动画结束,将动画设为不显示并移除
	m_bShowAboutTextAndAni = false;
	C2DSkinAni*		pNewSkinAni = dynamic_cast<C2DSkinAni*>(getChildByTag(NID_ANI_WAR));
	if(pNewSkinAni)
	{
		pNewSkinAni->setVisible(false);
		removeChild(pNewSkinAni);
		//pNewSkinAni->release();
	}

	//取得选关卡的界面.如果存在,则直接显示,如果不存在则新建并加载.这一步是因为有个后退按钮可以退到开始界面,再次点击"开始游戏"按钮时就不需要重复创建了.
	CGameUI*		pChoseChardUI = dynamic_cast<CGameUI*>(getChildByTag(NID_UI_CHOSEMAP));
	if(pChoseChardUI)
	{
		pChoseChardUI->setVisible(true);
	}
	else
	{
		//选关卡界面
		CGameUI*		pNewUI = new CGameUI();
		pNewUI->autorelease();
		pNewUI->LoadUITree("ui_chosecard.ui");
		//设置界面的位置
		pNewUI->setOffset(0, 0);
		this->addChild(pNewUI,1,NID_UI_CHOSEMAP);

		//设置按钮的回调,只有玩过的能选择。
		char	szCtrlName[64];
		for(int i = 0 ; i < 15 ; i++)
		{
			sprintf(szCtrlName,"UICtrl%d_Button",i+3);
			CUICtrl*	pUICtrl = pNewUI->QueryUICtrl(szCtrlName);
			if(pUICtrl)
			{
				CUIButton*	pUIButton = dynamic_cast<CUIButton*>(pUICtrl);
				if(pUIButton)
				{
					//如果关卡能玩,则设置回调函数响应按钮,否则不能响应并置为灰色处理
					if( i <= m_nLastPlayMapID )
					{
						//设置在按下时响应的回调函数,可带自定义参数
						pUIButton->setClickBeginCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::ChoseCardBtnCallBack), (void*)i));

						//关卡上的文字
						char szText[10];
						sprintf(szText,"%d",i+1);
						m_pTankTextLabel[i] = CCLabelBMFont::create(szText, "number.fnt");
						POINT tPoint = pUICtrl->GetWorldPos();
						int   nWidth = pUICtrl->getWidth();
						int   nHeight = pUICtrl->getHeight();
						if(i >= 10)
						{
							m_pTankTextLabel[i]->setPosition(ccp(tPoint.x+nWidth/4,tPoint.y-nHeight/2));
						}
						else
						{
							m_pTankTextLabel[i]->setPosition(ccp(tPoint.x+nWidth/3,tPoint.y-nHeight/2));
						}
						m_pTankTextLabel[i]->setAlignment(kCCTextAlignmentLeft);
						m_pTankTextLabel[i]->setAnchorPoint(ccp(0,0));
						pNewUI->addChild(m_pTankTextLabel[i],1);
					}
					else
					{
						//设置按钮灰色
						CCSprite*	pScrpit = pUIButton->getSprite();
						if( pScrpit )
						{
							pScrpit->setColor(ccc3(128,128,128));
						}
					}
				}
				
			}
		}
	}
}

增加一个空函数,用于在后面编写创建关卡的处理

//开始第N关
void HelloWorld::ChoseCardBtnCallBack(CCNode* pSender, void* data)
{

}

    在这些可以选择关卡的按钮上我们设定了相应的点击回调处理函数,这个函数将实现相应关卡的场景加载与敌我坦克的设置,到这里,我们将开始游戏场景的处理.

    首先我们要熟悉一下工具箱的场景类.我们打开HHRLib的HHR_SceneManage.h看一下,这个CGameScene类是由CCLayer派生出来的。它的主要功能是解析场景文件并显示。

    它的头文件里有一个类CGameSceneNpc,这是一个NPC的基类,在CGameScene里会有相应的容器来存放游戏场景中需要的NPC实例,而我们只要由CGameSceneNpc派生出我们需要的NPC并调用CGameScene的成员函数AddNew2DActiveObj就可以把我们的NPC实体放到场景中了。我们的NPC的逻辑可以在其重载基类的update中进行处理。

    比如,现在我们加入我们玩家的主坦克类。

    新建文件CMyTank.h/cpp,由CGameSceneNpc类派生出CMyTank类。

#ifndef _CMYTANK_H
#define _CMYTANK_H
//===========================================================
//Auto:火云红孩儿 QQ:285421210
//Date:2013-10-11
//Desc:玩家的坦克
//===========================================================
#include "Header.h"
#include "HHR_Character.h"
#include "HHR_SceneManage.h"

//当前玩家的坦克
class CMyTank	:public CGameSceneNPC
{
public:
	CMyTank();
	~CMyTank();
public:
	//初始化
	virtual void	Init();
	//每帧调用的更新
	virtual void	update(float fDelaySecond);
	//每帧调用的渲染
	virtual void	Render();
public:
	//设置当前触屏按键
	void	SetDirKey(enuDirKey vDir);
	//设置开炮
	void	SetAttackKey(bool bAttack);

	//设置无敌状态
	void	setNBState();
	//是否无敌状态
	bool	IsNBState();

	//设置发射穿甲弹
	void	setNBBulletState();
	//是否发射穿甲弹
	bool	IsNBBulletState();

	//坦克血量
	VAR_SETGET(int,m_nHP,HP);
private:
	//上次的攻击时间
#include "CMyTank.h"
CMyTank::CMyTank()
{
	m_dwLastAttackTime = 0;
	m_nDir = DK_UP;
	m_nDirKey = DK_NONE;
	m_bAttackKey = false;
	m_nHP = 10;
	m_bNB = false;
	m_NBTime = 0;
	m_bNBBullet = false;
	m_NBBulletTime = 0;
}
CMyTank::~CMyTank()
{

}

//初始化
void		CMyTank::Init()
{

}
//每帧调用的更新
Void		CMyTank::update(float fDelaySecond)
{
	if( m_pCharacter && CGameSceneNPC::m_pCurrGameMap)
	{	
		//无敌状态时间计算
		if(m_bNB)
		{
			m_NBTime += fDelaySecond ;
			if(m_NBTime > 10000)
			{
				m_bNB = false;
				m_NBTime = 0;
			}
		}
		//穿甲弹状态时间计算
		if(m_bNBBullet)
		{
			m_NBBulletTime += fDelaySecond ;
			if(m_NBBulletTime > 10000)
			{
				m_bNBBullet = false;
				m_NBBulletTime = 0;
			}
		}

		fDelaySecond = min(fDelaySecond,0.2);
		//取得场景信息
		stSceneHeader*	tpSceneHeader =	CGameSceneNPC::m_pCurrGameMap->GetSceneHeader();
		CCPoint	tScenePos_Old;
		CCPoint	tScenePos;
		tScenePos_Old =	tScenePos = m_pCharacter->GetScenePos();
		CCSize	tBoundingBox = m_pCharacter->GetBoundingBoxSize();

		enuDirKey		nDir = m_nDirKey;

		//按下左键
		if ((::GetKeyState('A'))&0x8000) 
		{
			nDir = DK_LEFT;
		}
		//按下右键
		if ((::GetKeyState('D'))&0x8000) 
		{
			nDir = DK_RIGHT;
		}
		//按下上键
		if ((::GetKeyState('W'))&0x8000) 
		{
			nDir = DK_UP;
		}
		//按下下键
		if ((::GetKeyState('S'))&0x8000) 
		{
			nDir = DK_DOWN;
		}

		//按下下键
		if (m_bAttackKey || (::GetKeyState('J'))&0x8000) 
		{
			struct timeval tv;
			gettimeofday(&tv,NULL);
			double	   	    dwCurrTime = tv.tv_sec*1000.0+tv.tv_usec* 0.001;
			//开炮时间
			if(dwCurrTime - m_dwLastAttackTime > 1000)
			{	
				//创建一个子弹角色
				//这里待加入创建子弹的代码
				m_dwLastAttackTime = dwCurrTime ;
			}
		}

		if(nDir<0)return;
		m_nDir = nDir;
		float	fHalfWidth = tBoundingBox.width * 0.5 ;
		float	fHalfHeight = tBoundingBox.height * 0.5 ;

		float	fMoveSpeed		= 80;
		bool	bCollResult		= false;
		
		int		nLayer			= 1;
		//当前所在格子
		POINT	tTile			= CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x,tScenePos.y,false);

		//取得格子对应的中心点
		CCPoint	tTile_Center	= CGameSceneNPC::m_pCurrGameMap->GetTileCenterPoint(nLayer,tTile.x,tTile.y,false);		
		//设置图片的旋转方向
		m_pCharacter->GetCurrBody()->setRotateZ(90 * nDir);	

		switch(nDir)
		{
		case DK_LEFT:
			{
				//左
				tScenePos.x  -= fMoveSpeed*fDelaySecond ;

				//取得相应层的相应格子数据
				POINT	tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x-fHalfWidth,tScenePos.y,false);
		
				stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);

				if(tpBlockRenderInfo)
				{
					if(tpBlockRenderInfo->m_bIsObstruct)
					{
						bCollResult = true;
						tScenePos = tScenePos_Old ;
					}
				}
				if(false == bCollResult)
				{
					tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x-fHalfWidth,tScenePos.y - fHalfHeight * 0.6,false);

					tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);
					if(tpBlockRenderInfo)
					{
						if(tpBlockRenderInfo->m_bIsObstruct)
						{
							bCollResult = true;
							tScenePos = tScenePos_Old ;
						}
					}
				}
				if(false == bCollResult)
				{
					tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x-fHalfWidth,tScenePos.y + fHalfHeight * 0.6,false);

					tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);
					if(tpBlockRenderInfo)
					{
						if(tpBlockRenderInfo->m_bIsObstruct)
						{
							bCollResult = true;
							tScenePos = tScenePos_Old ;
						}
					}
				}
			}
			break;
		case DK_UP:
			{
				//上
				tScenePos.y  += fMoveSpeed*fDelaySecond ;
				//取得相应层的相应格子数据
				POINT	tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x,tScenePos.y+fHalfHeight,false);
		
				stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);

				if(tpBlockRenderInfo)
				{
					if(tpBlockRenderInfo->m_bIsObstruct)
					{
						bCollResult = true;
						tScenePos = tScenePos_Old ;
					}
				}
				if(false == bCollResult)
				{
					tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x-fHalfWidth * 0.6,tScenePos.y + fHalfHeight,false);

					tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);
					if(tpBlockRenderInfo)
					{
						if(tpBlockRenderInfo->m_bIsObstruct)
						{
							bCollResult = true;
							tScenePos = tScenePos_Old ;
						}
					}
				}
				if(false == bCollResult)
				{
					tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x+fHalfWidth* 0.6,tScenePos.y + fHalfHeight ,false);

					tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);
					if(tpBlockRenderInfo)
					{
						if(tpBlockRenderInfo->m_bIsObstruct)
						{
							bCollResult = true;
							tScenePos = tScenePos_Old ;
						}
					}
				}
			}
			break;
		case DK_RIGHT:
			{
				//右
				tScenePos.x  += fMoveSpeed*fDelaySecond ;

				//取得相应层的相应格子数据
				POINT	tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x+fHalfWidth,tScenePos.y,false);
		
				stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);

				if(tpBlockRenderInfo)
				{
					if(tpBlockRenderInfo->m_bIsObstruct)
					{
						bCollResult = true;
						tScenePos = tScenePos_Old ;
					}
				}
				if(false == bCollResult)
				{
					tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x+fHalfWidth,tScenePos.y - fHalfHeight * 0.6,false);

					tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);
					if(tpBlockRenderInfo)
					{
						if(tpBlockRenderInfo->m_bIsObstruct)
						{
							bCollResult = true;
							tScenePos = tScenePos_Old ;
						}
					}
				}
				if(false == bCollResult)
				{
					tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x+fHalfWidth,tScenePos.y + fHalfHeight * 0.6,false);

					tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);
					if(tpBlockRenderInfo)
					{
						if(tpBlockRenderInfo->m_bIsObstruct)
						{
							bCollResult = true;
							tScenePos = tScenePos_Old ;
						}
					}
				}
			}
			break;
		case DK_DOWN:
			{
				//下
				tScenePos.y -= fMoveSpeed*fDelaySecond ;
				//取得相应层的相应格子数据
				POINT	tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x,tScenePos.y-fHalfHeight,false);
		
				stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);

				if(tpBlockRenderInfo)
				{
					if(tpBlockRenderInfo->m_bIsObstruct)
					{
						bCollResult = true;
						tScenePos = tScenePos_Old ;
					}
				}
				if(false == bCollResult)
				{
					tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x-fHalfWidth * 0.6,tScenePos.y - fHalfHeight,false);

					tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);
					if(tpBlockRenderInfo)
					{
						if(tpBlockRenderInfo->m_bIsObstruct)
						{
							bCollResult = true;
							tScenePos = tScenePos_Old ;
						}
					}
				}
				if(false == bCollResult)
				{
					tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x+fHalfWidth* 0.6,tScenePos.y - fHalfHeight ,false);

					tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);
					if(tpBlockRenderInfo)
					{
						if(tpBlockRenderInfo->m_bIsObstruct)
						{
							bCollResult = true;
							tScenePos = tScenePos_Old ;
						}
					}
				}
			}
			break;
		}

		if(false == bCollResult)
		{
			if(tScenePos.x < tBoundingBox.width*0.5)
			{
				tScenePos.x = tBoundingBox.width*0.5;
				bCollResult = true;
			}
			else if(tScenePos.x > (tpSceneHeader->m_nSceneWidth-tBoundingBox.width*0.5))
			{
				tScenePos.x = tpSceneHeader->m_nSceneWidth - tBoundingBox.width*0.5;
				bCollResult = true;
			}
			else if(tScenePos.y < tBoundingBox.height*0.5)
			{
				tScenePos.y = tBoundingBox.height*0.5;
				bCollResult = true;
			}
			else if(tScenePos.y > (tpSceneHeader->m_nSceneHeight-tBoundingBox.height*0.5))
			{
				tScenePos.y = (tpSceneHeader->m_nSceneHeight-tBoundingBox.height*0.5);
				bCollResult = true;
			}
		}

		m_pCharacter->SetScenePos(tScenePos);

		//接到物品
		tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x,tScenePos.y,false);
		//取得相应层的相应格子数据
		stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(2,tTile.x,tTile.y);
		if(tpBlockRenderInfo)
		{
			if(RET_IMAGE == tpBlockRenderInfo->m_sRenderElementType)
			{
				//HP
				if(0==strcmp(tpBlockRenderInfo->m_strNpcName.c_str(),"eff3.png"))
				{
					m_nHP = 10;
					CGameSceneNPC::m_pCurrGameMap->SetTileRenderInfo(2,tTile.x,tTile.y,-1);
				}
				//无敌
				if(0==strcmp(tpBlockRenderInfo->m_strNpcName.c_str(),"eff2.png"))
				{
					setNBState();
					CGameSceneNPC::m_pCurrGameMap->SetTileRenderInfo(2,tTile.x,tTile.y,-1);
				}
				//穿甲弹
				if(0==strcmp(tpBlockRenderInfo->m_strNpcName.c_str(),"eff1.png"))
				{
					setNBBulletState();
					CGameSceneNPC::m_pCurrGameMap->SetTileRenderInfo(2,tTile.x,tTile.y,-1);
				}
				
			}
		}
	}
}
//每帧调用的渲染
Void		CMyTank::Render()
{
	CGameSceneNPC::Render();
	//显示血条
	C2DCharacter*	tpCharacter = getCharInfo();
	if( tpCharacter )
	{
		CCPoint		tCenetrPos   = tpCharacter->GetScenePos();
		CCSize		tBoundingBox = tpCharacter->GetBoundingBoxSize();
		CCPoint		tScreenPos	 = CGameSceneNPC::m_pCurrGameMap->GetScreenPosition(tCenetrPos.x,tCenetrPos.y);

		int nHPBorder = 4;
		ccDrawColor4B(255, 255, 255, 255);
		ccDrawRect(ccp(tScreenPos.x-tBoundingBox.width*0.5,tScreenPos.y+tBoundingBox.height*0.5),ccp(tScreenPos.x+tBoundingBox.width*0.5,tScreenPos.y+tBoundingBox.height*0.5-nHPBorder));

		//普通
		int nLeft = tBoundingBox.width - tBoundingBox.width * m_nHP / 10 ;
		ccDrawSolidRect(ccp(tScreenPos.x-tBoundingBox.width*0.5+nLeft+1,tScreenPos.y+tBoundingBox.height*0.5-1),ccp(tScreenPos.x+tBoundingBox.width*0.5-1,tScreenPos.y+tBoundingBox.height*0.5-nHPBorder+1),ccc4f(1.0,0,0,1.0));
	}
}
//设置当前触屏按键
void		CMyTank::SetDirKey(enuDirKey vDir)
{
	m_nDirKey = vDir;
}
//设置开炮
void		CMyTank::SetAttackKey(bool bAttack)
{
	m_bAttackKey = bAttack;
}	
//设置无敌状态
Void		CMyTank::setNBState()
{
	m_bNB = true;
	m_NBTime = 0;
	
}
//是否无敌状态
bool		CMyTank::IsNBState()
{
	return m_bNB;
}	
//设置发射穿甲弹
void		CMyTank::setNBBulletState()
{
	m_bNBBullet = true;
	m_NBBulletTime = 0;
}
//是否发射穿甲弹
bool		CMyTank::IsNBBulletState()
{
	return m_bNBBullet;
}

double m_dwLastAttackTime;//当前的方向int m_nDir;//触屏按键enuDirKey m_nDirKey;//触屏开炮bool m_bAttackKey;//无敌状态bool m_bNB;//无敌的起始时间float m_NBTime;//是否发射穿甲弹bool m_bNBBullet;//发射穿甲弹的起始时间float m_NBBulletTime;};#endif

    这样,我们的主坦克就实现了。在这个类里我们实现了坦克的控制与行走,发射子弹等处理。接下来我们来一下由AI来控制的敌方坦克, 我们新建文件AI_Tank.h/cpp,在红孩儿工具箱的场景中,CGameSceneNPC类是可以挂接私有属性结构CGameSceneNPCAttrib和AI处理功能接口类CGameSceneNPCAI。这样可以方便的进行扩展,每一个NPC类实例有单独的属性结构CGameSceneNPCAttrib的实例。而CGameSceneNPCAI则可以实现所有同类的NPC都具有的相同处理方法。

	#ifndef _AI_TANK_H
#define _AI_TANK_H
//===========================================================
//Auto:火云红孩儿 QQ:285421210
//Date:2013-10-11
//Desc:敌人的坦克
//===========================================================

#include "Header.h"
#include "HHR_Character.h"
#include "HHR_SceneManage.h"
//坦克类
class CEnemyTank	:public CGameSceneNPC
{
public:
	//每帧调用的渲染
	virtual void		Render();
}
;
//坦克的私有属性
class CAttrib_Tank	:public	CGameSceneNPCAttrib
{		
public:
	//构造与析构
	CAttrib_Tank();
	~CAttrib_Tank();
public:
	//坦克类型
	VAR_SETGET(int,m_nTankType,TankType);
	//坦克血量
	VAR_SETGET(int,m_nHP,HP);
	//运动速度
	VAR_SETGET(float,m_fMoveSpeed,MoveSpeed);
	//方向 0左 1上 2右 3下
	VAR_SETGET(int,m_nDir,Dir);
	//设置上次换方向的时间
	VAR_SETGET(double,m_dwLastChangeDirTime,LastChangeDirTime);
	//改变方向的机率
	VAR_SETGET(int,m_nChangeDirProbability,ChangeDirProbability);
	//设置上次开炮的时间
	VAR_SETGET(double,m_dwLastAttackTime,LastAttackTime);

public:
	//按照相应机率取得是否改变方向的判定
	bool	GetRandChangeDirValue();
	//设置无敌状态
	void	setNBState(bool bNB = true);
	//是否无敌状态
	bool	IsNBState();

	//
	//设置发射穿甲弹
	void	setNBBulletState(bool bNBBullet = true);
	//是否发射穿甲弹
	bool	IsNBBulletState();


	//设置上次开炮的时间
	VAR_SETGET(float,m_NBTime,NBTime);
	//设置上次开炮的时间
	VAR_SETGET(float,m_NBBulletTime,NBBulletTime);

private:
	//无敌状态
	bool			m_bNB;
	//是否发射穿甲弹
	bool			m_bNBBullet;
}
;

//坦克的AI
class CAi_Tank	:public CGameSceneNPCAI
{
public:
	//构造与析构
	CAi_Tank();
	~CAi_Tank();
public:
	//设置NPC所用的私有属性
	void	registerDestNPC(CGameSceneNPC*	pNPC);
	//每帧调用的更新
	virtual void	update(CGameSceneNPC*	pNPC,float delay);
private:
	//取得一个随机可移动的方向
	int	GetRandDir(int vLayer,int vTileX,int vTileY);
};

#endif

对应的CPP:

#include "AI_Tank.h"

//每帧调用的渲染
void	CEnemyTank::Render()
{
	CGameSceneNPC::Render();

	//显示血条
	C2DCharacter*	tpCharacter = getCharInfo();
	if( tpCharacter )
	{
		CCPoint		tCenetrPos   = tpCharacter->GetScenePos();
		CCSize		tBoundingBox = tpCharacter->GetBoundingBoxSize();
		CCPoint		tScreenPos	 = CGameSceneNPC::m_pCurrGameMap->GetScreenPosition(tCenetrPos.x,tCenetrPos.y);
		//取得所用的属性
		CGameSceneNPCAttrib*	pTankAttrib = getNpcAttrib();
		CAttrib_Tank*			pEnemyTankAttrib = dynamic_cast<CAttrib_Tank*>(pTankAttrib);
		if(pEnemyTankAttrib)
		{
			int nHP = pEnemyTankAttrib->getHP();
			int nHPBorder = 4;
			ccDrawColor4B(255, 255, 255, 255);
			ccDrawRect(ccp(tScreenPos.x-tBoundingBox.width*0.5,tScreenPos.y+tBoundingBox.height*0.5),ccp(tScreenPos.x+tBoundingBox.width*0.5,tScreenPos.y+tBoundingBox.height*0.5-nHPBorder));

			int nType = pEnemyTankAttrib->getTankType();

			switch(nType)
			{
			case 0:
				{
					//普通
					int nLeft = tBoundingBox.width - tBoundingBox.width * nHP / 2 ;
					ccDrawSolidRect(ccp(tScreenPos.x-tBoundingBox.width*0.5+nLeft+1,tScreenPos.y+tBoundingBox.height*0.5-1),ccp(tScreenPos.x+tBoundingBox.width*0.5-1,tScreenPos.y+tBoundingBox.height*0.5-nHPBorder+1),ccc4f(1.0,0,0,1.0));
				}
				break;
			case 1:
				{
					//快速
					int nLeft = tBoundingBox.width - tBoundingBox.width * nHP ;
					ccDrawSolidRect(ccp(tScreenPos.x-tBoundingBox.width*0.5+nLeft+1,tScreenPos.y+tBoundingBox.height*0.5-1),ccp(tScreenPos.x+tBoundingBox.width*0.5-1,tScreenPos.y+tBoundingBox.height*0.5-nHPBorder+1),ccc4f(1.0,0,0,1.0));
				}
				break;
			case 2:
				{
					//重型
					int nLeft = tBoundingBox.width - tBoundingBox.width * nHP / 4;
					ccDrawSolidRect(ccp(tScreenPos.x-tBoundingBox.width*0.5+nLeft+1,tScreenPos.y+tBoundingBox.height*0.5-1),ccp(tScreenPos.x+tBoundingBox.width*0.5-1,tScreenPos.y+tBoundingBox.height*0.5-nHPBorder+1),ccc4f(1.0,0,0,1.0));
				}
				break;
			}

		}
	}
}

//构造与析构
CAttrib_Tank::CAttrib_Tank()
{
	m_nTankType				= 0;
	m_nHP					= 100;
	m_fMoveSpeed			= 50;
	m_nDir					= 3;
	m_dwLastChangeDirTime	= 0;
	m_nChangeDirProbability = 30;
	m_dwLastAttackTime		= 0;
	m_bNB					= false;
	m_NBTime				= 0;
	m_bNBBullet				= false;
	m_NBBulletTime			= 0;
}
CAttrib_Tank::~CAttrib_Tank()
{

}
//按照相应机率取得是否改变方向的判定
bool CAttrib_Tank::GetRandChangeDirValue()
{
	int nRand = rand() % 100 ;
	return nRand < m_nChangeDirProbability ;
}
//设置无敌状态
void	CAttrib_Tank::setNBState(bool bNB)
{
	m_bNB = bNB;
	m_NBTime = 0;
}
//是否无敌状态
bool	CAttrib_Tank::IsNBState()
{
	return m_bNB;
}	
//设置发射穿甲弹
void	CAttrib_Tank::setNBBulletState(bool bNBBullet)
{
	m_bNBBullet = bNBBullet;
	m_NBBulletTime = 0;
}
//是否发射穿甲弹
bool	CAttrib_Tank::IsNBBulletState()
{
	return m_bNBBullet;
}
//构造
CAi_Tank::CAi_Tank()
{

}

CAi_Tank::~CAi_Tank()
{

}
//设置NPC所用的私有属性
void	CAi_Tank::registerDestNPC(CGameSceneNPC*	pNPC)
{
	if(pNPC)
	{
		pNPC->setNpcAttrib(new CAttrib_Tank());
	}
}
//每帧调用的更新
void	CAi_Tank::update(CGameSceneNPC*	pNPC,float fDelaySecond)
{
	if(!pNPC)return ;
			
	fDelaySecond = min(fDelaySecond,0.2);
	C2DCharacter*	pCharacter = pNPC->getCharInfo();
	CAttrib_Tank*	pAttrib_Tank = dynamic_cast<CAttrib_Tank*>(pNPC->getNpcAttrib());
	if(pCharacter && pAttrib_Tank && CGameSceneNPC::m_pCurrGameMap)
	{	
		//无敌状态时间计算
		if(pAttrib_Tank->IsNBState())
		{
			float	fNBTime = pAttrib_Tank->getNBTime();
			fNBTime += fDelaySecond ;
			if(fNBTime > 10000)
			{
				pAttrib_Tank->setNBState(false);
				pAttrib_Tank->setNBTime(0);
			}
		}
		//穿甲弹状态时间计算
		if(pAttrib_Tank->IsNBBulletState())
		{
			float	fNBBulletTime = pAttrib_Tank->getNBBulletTime();
			fNBBulletTime += fDelaySecond ;
			if(fNBBulletTime > 10000)
			{
				pAttrib_Tank->setNBBulletState(false);
				pAttrib_Tank->setNBBulletTime(0);
			}
		}

		//播放判断
		struct timeval tv;
		gettimeofday(&tv,NULL);
		double	   	    dwCurrTime = tv.tv_sec*1000.0+tv.tv_usec* 0.001;
		//坦克类型
		int nTankType = pAttrib_Tank->getTankType();
		//取得场景信息
		stSceneHeader*	tpSceneHeader =	CGameSceneNPC::m_pCurrGameMap->GetSceneHeader();
		CCPoint	tScenePos_Old;
		CCPoint	tScenePos;
		tScenePos_Old =	tScenePos = pCharacter->GetScenePos();
		CCSize	tBoundingBox = pCharacter->GetBoundingBoxSize();
		float	fHalfWidth = tBoundingBox.width * 0.5 ;
		float	fHalfHeight = tBoundingBox.height * 0.5 ;

		float	fMoveSpeed		= pAttrib_Tank->getMoveSpeed();
		bool	bChangeDir		= false;
		int		nDir			= pAttrib_Tank->getDir();
		int		nLayer			= 1;
		//当前所在格子
		POINT	tTile			= CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x,tScenePos.y,false);
		//取得格子对应的中心点
		CCPoint	tTile_Center	= CGameSceneNPC::m_pCurrGameMap->GetTileCenterPoint(nLayer,tTile.x,tTile.y,false);
		//是否进入格子中央
		bool	bInTileCenter	= false;			
		//设置图片的旋转方向
		pCharacter->GetCurrBody()->setRotateZ(90 * nDir);	

		switch(nDir)
		{

		case DK_UP:
			{
				//上
				float fPosY  = tScenePos.y + fMoveSpeed*fDelaySecond ;
				if(tScenePos.y < tTile_Center.y && fPosY >= tTile_Center.y)
				{
					bInTileCenter = true;
				}
				tScenePos.y  = fPosY;
				//取得相应层的相应格子数据
				POINT	tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x,tScenePos.y+fHalfHeight,false);
		
				stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);

				if(tpBlockRenderInfo)
				{
					if(tpBlockRenderInfo->m_bIsObstruct)
					{
						bChangeDir = true;
						tScenePos = tScenePos_Old ;
					}
				}
			}
			break;
		case DK_RIGHT:
			{
				//右
				float fPosX  = tScenePos.x + fMoveSpeed*fDelaySecond ;
				if(tScenePos.x < tTile_Center.x && fPosX >= tTile_Center.x)
				{
					bInTileCenter = true;
				}
				tScenePos.x  = fPosX;

				//取得相应层的相应格子数据
				POINT	tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x+fHalfWidth,tScenePos.y,false);
		
				stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);

				if(tpBlockRenderInfo)
				{
					if(tpBlockRenderInfo->m_bIsObstruct)
					{
						bChangeDir = true;
						tScenePos = tScenePos_Old ;
					}
				}
			}
			break;
		case DK_DOWN:
			{
				//下
				float fPosY  = tScenePos.y - fMoveSpeed*fDelaySecond ;
				if(tScenePos.y > tTile_Center.y && fPosY <= tTile_Center.y)
				{
					bInTileCenter = true;
				}
				tScenePos.y  = fPosY;
				//取得相应层的相应格子数据
				POINT	tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x,tScenePos.y-fHalfHeight,false);
		
				stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);

				if(tpBlockRenderInfo)
				{
					if(tpBlockRenderInfo->m_bIsObstruct)
					{
						bChangeDir = true;
						tScenePos = tScenePos_Old ;
					}
				}
			}
			break;
		case DK_LEFT:
			{
				//左
				float fPosX  = tScenePos.x - fMoveSpeed*fDelaySecond ;
				if(tScenePos.x > tTile_Center.x && fPosX <= tTile_Center.x)
				{
					bInTileCenter = true;
				}
				tScenePos.x  = fPosX;

				//取得相应层的相应格子数据
				POINT	tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x-fHalfWidth,tScenePos.y,false);
		
				stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y);

				if(tpBlockRenderInfo)
				{
					if(tpBlockRenderInfo->m_bIsObstruct)
					{
						bChangeDir = true;
						tScenePos = tScenePos_Old ;
					}
				}
			}
			break;
		}

		if(false == bChangeDir)
		{
			if(tScenePos.x < tBoundingBox.width*0.5)
			{
				tScenePos.x = tBoundingBox.width*0.5;
				bChangeDir = true;
			}
			else if(tScenePos.x > (tpSceneHeader->m_nSceneWidth-tBoundingBox.width*0.5))
			{
				tScenePos.x = tpSceneHeader->m_nSceneWidth - tBoundingBox.width*0.5;
				bChangeDir = true;
			}
			else if(tScenePos.y < tBoundingBox.height*0.5)
			{
				tScenePos.y = tBoundingBox.height*0.5;
				bChangeDir = true;
			}
			else if(tScenePos.y > (tpSceneHeader->m_nSceneHeight-tBoundingBox.height*0.5))
			{
				tScenePos.y = (tpSceneHeader->m_nSceneHeight-tBoundingBox.height*0.5);
				bChangeDir = true;
			}
		}


		pCharacter->SetScenePos(tScenePos);

		//接到物品
		POINT	tNewTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayer,tScenePos.x,tScenePos.y,false);
		//取得相应层的相应格子数据
		stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tNewTile.x,tNewTile.y);
		if(tpBlockRenderInfo)
		{
			if(RET_IMAGE == tpBlockRenderInfo->m_sRenderElementType)
			{

				//穿甲弹
				if(0==strcmp(tpBlockRenderInfo->m_strNpcName.c_str(),"eff1.png"))
				{
					pAttrib_Tank->setNBBulletState();
				}
				//无敌
				if(0==strcmp(tpBlockRenderInfo->m_strNpcName.c_str(),"eff2.png"))
				{
					pAttrib_Tank->setNBState();
				}
				//HP
				if(0==strcmp(tpBlockRenderInfo->m_strNpcName.c_str(),"eff3.png"))
				{
					switch(nTankType)
					{
					case 0:
						{
							//普通
							pAttrib_Tank->setHP(2);
						}
						break;
					case 1:
						{
							//快速
							pAttrib_Tank->setHP(1);
						}
						break;
					case 2:
						{
							//重型
							pAttrib_Tank->setHP(4);
						}
						break;
					}
					
				}
				CGameSceneNPC::m_pCurrGameMap->SetTileRenderInfo(nLayer,tNewTile.x,tNewTile.y,-1);
			}
		}

		//开炮时间
		double dwLastAttackTime = pAttrib_Tank->getLastAttackTime() ;
		if(dwCurrTime - dwLastAttackTime > 4000)
		{	
			//发射子弹的代码
			pAttrib_Tank->setLastAttackTime(dwCurrTime) ;
		}

		//如果方向改变
		if(bChangeDir)
		{
			//取得相应层的相应格子数据
			int nNextDir = GetRandDir(nLayer,tNewTile.x,tNewTile.y);
			pAttrib_Tank->setDir(nNextDir);
			//设置图片的旋转方向
			pCharacter->GetCurrBody()->setRotateZ(90 * nNextDir);
		}
		else
		{
			if(bInTileCenter)	
			{
				//放在中心点
				pCharacter->SetScenePos(tTile_Center);

				switch(nDir)
				{
				case DK_LEFT:
				case DK_RIGHT:
					{
						//取得当前层的格子信息
						stEditLayerInfo*	tpLayerInfo = CGameSceneNPC::m_pCurrGameMap->GetCurrLayerInfo(nLayer);

						//左
						//tScenePos.x  -= fMoveSpeed*fDelaySecond ;
						//取得相应层的相应格子数据
						vector<int>	nRandDirVec;
						stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y-1);
						if(tpBlockRenderInfo)
						{
							if(false == tpBlockRenderInfo->m_bIsObstruct)
							{
								nRandDirVec.push_back(1);
							}
						}
						else if(tTile.y > 0)
						{
							nRandDirVec.push_back(1);
						}
						tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x,tTile.y+1);
						if(tpBlockRenderInfo)
						{
							if(false == tpBlockRenderInfo->m_bIsObstruct)
							{
								nRandDirVec.push_back(3);
							}
						}
						else if(tTile.y < tpLayerInfo->m_nTileRows)
						{
							nRandDirVec.push_back(3);
						}
						int		nNextDirNum = nRandDirVec.size();
						bool	bCheckChangeDir  = pAttrib_Tank->GetRandChangeDirValue();
						if( nNextDirNum > 0 && bCheckChangeDir)
						{
							double dwLastChangeDirTime = pAttrib_Tank->getLastChangeDirTime() ;
							if(dwCurrTime - dwLastChangeDirTime > 5000)
							{					
								int nNextDirIndex = rand()%nNextDirNum;
								int nNextDir = nRandDirVec[nNextDirIndex];
								pAttrib_Tank->setLastChangeDirTime(dwCurrTime);
								pAttrib_Tank->setDir(nNextDir);
							}
						}
					}
					break;
				case DK_UP:
				case DK_DOWN:
					{
						stEditLayerInfo*	tpLayerInfo = CGameSceneNPC::m_pCurrGameMap->GetCurrLayerInfo(nLayer);
						//下
						//tScenePos.y  -= fMoveSpeed*fDelaySecond ;
						vector<int>	nRandDirVec;
						stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x-1,tTile.y);
						if(tpBlockRenderInfo)
						{
							if(false == tpBlockRenderInfo->m_bIsObstruct)
							{
								nRandDirVec.push_back(0);
							}
						}
						else if(tTile.x > 0)
						{
							nRandDirVec.push_back(0);
						}
						tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayer,tTile.x+1,tTile.y);
						if(tpBlockRenderInfo)
						{
							if(false == tpBlockRenderInfo->m_bIsObstruct)
							{
								nRandDirVec.push_back(2);
							}
						}
						else if(tTile.x < tpLayerInfo->m_nTileCows)
						{
							nRandDirVec.push_back(2);
						}
						int		nNextDirNum = nRandDirVec.size();
						bool	bCheckChangeDir  = pAttrib_Tank->GetRandChangeDirValue();
						if( nNextDirNum > 0 && bCheckChangeDir)
						{
							double dwLastChangeDirTime = pAttrib_Tank->getLastChangeDirTime() ;
							if(dwCurrTime - dwLastChangeDirTime > 5000)
							{					
								int nNextDirIndex = rand()%nNextDirNum;
								int nNextDir = nRandDirVec[nNextDirIndex];
								pAttrib_Tank->setLastChangeDirTime(dwCurrTime);
								pAttrib_Tank->setDir(nNextDir);
							}
						}

					}
					break;
				}
			}
		}
	}
}

//取得一个随机可移动的方向
int	CAi_Tank::GetRandDir(int vLayer,int vTileX,int vTileY)
{
	//取得相应层的相应格子数据
	vector<int>	nRandDirVec;
	stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(vLayer,vTileX,vTileY-1);
	if(tpBlockRenderInfo)
	{
		if(false == tpBlockRenderInfo->m_bIsObstruct)
		{
			nRandDirVec.push_back(1);
		}
	}
	else
	{
		nRandDirVec.push_back(1);
	}
	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(vLayer,vTileX,vTileY+1);
	if(tpBlockRenderInfo)
	{
		if(false == tpBlockRenderInfo->m_bIsObstruct)
		{
			nRandDirVec.push_back(3);
		}
	}
	else
	{
		nRandDirVec.push_back(3);
	}
	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(vLayer,vTileX-1,vTileY);
	if(tpBlockRenderInfo)
	{
		if(false == tpBlockRenderInfo->m_bIsObstruct)
		{
			nRandDirVec.push_back(0);
		}
	}
	else
	{
		nRandDirVec.push_back(0);
	}
	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(vLayer,vTileX+1,vTileY);
	if(tpBlockRenderInfo)
	{
		if(false == tpBlockRenderInfo->m_bIsObstruct)
		{
			nRandDirVec.push_back(2);
		}
	}
	else
	{
		nRandDirVec.push_back(2);
	}
	int nNextDirNum = nRandDirVec.size();
	if( nNextDirNum > 0 )
	{
		int nNextDirIndex = rand()%nNextDirNum;
		return nRandDirVec[nNextDirIndex];
	}

	return -1;
}

通过这些代码的编写,我们给每一个坦克定义了一个单独的属性结构CAttrib_Tank以及对应的AI处理接口类CAi_Tank。

我们可以看到,在发射子弹的代码处没有做处理,我们下面来完成子弹的实现,创建

CBullet.h/cpp:

#ifndef _CBULLET_H
#define _CBULLET_H
//===========================================================
//Auto:火云红孩儿 QQ:285421210
//Date:2013-10-11
//Desc:子弹类
//===========================================================
#include "Header.h"
#include "HHR_Character.h"
#include "HHR_SceneManage.h"
#include "EffectManager.h"
#include "EffectNode.h"
//子弹
class CBullet	:public CGameSceneNPC
{
public:
	CBullet();
	~CBullet();
public:
	//初始化
	virtual void	Init();
	//每帧调用的更新
	virtual void	update(float fDelaySecond);
public:
	//敌方还是多方
	VAR_SETGET(int,m_bIsEnemy,Enemy);
	//运动速度
	VAR_SETGET(float,m_fMoveSpeed,MoveSpeed);
	//方向 0左 1上 2右 3下
	VAR_SETGET(int,m_nDir,Dir);
	//是否能打穿铁皮
	VAR_SETIS(bool,m_bNB,NB);
};

#endif

对应CPP:

#include "CBullet.h"
#include "HelloWorldScene.h"
#include "AI_Tank.h"
#include "CMyTank.h"
#include "SimpleAudioEngine.h"
using namespace CocosDenshion;
CBullet::CBullet()
{
	m_fMoveSpeed = 100;
	m_nDir = -1;
	m_bIsEnemy = true;
	m_bNB = false;
}
CBullet::~CBullet()
{

}

//初始化
void	CBullet::Init()
{

}

//每帧调用的更新
void	CBullet::update(float fDelaySecond)
{
	if( m_pCharacter && CGameSceneNPC::m_pCurrGameMap)
	{	
		fDelaySecond = min(fDelaySecond,0.2);
		//取得场景信息
		CCPoint	tScenePos = m_pCharacter->GetScenePos();
		//取得绑定盒大小
		CCSize	tBoundingBox = m_pCharacter->GetBoundingBoxSize();
		//取得场景信息
		stSceneHeader*	tpSceneHeader =	CGameSceneNPC::m_pCurrGameMap->GetSceneHeader();
		//设置图片的旋转方向
		m_pCharacter->GetCurrBody()->setRotateZ(90 * m_nDir);	
		switch(m_nDir)
		{
		case DK_UP:
			{
				//上
				tScenePos.y  += m_fMoveSpeed*fDelaySecond ;
			}
			break;
		case DK_RIGHT:
			{
				//右
				tScenePos.x  += m_fMoveSpeed*fDelaySecond ;	
			}
			break;
		case DK_DOWN:
			{
				//下
				tScenePos.y  -= m_fMoveSpeed*fDelaySecond ;
			}
			break;
		case DK_LEFT:
			{
				//左
				tScenePos.x  -= m_fMoveSpeed*fDelaySecond ;	
			}
			break;
		}
			
		m_pCharacter->SetScenePos(tScenePos);
		int		nBulletLayerIndex = 2;
		int		nLayerIndex = 1;
		POINT	tTile = CGameSceneNPC::m_pCurrGameMap->GetTile(nLayerIndex,tScenePos.x,tScenePos.y,false);
		//取得相应层的相应格子数据
		stBlockRenderInfo*	tpBlockRenderInfo = CGameSceneNPC::m_pCurrGameMap->GetTileRenderInfo(nLayerIndex,tTile.x,tTile.y);
		if(tpBlockRenderInfo)
		{
			if(RET_IMAGE == tpBlockRenderInfo->m_sRenderElementType)
			{
				if(0==strcmp(tpBlockRenderInfo->m_strNpcName.c_str(),"wall2.png"))
				{
					//破损墙
					CCParticleSystemQuad*	pFireSystem = new CCParticleSystemQuad();
					pFireSystem->initWithFile("explode.plist");
					CCPoint		tScreenPos = CGameSceneNPC::m_pCurrGameMap->GetScreenPosition(tScenePos.x,tScenePos.y);
					pFireSystem->setPosition(tScreenPos.x,tScreenPos.y);
					CGameSceneNPC::m_pCurrGameMap->AddNew2DParticleSystem(nBulletLayerIndex,pFireSystem);
					CGameSceneNPC::m_pCurrGameMap->Del2DActiveObj(nBulletLayerIndex,this);
					CGameSceneNPC::m_pCurrGameMap->SetTileRenderInfo(nLayerIndex,tTile.x,tTile.y,-1);
					SimpleAudioEngine::sharedEngine()->playEffect("hurt.mp3");

				}
				if(0==strcmp(tpBlockRenderInfo->m_strNpcName.c_str(),"wall.png"))
				{
					//完整墙
					CCParticleSystemQuad*	pFireSystem = new CCParticleSystemQuad();
					pFireSystem->initWithFile("explode.plist");
					CCPoint		tScreenPos = CGameSceneNPC::m_pCurrGameMap->GetScreenPosition(tScenePos.x,tScenePos.y);
					pFireSystem->setPosition(tScreenPos.x,tScreenPos.y);
					CGameSceneNPC::m_pCurrGameMap->AddNew2DParticleSystem(nBulletLayerIndex,pFireSystem);
					CGameSceneNPC::m_pCurrGameMap->Del2DActiveObj(nBulletLayerIndex,this);
					//将其变为破损墙
					int	nWall2ResID = CGameSceneNPC::m_pCurrGameMap->GetRenderInfoID(RET_IMAGE,"wall2.png","");
					CGameSceneNPC::m_pCurrGameMap->SetTileRenderInfo(nLayerIndex,tTile.x,tTile.y,nWall2ResID);
					SimpleAudioEngine::sharedEngine()->playEffect("hurt.mp3");
				}
				if(0==strcmp(tpBlockRenderInfo->m_strNpcName.c_str(),"tone.png"))
				{
					//木桶
					CCParticleSystemQuad*	pFireSystem = new CCParticleSystemQuad();
					pFireSystem->initWithFile("explode.plist");
					CCPoint		tScreenPos = CGameSceneNPC::m_pCurrGameMap->GetScreenPosition(tScenePos.x,tScenePos.y);
					pFireSystem->setPosition(tScreenPos.x,tScreenPos.y);
					CGameSceneNPC::m_pCurrGameMap->AddNew2DParticleSystem(nBulletLayerIndex,pFireSystem);
					CGameSceneNPC::m_pCurrGameMap->Del2DActiveObj(nBulletLayerIndex,this);
					SimpleAudioEngine::sharedEngine()->playEffect("hurt.mp3");

					//随机产生
					int randvalue = rand()%3;
					switch(randvalue)
					{
					case 0:
						{
							//穿甲弹
							int	nEffectIndex = CGameSceneNPC::m_pCurrGameMap->GetRenderInfoID(RET_IMAGE,"eff1.png","");
							CGameSceneNPC::m_pCurrGameMap->SetTileRenderInfo(nLayerIndex,tTile.x,tTile.y,nEffectIndex);

						}
						break;
					case 1:
						{
							//HP
							int	nEffectIndex = CGameSceneNPC::m_pCurrGameMap->GetRenderInfoID(RET_IMAGE,"eff2.png","");
							CGameSceneNPC::m_pCurrGameMap->SetTileRenderInfo(nLayerIndex,tTile.x,tTile.y,nEffectIndex);

						}
						break;
					case 2:
						{
							//无敌
							int	nEffectIndex = CGameSceneNPC::m_pCurrGameMap->GetRenderInfoID(RET_IMAGE,"eff3.png","");
							CGameSceneNPC::m_pCurrGameMap->SetTileRenderInfo(nLayerIndex,tTile.x,tTile.y,nEffectIndex);
						}
						break;
					}
				}
				if(0==strcmp(tpBlockRenderInfo->m_strNpcName.c_str(),"metal.png"))
				{
					//金属块打不动
					CCParticleSystemQuad*	pFireSystem = new CCParticleSystemQuad();
					pFireSystem->initWithFile("explode.plist");
					CCPoint		tScreenPos = CGameSceneNPC::m_pCurrGameMap->GetScreenPosition(tScenePos.x,tScenePos.y);
					pFireSystem->setPosition(tScreenPos.x,tScreenPos.y);
					CGameSceneNPC::m_pCurrGameMap->AddNew2DParticleSystem(nBulletLayerIndex,pFireSystem);
					CGameSceneNPC::m_pCurrGameMap->Del2DActiveObj(nBulletLayerIndex,this);
					SimpleAudioEngine::sharedEngine()->playEffect("hurt.mp3");
					if(m_bNB)
					{
						//如果够牛逼,就能击穿
						CGameSceneNPC::m_pCurrGameMap->SetTileRenderInfo(nLayerIndex,tTile.x,tTile.y,-1);
					}
				}
			}
			if(RET_ANI == tpBlockRenderInfo->m_sRenderElementType)
			{
				if(0==strcmp(tpBlockRenderInfo->m_strNpcName.c_str(),"home.gan"))
				{
					//基地被炸,结束了
					CGameSceneNPC::m_pCurrGameMap->Del2DActiveObj(nBulletLayerIndex,this);
					CGameSceneNPC::m_pCurrGameMap->SetTileRenderInfo(nLayerIndex,tTile.x,tTile.y,-1);

					//击毁一个敌方坦克
					HelloWorld*	tpHelloWorld = dynamic_cast<HelloWorld*>(CGameSceneNPC::m_pCurrGameMap->getParent());
					if(tpHelloWorld)
					{
						tpHelloWorld->KillMyTank();

						CCPoint		tScreenPos = CGameSceneNPC::m_pCurrGameMap->GetScreenPosition(tScenePos.x,tScenePos.y);									

						//加载爆炸效果
						EffectNode*		gpEffect = EffectManager::GetInstance()->LoadEffect("101");
						MyVector3 vPos;
						vPos.x = tScreenPos.x;
						vPos.y = tScreenPos.y;
						vPos.z = 10;
						//设置效果位置
						gpEffect->SetPos(vPos);
						MyVector3 vScale;
						vScale.x=0.5;
						vScale.y=0.5;
						vScale.z=0;
						//设置效果缩放
						gpEffect->SetScale(vScale);
						//播放效果
						gpEffect->PlayEffect();

					}
					return ;
				}
			}
		}
		//取得坦克数量
		
		int nTankNum  = CGameSceneNPC::m_pCurrGameMap->Get2DActiveObjNum(nLayerIndex);
		for(int t = 0 ; t < nTankNum ; t++)
		{
			CGameSceneNPC*	tpTank = CGameSceneNPC::m_pCurrGameMap->Get2DActiveObj(nLayerIndex,t);
			if( tpTank )
			{
				bool		bEnemyTank = false;
				//取得所用的属性
				CGameSceneNPCAttrib*	pTankAttrib = tpTank->getNpcAttrib();
				CAttrib_Tank*			pEnemyTankAttrib = dynamic_cast<CAttrib_Tank*>(pTankAttrib);
				if(pEnemyTankAttrib)
				{
					bEnemyTank = true;
				}

				//敌方子弹只攻击我方
				if(m_bIsEnemy && bEnemyTank)
				{
					continue;
				}
				//我方子弹只攻击敌方
				if(!m_bIsEnemy && !bEnemyTank)
				{
					continue;
				}
				
				//取得角色信息
				C2DCharacter*	tpCharacter = tpTank->getCharInfo();
				if( tpCharacter )
				{
					CCPoint		tCenetrPos   = tpCharacter->GetScenePos();
					CCSize		tBoundingBox = tpCharacter->GetBoundingBoxSize();

					if(tScenePos.x < (tCenetrPos.x - tBoundingBox.width*0.5))continue ;
					if(tScenePos.y < (tCenetrPos.y - tBoundingBox.height*0.5))continue ;
					if(tScenePos.x > (tCenetrPos.x + tBoundingBox.width*0.5))continue ;
					if(tScenePos.y > (tCenetrPos.y + tBoundingBox.height*0.5))continue ;

					CCParticleSystemQuad*	pFireSystem = new CCParticleSystemQuad();
					pFireSystem->initWithFile("explode.plist");
					CCPoint		tScreenPos = CGameSceneNPC::m_pCurrGameMap->GetScreenPosition(tScenePos.x,tScenePos.y);
					pFireSystem->setPosition(tScreenPos.x,tScreenPos.y);
					CGameSceneNPC::m_pCurrGameMap->AddNew2DParticleSystem(nBulletLayerIndex,pFireSystem);

					CGameSceneNPC::m_pCurrGameMap->Del2DActiveObj(nBulletLayerIndex,this);
					SimpleAudioEngine::sharedEngine()->playEffect("hurt.mp3");

					if(true == bEnemyTank)
					{
						CAttrib_Tank*	tpTankAttrib = dynamic_cast<CAttrib_Tank*>(tpTank->getNpcAttrib());
						if(tpTankAttrib&&tpTankAttrib->IsNBState() == false)
						{
							int	tHP = tpTankAttrib->getHP();
							if(m_bNB)
							{
								tHP-=2;
							}
							else
							{
								tHP--;
							}

							tpTankAttrib->setHP(tHP);
							if(tHP <= 0)
							{
								CGameSceneNPC::m_pCurrGameMap->Del2DActiveObj(nLayerIndex,tpTank);
								//击毁一个敌方坦克
								HelloWorld*	tpHelloWorld = dynamic_cast<HelloWorld*>(CGameSceneNPC::m_pCurrGameMap->getParent());
								if(tpHelloWorld)
								{
									tpHelloWorld->KillEmenyTank(tpTankAttrib->getTankType());

									CCPoint		tScreenPos = CGameSceneNPC::m_pCurrGameMap->GetScreenPosition(tScenePos.x,tScenePos.y);									
													
									//加载爆炸效果
									EffectNode*		gpEffect = EffectManager::GetInstance()->LoadEffect("101");
									MyVector3 vPos;
									vPos.x = tScreenPos.x;
									vPos.y = tScreenPos.y;
									vPos.z = 10;
									//设置效果位置
									gpEffect->SetPos(vPos);
									MyVector3 vScale;
									vScale.x=0.5;
									vScale.y=0.5;
									vScale.z=0;
									//设置效果缩放
									gpEffect->SetScale(vScale);
									//播放效果
									gpEffect->PlayEffect();

								}
							}
						}
					}
					else
					{
						
						CMyTank*	tpMyTank = dynamic_cast<CMyTank*>(tpTank);
						if(tpMyTank&&tpMyTank->IsNBState() == false)
						{

							int	tHP = tpMyTank->getHP();
							if(m_bNB)
							{
								tHP-=2;
							}
							else
							{
								tHP--;
							}

							tpMyTank->setHP(tHP);
							if(tHP <= 0)
							{
								CGameSceneNPC::m_pCurrGameMap->Del2DActiveObj(1,tpTank);
								//击毁一个敌方坦克
								HelloWorld*	tpHelloWorld = dynamic_cast<HelloWorld*>(CGameSceneNPC::m_pCurrGameMap->getParent());
								if(tpHelloWorld)
								{
									tpHelloWorld->KillMyTank();

									CCPoint		tScreenPos = CGameSceneNPC::m_pCurrGameMap->GetScreenPosition(tScenePos.x,tScenePos.y);									
													
									//加载爆炸效果
									EffectNode*		gpEffect = EffectManager::GetInstance()->LoadEffect("101");
									MyVector3 vPos;
									vPos.x = tScreenPos.x;
									vPos.y = tScreenPos.y;
									vPos.z = 10;
									//设置效果位置
									gpEffect->SetPos(vPos);
									MyVector3 vScale;
									vScale.x=0.5;
									vScale.y=0.5;
									vScale.z=0;
									//设置效果缩放
									gpEffect->SetScale(vScale);
									//播放效果
									gpEffect->PlayEffect();


								}
							}
						}
						
					}
					return ;
				}
				
			}
		}

		//删除
		if(tScenePos.x < tBoundingBox.width*0.5)
		{
			CGameSceneNPC::m_pCurrGameMap->Del2DActiveObj(nBulletLayerIndex,this);
		}
		else if(tScenePos.x > (tpSceneHeader->m_nSceneWidth-tBoundingBox.width*0.5))
		{
			CGameSceneNPC::m_pCurrGameMap->Del2DActiveObj(nBulletLayerIndex,this);
		}
		else if(tScenePos.y < tBoundingBox.height*0.5)
		{
			CGameSceneNPC::m_pCurrGameMap->Del2DActiveObj(nBulletLayerIndex,this);
		}
		else if(tScenePos.y > (tpSceneHeader->m_nSceneHeight-tBoundingBox.height*0.5))
		{
			CGameSceneNPC::m_pCurrGameMap->Del2DActiveObj(nBulletLayerIndex,this);
		}
	}
}

    这个子弹类的重点,是在每次update更新位置时对所在格子里的物体进行判断并在击中相应的物体后创建粒子爆炸效果,删除子弹及相应物体。

    字弹类完成后,我们在敌我坦克的update函数中的发射子弹处理中加入相应的代码.

CMyTank.cpp:
//按下下键
		if (m_bAttackKey || (::GetKeyState('J'))&0x8000) 
		{
			struct timeval tv;
			gettimeofday(&tv,NULL);
			double	   	    dwCurrTime = tv.tv_sec*1000.0+tv.tv_usec* 0.001;
			//开炮时间
			if(dwCurrTime - m_dwLastAttackTime > 1000)
			{	
				//创建一个子弹角色
				C2DCharacter*	tpPlayerChar = g_CharacterManage.CreateNewCharacter("Bullet");
				if(tpPlayerChar)
				{
					//如果是穿甲弹,则播放穿甲弹对应的角色动作
					if(m_bNBBullet)
					{
						tpPlayerChar->PlayAction("run_nb",-1);
					}
					else
					{
						tpPlayerChar->PlayAction("run",-1);
					}
					//产生炮弹
					tpPlayerChar->SetScenePos(tScenePos);
					tpPlayerChar->GetCurrBody()->setZ(3);

					CBullet*	pNewBullet = new CBullet();
					pNewBullet->setCharInfo(tpPlayerChar);
					pNewBullet->setEnemy(false);
					pNewBullet->setDir(m_nDir);
					pNewBullet->setNB(m_bNBBullet);
					pNewBullet->setMoveSpeed(250);
					pNewBullet->Init();

					int nBulletLayerIndex = 2;
					CGameSceneNPC::m_pCurrGameMap->AddNew2DActiveObj(nBulletLayerIndex,pNewBullet);
					m_dwLastAttackTime = dwCurrTime ;
				}
			}
		}

AI_Tank.cpp:

//开炮时间
		double dwLastAttackTime = pAttrib_Tank->getLastAttackTime() ;
		if(dwCurrTime - dwLastAttackTime > 4000)
		{	

			C2DCharacter*	tpPlayerChar = g_CharacterManage.CreateNewCharacter("bullet");
			if(tpPlayerChar)
			{
				if(2 == nTankType || pAttrib_Tank->IsNBBulletState())
				{
					//穿甲弹
					tpPlayerChar->PlayAction("run_nb",-1);
				}
				else
				{
					//普通子弹
					tpPlayerChar->PlayAction("run",-1);
				}
				//产生炮弹
				tpPlayerChar->SetScenePos(tScenePos);
				tpPlayerChar->GetCurrBody()->setZ(3);
				CBullet*	pNewBullet = new CBullet();
				pNewBullet->setCharInfo(tpPlayerChar);
				pNewBullet->setEnemy(true);
				pNewBullet->setDir(nDir);
				pNewBullet->setNB(pAttrib_Tank->IsNBBulletState());
				pNewBullet->setMoveSpeed(250);
				pNewBullet->Init();
				CGameSceneNPC::m_pCurrGameMap->AddNew2DActiveObj(2,pNewBullet);
				pAttrib_Tank->setLastAttackTime(dwCurrTime) ;
			}
		}

   这样运行起来就可以发射子弹了.到这时为止,游戏场景,坦克,攻击逻辑和效果都算做完了.我们回到HelloWorldScene. Cpp,在点选关卡按钮函数中加入相应的处理:

//开始第N关
void HelloWorld::ChoseCardBtnCallBack(CCNode* pSender, void* data)
{
	//关闭选关卡的界面
	CGameUI*		pChoseChardUI = dynamic_cast<CGameUI*>(getChildByTag(NID_UI_CHOSEMAP));
	if(pChoseChardUI)
	{
		pChoseChardUI->setVisible(false);
	}
	//如果没有创建场景,在这里创建出来.
	if(NULL == m_pGameScene)
	{
		m_pGameScene = new CGameScene();
		m_pGameScene->autorelease();
		m_pGameScene->SetCameraPosition(0,0);
		m_pGameScene->setTouchEnabled(false);
		//pNewScene->ShowTileInfo(true);
		this->addChild(m_pGameScene,1,101);
	}

	//取得关卡ID并初始化一些变量
	int nCardIndex = (int)data;
	m_nCurrMapID = nCardIndex+1 ;
	m_EnemyTankVec.clear();
	//坦克总数
	m_EnemyDeadTotal = 0;
	//每关卡坦克数量递增以增加难度
	m_EnemyTankTotal = 4 + m_nCurrMapID ;
	m_EnenyTankCount = 0;
	m_nEnemyTank1_DeadCount = m_nEnemyTank2_DeadCount = m_nEnemyTank3_DeadCount = 0;
	m_nEnemyTank1_AwardCount = m_nEnemyTank2_AwardCount = m_nEnemyTank3_AwardCount = 0;
	char szCardName[100];
	
	sprintf(szCardName,"card%d.gsc",m_nCurrMapID++);
	if(true == m_pGameScene->LoadSceneFile_Bin(szCardName))
	{
		//为场景增加一些图片资源,分别为穿甲弹,无敌药瓶和回血瓶.末参数设置为不阻挡,
		m_pGameScene->AddRenderInfo(RET_IMAGE,"eff1.png","",false);
		m_pGameScene->AddRenderInfo(RET_IMAGE,"eff2.png","",false);
		m_pGameScene->AddRenderInfo(RET_IMAGE,"eff3.png","",false);
		//增加第二种墙的资源,即被击中后的半击毁墙体。末参数设为阻挡。
		m_pGameScene->AddRenderInfo(RET_IMAGE,"wall2.png","",true);
		//增加一层用于子弹显示
		m_pGameScene->AddNewLayer();
		//附加效果管理器到当前层。效果库是一个单件。在这里加入一下。
		m_pGameScene->addChild(EffectManager::GetInstance(), 10); 
		//加载角色包。
		if(true == g_CharacterManage.LoadRolTreeFromBin("tank.gro"))
		{
			//创建玩家角色
			C2DCharacter*	tpPlayerChar = g_CharacterManage.CreateNewCharacter("Player");
			if(tpPlayerChar)
			{
				//调用其动作
				tpPlayerChar->PlayAction("run",-1);
				//取得事件点数量,
				int		nLayerIndex = 1;
				int		nEventTileNum =	 m_pGameScene->GetEventTileNum(nLayerIndex);
				//取得对应层的指定事件点
				for(int n = 0 ; n < nEventTileNum ; n++)
				{
					stEventTile*	tpEventTile = m_pGameScene->GetEventTile(nLayerIndex,n);
					if(tpEventTile)
					{
						//如果事件点ID为1,设置为我方主坦克位。
						if(tpEventTile->m_nEventID == 1)
						{
							//取得相应格子的实际位置点。
							CCPoint		tBorthPt = m_pGameScene->GetTileCenterPoint(nLayerIndex,tpEventTile->m_sTile.x,tpEventTile->m_sTile.y,false);
							//设置我方主坦克在这个位置上。
							tpPlayerChar->SetScenePos(tBorthPt);
							//创建一个逻辑NPC类。
							m_pMyTank = new CMyTank();
							m_pMyTank->setCharInfo(tpPlayerChar);
							m_pMyTank->Init();
							//将NPC放入到场景中。
							m_pGameScene->AddNew2DActiveObj(1,m_pMyTank);
						}
						else if(tpEventTile->m_nEventID == 0)
						{
							//记录敌人坦克出生点
							POINT tPt;
							tPt.x = tpEventTile->m_sTile.x;
							tPt.y = tpEventTile->m_sTile.y;
							m_EnemyTankVec.push_back(tPt);
						}
					}
				}
				//创建出敌人坦克AI
				m_pEnemyTankAI = new CAi_Tank();
				//每2秒出现一个坦克
				schedule(schedule_selector(HelloWorld::EnemyTankBorth), 2.0f);
}
			}
		}
	}

}

     在初始化场景时,这里会遍历所有的事件点,根据不同的事件点做不同的处理,比如在事件点ID为0处创建敌方坦克,在事件点ID为1处创建我方坦克.因为创建敌方坦克是个持续的过程,这里通过每2秒调用的回调函数EnemyTankBorth来进行处理.

 

void HelloWorld::EnemyTankBorth(float dt)
{
	
	if(m_EnenyTankCount < m_EnemyTankTotal)
	{
		m_EnenyTankCount++;	
		int nBorthTileIndex = m_EnenyTankCount % m_EnemyTankVec.size() ; 

		//创建一个2D动画角色
		char szRandEnemy[64];
		int  nRandValue = rand()%3;
		sprintf(szRandEnemy,"Enemy%d",nRandValue+1);
		C2DCharacter*	tpEnemyChar = g_CharacterManage.CreateNewCharacter(szRandEnemy);
		if(tpEnemyChar)
		{

			//播放相应的动画名称
			tpEnemyChar->PlayAction("run",-1);
			int			nLayerIndex = 1;
			CCPoint		tBorthPt = m_pGameScene->GetTileCenterPoint(nLayerIndex,m_EnemyTankVec[nBorthTileIndex].x,m_EnemyTankVec[nBorthTileIndex].y,false);
			tpEnemyChar->SetScenePos(tBorthPt);

			//创建敌人坦克	
			CEnemyTank*	m_pEnemyTank = new CEnemyTank();
			m_pEnemyTank->setCharInfo(tpEnemyChar);
			m_pEnemyTank->Init();
				
			//设置坦克AI
			m_pEnemyTank->setNpcAI(m_pEnemyTankAI);
			m_pGameScene->AddNew2DActiveObj(1,m_pEnemyTank);

			//设置速度
			CAttrib_Tank*	tpTankAttrib = dynamic_cast<CAttrib_Tank*>(m_pEnemyTank->getNpcAttrib());
			if(tpTankAttrib)
			{
				tpTankAttrib->setTankType(nRandValue);
				switch(nRandValue)
				{
				case 0:
					//普通
					tpTankAttrib->setHP(2);
					tpTankAttrib->setMoveSpeed(50);
					break;
				case 1:
					//快速
					tpTankAttrib->setHP(1);
					tpTankAttrib->setMoveSpeed(70);
					break;
				case 2:
					//重型
					tpTankAttrib->setHP(4);
					tpTankAttrib->setMoveSpeed(30);
					break;
				}
				
			}

		}
	}
	else
	{
		//注销当前定时触发的回调函数
		unschedule(schedule_selector(HelloWorld::EnemyTankBorth));		
	}
}

这样刷敌人坦克的处理也就做好了.下面我们还要处理一下击毁敌方坦克和我方坦克或基地被击毁的函数.

//击毁一个敌人坦克
void HelloWorld::KillEmenyTank(int vTankType)
{
	m_EnemyDeadTotal++;
	//统计每种坦克被击毁的数量
	switch(vTankType)
	{
	case 0:
		m_nEnemyTank1_DeadCount++;
		break;
	case 1:
		m_nEnemyTank2_DeadCount++;
		break;
	case 2:
		m_nEnemyTank3_DeadCount++;
		break;
	}
	//如果所有敌坦克被灭,则设置本关卡完成
	if(m_EnemyDeadTotal == m_EnemyTankTotal)
	{
		m_bWinThisMap = true;
	}
}	
//击毁我方坦克
void HelloWorld::KillMyTank()
{
	//我方被毁,也要进入到关卡结束界面,所以可以在这里设置一下变量以进入关卡结束界面.
	m_EnemyDeadTotal = m_EnemyTankTotal ;
	//判定失败
	m_bWinThisMap = false;
	//因为关卡开始时m_nCurrMapID++,所以这里要还原一下.
	m_nCurrMapID--;
}

重载一下HelloWorld的darw函数,判断是否结束当前关卡.

//渲染
void HelloWorld::draw(void)
{
	//如果敌坦克被灭,进入到关卡结束界面.
	if(m_EnemyTankTotal >0 && m_EnemyDeadTotal == m_EnemyTankTotal)
	{
		FinishThisMap();
	}
	CCLayer::draw();
}

FinishThisMap函数中我们将需要显示一个统计界面.使用工具箱制作一个界面,然后将数字也用工具箱转成需要的字图以便显示统计数字.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏


做完需要的界面和字图后将其保存为ui_finish.uinumber.fnt,我们继续完成FinishThisMap函数.

//关卡结束界面
void HelloWorld::FinishThisMap()
{
	if(m_pGameScene)
	{
		//隐藏场景与控制界面
		m_pGameScene->setVisible(false);
		CGameUI*	pFXPUI = dynamic_cast<CGameUI*>(getChildByTag(NID_UI_FXP));
		if(pFXPUI)
		{
			pFXPUI->setVisible(false);
		}
	}
	//方向盘与攻击按键界面
	CGameUI*		pFinishUI = dynamic_cast<CGameUI*>(getChildByTag(NID_UI_FINISH));
	if(pFinishUI)
	{
		pFinishUI->setVisible(true);
		CUICtrl*	pUICtrl = pFinishUI->QueryUICtrl("UICtrl6_Rect");
		if(pUICtrl)
		{
			pUICtrl->setVisible(false);
		}
		pUICtrl = pFinishUI->QueryUICtrl("UICtrl7_Rect");
		if(pUICtrl)
		{
			pUICtrl->setVisible(false);
		}
	}
	else
	{
		pFinishUI = new CGameUI();
		pFinishUI->autorelease();
		pFinishUI->LoadUITree("ui_finish.ui");
		//设置界面的位置
		pFinishUI->setOffset(0, 0);
		this->addChild(pFinishUI,2,NID_UI_FINISH);

		m_pTankTextLabel1 = CCLabelBMFont::create("","number.fnt");
		m_pTankTextLabel1->setPosition(ccp(170, 380));
		m_pTankTextLabel1->setAlignment(kCCTextAlignmentLeft);
		m_pTankTextLabel1->setAnchorPoint(ccp(0,0));
		pFinishUI->addChild(m_pTankTextLabel1,1);

		m_pTankTextLabel2 = CCLabelBMFont::create("","number.fnt");
		m_pTankTextLabel2->setPosition(ccp(170, 300));
		m_pTankTextLabel2->setAlignment(kCCTextAlignmentLeft);
		m_pTankTextLabel2->setAnchorPoint(ccp(0,0));
		pFinishUI->addChild(m_pTankTextLabel2,1);

		m_pTankTextLabel3 = CCLabelBMFont::create("","number.fnt");
		m_pTankTextLabel3->setPosition(ccp(170, 220));
		m_pTankTextLabel3->setAlignment(kCCTextAlignmentLeft);
		m_pTankTextLabel3->setAnchorPoint(ccp(0,0));
		pFinishUI->addChild(m_pTankTextLabel3,1);

		m_pWardTotalLabel = CCLabelBMFont::create("","number.fnt");
		m_pWardTotalLabel->setPosition(ccp(230, 140));
		m_pWardTotalLabel->setAlignment(kCCTextAlignmentLeft);
		m_pWardTotalLabel->setAnchorPoint(ccp(0,0));
		pFinishUI->addChild(m_pWardTotalLabel,1);
	}
	//显示数量
	char	tTemp[1024];
	sprintf(tTemp,"x %d = %d", m_nEnemyTank1_DeadCount,0);
	m_pTankTextLabel1->setCString(tTemp);
	sprintf(tTemp,"x %d = %d", m_nEnemyTank2_DeadCount,0);
	m_pTankTextLabel2->setCString(tTemp);
	sprintf(tTemp,"x %d = %d", m_nEnemyTank3_DeadCount,0);
	m_pTankTextLabel3->setCString(tTemp);
	sprintf(tTemp,"%d", m_nAwardTotal);
	m_pWardTotalLabel->setCString(tTemp);	
	//设置攻击按钮的回调
	CUICtrl*	pUICtrl = pFinishUI->QueryUICtrl("UICtrl6_Button");
	if(pUICtrl)
	{
		CUIButton*	pUIButton = dynamic_cast<CUIButton*>(pUICtrl);
		if(pUIButton)
		{
			//设置在按下时响应的回调函数,可带自定义参数
			pUIButton->setClickBeginCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::NextMapBtnCallBack), (void*)0));
		}
	}
	//胜利还是失败的显示
	if(m_bWinThisMap)
	{
		CUICtrl*	pUICtrl = pFinishUI->QueryUICtrl("UICtrl7_Rect");
		if(pUICtrl)
		{
			CUIRect*	pUIRect = dynamic_cast<CUIRect*>(pUICtrl);
			if(pUIRect)
			{
				pUIRect->setVisible(true);
				C2DSkinAni*	pSkinAni = pUIRect->getSkinAni();
				if(pSkinAni)
				{
					pSkinAni->Play(1);
				}
			}
		}
	}
	else
	{
		CUICtrl*	pUICtrl = pFinishUI->QueryUICtrl("UICtrl8_Rect");
		if(pUICtrl)
		{
			CUIRect*	pUIRect = dynamic_cast<CUIRect*>(pUICtrl);
			if(pUIRect)
			{
				pUIRect->setVisible(true);
				C2DSkinAni*	pSkinAni = pUIRect->getSkinAni();
				if(pSkinAni)
				{
					pSkinAni->Play(1);
				}
			}
		}
	}

//快速更新统计数字的效果,使用每0.01秒的回调函数来更新数字从0到指定数字的变化。

schedule(schedule_selector(HelloWorld::ShowEnemyTankCountText), 0.01f);

 

      m_EnemyTankTotal= 0;

}

 

//敌人坦克被击毁统计

void HelloWorld::ShowEnemyTankCountText(floatdt)

{

      if(m_pTankTextLabel1&&m_pTankTextLabel2&&m_pTankTextLabel3)

      {

           boolbFinish = true;

           if(m_nEnemyTank1_AwardCount< m_nEnemyTank1_DeadCount*10)

           {

                 m_nEnemyTank1_AwardCount++;

                 bFinish= false;

           }

           if(m_nEnemyTank2_AwardCount< m_nEnemyTank2_DeadCount*8)

           {

                 m_nEnemyTank2_AwardCount++;

                 bFinish= false;

           }

           if(m_nEnemyTank3_AwardCount< m_nEnemyTank3_DeadCount*30)

           {

                 m_nEnemyTank3_AwardCount++;

                 bFinish= false;

           }

           m_nAwardTotal= m_nEnemyTank1_AwardCount + m_nEnemyTank2_AwardCount +m_nEnemyTank3_AwardCount;

           //显示数量

           char     tTemp[1024];

           sprintf(tTemp,"x%d=%d",m_nEnemyTank1_DeadCount,m_nEnemyTank1_AwardCount);

           m_pTankTextLabel1->setString(tTemp,true);

           sprintf(tTemp,"x%d=%d",m_nEnemyTank2_DeadCount,m_nEnemyTank2_AwardCount);

           m_pTankTextLabel2->setString(tTemp,true);

           sprintf(tTemp,"x%d=%d",m_nEnemyTank3_DeadCount,m_nEnemyTank3_AwardCount);

           m_pTankTextLabel3->setString(tTemp,true);

           sprintf(tTemp,"%d", m_nAwardTotal);

           m_pWardTotalLabel->setString(tTemp,true);

           if(bFinish)

           {

      //注销显示敌方坦克数量的函数.

                       unschedule(schedule_selector(HelloWorld::ShowEnemyTankCountText));      

           }

      }

}

 

在ui_finish.ui中我们为继续游戏按钮增加了一个点击响应的回函数.

//下一关按钮回调
void HelloWorld::NextMapBtnCallBack(CCNode* pSender, void* data)
{
	LoadNextMap();
}

//开始下一关
void HelloWorld::LoadNextMap()
{
	CGameUI*		pChoseChardUI = dynamic_cast<CGameUI*>(getChildByTag(NID_UI_FINISH));
	if(pChoseChardUI)
	{
		pChoseChardUI->setVisible(false);
	}

	if(m_pGameScene)
	{
		m_EnemyTankVec.clear();
		//坦克总数
		m_EnemyDeadTotal = 0;
		m_EnemyTankTotal = 4 + m_nCurrMapID ;
		m_EnenyTankCount = 0;
		m_nEnemyTank1_DeadCount = m_nEnemyTank2_DeadCount = m_nEnemyTank3_DeadCount = 0;
		m_nEnemyTank1_AwardCount = m_nEnemyTank2_AwardCount = m_nEnemyTank3_AwardCount = 0;
		

		//保存关卡
		if(m_nLastPlayMapID < m_nCurrMapID-1)
		{
			m_nLastPlayMapID = m_nCurrMapID-1;
			CCUserDefault::sharedUserDefault()->setIntegerForKey("MapID", m_nLastPlayMapID);
			CCUserDefault::sharedUserDefault()->flush(); 
		}

		//根据是否胜利进入关卡
		char szCardName[100];
		if(m_bWinThisMap)
		{
			sprintf(szCardName,"card%d.gsc",m_nCurrMapID++);
		}
		else
		{
			sprintf(szCardName,"card%d.gsc",m_nCurrMapID);
		}
		
		if(m_nCurrMapID > 15)
		{
			//播放通关动画
			scheduleOnce(schedule_selector(HelloWorld::CreatWinnerUI), 0.0f);
		}
		else
		{
			if(true == m_pGameScene->LoadSceneFile_Bin(szCardName))
			{
				//增加一些物件资源
				m_pGameScene->AddRenderInfo(RET_IMAGE,"eff1.png","",false);
				m_pGameScene->AddRenderInfo(RET_IMAGE,"eff2.png","",false);
				m_pGameScene->AddRenderInfo(RET_IMAGE,"eff3.png","",false);
				//增加第二种墙的资源
				m_pGameScene->AddRenderInfo(RET_IMAGE,"wall2.png","",true);
				//增加一层用于子弹显示
				m_pGameScene->AddNewLayer();
				//设置显示
				m_pGameScene->setVisible(true);

				if(true == g_CharacterManage.LoadRolTreeFromBin("tank.gro"))
				{

					C2DCharacter*	tpPlayerChar = g_CharacterManage.CreateNewCharacter("Player");
					if(tpPlayerChar)
					{
						tpPlayerChar->PlayAction("run",-1);
						//取得事件点数量
						int		nLayerIndex = 1;
						int		nEventTileNum =	 m_pGameScene->GetEventTileNum(nLayerIndex);
						//取得对应层的指定事件点
						for(int n = 0 ; n < nEventTileNum ; n++)
						{
							stEventTile*	tpEventTile = m_pGameScene->GetEventTile(nLayerIndex,n);
							if(tpEventTile)
							{
								if(tpEventTile->m_nEventID == 1)
								{
									//产生我方坦克
									CCPoint		tBorthPt = m_pGameScene->GetTileCenterPoint(nLayerIndex,tpEventTile->m_sTile.x,tpEventTile->m_sTile.y,false);
									tpPlayerChar->SetScenePos(tBorthPt);

									m_pMyTank = new CMyTank();
									m_pMyTank->setCharInfo(tpPlayerChar);
									m_pMyTank->Init();
									m_pGameScene->AddNew2DActiveObj(1,m_pMyTank);
								}
								else if(tpEventTile->m_nEventID == 0)
								{
									//记录敌人坦克出生点
									POINT tPt;
									tPt.x = tpEventTile->m_sTile.x;
									tPt.y = tpEventTile->m_sTile.y;
									m_EnemyTankVec.push_back(tPt);
								}
							}
						}
						//创建出敌人坦克AI
						m_pEnemyTankAI = new CAi_Tank();
						//每2秒出现一个坦克
						schedule(schedule_selector(HelloWorld::EnemyTankBorth), 2.0f);
					}

				}
			}
		}
	}
}

      播放通关动画的处理在函数CreatWinnerUI中,当然,我们先用工具箱制作好通过的胜利动画,这里简单的做了一个老照片的渐渐显示的动画,与开始时的老照片动画类似,不多做讲述.

   使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

做好后保存为sl.ani并导出sl.gan供调用.

//通关的界面
void HelloWorld::CreatWinnerUI(float dt)
{
	//过场动画
	C2DSkinAni*		pNewSkinAni = new C2DSkinAni();
	if(pNewSkinAni)
	{
		pNewSkinAni->ReadAllSubBodyFromBin("sl.gan");
		CCSize tBoundingBox = pNewSkinAni->GetBoundingBoxSize();
		pNewSkinAni->autorelease();
		pNewSkinAni->Play(1);
		this->addChild(pNewSkinAni,4,NID_ANI_WAR);
		CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
		pNewSkinAni->SetOffset(visibleSize.width/2,visibleSize.height/2 );
		//最后一帧加一个动画调用
		pNewSkinAni->SetEndFrameCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::WinnerEndFrameCallBack), (void*)0xbebabeba));
	}
}	
//通关的动画显示结束的回调,再次回到开始界面.
void	HelloWorld::WinnerEndFrameCallBack(CCNode* pSender, void* data)
{
	C2DSkinAni*		pNewSkinAni = dynamic_cast<C2DSkinAni*>(getChildByTag(NID_ANI_WAR));
	if(pNewSkinAni)
	{
		pNewSkinAni->setVisible(false);
		removeChild(pNewSkinAni);
		//pNewSkinAni->release();
	}
	
	CGameUI*		pNewUI = dynamic_cast<CGameUI*>(getChildByTag(NID_UI_START));
	if(pNewUI)
	{
		pNewUI->setVisible(true);
	}
}

到现在这一步,基本逻辑就算完成了.最后我们再为它增加一个触屏控制界面,以使其可以在移动设备上进行控制.

使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

导出ui_fxp.ui.在场景初始化完成时加入代码:

//方向盘与攻击按键界面
			CGameUI*		pFXPUI = new CGameUI();
			pFXPUI->autorelease();
			pFXPUI->LoadUITree("ui_fxp.ui");
			//设置界面的位置
			pFXPUI->setOffset(0, 0);
			this->addChild(pFXPUI,4,NID_UI_FXP);

			//设置方向盘按钮的回调
			CUICtrl*	pUICtrl = pFXPUI->QueryUICtrl("UICtrl6_Button");
			if(pUICtrl)
			{
				CUIButton*	pUIButton = dynamic_cast<CUIButton*>(pUICtrl);
				if(pUIButton)
				{
					//设置在按下时响应的回调函数,可带自定义参数
					pUIButton->setClickBeginCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::FXPBtnCallBack), (void*)0));
					pUIButton->setClickEndCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::FXPBtnCallBack), (void*)-1));
				}
			}
			pUICtrl = pFXPUI->QueryUICtrl("UICtrl7_Button");
			if(pUICtrl)
			{
				CUIButton*	pUIButton = dynamic_cast<CUIButton*>(pUICtrl);
				if(pUIButton)
				{
					//设置在按下时响应的回调函数,可带自定义参数
					pUIButton->setClickBeginCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::FXPBtnCallBack), (void*)1));
					pUIButton->setClickEndCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::FXPBtnCallBack), (void*)-1));
				}
			}
			pUICtrl = pFXPUI->QueryUICtrl("UICtrl8_Button");
			if(pUICtrl)
			{
				CUIButton*	pUIButton = dynamic_cast<CUIButton*>(pUICtrl);
				if(pUIButton)
				{
					//设置在按下时响应的回调函数,可带自定义参数
					pUIButton->setClickBeginCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::FXPBtnCallBack), (void*)2));
					pUIButton->setClickEndCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::FXPBtnCallBack), (void*)-1));
				}
			}
			pUICtrl = pFXPUI->QueryUICtrl("UICtrl9_Button");
			if(pUICtrl)
			{
				CUIButton*	pUIButton = dynamic_cast<CUIButton*>(pUICtrl);
				if(pUIButton)
				{
					//设置在按下时响应的回调函数,可带自定义参数
					pUIButton->setClickBeginCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::FXPBtnCallBack), (void*)3));
					pUIButton->setClickEndCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::FXPBtnCallBack), (void*)-1));
				}
			}
			//设置攻击按钮的回调
			pUICtrl = pFXPUI->QueryUICtrl("UICtrl21_Button");
			if(pUICtrl)
			{
				CUIButton*	pUIButton = dynamic_cast<CUIButton*>(pUICtrl);
				if(pUIButton)
				{
					//设置在按下时响应的回调函数,可带自定义参数
					pUIButton->setClickBeginCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::AttackBtnCallBack), (void*)1));
					pUIButton->setClickEndCallFuncND(CCCallFuncND::create(this, callfuncND_selector(HelloWorld::AttackBtnCallBack), (void*)0));
				}
			}


增加两个回调函数:
//方向盘按钮回调
void HelloWorld::FXPBtnCallBack(CCNode* pSender, void* data)
{
	if(m_pMyTank)
	{
		int		nValue = (int)data;
		switch(nValue)
		{
		case 0:
			{//up
				m_pMyTank->SetDirKey(DK_UP);
			}
			break;
		case 1:
			{//right
				m_pMyTank->SetDirKey(DK_RIGHT);
			}
			break;
		case 2:
			{//down
				m_pMyTank->SetDirKey(DK_DOWN);
			}
			break;
		case 3:
			{//left
				m_pMyTank->SetDirKey(DK_LEFT);
			}
			break;
		default:
			{
				m_pMyTank->SetDirKey(DK_NONE);
			}
			break;
		}
	}
}
//攻击按钮回调
void HelloWorld::AttackBtnCallBack(CCNode* pSender, void* data)
{
	if(m_pMyTank)
	{
		if( 0 == data )
		{
			m_pMyTank->SetAttackKey(false);
		}
		else
		{
			m_pMyTank->SetAttackKey(true);
		}
	}
}

这样这个《坦克大战》就算基本完成了.我们再找一些背景音乐与音效加入到其中播放,最后编译运行一下,看着坦克勇猛的向前冲击,是否很带感呢?

 使用《红孩儿工具箱》开发基于Cocos2d-x的《坦克大战》游戏

      谢谢大家的学习,下一节《超级玛丽》再见!

分类: cocos2d, cocos2d-x 标签:

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

2013年9月23日 没有评论

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

 

    
大家好,我是红孩儿.经过一年左右业余时间的持续开发,《红孩儿工具箱》已经初步完成了一些不错的功能,大家可以用它来辅助我们基于Cocos2d-x的游戏开发工作,帮助我们提升工作效率,实现一些复杂的场景和动画效果。好啦,废话不多讲,今天我来讲解一下如何使用红孩儿工具箱制作小游戏-《打地鼠》GO

《打地鼠》这个游戏很简单,就是玩家在看到地鼠从土地里钻出来时用挥舞的锤子砸向地鼠就可以了。这个游戏需要做一个简单的场景,然后还需要一些动画,比如地鼠从土里钻出来的动画和锤子落下的动画等,然后还需要有文字显示积分。下面我们来讲解一下如何使用红孩儿工具箱来制做这一切。

一.拼图PLIST生成
     首先我们需要从www.game2z.com  网站,在左上角的 下载红孩儿工具箱最新版 链接处下载最新的0.2.2版本。下载后我们将其解压到自定义的目录。打开后我们可以看到工具箱EXE程序和一个dx9的dll.还有一个资源目录Resource,一个测试设备图片存放目录Dev以及一个配套的SDK目录.红孩儿工具箱只需要这个EXE和对应的这个dll就可以运行在Windows操作系统了.如果你的操作系统中安装了相应的dll,那么也就是说,整个红孩儿工具箱,只需要一个3M多的EXE程序就可以正常运行了.是不是很小巧绿色环保?


    下面我们在工具箱所在目录的资源目录Resource中新建立一个自已的项目资源目录DaDiShu。然后我们从Cocos2d-x的HelloLua工程资源里找到一些农场的图片,还有沙地和植物的图片,把它们拷贝到DaDiShu目录。这将是构成我们场景的重要资源。


使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

 

     我们将它们拷到DaDiShu下。然后启动工具箱,这时我们可以看到红孩儿工具箱的登录界面,我们一般查看可以选择离线登录,但离线登录不能导出动画和场景,主要是用于查看和编辑。如果是想导出,那就最好到www.game2z.com论坛注册一个正式用户的账号。在这里我们输入注册账号和密码后,点击“登录”,稍等片刻完成验证后即可进入工具箱主界面。

     使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

 

    我们首先要做的,是先设置一下我们的工程资源目录,点击“设置工程资源目录”按钮,选择我们的资源目录即可,一旦设置完成,工具箱就会开始遍历和登记工程目录下所有的文件资源,这样我们就可以使用工程资源目录下的图片了.下次再使用工具箱时也会自动的设置好资源目录.
    

    我们首先想做的是把crop.png图片进行碎图PLIST生成。Cocos2d-x是可以识别碎图PLIST文件的,工具箱也可以识别带有PLIST文件的PNG图片来使用碎图设置精灵,我们在工具箱主界面的第一个功能编辑页“拼图/切图”中就可以完成这个需求,“拼图/切图”页默认的是“拼图”导出面板,我们点击一下工具箱全局配置信息下面的“切图/拼图-[拼图”复选框,现在切换为“切图”编辑面板。这时候就可以将图片进行切图操作了。


使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

 

    我们点击“选择图片文件”打开crop.png图片,这时候在工具箱的左边图片元件树控件就会显示出crop这个图片树项,同时在视图窗口中会显示这张图片。如图:

 使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

          我们在左边的crop树项上右键,可以弹出菜单,这时候我们选择一下“按行列数切块”,在弹出的对话框中输入块名称crop,行数填1,列数填4即可,也可以在弹出菜单中选择“固定大小切块”,在弹出的对话框中输入切块宽高和间距边距,确定后我们可以看到在crop树项下生成了相应的图块。这时我们可以选择各树项进行查看,也可以在图片上直接点击显示图块项。

 使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


       我们可以看到相应的图块会显示出调整矩形编辑框,这种方式是可以让我们通过鼠标点中编辑框结点来修改图块的大小和位置的。不过在这里使用行列数切块就不需要调整了,如果小伙伴们有自已的需求,也可以通过对图片项弹出菜单中的“加入新图块”来手动进行图块的切分。

        我们在编辑好图块后,确认一下图片导出的名称和目录,点击“立即导出”,这时工具箱就可以为我们的图片crop生成相应的PLIST。如图:

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


生成的文件:

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


在工具箱后面的场景制做中,我们就可以使用这个PLIST来进行各状态植物图片的摆放。

二。场景编辑

现在我们切换到场景编辑页中,如图:

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


       我们点击“创建”,来新建一个地图,这个《打地鼠》游戏场景不需要滚屏,只有一张背景图farm.jpg大小就可以了,在这里我们输入场景名称farm,场景宽800,高800。然后确定,就创建了场景,我们可以看到主视窗里场景大小的一个边框,我们的场景就是要放在这里面的。如图:

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


      好,我们现在来规划一下这个场景的分层。我们可以将这个场景分为三层,第一层是农场背景,第二层是沙地格子,第三层是植被。这样将沙地格子放在农场背景中,植被放在沙地格子中,就可以实现一个农场的场景了。下面我们来实现一下。

      在右边的面板下部右键弹出菜单,选择“新增地图层”,然后会弹出一个地图层设置的对话框:

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


        我们计划第一层只是放一个农场图片,所以没有必要考虑格子,输入层的名称“BK”后确定即可,之后会增加一个图层页BK,这个图层页中有一个空白树控件,是用来管理放在这一层的各种资源,比如图片,动画,粒子等。


      使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


       我们在这个图层页中资源树区域右键点击,选中弹出菜单“增加新图类”项,选中后会弹出一个图片分类对话框,输入图片类名称“farm”,然后就会有一个图片类树项显示出来,我们可以在这个分类项上点击右键,在弹出菜单里我们可以看到很多可以增加的资源选项,我们在这里只加入一张背景图,所以选择“增加新图片”。

          使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


        在弹出的文件选择对话框中我们找到farm.jpg,然后确定,就可以将农场背景图加入到这一层的资源树中了。需要注意的是在浏览框中文件类型我们要选择jpg格式.因为这个图片是jpg的.


      使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


        我们点击图片项,可以看到在右上角的显示窗口中显示出这个资源项,然后我们把鼠标移动到主视窗中场景左上角附近点击一下,这时农场的背景图就被放入到场景中了。

        使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


         右键取消选中的资源,然后我们点选场景中的这个背景图片,在它被选中后我们在右边面板中点击“物件编辑”切换到物件的位置输入面板,输入位置0,0,就可以使其完全与场景框贴合在一起,这样我们第一层背景图就编辑好了。[为了方便查看,这里我按着Ctrl键的同时用鼠标滚轮缩放视图到合适大小.

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


       下面我们来创建第二层沙土格子的图层,我们在地图层选项页右边空白处右键,在弹出的菜单中选择“新增地图层”:


      使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


    
之后我们可以看到建立新图层的对话框,我们现在要考虑的是对于沙土格子land.png这张图片,他是要基于网格进行拼合的,而它本身是有斜度的。对于有斜度的网格,我们要选择斜视角的观察方式,在这里我们输入格子的宽度为lan.png的宽高182×94,之后输入网格的位置偏移,这个位置偏移就是以场景左上角为起点向右下进行偏移的距离,结果位置将作为第一个格子(0,0)的起点。这个起点在普通观察方式下就是格子的左上角,在斜视角的观察方式下则是格子左角。我们在这里可以输入X偏移30,Y偏移440,然后设置格子的行数为5,列数为3,点击确定后这一层就创建成功了,我们可以选中视图区左上角的“显示格子”看到在场景的相对偏移位置作为起点显示出了53列的斜视角网格。如图:

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

          如果我们觉得格子大小或位置不合适,也可以在相应的图层页项上右键点击,在弹出菜单里点击“修改当前图层”进行反复修改:


    使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


经过反复确认后,偏移位置x设为30,y设为460比较好,能够比较准确的将53列的格子放置在土地区域。

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


   好,我们在这一层的资源树控件中按照之前的方式增加图片类“land”,然后把land.png加入进来选中它,这时候我们就可以在相应的格子上刷图了,我们将鼠标在选中land.png的状态下移到场景中,如果我们选中了视图区左上角的“显示格子”复选框,那么在相应的格子上点击,就会自动在格子中刷上相应的图片。如果我们按下Ctrl键的同时从第1个格子向最后一个格子拖动那么将直接进行区域刷格子。如图:

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

      这样,第二层的沙土格子层我们就创建出来了。现在我们要继续创建植物层,植物层不需要格子,所以和第一层背景的建立方式是一样的,在右边面板选项空白处右键,在弹出的菜单中选择“新增地图层”进入到建立新图层的对话框,输入“OBJ”直接确定就可以。下面我们将crop.png加入到这一层的资源树中,这时我们可以看到它会自动的识别所有的图块。我们可以点击图片crop.png来放到场景中,也可以选中其下的crop_0~4来放入到场景中,在这里我们随便选一些图块放到场景沙土地的相应位置上如图:

     

    使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


   

        现在我们第三层也就做好了,是不是很简单?

        最后我们来生成一些事件点来创建地鼠。因为地鼠是从沙土地上生成出来的,所以这些事件点也就放在格子中,我们在右边面板切换到第二层沙土格子层。在图类上右键,选择弹出菜单项“增加事件格”,取中视图区左上角的“显示格子”复选框,同时为了方便查看,我们可以选择显示方式为“高亮当前层”项,这时候只有当前层的元素是高亮显示的。然后我们在场景沙土地的一些位置点击,把创建地鼠的事件格子编辑好后,切换为“显示所有层”,整个场景就算完成了。如图:


       使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏 

          

三.动画制作

首先我从网上找到老鼠,锤子,金币的图片并将它们用photoshop制做成动画序列图。

图一:地鼠钻出来的序列图

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

图二:锤子落下的序列图

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

图三:金币闪现的序列图

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


    好了,现在我们切换到“切图”面板,然后按照之前crop.png的切图方式来为它们生成相应的plist

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

     现在这些序列图已经有图块Plist了,我们开始为它们制做相就原动画.打开动画编辑面板,我们开始制做相应的序列帧动画.首先我们要建立一个图片结点.图片结点本身就是一个独立的精灵,可以表现动画.我们在左边的结点树控件的Root结点上右键单击,在弹出的菜单中选择“增加图片结点”:

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

     
工具箱在图片结点和骨骼结点的显示图标是可以自定义的,如果没有定义,此时会弹出提示未设置图片结点默认图标的消息框,确定后会弹出相应的设置对话框,这时我们可以选择Resource下的cocos2d-x.pngBone.png来作为图片结点和骨骼结点的默认图标.

    

   使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

        确定后就会出现cocos2d-x.png来显示在视窗的中心点了.

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

     
这时我们在这个图片结点项上右键,在弹出菜单中选择“生成固定帧间隔关键帧动画”就可以为这个图片结点弹出一个设置序列帧动画的对话框.

    使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

    
在“创建固定帧间隔的关键帧动画”对话框中,有两种图片源选取方式,第一种是使用图片序列,就是一般多张图片如attack_0.pngattack_1.pngattack10.png这种多张同名使用不同数字后缀的图片进行生成,因为我们的图片是拼在一起的,所以这里我们选中“使用拼合图内图块序列生成”的方式.之后点击“查找图块”,这时会弹出一个新面板.

    使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

    

    这个新面板是做什么用的呢?它就是图片及动画资源库面板.在这里我们可以点击“导入资源文件到当前资源库中”这个按钮来将我们需要的图片加入.如果有时候我们编辑的时候修改了图片,那也可以在这里随时点击刷新资源文件进行重新加载.

    我们现在将锤子图片导入进来.并选中任意一个图块:

     使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


     
这时候我们可以看到图片和图块都显示正确,然后我们点击“应用此图块”,这时候资源库对话框就消失了,我们会到之前的“创建固定帧间隔的关键帧动画”对话框中.可以看到图块起始为0,图块结束自动变为了2,我们把动画的间隔时间还使用默认的100毫秒,点击确定后,我们可以看到在动画编辑面板下部的帧显示区有3帧被设为了关键帧我们点击他们可以在主视窗中就显示出相应帧的动画了.

    使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


    

      我们可以在下面的帧显示区右部分点击“播放动画”复选框来直接查看动画的播放.并且可以手动随时调节帧间隔的时间来改变动画的速度.

      这样,这个锤子砸下来的动画就做好了.我们保存一下.在弹出的保存文件对话框里我们可以输入attack,格式仍为ani,确定后会再弹出一个修改动画名称的对话框,我们仍然输入attack后确定.这个动画就被保存为attack.ani文件了.同时会导出一个attack.plist.这个plist我们就可以在cocos2d-x中进行播放了.

      使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

       按照上面锤子动画的制做方式我们继续完成老鼠出生的动画和弹出金币的动画,它们对应的图片为laoshu.pngjinbi.png,动画名称可以分别设为borthgoal.导出的动画也就分别是borth.plistgoal.plist.

       现在三个动画和三个序列帧的PLIST就都导出了.我们就把游戏中的场景和动画制做完成了.


四.字图制作

       我们现在还需要一些文字来显示游戏名称和得分.我想做成彩色的文字显示在游戏画面合适的位置.这要用到Cocos2d-x支持的字图.即使用png+fnt的方式显示字库,其中png图片存储文字的显示结果,fnt存储文字的位置信息.强大的红孩儿工具箱也提供了字图的编辑和导出功能.我们来试一下.

        我们现在切换到“字体编辑”面板,然后我们在文档信息框里删除原有文字后输入“打地鼠0123456789”,接下来我们选择自已想要的字体,这里我选的是华文琥珀,字大小为36.确定后我们可以看到文档信息框里文字变成了相应的字体.

         使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


         这时我们点击“开始生成”,即可以很快看到生成出来的字图:


       使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


       这黑黑的文字不是我们想要的,现在我们来进行一下美化,点击右上角“编辑文字效果”按钮切换到编辑文字效果的面板。

     使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


       
这时我们可以选择字体颜色,调节字的间距,以及选一种自已喜欢的阴影方式。之后我们还可以在图片着色部分中加载一张过渡图片来进行着色.比如这里我们使用font_effect1.png来进行单字应用的叠加着色.同时字体用红,阴影用黑,右下描边,阴影粗细填5,间距为1,一个五彩的字图就出现了:

        使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

       

       感觉玩的差不多了,可以点击导出fntpng字图保存为text.pngtext.fnt.这样我们的游戏就可以使用这个字图来进行游戏名称的显示和积分的显示了.

       使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


         

五.粒子制作

       最后,我还想在农场里加入一个雪花落下的粒子效果,这样可以更加增加场景的气氛感。我们切换到工具箱的“粒子编辑”面板。在这里我们可以看到在主视窗中有一个粒子效果。右边面板有一个可以选择的粒子模版库的下拉列表和一些属性值编辑区域

       使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


         
在粒子模版库中放置了一些Cocos2d-x可以支持的粒子系统的样式。我们在这里要做一个下雪的效果,所以直接选择snow就可以了。

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


       我们现在能看到下雪效果的粒子系统了,但是它的粒子是没有贴图的,还只是渐变色块,我们点击“更改图片”就可以找一个球体的图片来做为雪花的贴图。


  使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


         这样漂亮的雪点的效果就出现了。


使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


         我们可以根据需要作改一些属性参数。比如横向的粒子系统宽度和起始颜色。

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


      最后,我们点击“保存”,可以将其保存成snow.plist放在资源目录。

  使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


       
下雪的粒子系统我们就算做好了,下面再切换到场景编辑部分把下雪的粒子系统加入到场景中。直接在第三个图层下的图类项上右键,在弹出菜单中选择“增加粒子系统”,之后将snow.plist导入就可以了。

        使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏


       

    最后保存,并导出地图的XML就完成了场景中的下雪效果

    这样我们就把工具处理部分就算完成了,我们下面进入到代码的编写部分.

六.代码编写

     为了方便工程的建立,我们可以直接将Cocos2d-x中的HelloCpp工程目录复制一份出来,然后修改工程名,之后将工具箱附带SDK中的C++目录中的Map子目录中的代码拷贝到Classes中。

 使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

     然后我们打开工程,将相应文件加入到工程中

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

   

现在我来介绍一下工具箱开源的场景代码,除了解析xml而加入的tinyxml之外,这里主要就是两个类文件


HHRMapLayer.h/cppHHRMapReader.h/cpp,它们分别是显示场景和解析场景文件的类。这两个类文件可以开源的哦~,大家学习之后就可以自行进行扩展并进行二次开发了。

CHHRMapReader
是用于工具箱的地图XML文件的解析,支持多层场景,任意网格设置,摆放图片和粒子系统,设置事件点,路径点。我们可以用工具箱来制做场景,导出成XML格式后用它来进行解析。


头文件代码:

#ifndef _HHRMAPREADER_H
 
#define _HHRMAPREADER_H
 
//==================================================================//
 
//File:        HHRMapReaer.h
 
//Date:        2013-09-05
 
//Auto:        Honghaier 
//Blog:        http://blog.csdn.net/honghaier
 
//Web:        http://www.game2z.com
 
//Desc:        用于红孩儿工具箱的地图解析,目前只支持图片和粒子系统
 
//==================================================================//
 
#include "cocos2d.h"
 
#include "ccTypes.h"
 
#include <stdio.h>
 
#include <string>
 
#include <vector>
 
using namespace std;
 
using namespace cocos2d;
 

#if (CC_TARGET_PLATFORM != CC_PLATFORM_WIN32)
 
//ANDROID平台上用的,用于位置
 
struct POINT
 
{
 
        int         x;
 
        int  y;
 
};
 
#define _MAX_PATH        256
 

#endif
 

#define BREAK_IF(a)                if(!a)break;
 
//地图头信息
 
struct stMapHeader
 
{
 
        int                m_nVersion;                                                        //版本号
 
        string        m_strMapName;                                                //场景名称
 
        int                m_nSceneWidth;                                                //场景宽度
 
        int                m_nSceneHeight;                                                //场景高度
 
        int                m_nLayerNum;                                                //场景层数
 
};
 

//元素类型
 
enum        enuElementType
 
{
 
        RET_NONE         = 0,        //什么也没有
 
        RET_IMAGE         = 1,        //静态图
 
        RET_ANI                 = 2,        //动画
 
        RET_PARTICLE = 3,        //粒子效果
 
        RET_EFFECT         = 4,        //效果
 
        RET_NPC                 = 5,        //角色
 
        RET_OBSTRUCT = 6,        //阻挡格
 
        RET_EVENT         = 7,   //事件格
 
};
 

//精灵扩展类
 
class  CCSpriteEx        :public CCSprite
 
{
 
public:
 
        //取得四边形顶点指针
 
        inline ccV3F_C4B_T2F_Quad* getQuadPtr(void) { return &m_sQuad; }        
 
};
 

//粒子系统扩展类
 
class        CCParticleSystemQuad_Ex        :public CCParticleSystemQuad
 
{
 
public:
 
        //取得PLIST
 
        const char*         getPListPath()
 
        {
 
                return m_sPlistFile.c_str();
 
        }
 
        //打开相应的粒子PLIST文件
 
        bool  LoadParticleInfoFromPListFile(const char* szPListFile)
 
        {
 
                return initWithFile(szPListFile);
 
        }
 
};
 

//资源信息
 
struct stMapResInfo
 
{
 
        enuElementType                                m_Type;                                                //类型
 
        string                                                m_strFileName;                                //文件名(图片名,动画PLIST名或粒子系统PLIST名)
 
        string                                                m_strBlockName;                                //图块名
 
        CCSpriteEx*                                        m_pSprite;                                        //精灵
 
        CCParticleSystemQuad_Ex*        m_pParticleSystem;                        //粒子系统
 
};
 

//批次结点处理
 
struct stBatchNodeInfo
 
{
 
        string                                m_strFileName;                //文件名(图片名)
 
        CCSpriteBatchNode*        m_pBatchNode;
 
}
 
;
 

//视角
 
enum enuViewType
 
{
 
        VT_NORMAL                = 0,        //普通
 
        VT_SKEW                        = 1,        //斜视角
 
        VT_FREE                        = 2,        //不使用格子
 
};
 

//自由物体
 
struct stFreeObj
 
{
 
        stMapResInfo*        m_pResInfo;                                //对应的资源索引
 
        CCSprite*                m_pSprite;                                //排序时加入的精灵
 
        float                        m_fPosX;                                //X位置
 
        float                        m_fPosY;                                //Y位置
 
        float                        m_fLowerHeight;                        //最低位置(用于排序)
 
};
 

//路径
 
struct stPathNode
 
{
 
        bool                        m_bIsTile;                                //是否是格子
 
        POINT                        m_sSrcTile;                                //起点格子,如果是格子,则为格子位置,否则为像素位置
 
        POINT                        m_sDestTile;                        //终点格子,如果是格子,则为格子位置,否则为像素位置
 
}
 
;
 

//事件
 
struct stEventTile
 
{
 
        int                                m_nEventID;                                //事件ID
 
        bool                        m_bIsTile;                                //是否是格子
 
        POINT                        m_sTile;                                //如果是格子,则为格子位置,否则为像素位置
 
};
 

//层结构
 
struct stMapLayerInfo
 
{        
 
        enuViewType                        m_ViewType;                        //视角
 
        int                                        m_nTileWidth;                //格子宽度
 
        int                                        m_nTileHeight;                //格子高度
 
        int                                        m_nTileRows;                //格子行数
 
        int                                        m_nTileCows;                //格子列数
 
        int                                        m_nStartPosX;                //横向格子起点位置
 
        int                                        m_nStartPosY;                //纵向格子起点位置
 
        stMapResInfo**                m_pTileResArray;        //格子对应资源索引
 
        vector<stFreeObj>        m_ObjectVec;                //自由物体容器
 
        int                                        m_ObjectNum;                //自由物体数量
 
        vector<stPathNode>        m_PathNodeVec;                //路径容器
 
        int                                        m_PathNodeNum;                //路径点数量
 
        vector<stEventTile> m_EventTileVec;                //事件容器
 
        int                                        m_EventTileNum;                //事件点数量
 
};
 

//地图解析类
 
class CHHRMapReader
 
{
 
public:
 
        //构造
 
        CHHRMapReader();
 
        //析构
 
        ~CHHRMapReader();
 

public:
 
        //从文件中载入地图
 
        bool                        LoadDataFromFile(const char* szFileName);
 
        //从内存中载入地图
 
        bool                        LoadDataFromData(const char* szData);
 
        //清空
 
        void                        CleanUp();
 

public:
 

        //取得场景头信息
 
        stMapHeader*        GetMapHeader();
 
        //取得资源数量
 
        int                                GetResInfoNum();
 
        //取得资源指针
 
        stMapResInfo*        GetResInfoPtr(int vIndex);
 

        //取得层数量
 
        int                                GetLayerInfoNum();
 
        //取得层信息指针
 
        stMapLayerInfo*        GetLayerInfoPtr(int vLayerIndex);
 

        //取得相应格子的资源数据
 
        stMapResInfo*        GetTileResData(int vLayerIndex,int vTileX,int vTileY);
 
        
 
        //取得对应层的自由物体数量
 
        int                                GetLayerFreeObjNum(int vLayerIndex);
 
        //取得对应层的自由物体
 
        stFreeObj*                GetLayerFreeObj(int vLayerIndex,int vObjIndex);
 
        
 
        //取得路径数量
 
        int                                GetLayerPathNodeNum(int vLayerIndex);
 
        //取得对应层的制定路径
 
        stPathNode*                GetLayerPathNode(int vLayerIndex,int vPathNodeIndex);
 

        //取得指定格子是分支路径起点的分支路径数量
 
        int                                GetNextPathNodeNum(int vLayerIndex,int vTileX,int vTileY,vector<int>& vNextPathNodeVec);
 

        //取得事件点数量
 
        int                                GetLayerEventTileNum(int vLayerIndex);
 
        //取得对应层的事件点
 
        stEventTile*        GetLayerEventTile(int vLayerIndex,int vEventTileIndex);
 


private:
 
        //头信息
 
        stMapHeader                                                m_MapHeader;
 
        //资源信息容器
 
        vector<stMapResInfo>                        m_MapResInfoVec;
 
        //地图层信息容器
 
        vector<stMapLayerInfo>                        m_MapLayerInfoVec;
 
        //批次结点信息
 
        vector<stBatchNodeInfo>                        m_SpriteBatchNode;
 

};
 

#endif

        从这个文件里我们可以看到CHHRMapReader能够从XML场景信息文件中读取场景信息,层数,网格信息,自由物体信息,路径信息,以及事件点信息,很多信息都是基于图层索引来获取的,这样是为了适应工具箱的多层随意网格的特性。

        CHHRMapLayer是用于使用Cocos2d-x进行场景显示的类。他本身就可以显示出XML地图了。

#ifndef _HHRMAPLAYER_H
 
#define _HHRMAPLAYER_H
 
//==================================================================//
 
//File:        HHRMapLayer.h
 
//Date:        2013-09-05
 
//Auto:        Honghaier 
//Blog:        http://blog.csdn.net/honghaier
 
//Web:        http://www.game2z.com
 
//Desc:        用于红孩儿工具箱的地图显示,目前只支持图片和粒子系统
 
//==================================================================//
 
#include "HHRMapReader.h"
 
using namespace std;
 
USING_NS_CC;
 

//场景的精灵
 
struct stHHRMapSprite
 
{
 
        CCSprite*                                m_Sprite;
 
        CCPoint                                        m_sPosition;                        //位置
 
};
 
//场景的精灵层
 
struct stHHRMapSpriteLayer
 
{
 
        vector<stHHRMapSprite*>        m_MapSpriteVec;                        //精灵
 
};
 

//红孩儿工具箱的地图层显示
 
class CHHRMapLayer        :public        CCLayer
 
{
 
public:
 
        //构造
 
        CHHRMapLayer();
 
        //析构
 
        ~CHHRMapLayer();
 

public:
 
        //加载场景XML文件
 
        bool                                LoadHHRMap(const char* szFileName);
 
        //地图读取器
 
        CHHRMapReader*                GetMapRender();
 
        //设置观察点
 
        void                                SetCameraPos(float fX,float fY);
 
        //取得摄像机的当前位置
 
        CCPoint                                GetCameraPosition();
 
        //移动摄像机的位置
 
        void                                MoveCamera(float vOffsetX,float vOffsetY);
 

        //通过位置取得格子
 
        POINT                                GetClickTile(int vLayerIndex,int vPosX,int vPosY);
 
        //通过格子取得位置(格子中心点位置)
 
        CCPoint                                GetTileCenterPos(int vLayerIndex,int vTileX,int vTileY,bool bIsScreenPos = true);
 

        //放入一个新的精灵到一个格子上
 
        stHHRMapSprite*                AddSpriteToTile(CCSprite*        pSprite,int vShowIndex,int vLayerIndex,int vTileX,int vTileY);
 
        //放入一个新的精灵到场景中
 
        stHHRMapSprite*                AddSpriteToMap(CCSprite*        pSprite,int vShowIndex,int vPosX,int vPosY);
 
        //开启精灵的动画播放
 
        void                                RunSpriteAction(CCSprite*        pSprite);
 
        //取得放入的精灵数量
 
        int                                        GetSpriteNum(int vLayerIndex);
 
        //取得指定的精灵
 
        stHHRMapSprite*                GetSprite(int vLayerIndex,int vSpriteIndex);
 
        //删除指定的精灵
 
        void                                DelSprite(CCSprite*        pSprite);
 
        //删除所有的精灵
 
        void                                DelAllSprites();
 

        //设置使用按下拖放
 
        void                                SetTouchDrag(bool bTouchDrag);
 

public:
 
        //渲染
 
        virtual void                draw(void);
 
        //加载时调用
 
        virtual void                onEnter();
 
        //释放时调用
 
        virtual void        onExit();
 
        //响应触屏事件处理
 
        virtual void                ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
 
        virtual void                ccTouchesMoved(CCSet *pTouches, CCEvent *pEvent);
 
        virtual void                ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent);
 
        virtual CCObject*        copyWithZone(CCZone *pZone);
 
        virtual void                touchDelegateRetain();
 
        virtual void                touchDelegateRelease();
 

private:
 

        //更新场景
 
        void                                UpdateScene();
 
        //渲染指定格子
 
        void                                RenderTile(int vLayerIndex,int vTileX,int vTileY,ccColor4B color);
 
        //渲染对应的图素
 
        void                                RenderElement(enuViewType        vCameraType,CCSize        vTileSize,stMapResInfo*        pRenderInfo,float vPosX,float vPosY,float vPosZ);
 

private:
 

        //地图读取器
 
        CHHRMapReader                                m_MapReader;
 
        //当前放在场景层中的精灵
 
        vector<stHHRMapSpriteLayer>        m_SpriteLayerVec;
 
        //当前观察点
 
        CCPoint                                                m_sCameraPos;
 
        //按下时记录上一个点的位置
 
        CCPoint                                                m_sTouchLastPos;
 
        //是否响应拖动处理
 
        bool                                                m_bTouchDrag;
 
        //上次更新的时间                
        double                                                m_dwLastTime;
 
        //错误返回字符串
 
        string                                                m_ErrorStr ;
 
}        
 
;
 
#endif

         从这个代码中我们可以看到CHHRMapLayer就是一个CCLayer,它拥有CCLayer的特性,可以直接放在场景中进行显示。它还可以进行进行格子的获取以及将一个精灵放置在格子上。

        我们这现在就使用这两个类来进行《打地鼠》的制做。

         我们打开HelloWorldScene.h,在这里引入”HHRMapLayer.h”,并为HelloWorld创建一个地图显示层的成员指针用于实例化这个地图显示层。并加入基于鼠标位置显示在屏幕上的锤子精灵以及显示分数的字图文字标签。

#ifndef __HELLOWORLD_SCENE_H__
 
#define __HELLOWORLD_SCENE_H__
 
#include "cocos2d.h"
 
#include "HHRMapLayer.h"
 

class HelloWorld : public cocos2d::CCLayer
 
{
 

public:
 
    // Here's a difference. Method 'init' in cocos2d-x returns bool, instead of returning 'id' in cocos2d-iphone
 
    virtual bool init();  
 

    // there's no 'id' in cpp, so we recommend returning the class instance pointer
 
    static cocos2d::CCScene* scene();
 
    
    // a selector callback
 
    void menuCloseCallback(CCObject* pSender);
 

        //老鼠出生的回调函数
 
        void LaoShuBorth(float dt);
 

        //渲染
 
        virtual void                draw(void);
 
        //响应触屏事件处理
 
        virtual void                ccTouchesBegan(CCSet *pTouches, CCEvent *pEvent);
 
        //删除精灵
 
        void                                DelSpriteFunc(CCNode* pTarget);
 
protected:
 
        //地图显示层
 
        CHHRMapLayer*                m_pHHRMapLayer;                        
 
        //锤子
 
        CCSprite*                        m_pChuiZiSprite;
 
    //计分器
 
        CCLabelBMFont*                m_pCountLabel;
 
        //计数
 
        int                                        m_nAttackMouseCount;
 

    CREATE_FUNC(HelloWorld);
 
};
 


#endif // __HELLOWORLD_SCENE_H__

       然后我们打开HelloWorldScene.cpp,在HelloWorld::init函数中加载地图并创建相应的精灵

// on "init" you need to initialize your instance
 
bool HelloWorld::init()
 
{
 
    //////////////////////////////
 
    // 1. super init first
 
    if ( !CCLayer::init() )
 
    {
 
        return false;
 
    }
 
    
    CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
 
    CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();
 

    /////////////////////////////
 
    // 2. add a menu item with "X" image, which is clicked to quit the program
 
    //    you may modify it.
 

    // add a "close" icon to exit the progress. it's an autorelease object
 
    CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
 
                                        "CloseNormal.png",
 
                                        "CloseSelected.png",
 
                                        this,
 
                                        menu_selector(HelloWorld::menuCloseCallback));
 
    
        pCloseItem->setPosition(ccp(origin.x + visibleSize.width - pCloseItem->getContentSize().width/2 ,
 
                                origin.y + pCloseItem->getContentSize().height/2));
 

    // create menu, it's an autorelease object
 
    CCMenu* pMenu = CCMenu::create(pCloseItem, NULL);
 
    pMenu->setPosition(CCPointZero);
 
    this->addChild(pMenu, 1);
 


        //因为我们要显示的文字是汉字,所以为了避免乱码,我们在这里将文字存入到XML中,然后在Cocos2d-x中读取。
 
        CCDictionary *strings = CCDictionary::createWithContentsOfFile("string.plist"); 
        const char *title = ((CCString*)strings->objectForKey("title"))->m_sString.c_str(); 
        CCLabelBMFont* pLable = CCLabelBMFont::create(title, "text.fnt");
 
    pLable->setPosition(ccp(visibleSize.width - 120, visibleSize.height - 100));
 
    this->addChild(pLable,1);
 

        //显示计数
 
        m_pCountLabel = CCLabelBMFont::create("0", "text.fnt");
 
    m_pCountLabel->setPosition(ccp(visibleSize.width - 120, visibleSize.height - 200));
 
    this->addChild(m_pCountLabel,1);
 
        m_nAttackMouseCount = 0;
 
        //-===载入地图=========================================
 
        m_pHHRMapLayer = new CHHRMapLayer();
 
        if(true == m_pHHRMapLayer->LoadHHRMap("park.xml"))
 
        {
 
                m_pHHRMapLayer->autorelease();
 
                addChild(m_pHHRMapLayer,0,0);
 
                m_pHHRMapLayer->SetTouchDrag(false);
 
        }
 
        else
 
        {
 
                delete m_pHHRMapLayer ;
 
                m_pHHRMapLayer = NULL;
 
        }
 

        CCSpriteFrameCache *frameCache = CCSpriteFrameCache::sharedSpriteFrameCache();
 
        //锤子的拼图
 
        frameCache->addSpriteFramesWithFile("chuizi.plist");
 
        //老鼠的拼图
 
        frameCache->addSpriteFramesWithFile("laoshu.plist");
 
        //金币的拼图
 
        frameCache->addSpriteFramesWithFile("jinbi.plist");
 

        CCAnimationCache::purgeSharedAnimationCache();
 
        CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache();
 


        //老鼠出现的动画
 
        animCache->addAnimationsWithFile("dishu_borth.plist");
 
        //锤子击打的动画
 
        animCache->addAnimationsWithFile("attack.plist");
 
        //金币弹出奖励的动画
 
        animCache->addAnimationsWithFile("jiangli.plist");
 

        //锤子
 
        m_pChuiZiSprite = CCSprite::create();
 
        CCSpriteFrame *frame = frameCache->spriteFrameByName("ChuiZi_0");
 
        m_pChuiZiSprite->setDisplayFrame(frame);
 
        m_pChuiZiSprite->setAnchorPoint(ccp(0.8,0.2));
 
        addChild(m_pChuiZiSprite);
 
        //每2秒出现一只老鼠
 
        schedule(schedule_selector(HelloWorld::LaoShuBorth), 2.0f);
 

        srand(time(0));
 
        //设置可以触屏响应
 
        setTouchEnabled(true);
 
        SimpleAudioEngine::sharedEngine()->preloadEffect("hit.mp3");
 
    return true;
 
}

       首先是创建了一个显示《打地鼠》标题的文字标签,然后是显示计数的文字标签。在这里我们使用了中文,为了避免因编码显示错乱,我们创建了一个XML文件string.plist来使用UTF8存储文字“打地鼠”并在Cocos2d-x中使用。

     

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

     

       随后我们实例化地图层,并调用其LoadHHRMap来加载一个工具箱导出的XML地图文件。再后面就是将拼图PLIST和动画PLIST加载进来,创建锤子精灵和动画,设定一个定时器,每2秒调用一次回调函数LaoShuBorth,哈哈,我那烂烂的E语。为了更加生动,我们在这里还预加入一个音乐文件hit.map来播放击中老鼠时的音效。

       之后我们来编写LaoShuBorth,它每2秒被回调的时候,我们可以创建老鼠的精灵和动画并放入到场景的相应事件点格子位置就可以了。事件点即可以基于格子,也可以基于像素,这个最好在代码中判断一下。

    

void HelloWorld::LaoShuBorth(float dt)
 
{
 
        if(m_pHHRMapLayer)
 
        {
 
                CHHRMapReader*        tpMapReader = m_pHHRMapLayer->GetMapRender();
 
                if( tpMapReader )
 
                {
 
                        int                nEventTileNum                = tpMapReader->GetLayerEventTileNum(1);
 
                        int                nEventTileIndex                = rand() % nEventTileNum ;
 
                        stEventTile*        pEventTile        = tpMapReader->GetLayerEventTile(1,nEventTileIndex);
 
                        if(pEventTile->m_bIsTile)
 
                        {
 
                                //取得事件ID
 
                                int nEventID = pEventTile->m_nEventID ;
 

                                CCSpriteFrameCache *frameCache = CCSpriteFrameCache::sharedSpriteFrameCache();
 

                                CCSprite *dishu = CCSprite::create();
 
                                CCSpriteFrame *frame = frameCache->spriteFrameByName("dishu_0");
 
                                dishu->setDisplayFrame(frame);
 
                                CCSize winSize = CCDirector::sharedDirector()->getWinSize();
 
                                dishu->setAnchorPoint(ccp(0.5,0.0));
 
                                m_pHHRMapLayer->AddSpriteToTile(dishu,1,1,pEventTile->m_sTile.x,pEventTile->m_sTile.y);
 

                                CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache();
 
                                CCAnimation *Borth = animCache->animationByName("borth");
 
                                Borth->setRestoreOriginalFrame(true);
 
                                CCAnimate *BorthAni = CCAnimate::create(Borth);
 
                                // run the animation
 


                                dishu->runAction( CCSequence::create( BorthAni,  CCCallFuncN::create(this, callfuncN_selector(HelloWorld::DelSpriteFunc)),NULL));
 
                                m_pHHRMapLayer->RunSpriteAction(dishu);
 
                                //由场景层管理,不需要放入当前层
 
                                //addChild(dishu);
 
                        }
 
                }
 
        }
 
        
 
}

    这里要注意的是,如果我们放入一个精灵到地图中,我们调用地图层的AddSpriteToTile函数,但如果这个精灵本身是个序列帧动画,我们还要在它runAction之后再调用一下地图层的RunSpriteAction
保证它被更新播放。在给它运行一个动画时,最好放入一个动画序列,在这个动画序列的最后再调用一个删除精灵的回调函数以保证精灵可以在合适的时机释放。

//删除精灵
 
void        HelloWorld::DelSpriteFunc(CCNode* pTarget)
 
{
 
        if(m_pHHRMapLayer)
 
        {
 
                CCSprite        *pMouseSprite = dynamic_cast<CCSprite*>(pTarget);
 
                if(pMouseSprite)
 
                {
 
                                m_pHHRMapLayer->DelSprite(pMouseSprite);
 
                }
 
        }
 
}

     这样我们运行一下就可以看到我们前面用工具箱制做的地图了,也可以发现每2秒会从事件点对应的沙地格子上生成一个老鼠。

     现在我们希望在鼠标移动时,锤子能跟随移动,怎么做呢?我们需要重载一下HelloWorlddraw函数,并在此中加入获取鼠标位置的代码,然后将鼠标位置设置给锺子。

//渲染
 
void HelloWorld::draw(void)
 
{
 
        
 
        POINT        tCurrPos;
 
        ::GetCursorPos(&tCurrPos);
 
        CCEGLView*        pGELView = CCDirector::sharedDirector()->getOpenGLView();
 
        ::ScreenToClient(pGELView->getHWnd(),&tCurrPos);
 
        if(m_pChuiZiSprite)
 
        {
 
                CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
 
                m_pChuiZiSprite->setPosition(ccp(tCurrPos.x/pGELView->getFrameZoomFactor() ,visibleSize.height - tCurrPos.y/pGELView->getFrameZoomFactor()));
 
        }
 

        CCLayer::draw();
 
}

    下面就只有点击鼠标时,播放锤子打击老鼠时弹出金币,和加分的处理了,在Cocos2d-x中我们可以重载HelloWorldccTouchesBegan函数来实现对鼠标按下的响应并设置HelloWorld层开启触屏事件响应。如果是在手机或Pad上,那就是我们点击屏幕时的响应函数。

        

//响应触屏事件处理
 
void HelloWorld::ccTouchesBegan(CCSet *pTouches, CCEvent* event)
 
{
 
        CCSetIterator iter = pTouches->begin();
 
        for (; iter != pTouches->end(); iter++)
 
        {
 
                CCTouch* pTouch = (CCTouch*)(*iter);
 
                CCPoint        tClickPt = pTouch->getLocation();
 


                CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache();
 
                CCAnimation *Borth = animCache->animationByName("chuizi");
 
                Borth->setRestoreOriginalFrame(true);
 
                CCAnimate *AttackAni = CCAnimate::create(Borth);
 
                // 运行击打动画
 
                m_pChuiZiSprite->runAction(AttackAni);
 

                // 点击位置,做一个(100,50)的偏移,将攻击点移到锤子头部位置附近
 
                POINT        tCurrPoint;
 
                tCurrPoint.x = tClickPt.x ;
 
                tCurrPoint.y = tClickPt.y ;
 

                CCPoint        tCameraPos = m_pHHRMapLayer->GetCameraPosition();
 
                //击中的老鼠
 
                int                nLaoShuNum = m_pHHRMapLayer->GetSpriteNum(1);
 
                for(int s = 0 ; s < nLaoShuNum ; s++)
 
                {
 
                        stHHRMapSprite*                tpHHRMapSprite = m_pHHRMapLayer->GetSprite(1,s);
 
                        if(tpHHRMapSprite)
 
                        {
 
                                if(tpHHRMapSprite->m_Sprite)
 
                                {
 
                                        int nScreenPosX = tpHHRMapSprite->m_sPosition.x - tCameraPos.x;
 
                                        int nScreenPosY = tpHHRMapSprite->m_sPosition.y - tCameraPos.y;
 

                                        RECT tClickRect;
 
                                        tClickRect.left                = nScreenPosX ;
 
                                        tClickRect.top                = nScreenPosY ;
 
                                        tClickRect.right        = nScreenPosX + tpHHRMapSprite->m_Sprite->getContentSize().width ;
 
                                        tClickRect.bottom        = nScreenPosY + tpHHRMapSprite->m_Sprite->getContentSize().height;
 

                                        if(PtInRect(&tClickRect,tCurrPoint))
 
                                        {
 
                                                m_pHHRMapLayer->DelSprite(tpHHRMapSprite->m_Sprite);
 
                                                m_nAttackMouseCount++;
 
                                                char szTemp[10];
 
                                                sprintf(szTemp,"%d",m_nAttackMouseCount);
 
                                                m_pCountLabel->setString(szTemp);
 
                                                SimpleAudioEngine::sharedEngine()->playEffect("hit.mp3");
 
                                                
 
                                                //击中的格子
 
                                                POINT        tClickTile = m_pHHRMapLayer->GetClickTile(1,nScreenPosX,nScreenPosY);
 
                                                if(tClickTile.x >= 0 && tClickTile.y >= 0)
 
                                                {
 
                                                        CCSpriteFrameCache *frameCache = CCSpriteFrameCache::sharedSpriteFrameCache();
 

                                                        CCSprite *jinbi = CCSprite::create();
 
                                                        CCSpriteFrame *frame = frameCache->spriteFrameByName("JinBi_0");
 
                                                        jinbi->setDisplayFrame(frame);
 
                                                        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
 
                                                        jinbi->setAnchorPoint(ccp(0.5,0.0));
 

                                                        m_pHHRMapLayer->AddSpriteToTile(jinbi,2,1,tClickTile.x,tClickTile.y);
 

                                                        //跳金币动画
 
                                                        CCAnimationCache *animCache = CCAnimationCache::sharedAnimationCache();
 
                                                        CCAnimation *pAnimation = animCache->animationByName("jiangli");
 
                                                        Borth->setRestoreOriginalFrame(true);
 
                                                        CCAnimate *JiangLiAni = CCAnimate::create(pAnimation);
 
                                                        //渐隐消失动画
 
                                                        CCActionInterval*  action1 = CCFadeOut::create(1.0f);
 
                                                        //运行动画序列,先跳金币后消失
 
                                                        jinbi->runAction( CCSequence::create( JiangLiAni, action1,  CCCallFuncN::create(this, callfuncN_selector(HelloWorld::DelSpriteFunc)),NULL));
 
                                                        //设置这个精灵运行动画
 
                                                        m_pHHRMapLayer->RunSpriteAction(jinbi);
 
                                                }
 
                                                
 
                                                break;
 
                                        }
 
                                }
 
                        }
 
                }
 

        }
 

}        

          

       这里面的代码较多,主要流程就是先获取到鼠标点位置,然后遍历场景中的所有地鼠的精灵,之后通过点与矩形的位置判断来确定是否击中地鼠。如果击中,增加分数并播放地鼠的精灵并播放击中的音效。同时在相应的格子上创建金币的精灵动画并播放。

       好了,这样《打地鼠》游戏就算完成了,运行一下,嘿嘿,还不错嘛~

使用《红孩儿工具箱》开发基于Cocos2d-x的《打地鼠》游戏

结束语

谢谢大家的对工具箱的支持和关注,我会继续完善工具箱并编写更多的游戏实例。敬请期待!

另外有任何关于工具箱的问题可以到http://www.game2z.com工具箱专版与我进行交流!

我的新浪微博:http://weibo.com/u/1834515945 工具箱用户群:20970366

分类: cocos2d-x 标签:

cocos2d-x创建新项目模板

2013年7月31日 没有评论

  1、起因

    长期使用项目中自带的HelloWorldScene来创建模板工程,不知大家有木有感到厌烦?

    我是个懒人,所以就弄了个新的模板工程。这样最起码可以不用每次都把HelloWorldScene删掉再创建一个Scene了,求不被鄙视…… 另外因为通常都会用cocosBuilder作为界面的编辑工具,我就将ccbi文件也加进模板项目中了。

2、内容概述

    这个模板项目大致包含有:

1、主Scene变更为GameScene。其中有两个层,分别是GameLayer与HudLayer。

        2、HudLayer中包含一个cocosBuilder导出的状态显示层。

        3、HudLayer中包含自动更新检测与退出按钮。

    有图有真相:

cocos2d-x创建新项目模板

   项目运行起来是这样的:

  cocos2d-x创建新项目模板

    像素设定为1024×768。

    工程目录是这样的:

        cocos2d-x创建新项目模板

 3、原理

    vs项目模板是在VS2012/VC/vcprojects中。我们当然可以直接在其中做更改。不过有个更好的方式是从通过示例项目添加入手,编写一个与之对应的新模板项目。
    找到cocos2d-2.1.2/template/msvc这个文件夹。当初我们添加vs项目时运行的就是InstallWizardForVS2012.js这个脚本。我们只要复制这个过程,定位到一个新的项目中,是不是就可以了呢?说干就干。

4、动手更改

    首先复制一份InstallWizardForVS2012.js。

    不要运行,以编辑的方式打开这个新的副本。

    找到这部分:

 // Wizard Info
    var nNumWizards = 1;

    var astrWizardName = new Array();
    astrWizardName[0] = "CCAppWiz.win32";

    var nCntr;
    for (nCntr = 0; nCntr < nNumWizards; nCntr++) {
        var strSourceFolder = FileSys.BuildPath(strScriptPath, astrWizardName[nCntr]);
......

    CCAppWiz.win32是项目名,这就是我们要找的了。所以我们还要复制一份CCAppWiz.win32。我重命名为layerWithHud.win32。项目名改了。项目内容自然也要改。把文件夹中的CCAppWiz都改掉,效果如下:

    cocos2d-x创建新项目模板

     下面我们来编辑文件内容。 首先是.vsdir。

     这个文件是与VS模板所对应的。我将其更改如下:

layerWithHud.win32.vsz| |LayerWithHud Application |1|Create A Application With Basic Layer and Hud.| |6777|4096|#1154

     第一个字段表示所对应的.vsz文件的名字。第二个字段是模板的名字。第四个是描述。改好保存。

    

    然后我们打开VS的项目。将自己之前调试好的类,正确的加入到项目中。这个项目是08生成的,我正好也有,就没转换项目,用vs2012打开并进行调整效果也是一样的。

      cocos2d-x创建新项目模板

    接下来是Templates/1033/Templates.inf。这个文件描述了要复制到新项目的文件都有些什么:

proj.win32/res/root.ico
proj.win32/root.rc
proj.win32/resource.h

proj.win32/main.h
proj.win32/main.cpp

Classes/AppDelegate.h
Classes/AppDelegate.cpp

[! if CC_USE_LUA]
../../../../../scripting/lua/cocos2dx_support/LuaCocos2d.cpp
../../../../../scripting/lua/cocos2dx_support/LuaCocos2d.h
../../../../../scripting/lua/cocos2dx_support/CCLuaEngine.cpp
../../../../../scripting/lua/cocos2dx_support/CCLuaEngine.h
../../../../../scripting/lua/cocos2dx_support/Cocos2dxLuaLoader.h
../../../../../scripting/lua/cocos2dx_support/Cocos2dxLuaLoader.cpp
../../../../../scripting/lua/cocos2dx_support/tolua_fix.c
../../../../../scripting/lua/cocos2dx_support/tolua_fix.h
[! else]
Classes/CCBHud.h
Classes/CCBHud.cpp
Classes/GameLayer.h
Classes/GameLayer.cpp
Classes/GameScene.h
Classes/GameScene.cpp
Classes/HudLayer.h
Classes/HudLayer.cpp
[! endif]

[! if CC_USE_LUA]
Resources/Default.png
Resources/crop.png
Resources/farm.jpg
Resources/land.png
Resources/menu2.png
Resources/Icon.png
Resources/dog.png
Resources/hello.lua
Resources/hello2.lua
Resources/menu1.png
Resources/background.mp3
Resources/effect1.wav
[! else]
Resources/CloseSelected.png
Resources/CloseNormal.png
Resources/HelloWorld.png
Resources/Texture/Time_New_Roman.fnt
Resources/Texture/Time_New_Roman.png
Resources/Texture/Time_New_Roman_small.fnt
Resources/Texture/Time_New_Roman_small.png
Resources/Texture/back.png
Resources/HudHorizontal.ccbi
CCBProject_Hub.rar
[! endif]

    如果定义了Lua,即:向导勾选了Lua,则进入Lua的条件语句。目前与Lua的逻辑无关。我的Class中的文件要复制过去,所以加入到其中。相应的ccbi文件与字体图片什么的也要一并写进去。每个文件都写有些麻烦,有人知道怎么添加整个文件夹,希望能告诉我。最后那个rar是cocosBuilder的项目。我打成一个rar随项目资源一起复制过去。

    

    改了一圈最终可以改动InstallWizardForVS2012.js这个副本文件啦,我们把它重命名为InstallLayerWizardForVS2012.js 找到之前那部分,改为:

 // Wizard Info
    var nNumWizards = 1;

    var astrWizardName = new Array();
    astrWizardName[0] = "layerWithHud.win32";

    var nCntr;
    for (nCntr = 0; nCntr < nNumWizards; nCntr++) {
        var strSourceFolder = FileSys.BuildPath(strScriptPath, astrWizardName[nCntr]);
    ......

    还有需要更改一处:

    // Read and write additional CCAppWiz.vsdir, add path to the wizard location
    try {
        var strDest = FileSys.BuildPath(strDestCCFolder, "LayerWithHud.vsdir");

        var ForWriting = 2;

        var fileDest = FileSys.OpenTextFile(strDest, ForWriting, true);
    ......

    这个.vsdir是储存在项目模板中的文件的名称,如果不更改,会将之前HelloWorldScene模板替换掉。

5、收尾

    运行刚刚改好的InstallLayerWizardForVS2012.js 弹出App Wizard successfully installed for VS2012!则说明安装正确了。如果不正确可能是目录的问题,检查一下相应的文件。

     如果一切顺利,重启VS即可看到你的新项目模板了。


本篇博客出自阿修罗道,转载请注明出处:http://blog.csdn.net/fansongy/article/details/9672375

分类: cocos2d-x 标签:

Cocos2d-x中的CurlTest深入分析

2013年3月18日 没有评论

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

红孩儿Cocos2d-X学习园地QQ3群:205100149

Cocos2d-x中的CurlTest深入分析


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

Cocos2d-html5-v2.1.1

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

         Curl定义:

                   curl is a command line tool fortransferring data with URL syntax, supporting DICT, FILE, FTP, FTPS, Gopher,HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3, POP3S, RTMP, RTSP, SCP, SFTP,SMTP, SMTPS, Telnet
and TFTP. curl supports SSL certificates, HTTP POST, HTTPPUT, FTP uploading, HTTP form based upload, proxies, cookies, user+passwordauthentication (Basic, Digest, NTLM, Negotiate, kerberos…), file transferresume, proxy tunneling and a busload of other useful
tricks.

                    大家好,今天我们来介绍一下这样一个东西。它叫做Curl,是一套可以访问各种网络协议服务的API,官方网站:http://curl.haxx.se/ .虽然我是第一次遇到它,但是官网上黑体字提醒我:

                    Possibly more than
500 million users already, I’m sure you canuse it as well!


                    Cocos2d-x中将curl做为第三方的库加入了进来,它被放在cocos2dx/platform/third_party/win32的curl目录下。

 

                    在TestCpp有这样一个演示CurlTest。它简单的应用Curl来访问了一下HTTP服务器。我们来学习一下,打开TestCpp工程下的CurlTest目录。这里有两个文件CurlTest.h/cpp。我们先看一下CurlTest.h:

//演示所用的层。
class CurlTest : public CCLayer
{
public:
	//构造
    CurlTest();
	//析构
    ~CurlTest();
	//当触屏结束时调用
    virtual void ccTouchesEnded(cocos2d::CCSet *pTouches, cocos2d::CCEvent *pEvent);

private:
	//文字标签
    cocos2d::CCLabelTTF*         m_pLabel;
};

//演示所用的场景
class CurlTestScene : public TestScene
{
public:
	//运行当前场景时调用的函数。
    virtual void runThisTest();
};

其对应的CPP:

//加入相应的头文件。
#include "CurlTest.h"
#include "stdio.h"
#include "stdlib.h"
//这里加入了curl库的头文件。
#include "curl/curl.h"
//构造函数。
CurlTest::CurlTest()
{
	//取得屏幕大小。
    CCSize s = CCDirector::sharedDirector()->getWinSize();
	//创建一个文字标签。
    CCLabelTTF* label = CCLabelTTF::create("Curl Test", "Arial", 28);
	//将标签放入到当前层中。
    addChild(label, 0);
	//设置位置
    label->setPosition( ccp(s.width/2, s.height-50) );
	//开启触屏
    setTouchEnabled(true);

    // 创建用于显示标题的文字标签。
    m_pLabel = CCLabelTTF::create("Touch the screen to connect", "Arial", 22);
	//设置标签居中显示。
    m_pLabel->setPosition(ccp(s.width / 2, s.height / 2));
	//将标签放入到当前层中。
    addChild(m_pLabel, 0);
    //占用它,对其引用计数器加一。
    m_pLabel->retain();
}


// 当触屏结束时调用的函数。
void CurlTest::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)
{
	//本演示关于CURL使用的语句都放在这里了。
	//首先,使用CURL必须要创建一个CULR指针,它是CURL的全局句柄。
    CURL *curl;
	//这里定义一个CURL库中API的返回值,用于取得API调用的结果。
    CURLcode res;
	//字符数组,用于存储返回值。
    char buffer[10];
	//第一步:初始化CURL,取得初始化成功后的CURL指针。
    curl = curl_easy_init();
    if (curl) 
    {
		//第二步,设定我们用此CURL指针来完成的动作。参数一为CURL指针,参数二为相应的动作类型枚举,这个枚举值在curl.h中定义,比如本例中的CURLOPT_URL,定义为CINIT(URL,  OBJECTPOINT, 2),即联接一个网站的HTTP服务。参数三为动作对应的数据参数,这里是网站的URL地址。
        curl_easy_setopt(curl, CURLOPT_URL, "www.baidu.com");
		//第三步,执行上面设定的动作处理。返回结果放在res中。
        res = curl_easy_perform(curl);
       //最后一步,清除CURL指针,结束对CURL库的使用。
        curl_easy_cleanup(curl);
		//如果动作处理成功,则显示联结成功,但没有开始接收任何数据。
        if (res == 0)
        {
            m_pLabel->setString("0 response");
        }
        else
        {
			  //如果动作处理失败,打印错误码。
            sprintf(buffer,"code: %i",res);
            m_pLabel->setString(buffer);
        }
    } 
    else 
    {
       //如果初始化失败,显示没有CURL
       m_pLabel->setString("no curl");
    } 
}
//析构函数。
CurlTest::~CurlTest()
{
	//对占用的标题文本标签计数器减一。不再继续占用以使它能够正常被释放。
    m_pLabel->release();
}

//运行当前演示场景调用的函数。
void CurlTestScene::runThisTest()
{
	//创建一个演示用的层。
    CCLayer* pLayer = new CurlTest();
    addChild(pLayer);
	//运行当前场景。
    CCDirector::sharedDirector()->replaceScene(this);
    pLayer->release();
}

                    我们运行起来,为了更好的显示动作,我打开HTTP Analyzer 来进行消息的捕捉,当我点击屏幕后,HTTP Analyzer会捕捉到相应的HTTP访问记录。如图:

Cocos2d-x中的CurlTest深入分析

 

                    可以看到,本程序向www.baidu.com网站发送了一条GET消息。这只是一个简单的连接过程演示,它完成的很棒。现在我们来点好玩的,比如我想把取得的网页下载下来该如何做呢?

                    CURL中提供了相应的动作类型来获取数据和写数据到文件。首先我们要先创建一个文件指针,然后通过CURL的动作设置函数来指定用于写文件的文件指针,然后我们创建一个写数据的函数并设置为CURL写数据的回调函数。

//定义的写数据的回调函数。
size_t write_callback(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
	size_t written = fwrite(ptr, size, nmemb, (FILE *)stream);
	return written;
}


//修改触屏结束的函数处理。
void CurlTest::ccTouchesEnded(CCSet *pTouches, CCEvent *pEvent)
{
    CURL *curl;
    CURLcode res;
    char buffer[10];	
    curl = curl_easy_init();
    if (curl) 
{
		//创建写入的文件。
		FILE *outfile;
		outfile = fopen("C://baidu.html", "wb");

        curl_easy_setopt(curl, CURLOPT_URL, "www.baidu.com");
		if(outfile)
		{
			//指定写入的文件指针。
			curl_easy_setopt(curl, CURLOPT_FILE, outfile);
		}
		//设置写数据的回调函数。
		curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
        res = curl_easy_perform(curl);
        curl_easy_cleanup(curl);
		//关闭文件
		fclose(outfile);
        if (res == 0)
        {

            m_pLabel->setString("0 response");
        }
        else
        {
            sprintf(buffer,"code: %i",res);
            m_pLabel->setString(buffer);
        }
    } 
    else 
    {
        m_pLabel->setString("no curl");
    } 

}

 

                    运行后,我们点击触屏,这里可以发现在C盘下生成一个baidu.html,点开后会是下面的样子:

Cocos2d-x中的CurlTest深入分析


                    是不是很有趣呢?CURL更详细的使用方法和各种动作介绍,可以到官网下载Curl的allexamples.zip来研究。里面有很多实例,同时也欢迎到www.game2z.com来与我一起讨论。下课!

分类: cocos2d, cocos2d-x 标签:

Cocos2d-x 2.0 按键加速处理深入分析

2013年3月13日 没有评论

       

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

红孩儿Cocos2d-X学习园地QQ2群:44208467

           Cocos2d-x 2.0 按键加速处理深入分析

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

Cocos2d-html5-v2.1.1

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

      大家好,我们经常会玩赛车游戏,当我们按下一个按键时,如果只能匀速运动是令人烦燥的。在游戏中用到加速度,利用加速度对于物体进行更真实的控制是必不可少的技能。今天我们来学习一下Cocos2d-x中如何利用按键来处理速度的累加。

 

    我们知道在WIN32编程中,一个键按下时所对应的窗口会收到WM_KEYDOWN消息,如果按下的键输入的是字符,窗口接着会收到WM_CHAR消息,而当按键抬起时对应的窗口会收到WM_KEYUP消息。在HelloWorld深入分析一节中,我们知道在CCEGLView类中有窗口消息的处理函数WindowProc,所以要进行按键的处理,就需要在接受按键消息时调用相应的函数。我们来看一下它在接受到WM_KEYDOWN,WM_CHAR和WM_KEYUP时做了什么。

LRESULT CCEGLView::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	…
case WM_KEYDOWN:
        if (wParam == VK_F1 || wParam == VK_F2)
        {
            CCDirector* pDirector = CCDirector::sharedDirector();
			//当SHIFT键被按下时,根据是否按下的是F1来决定软键盘收到的消息是响应回退还是主菜单。
            if (GetKeyState(VK_LSHIFT) < 0 ||  GetKeyState(VK_RSHIFT) < 0 || GetKeyState(VK_SHIFT) < 0)
                pDirector->getKeypadDispatcher()->dispatchKeypadMSG(wParam == VK_F1 ? kTypeBackClicked : kTypeMenuClicked);
        }
		//如果按键消息响应函数有效则调用函数指针m_lpfnAccelerometerKeyHook所指的函数。 
        if ( m_lpfnAccelerometerKeyHook!=NULL )
        {
            (*m_lpfnAccelerometerKeyHook)( message,wParam,lParam );
        }
        break;
    case WM_KEYUP:
		//如果按键消息响应函数有效则调用函数指针m_lpfnAccelerometerKeyHook所指的函数。
        if ( m_lpfnAccelerometerKeyHook!=NULL )
        {
            (*m_lpfnAccelerometerKeyHook)( message,wParam,lParam );
        }
        break;
    case WM_CHAR:
        {
			//如果输入的字符是控制符则进行相关处理。
            if (wParam < 0x20)
            {
                if (VK_BACK == wParam)
                {
                    CCIMEDispatcher::sharedDispatcher()->dispatchDeleteBackward();
                }
                else if (VK_RETURN == wParam)
                {
                    CCIMEDispatcher::sharedDispatcher()->dispatchInsertText("/n", 1);
                }
                else if (VK_TAB == wParam)
                {
                    // tab input
                }
                else if (VK_ESCAPE == wParam)
                {
                    // ESC input
                    CCDirector::sharedDirector()->end();
                }
            }
            else if (wParam < 128)
            {
                //如果是ASCII码,则响应文字输入。
                CCIMEDispatcher::sharedDispatcher()->dispatchInsertText((const char *)&wParam, 1);
            }
            else
            {	//大于128的使用宽字符,所以这里将其转化为多字节字符串来输入文本。
                char szUtf8[8] = {0};
                int nLen = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR)&wParam, 1, szUtf8, sizeof(szUtf8), NULL, NULL);
                CCIMEDispatcher::sharedDispatcher()->dispatchInsertText(szUtf8, nLen);
            }
			//如果按键消息响应函数有效则调用函数指针m_lpfnAccelerometerKeyHook所指的函数。
            if ( m_lpfnAccelerometerKeyHook!=NULL )
            {
               (*m_lpfnAccelerometerKeyHook)( message,wParam,lParam );
            }
        }
        break;
…
}

 

    可以看到,CCEGLView类中有一个成员函数指针m_lpfnAccelerometerKeyHook,它将具体的实际的按键处理交给了外部函数。

 

那么,是谁?在哪里去设置函数指针指向的函数呢?我们保留疑问,继续学习。

        我们打开CCLayer.h,我们来看一下CCLayer的声明:

class   CC_DLL CCLayer :public CCNode,
public CCTouchDelegate,public   CCAccelerometerDelegate,public CCKeypadDelegate

         在这一行里,我们可以清楚的知道CCLayer的本质是什么,它是具有输入功能的节点。

CCAccelerometerDelegate是加速键消息处理接口类,可以接收到加速键产生的速度信息并进行处理,我们打开源码

CCAccelerometerDelegate.h来看一下:

//这个结构用于记录当前时刻的X,Y,Z三个方向的速度信息。
typedef struct 
{
    double x;
    double y;
    double z;
    double timestamp;
} 
CCAcceleration;

//类CCAccelerometerDelegate
class CC_DLL CCAccelerometerDelegate
{
public:
	//接受CCAcceleration消息处理的虚函数。
    virtual void didAccelerate(CCAcceleration* pAccelerationValue) {CC_UNUSED_PARAM(pAccelerationValue);}
};

再回到CCLayer.h:

class CC_DLL CCLayer : public CCNode, public CCTouchDelegate, public CCAccelerometerDelegate, public CCKeypadDelegate
{
	…
	//重载CCAccelerometerDelegate中相应函数,具体实现相应功能。
	virtual void didAccelerate(CCAcceleration* pAccelerationValue) 
{CC_UNUSED_PARAM(pAccelerationValue);}
	…
	//取得是否开启响应按键处理。
bool isAccelerometerEnabled();
//设置是否开启响应按键处理。
void setAccelerometerEnabled(bool value);
…
protected:   
 bool m_bIsAccelerometerEnabled;
 …
}

打开CPP:

//取得是否开启响应按键处理。
bool CCLayer::isAccelerometerEnabled()
{
    return m_bIsAccelerometerEnabled;
}
//设置是否开启响应按键处理。
void CCLayer::setAccelerometerEnabled(bool enabled)
{
    if (enabled != m_bIsAccelerometerEnabled)
    {
        m_bIsAccelerometerEnabled = enabled;
		//如果正在运行中。
        if (m_bIsRunning)
        {
			//取得当前设备
            CCDirector* pDirector = CCDirector::sharedDirector();
            if (enabled)
            {
				//取得设备的加速键管理器,设置其内部的按键消息响应处理接口类对象指针指向当前层。
                pDirector->getAccelerometer()->setDelegate(this);
            }
            else
            {   //取得设备的加速键管理器,设置其内部的按键消息响应处理接口类对象指针为空。
                pDirector->getAccelerometer()->setDelegate(NULL);
            }
        }
    }
}

    设备也不甘寂寞啊。里面有一个getAccelerometer函数为取得加速键管理器。

打开CCDirector.h:

 

CC_PROPERTY(CCAccelerometer*,m_pAccelerometer, Accelerometer);

 

CCAccelerometer类的定义在CCAccelerometer.h中:

class CC_DLL CCAccelerometer
{
public:
	//构造与析构
    CCAccelerometer();
    ~CCAccelerometer();
	//设置加速键消息响应处理接口类实例指针。
    void setDelegate(CCAccelerometerDelegate* pDelegate);
	//更新当前时刻的速度处理。
    void update( double x,double y,double z,double timestamp );
private:
	//当前时刻的速度信息。
    CCAcceleration m_obAccelerationValue;
	//加速键消息响应处理接口类实例指针。
    CCAccelerometerDelegate* m_pAccelDelegate;
};

其对应CPP为:

namespace
{
	//定义一些命名空间中的全局变量。
	//X,Y,Z方向的速度
    double            g_accelX=0.0;
    double            g_accelY=0.0;
    double            g_accelZ=0.0;
	//X,Y,Z方向的加速度
    const double    g_accelerationStep=0.2f;
    const double    g_minAcceleration=-1.0f;
    const double    g_maxAcceleration=1.0f;
	//命名空间内的模版函数,用于取得val的有效值(即限定了最小和最大范围)
    template <class T>
    T CLAMP( const T val,const T minVal,const T maxVal )
    {
        CC_ASSERT( minVal<=maxVal );
        T    result=val;
        if ( result<minVal )
            result=minVal;
        else if ( result>maxVal )
            result=maxVal;

        CC_ASSERT( minVal<=result && result<=maxVal );
        return result;
    }
	//命名空间内的函数,用于响应按下键后的处理。
    bool handleKeyDown( WPARAM wParam )
    {//定义变量值设置是否有速度更新。
        bool    sendUpdate=false
		//通过接键来处理X,Y,Z方向的速度变化。
        switch( wParam )
        {
        case VK_LEFT:
            sendUpdate=true;   
//X方向速度减小 g_accelX=CLAMP( g_accelX-g_accelerationStep,g_minAcceleration,g_maxAcceleration );
            break;
        case VK_RIGHT:
            sendUpdate=true;
//X方向速度增大
g_accelX=CLAMP( g_accelX+g_accelerationStep,g_minAcceleration,g_maxAcceleration );
            break;
        case VK_UP:
            sendUpdate=true;
//Y方向速度增大
g_accelY=CLAMP( g_accelY+g_accelerationStep,g_minAcceleration,g_maxAcceleration );
            break;
        case VK_DOWN:
            sendUpdate=true;
//Y方向速度减小
g_accelY=CLAMP( g_accelY-g_accelerationStep,g_minAcceleration,g_maxAcceleration );
            break;
        case VK_OEM_COMMA:
            sendUpdate=true;
//Z方向速度增大
g_accelZ=CLAMP( g_accelZ+g_accelerationStep,g_minAcceleration,g_maxAcceleration );
            break;
        case VK_OEM_PERIOD:
            sendUpdate=true;
//Z方向速度减小
g_accelZ=CLAMP( g_accelZ-g_accelerationStep,g_minAcceleration,g_maxAcceleration );
            break;
        }
		//返回是否有速度更新。
        return sendUpdate;
    }
	//命名空间内的函数,用于响应键松开时的处理。
    bool handleKeyUp( WPARAM wParam )
    {//定义变量值设置是否有速度更新。
        bool    sendUpdate=false;
        switch( wParam )
        {
        case VK_LEFT:
        case VK_RIGHT:
            sendUpdate=true;
			//将速度重置为零
            g_accelX=0.0;
            break;
        case VK_UP:
        case VK_DOWN:
            sendUpdate=true;
				//将速度重置为零
            g_accelY=0.0;
            break;
        case VK_OEM_COMMA:
        case VK_OEM_PERIOD:
            sendUpdate=true;
				//将速度重置为零
            g_accelZ=0.0;
            break;
        }
		//返回是否有速度更新。
        return sendUpdate;
    }
	//命名空间内的函数,用于加速键响应处理函数。
    void myAccelerometerKeyHook( UINT message,WPARAM wParam,LPARAM lParam )
    {
		//取得加速键管理器指针
        cocos2d::CCAccelerometer    *pAccelerometer = cocos2d::CCDirector::sharedDirector()->getAccelerometer();
        bool                        sendUpdate=false;
        switch( message )
        {
        case WM_KEYDOWN:
			//调用按下加速键的处理。
            sendUpdate=handleKeyDown( wParam );
            break;
        case WM_KEYUP:
			//调用松开加速键的处理。
            sendUpdate=handleKeyUp( wParam );
            break;
        case WM_CHAR:
            break;
        default:
            // Not expected to get here!!
            CC_ASSERT( false );
            break;
        }
		//如果有速度更新。
        if ( sendUpdate )
        {
			//计算当前时间。
            const time_t    theTime=time(NULL);
            const double    timestamp=(double)theTime / 100.0;
//调用加速键管理器的更新函数更新当前速度         pAccelerometer->update( g_accelX,g_accelY,g_accelZ,timestamp );
        }
    }
	//重置速度
    void resetAccelerometer()
    {
        g_accelX=0.0;
        g_accelY=0.0;
        g_accelZ=0.0;
    }

}

NS_CC_BEGIN
//构造函数。
CCAccelerometer::CCAccelerometer() : 
    m_pAccelDelegate(NULL)
{
    memset(&m_obAccelerationValue, 0, sizeof(m_obAccelerationValue));
}

//析构
CCAccelerometer::~CCAccelerometer() 
{

}
//设置加速键所用的加速键消息处理接口类实例。
void CCAccelerometer::setDelegate(CCAccelerometerDelegate* pDelegate) 
{
	//保存加速键消息处理接口类实例
    m_pAccelDelegate = pDelegate;
    if (pDelegate)
    {
        //这里就解答了之前的疑问,也就是说,如果你对CCLayer设置开启响应按键处理,则会设置CCEGLView类实例使用的按键消息处理回调函数为命名空间内的函数myAccelerometerKeyHook。
CCEGLView::sharedOpenGLView()->setAccelerometerKeyHook( &myAccelerometerKeyHook );
    }
    else
    {
        //如果参数为空,这里就注销设置CCEGLView类实例使用的按键消息处理回调函数。同时重置速度值。
CCEGLView::sharedOpenGLView()->setAccelerometerKeyHook( NULL );
        resetAccelerometer();
    }
}
//加速键的更新函数,用于更新当前速度
void CCAccelerometer::update( double x,double y,double z,double timestamp ) 
{
	//保存相应的值到结构变量中。
    if (m_pAccelDelegate)
    {
        m_obAccelerationValue.x            = x;
        m_obAccelerationValue.y            = y;
        m_obAccelerationValue.z            = z;
        m_obAccelerationValue.timestamp = timestamp;

        //调用虚函数进行处理,实际处理方式就由m_pAccelDelegate的派生类实例(如CCLayer及其派生类实例)来具体实现了。
        m_pAccelDelegate->didAccelerate(&m_obAccelerationValue);
    }    
}

有点迷糊吧,现在我来解释一下这个圈子是怎么一回事。

见图:

Cocos2d-x 2.0 按键加速处理深入分析

原理说明:

      有一个函数myAccelerometerKeyHook,它会取得当前设备中的加速键管理器(CCAccelerometer)实例对象,并将接受到的按键消息做为参数调用其update函数,其update函数将按键消息生成的速度信息结构转发给相应的速度信息处理接口类(CCAccelerometerDelegate)实例指针,然后由此指针所指向的实例对象调用虚函数didAccelerate来进行实现相应的加速效果。

      当我们对一个速度信息处理接口类(CCAccelerometerDelegate)的派生类,如CCLayer及其派生类设置开启响应加速键消息处理时,系统会将其设置为设备的加速键管理器(CCAccelerometer)实例对象的速度信息处理接口类(CCAccelerometerDelegate)实例指针,并将函数myAccelerometerKeyHook设置为CCEGLView类实例使用的按键消息处理回调函数。

    原理解释清楚了,现在我们来看一下TestCpp中的AccelerometerTest。

打开头文件:

//演示层AccelerometerTest。
class AccelerometerTest: public CCLayer
{
protected:
	//用于演示加速变化的精灵。
CCSprite* m_pBall;
//上一帧的时间。
    double    m_fLastTime;

public:
	//构造与析构
    AccelerometerTest(void);
    ~AccelerometerTest(void);
	//关键之处了,具体实现按键后的速度变化。参数为按键后收到的速度信息。
    virtual void didAccelerate(CCAcceleration* pAccelerationValue);
	//取得标题。
virtual std::string title();
//当前层加载时的处理。
    virtual void onEnter();
};

//用于演示的场景。
class AccelerometerTestScene : public TestScene
{
public:
	//加载场景时的处理。
    virtual void runThisTest();
};

打开CPP:

//定义宏,限定_pos的有效范围值。
#define FIX_POS(_pos, _min, _max) /
    if (_pos < _min)        /
    _pos = _min;        /
else if (_pos > _max)   /
    _pos = _max;        /

//构造
AccelerometerTest::AccelerometerTest(void)
: m_fLastTime(0.0)
{
}
//析构
AccelerometerTest::~AccelerometerTest(void)
{
	//对占用的小球引用计数减一。因为在onEnter中进行了加一操作。
    m_pBall->release();
}
//取得标题。
std::string AccelerometerTest::title()
{
    return "AccelerometerTest";
}
//加载当前层时的处理。
void AccelerometerTest::onEnter()
{
	//调用基类的相应函数。
    CCLayer::onEnter();
	//设置开启响应加速键
    setAccelerometerEnabled(true);
	//取得窗口大小 
    CCSize s = CCDirector::sharedDirector()->getWinSize();
	//创建一个文字标签。
CCLabelTTF* label = CCLabelTTF::create(title().c_str(), "Arial", 32);
//将文字标签加入到当前层中。
    addChild(label, 1);
	//设置文字标签的位置。
    label->setPosition( CCPointMake(s.width/2, s.height-50) );
	//由一个小球的图片创建一个精灵。
    m_pBall = CCSprite::create("Images/ball.png");
	//设置小球的位置居于屏幕中央。
    m_pBall->setPosition(ccp(s.width / 2, s.height / 2));
	//将小球放到当前层中。
    addChild(m_pBall);
	//占用小球,让小球的引用计数器加一。这里加一,在析构时就必须减一才能使引用计数器保证不乱。不过个人认为这里引用计数加一没什么必要。因为addChild已经对小球精灵做了引用计数加一操作了,代表了占用,再加一略显繁琐。
    m_pBall->retain();
}
//具体实现按键后的速度变化。参数为按键后收到的速度信息。
void AccelerometerTest::didAccelerate(CCAcceleration* pAccelerationValue)
{
	//取得设备及窗口大小。
    CCDirector* pDir = CCDirector::sharedDirector();
    CCSize winSize   = pDir->getWinSize();

    //判断小球精灵是否有效。
    if ( m_pBall == NULL ) {
        return;
    }
	//取得小球的图像区域大小。
    CCSize ballSize  = m_pBall->getContentSize();
	//取得小球的当前位置。
    CCPoint ptNow  = m_pBall->getPosition();
	//将当前位置转换成界面坐标系的位置。
    CCPoint ptTemp = pDir->convertToUI(ptNow);
	//由收到的速度乘以一个系数后来影响位置。
    ptTemp.x += pAccelerationValue->x * 9.81f;
    ptTemp.y -= pAccelerationValue->y * 9.81f;
	//再转换为OPENGL坐标系的位置。貌似有点麻烦,其实直接在上面X,Y的加减上做上正确的方向即可。
    CCPoint ptNext = pDir->convertToGL(ptTemp);
	//限定位置的X,Y的有效范围,等于小球边缘始终在窗口内。
    FIX_POS(ptNext.x, (ballSize.width / 2.0), (winSize.width - ballSize.width / 2.0));
    FIX_POS(ptNext.y, (ballSize.height / 2.0), (winSize.height - ballSize.height / 2.0));
	//将位置传给小球。
    m_pBall->setPosition(ptNext);
}

//运行场景
void AccelerometerTestScene::runThisTest()
{
	//创建一个新的AccelerometerTest的实例并加入当前场景之中。
    CCLayer* pLayer = new AccelerometerTest();
addChild(pLayer);
//new时实例的计数器会加一,所以这里release后会减1保持对应。另注意:addChild(pLayer)后也会因占用实例而对其计数器加一。
    pLayer->release();
	//运行当前场景。
    CCDirector::sharedDirector()->replaceScene(this);
}

    运行结果如图:

Cocos2d-x 2.0 按键加速处理深入分析

    当我们按下键:上,下,右,左,<,>时,小球分别产生X,Y,Z的正负方向的加速度。WINDOWS在通过WM_KEYDOWN消息来即时处理按键响应时有延迟间隔的,所以按键响应不流畅,改成GetKeyState后就流畅多了,比如在CCEGLView的swapBuffer中加入相应的处理使每一帧都能进行按键的实时判断。

void CCEGLView::swapBuffers()
{
    if (m_hDC != NULL)
    {
        ::SwapBuffers(m_hDC);
    }	
	//如果按下左键
	if(GetKeyState(VK_LEFT) < 0)
	{
           if ( m_lpfnAccelerometerKeyHook!=NULL )
            {
                (*m_lpfnAccelerometerKeyHook)( WM_KEYDOWN,VK_LEFT,0 );
            }
	}
	….按上面写的如法炮制即可。
}

      本节到此就讲完了,讲课!




分类: cocos2d, cocos2d-x 标签:

Cocos2d-x 2.0 渲染到纹理深入分析

2013年3月1日 没有评论

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

红孩儿Cocos2d-X学习园地QQ群:249941957 

红孩儿Cocos2d-X学习园地QQ2群:44208467

Cocos2d-x 2.0 渲染到纹理深入分析

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

cocos2d-2.0-x-2.0.2@
Aug 30 2012

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

        前几天被邀请到车库咖啡参加了国内Cocos2d-x网上知名讲师的一个小聚会。心里是倍儿感荣幸,回来后越发喜爱Cocos2d-x了,越发喜爱写博客了,不过看着越来越庞大的Cocos2d-x工程和越来越少的时间表,心里也越来越着急了,除了写博客,还有工具箱也需要完善,没办法,只有多熬夜了。

 

       Cocos2d-x 2.0 渲染到纹理深入分析PS:刚刚上传了几个新的工具箱的视频到微博,是制做关键帧动画,导出PLIST并在Cocos2d-x中加载播放。

 

        好,继续今天开学。

        在游戏引擎开发中,常常会遇到RenderToTexture,简称RTT,即“渲染到纹理”技术。此项技术有什么用呢?当然有用,比如你要开发一个游戏人物的实时的3D头像,可以放一个摄像机在人物头部的相应位置,并创建一个RenderTexture或RenderTarget,之后将摄相机看到的画面输出到这个RenderTexture或RenderTarget中。然后使用它绘制到一个UI界面中,就可以实时的看到人物头部的动画,表情了。在Cocos2d-x中,RenderTexture则多用来做一些特殊效果,比如在之前讲网格动画一章时,令人赞叹的各种场景画面切割效果,如果没有RTT的支持是无法做到的,将场景画面先RTT到一张纹理上,然后在网格上贴上纹理,网格顶点做各种运动就实现了场景画面被切割的效果。

 

       现在我们将重新认识一下RenderTexture。在TestCpp中它做为独立的演示场景存在,名称为RenderTextureTest。我们先来看一下最重要的类CCRenderTexture。它是用于管理渲染目标纹理的类,与纹理不同的是,它必须是属于

打开CCRenderTexture.h:


//图像格式枚举,可以保存为JPG和PNG两种格式
typedef enum eImageFormat
{
    kCCImageFormatJPEG      = 0,
    kCCImageFormatPNG       = 1,
} tCCImageFormat;
//由结点派生
class CC_DLL CCRenderTexture : public CCNode 
{
    //精灵成员变量及存取接口
    CC_PROPERTY(CCSprite*, m_pSprite, Sprite)
public:
	//构造
    CCRenderTexture();
	//析构
    virtual ~CCRenderTexture();

    //创建一个渲染目标纹理。参数指定大小,像素格式和深度模板缓冲格式。内部调用create实现。
    CC_DEPRECATED_ATTRIBUTE static CCRenderTexture * renderTextureWithWidthAndHeight(int w ,int h, CCTexture2DPixelFormat eFormat, GLuint uDepthStencilFormat);

    //创建一个渲染目标纹理。参数指定大小,像素格式。内部调用create实现。
    CC_DEPRECATED_ATTRIBUTE static CCRenderTexture * renderTextureWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat);

    //创建一个渲染目标纹理。参数指定大小.。内部调用create实现。
    CC_DEPRECATED_ATTRIBUTE static CCRenderTexture * renderTextureWithWidthAndHeight(int w, int h);

    //第一个函数的create实现。
    static CCRenderTexture * create(int w ,int h, CCTexture2DPixelFormat eFormat, GLuint uDepthStencilFormat);

    //第二个函数的create实现。
    static CCRenderTexture * create(int w, int h, CCTexture2DPixelFormat eFormat);

    //第三个函数的create实现。
    static CCRenderTexture * create(int w, int h);

    //初始化,参数为大小和像素格式。
    bool initWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat);

    //初始化,参数为大小和像素格式,深度模板缓冲格式。
    bool initWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat, GLuint uDepthStencilFormat);

    //开始渲染到当前目标纹理。
    void begin();

    //清空颜色缓冲的值为指定值。
    void beginWithClear(float r, float g, float b, float a);

    //清空颜色缓冲和深度的值为指定值。
    void beginWithClear(float r, float g, float b, float a, float depthValue);

    //清空颜色缓冲和深度,模版值缓冲的值为指定值。
    void beginWithClear(float r, float g, float b, float a, float depthValue, int stencilValue);

    //LUA中调用的结束函数。
    inline void endToLua(){ end();};

    //结束渲染到当前目标纹理。
    void end();

    //清空目标纹理的颜色为指定色
    void clear(float r, float g, float b, float a);

    //清空目标纹理的深度值
    void clearDepth(float depthValue);

    //清空目标纹理的模板缓冲值
    void clearStencil(int stencilValue);

    //由目标纹理的数据产生出CCImage实例。
    CCImage* newCCImage();

    //保存目标纹理到相应图片文件。
    bool saveToFile(const char *szFilePath);

    //保存目标纹理到相应图片文件,指定图像格式。
    bool saveToFile(const char *name, tCCImageFormat format);
    
    //监听消息,保存目标纹理。
    void listenToBackground(CCObject *obj);

protected:
	//FBO对象,即帧缓冲区,一帧中像素数据保存的缓冲区域。可参看http://www.cnblogs.com/aokman/archive/2010/11/14/1876987.html
    GLuint       m_uFBO;
	//深度缓冲。
    GLuint       m_uDepthRenderBufffer;
	//保存旧的FBO对象。
    GLint        m_nOldFBO;
	//使用的纹理。
    CCTexture2D* m_pTexture;
	//用于保存当前纹理数据的可变纹理对象。
    CCImage*     m_pUITextureImage;
	//像素格式
    GLenum       m_ePixelFormat;
};

然后是CPP:

//构造
CCRenderTexture::CCRenderTexture()
: m_pSprite(NULL)
, m_uFBO(0)
, m_uDepthRenderBufffer(0)
, m_nOldFBO(0)
, m_pTexture(0)
, m_pUITextureImage(NULL)
, m_ePixelFormat(kCCTexture2DPixelFormat_RGBA8888)
{
    //设置监听EVENT_COME_TO_BACKGROUND事件,如果响应调用CCRenderTexture::listenToBackground函数。
    CCNotificationCenter::sharedNotificationCenter()->addObserver(this,
                                                                             callfuncO_selector(CCRenderTexture::listenToBackground),
                                                                             EVENT_COME_TO_BACKGROUND,
                                                                             NULL);
}
//析构
CCRenderTexture::~CCRenderTexture()
{
	//释放FBO
    glDeleteFramebuffers(1, &m_uFBO);
	//释放深度缓冲
    if (m_uDepthRenderBufffer)
    {
        glDeleteRenderbuffers(1, &m_uDepthRenderBufffer);
    }
	//释放
    CC_SAFE_DELETE(m_pUITextureImage);
    //移除监听响应函数。
    CCNotificationCenter::sharedNotificationCenter()->removeObserver(this, EVENT_COME_TO_BACKGROUND);
}
//监听消息,保存目标纹理。
void CCRenderTexture::listenToBackground(cocos2d::CCObject *obj)
{
//如果使用可变纹理。
#if CC_ENABLE_CACHE_TEXTURE_DATA
    //释放上一个m_pUITextureImage
    CC_SAFE_DELETE(m_pUITextureImage);
    // 产生当前渲染目标的CCImage
    m_pUITextureImage = newCCImage();
	//如果成功则将纹理m_pTexture中数据填充到可变纹理。
    if (m_pUITextureImage)
    {
        const CCSize& s = m_pTexture->getContentSizeInPixels();
        VolatileTexture::addDataTexture(m_pTexture, m_pUITextureImage->getData(), kTexture2DPixelFormat_RGBA8888, s);
    } 
    else
    {
        CCLOG("Cache rendertexture failed!");
    }
#endif
}
//取得精灵成员
CCSprite * CCRenderTexture::getSprite()
{
    return m_pSprite;
}
//设置精灵成员。
void CCRenderTexture::setSprite(CCSprite* var)
{
    m_pSprite = var;
}
//创建一个渲染目标纹理。参数指定大小,像素格式。内部调用create实现。
CCRenderTexture * CCRenderTexture::renderTextureWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat)
{
    return CCRenderTexture::create(w, h, eFormat);
}
//上面的create实现。 
CCRenderTexture * CCRenderTexture::create(int w, int h, CCTexture2DPixelFormat eFormat)
{
	//创建一个渲染目标纹理。
    CCRenderTexture *pRet = new CCRenderTexture();
	//调用相应的初始化函数。
    if(pRet && pRet->initWithWidthAndHeight(w, h, eFormat))
    {
		//成功后交由内存管理器进行管理。
        pRet->autorelease();
        return pRet;
    }
	//不成功则释放置空返回NULL。
    CC_SAFE_DELETE(pRet);
    return NULL;
}
//创建一个渲染目标纹理。参数指定大小,像素格式和深度模板缓冲格式。内部调用create实现。
CCRenderTexture * CCRenderTexture::renderTextureWithWidthAndHeight(int w ,int h, CCTexture2DPixelFormat eFormat, GLuint uDepthStencilFormat)
{
    return CCRenderTexture::create(w, h, eFormat, uDepthStencilFormat);
}
//上面的create实现。
CCRenderTexture * CCRenderTexture::create(int w ,int h, CCTexture2DPixelFormat eFormat, GLuint uDepthStencilFormat)
{
	//创建一个渲染目标纹理。
    CCRenderTexture *pRet = new CCRenderTexture();
	//调用相应的初始化函数。
    if(pRet && pRet->initWithWidthAndHeight(w, h, eFormat, uDepthStencilFormat))
    {
        pRet->autorelease();
        return pRet;
    }
    CC_SAFE_DELETE(pRet);
    return NULL;
}
//创建一个渲染目标纹理。参数指定大小。内部调用create实现。
CCRenderTexture * CCRenderTexture::renderTextureWithWidthAndHeight(int w, int h)
{
    return CCRenderTexture::create(w, h);
}
//上面的create实现。
CCRenderTexture * CCRenderTexture::create(int w, int h)
{
	//创建一个渲染目标纹理。
    CCRenderTexture *pRet = new CCRenderTexture();
	//调用相应的初始化函数。
    if(pRet && pRet->initWithWidthAndHeight(w, h, kCCTexture2DPixelFormat_RGBA8888, 0))
    {
        pRet->autorelease();
        return pRet;
    }
    CC_SAFE_DELETE(pRet);
    return NULL;
}
//初始化,参数为大小和像素格式格式。
bool CCRenderTexture::initWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat)
{
    return initWithWidthAndHeight(w, h, eFormat, 0);
}
    //初始化,参数为大小和像素格式,深度模板缓冲格式。
bool CCRenderTexture::initWithWidthAndHeight(int w, int h, CCTexture2DPixelFormat eFormat, GLuint uDepthStencilFormat)
{
	//有效性判断。
    CCAssert(m_ePixelFormat != kCCTexture2DPixelFormat_A8, "only RGB and RGBA formats are valid for a render texture");
	//
    bool bRet = false;
	//使用do –while结构来保证出错及时中断退出
    do 
    {
		//宽高要乘以缩放值。
        w *= (int)CC_CONTENT_SCALE_FACTOR();
        h *= (int)CC_CONTENT_SCALE_FACTOR();
		//保存当前的FBO对象。
        glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
		//创建临时变量保存纹理的真实大小。
        unsigned int powW = 0;
        unsigned int powH = 0;
		//看是否支持纹理的非2幂次方,做相应的处理。
        if( CCConfiguration::sharedConfiguration()->supportsNPOT() ) {
		//如果支持就用w,h做为纹理大小。
            powW = w;
            powH = h;
        } else {
		//如果不支持要转换为2的幂次方大小。
            powW = ccNextPOT(w);
            powH = ccNextPOT(h);
        }
		//为像素申请内存。每像素4字节。
        void *data = malloc((int)(powW * powH * 4));
		//内存申请失败则中断退出。
        CC_BREAK_IF(! data);
		//清空内存为0。
        memset(data, 0, (int)(powW * powH * 4));
		//保存像素格式。
        m_ePixelFormat = eFormat;
		//创建一个新的纹理。
        m_pTexture = new CCTexture2D();
		//由上面的像素数据和大小,像素格式信息填充纹理。
        if (m_pTexture) {
                m_pTexture->initWithData(data, (CCTexture2DPixelFormat)m_ePixelFormat, powW, powH, CCSizeMake((float)w, (float)h));
                free( data );
        } else {
                free( data );  // ScopeGuard (a simulated Finally clause) would be more elegant.
                break;
        }
		//取得当前的FBO对象。
        GLint oldRBO;
        glGetIntegerv(GL_RENDERBUFFER_BINDING, &oldRBO);
        // 创建新的FBO并绑定。
        glGenFramebuffers(1, &m_uFBO);
        glBindFramebuffer(GL_FRAMEBUFFER, m_uFBO);

        //设置将帧缓冲区颜色数据输出到纹理。
        glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_pTexture->getName(), 0);
		//如果有使用深度缓冲。
        if (m_uDepthRenderBufffer != 0) 
        {
            //创建一个渲染目标深度缓冲区并绑定。
            glGenRenderbuffers(1, &m_uDepthRenderBufffer);
            glBindRenderbuffer(GL_RENDERBUFFER, m_uDepthRenderBufffer);
		  //设置当前渲染目标缓冲氏的像素格式和大小。
            glRenderbufferStorage(GL_RENDERBUFFER, uDepthStencilFormat, (GLsizei)powW, (GLsizei)powH);
			//设置将帧缓冲区深度数据输出到这个新创建的深度缓冲区。
            glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_uDepthRenderBufffer);

            //如果深度缓冲格式是24位深度,8位模版缓冲,则设置将帧缓冲区的模版数据输出到这个新创建的深度缓冲区中。
            if (uDepthStencilFormat == CC_GL_DEPTH24_STENCIL8)
                glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, m_uDepthRenderBufffer);
        }


        // 检查上面操作是否正常执行。
        CCAssert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE, "Could not attach texture to framebuffer");
		//设置纹理不使用抗距齿模糊。
        m_pTexture->setAliasTexParameters();
		//由纹理创建精灵成员。
        m_pSprite = CCSprite::createWithTexture(m_pTexture);
		//释放纹理。
        m_pTexture->release();
		//精灵设置Y镜像并放入到当前结点下。
        m_pSprite->setScaleY(-1);
        this->addChild(m_pSprite);
		//设置ALPHA混合方案。
        ccBlendFunc tBlendFunc = {GL_ONE, GL_ONE_MINUS_SRC_ALPHA };
        m_pSprite->setBlendFunc(tBlendFunc);
		//还原所用的
        glBindRenderbuffer(GL_RENDERBUFFER, oldRBO);
        glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
        bRet = true;
    } while (0);
    return bRet;
    
}
//开始渲染到当前目标纹理。
void CCRenderTexture::begin()
{
    // 保存当前矩阵
    kmGLPushMatrix();
	//取得纹理的大小。
    const CCSize& texSize = m_pTexture->getContentSizeInPixels();

    // 取得设备的窗口大小,计算出视口和投影矩阵
    CCDirector *director = CCDirector::sharedDirector();
    CCSize size = director->getWinSizeInPixels();
    float widthRatio = size.width / texSize.width;
    float heightRatio = size.height / texSize.height;
    glViewport(0, 0, (GLsizei)texSize.width, (GLsizei)texSize.height);
    kmMat4 orthoMatrix;
    kmMat4OrthographicProjection(&orthoMatrix, (float)-1.0 / widthRatio,  (float)1.0 / widthRatio,
        (float)-1.0 / heightRatio, (float)1.0 / heightRatio, -1,1 );
    kmGLMultMatrix(&orthoMatrix);
	//取得当前的FBO
    glGetIntegerv(GL_FRAMEBUFFER_BINDING, &m_nOldFBO);
    glBindFramebuffer(GL_FRAMEBUFFER, m_uFBO);
}
//清空颜色缓冲。
void CCRenderTexture::beginWithClear(float r, float g, float b, float a)
{
	//开始渲染到目标纹理。
    this->begin();

    //临时变量,用于保存当前帧的色彩缓冲的清空值数据。
    GLfloat    clearColor[4];
glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor); 

	//按指定清空值数值清空色彩缓冲区。
    glClearColor(r, g, b, a);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //恢复色彩缓冲的清空值数据。
    glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);     
}
//清空颜色缓冲和深度缓冲的值为指定值。
void CCRenderTexture::beginWithClear(float r, float g, float b, float a, float depthValue)
{
	//开始渲染到目标纹理。
    this->begin();

    //临时变量,用于保存当前帧的各缓冲的清空值数据。
    GLfloat	clearColor[4];
    GLfloat depthClearValue;
    glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor);
    glGetFloatv(GL_DEPTH_CLEAR_VALUE, &depthClearValue);

	//按指定清空值数值清空各绘冲区。
    glClearColor(r, g, b, a);
    glClearDepth(depthValue);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    //恢复各缓冲的清空值数据。
    glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
    glClearDepth(depthClearValue);
}
//清空颜色缓冲和深度,模版值缓冲的值为指定值。
void CCRenderTexture::beginWithClear(float r, float g, float b, float a, float depthValue, int stencilValue)
{
	//开始渲染到目标纹理。
    this->begin();

    //临时变量,用于保存当前帧的各缓冲的清空值数据。
    GLfloat	clearColor[4];
    GLfloat depthClearValue;
    int stencilClearValue;
    glGetFloatv(GL_COLOR_CLEAR_VALUE,clearColor);
    glGetFloatv(GL_DEPTH_CLEAR_VALUE, &depthClearValue);
    glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencilClearValue);
	//按指定清空值数值清空各绘冲区。
    glClearColor(r, g, b, a);
    glClearDepth(depthValue);
    glClearStencil(stencilValue);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    // 恢复各缓冲的清空值数据。
    glClearColor(clearColor[0], clearColor[1], clearColor[2], clearColor[3]);
    glClearDepth(depthClearValue);
    glClearStencil(stencilClearValue);
}
//结束渲染到当前目标纹理。
void CCRenderTexture::end()
{
	//还原旧的FBO
    glBindFramebuffer(GL_FRAMEBUFFER, m_nOldFBO);
	//恢复矩阵
    kmGLPopMatrix();
	//取得设备。
    CCDirector *director = CCDirector::sharedDirector();
	//取得窗口大小。
    CCSize size = director->getWinSizeInPixels();

    // 还原视口及投影矩阵。
    glViewport(0, 0, GLsizei(size.width * CC_CONTENT_SCALE_FACTOR()), GLsizei(size.height * CC_CONTENT_SCALE_FACTOR()));
    if ( director->getProjection() == kCCDirectorProjection3D && CC_CONTENT_SCALE_FACTOR() != 1 )
    {
        glViewport((GLsizei)(-size.width/2), (GLsizei)(-size.height/2), (GLsizei)(size.width * CC_CONTENT_SCALE_FACTOR()), (GLsizei)(size.height * CC_CONTENT_SCALE_FACTOR()));
    }
    director->setProjection(director->getProjection());
}
//清空目标纹理的颜色为指定色
void CCRenderTexture::clear(float r, float g, float b, float a)
{
	//开始渲染到目标纹理并清空目标纹理的颜色为指定色。
    this->beginWithClear(r, g, b, a);
	//结束渲染到目标纹理。
    this->end();
}
//清空目标纹理的深度值为指定值。
void CCRenderTexture::clearDepth(float depthValue)
{
	//开始渲染到目标纹理。
    this->begin();
    // 取得当前深度缓冲清空值。
    GLfloat depthClearValue;
    glGetFloatv(GL_DEPTH_CLEAR_VALUE, &depthClearValue);
	//设置新深度清空值并清空模板缓冲。
    glClearDepth(depthValue);
    glClear(GL_DEPTH_BUFFER_BIT);

    //还用深度缓冲清空值。
    glClearDepth(depthClearValue);
	//结束渲染到目标纹理。
    this->end();
}
//清空目标纹理的模版缓冲值为指定色
void CCRenderTexture::clearStencil(int stencilValue)
{
    // 取得当前模板缓冲清空值。
    int stencilClearValue;
    glGetIntegerv(GL_STENCIL_CLEAR_VALUE, &stencilClearValue);
	//设置新模板清空值并清空模板缓冲。
    glClearStencil(stencilValue);
    glClear(GL_STENCIL_BUFFER_BIT);

    //还原模板缓冲清空值。
    glClearStencil(stencilClearValue);
}

//保存目标纹理到相应图片文件。
bool CCRenderTexture::saveToFile(const char *szFilePath)
{
    bool bRet = false;
	//取得当前渲染目标纹理产生的CCImage。
    CCImage *pImage = newCCImage();
    if (pImage)
    {	//保存到图片。
        bRet = pImage->saveToFile(szFilePath, kCCImageFormatJPEG);
    }
	//释放pImage
    CC_SAFE_DELETE(pImage);
    return bRet;
}
//保存目标纹理到相应图片文件,可以指定像素格式。
bool CCRenderTexture::saveToFile(const char *fileName, tCCImageFormat format)
{
    bool bRet = false;
	//只允许JPG和PNG格式
    CCAssert(format == kCCImageFormatJPEG || format == kCCImageFormatPNG,
             "the image can only be saved as JPG or PNG format");
	//取得当前渲染目标纹理产生的CCImage。
    CCImage *pImage = newCCImage();
    if (pImage)
    {
		//取得路径。
        std::string fullpath = CCFileUtils::sharedFileUtils()->getWriteablePath() + fileName;
        //保存到图片。
        bRet = pImage->saveToFile(fullpath.c_str(), true);
    }
	//释放pImage
    CC_SAFE_DELETE(pImage);
	//返回成败。
    return bRet;
}

//由目标纹理的数据产生出CCImage实例。/
CCImage* CCRenderTexture::newCCImage()
{
	//有效性判断。
    CCAssert(m_ePixelFormat == kCCTexture2DPixelFormat_RGBA8888, "only RGBA8888 can be saved as image");
    if (NULL == m_pTexture)
    {
        return NULL;
    }
	//取得纹理的大小。
    const CCSize& s = m_pTexture->getContentSizeInPixels();

    //取得宽高。 
    int nSavedBufferWidth = (int)s.width;
    int nSavedBufferHeight = (int)s.height;
	//定义临时指针变量。
    GLubyte *pBuffer = NULL;
    GLubyte *pTempData = NULL;
    CCImage *pImage = new CCImage();
	//do-while流程结构保证出错及时退出。
    do
{
		//创建最终结果的像素数据数组的内存。
        CC_BREAK_IF(! (pBuffer = new GLubyte[nSavedBufferWidth * nSavedBufferHeight * 4]));

		//申请临时像素数据数组的内存。
        if(! (pTempData = new GLubyte[nSavedBufferWidth * nSavedBufferHeight * 4]))
        {
            delete[] pBuffer;
            pBuffer = NULL;
            break;
        }
		//开始渲染到目标纹理。
        this->begin();
		//设置像素按字节对齐。
        glPixelStorei(GL_PACK_ALIGNMENT, 1);
		//将像素写入到pTempData中。
        glReadPixels(0,0,nSavedBufferWidth, nSavedBufferHeight,GL_RGBA,GL_UNSIGNED_BYTE, pTempData);
        this->end();

        //将pTempData从下往上拷到pBuffer中,为什么呢?因为前面initWithWidthAndHeight中精灵成员设置了Y镜像,所以图像是上下反的。 
        for (int i = 0; i < nSavedBufferHeight; ++i)
        {
            memcpy(&pBuffer[i * nSavedBufferWidth * 4], 
                &pTempData[(nSavedBufferHeight - i - 1) * nSavedBufferWidth * 4], 
                nSavedBufferWidth * 4);
        }
		//填充pImage
        pImage->initWithImageData(pBuffer, nSavedBufferWidth * nSavedBufferHeight * 4, CCImage::kFmtRawData, nSavedBufferWidth, nSavedBufferHeight, 8);
    } while (0);
	//释放申请的内存
    CC_SAFE_DELETE_ARRAY(pBuffer);
    CC_SAFE_DELETE_ARRAY(pTempData);

    return pImage;
}

       这个渲染目标纹理的核心功能类解析完之后,我们就没有什么还不可以理解的了。现在我们来打开RenderTextureTest.h:

//演示用的层,做为基类使用
class RenderTextureTest : public CCLayer
{
public:
	//加载当前层时的处理
    virtual void onEnter();
	//取得标题。
    virtual std::string title();
	//取得副标题。
    virtual std::string subtitle();
	//重新启动当前演示
    void restartCallback(CCObject* pSender);
	//下一个演示
    void nextCallback(CCObject* pSender);
	//上一个演示
    void backCallback(CCObject* pSender);
};

对应CPP:

//场景索引
static int sceneIdx = -1; 
//最大的演示层数量,四个
#define MAX_LAYER    4
//创建相应的演示层
CCLayer* createTestCase(int nIndex)
{
	//根据索引创建相应的演示层实例。
    switch(nIndex)
    {
    case 0: return new RenderTextureSave();
    case 1: return new RenderTextureIssue937();
    case 2: return new RenderTextureZbuffer();    
    case 3: return new RenderTextureTestDepthStencil();
    }

    return NULL;
}
//下一个演示实例
CCLayer* nextTestCase()
{
    sceneIdx++;
    sceneIdx = sceneIdx % MAX_LAYER;

    CCLayer* pLayer = createTestCase(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}
//上一个演示实例
CCLayer* backTestCase()
{
    sceneIdx--;
    int total = MAX_LAYER;
    if( sceneIdx < 0 )
        sceneIdx += total;    

    CCLayer* pLayer = createTestCase(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}
//重新运行当前的演示实例。
CCLayer* restartTestCase()
{
    CCLayer* pLayer = createTestCase(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}

//加载当前层时的处理。
void RenderTextureTest::onEnter()
{
	//先调用基类的相应函数。
    CCLayer::onEnter();
	//取得屏幕大小
    CCSize s = CCDirector::sharedDirector()->getWinSize();
	//创建相应的文字标签。
    CCLabelTTF* label = CCLabelTTF::create(title().c_str(), "Arial", 26);
	//将文字标签放到当前层下。
    addChild(label, 1);
	//将文字标签放置在屏幕中央上部。
    label->setPosition( ccp(s.width/2, s.height-50) );
	//取得副标题。
    std::string strSubtitle = subtitle();
    if( ! strSubtitle.empty() ) 
    {
		//如果副标题文字有效,创建相应的文字标签显示副标题。
        CCLabelTTF* l = CCLabelTTF::create(strSubtitle.c_str(), "Thonburi", 16);
		//将副标题放入当前层下并放在主标题之下位置。
        addChild(l, 1);
        l->setPosition( ccp(s.width/2, s.height-80) );
    }    
	//创建用于控制动画演示的菜单项
	//用于点击返回上一个动画演示的菜单项。
    CCMenuItemImage *item1 = CCMenuItemImage::create("Images/b1.png", "Images/b2.png", this, menu_selector(RenderTextureTest::backCallback) );
	//用于点击重新运行当前动画演示的菜单项。
    CCMenuItemImage *item2 = CCMenuItemImage::create("Images/r1.png","Images/r2.png", this, menu_selector(RenderTextureTest::restartCallback) );
	//用于点击重新运行下一个动画演示的菜单项。
    CCMenuItemImage *item3 = CCMenuItemImage::create("Images/f1.png", "Images/f2.png", this, menu_selector(RenderTextureTest::nextCallback) );
	//由上面三个菜单项创建菜单。
    CCMenu *menu = CCMenu::create(item1, item2, item3, NULL);
	//设置菜单和各菜单项的位置。
    menu->setPosition( CCPointZero );
    item1->setPosition( ccp( s.width/2 - item2->getContentSize().width*2, item2->getContentSize().height/2) );
    item2->setPosition( ccp( s.width/2, item2->getContentSize().height/2) );
    item3->setPosition( ccp( s.width/2 + item2->getContentSize().width*2, item2->getContentSize().height/2) );
	//将菜单放入到当前层下。
    addChild(menu, 1);
}
//响应重启当前演示的函数。
void RenderTextureTest::restartCallback(CCObject* pSender)
{
	//创建一个新的场景,创建一个新的当前演示层并放入到这个场景中。
    CCScene* s = new RenderTextureScene();
    s->addChild(restartTestCase()); 
	//运行创建的场景。

    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
}
//响应进行下一个演示的函数。
void RenderTextureTest::nextCallback(CCObject* pSender)
{
	//创建一个新的场景,创建下一个演示层并放入到这个场景中。
    CCScene* s = new RenderTextureScene();
    s->addChild( nextTestCase() );
	//运行创建的场景。
    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
}
//响应进行上一个演示的函数。
void RenderTextureTest::backCallback(CCObject* pSender)
{
	//创建一个新的场景,创建上一个演示层并放入到这个场景中。
    CCScene* s = new RenderTextureScene();
    s->addChild( backTestCase() );
	//运行创建的场景。
    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
} 
//取得标题。
std::string RenderTextureTest::title()
{
    return "No title";
}
//取得副标题。
std::string RenderTextureTest::subtitle()
{
    return "";
}

第一个演示:保存渲染目标纹理。

说明:在屏幕上触屏移动,可以不断的使用随机顶点色的火球点图片精灵来做为刷子绘制线条。点击右边的“Clear”可以将屏幕清为随机色。而这些图像都是输出到纹理的。点击“SaveImage”可以保存这个纹理为PNG或JPG。

截图

Cocos2d-x 2.0 渲染到纹理深入分析
代码:

//由上面的类派生出的类,可以将目标纹理保存成为图片。
class RenderTextureSave : public RenderTextureTest
{
public:
	//构造
    RenderTextureSave();
	//析构
    ~RenderTextureSave();
	//取得标题
    virtual std::string title();
	//取得副标题。
    virtual std::string subtitle();
	//响应触屏时移动事件
    virtual void ccTouchesMoved(CCSet* touches, CCEvent* event);
	//清除图片图像
    void clearImage(CCObject *pSender);
	//保存纹理到图片
    void saveImage(CCObject *pSender);

private:
	//目标纹理
    CCRenderTexture *m_pTarget;
	//代表刷子的精灵
    CCSprite *m_pBrush;
};

对应CPP:

//构选
RenderTextureSave::RenderTextureSave()
{
	//取得屏幕大小
    CCSize s = CCDirector::sharedDirector()->getWinSize();

    //创建一个目标纹理,用于把场景渲染到其中。
    m_pTarget = CCRenderTexture::create(s.width, s.height, kCCTexture2DPixelFormat_RGBA8888);
	//占用它,对其引用计数器加一。
    m_pTarget->retain();
	//设置目标纹理的位置放在屏幕中央。
    m_pTarget->setPosition(ccp(s.width / 2, s.height / 2));

    //将目标纹理放入到当前层下。
    this->addChild(m_pTarget, -1);

    //创建一个刷子的精灵,载入的是火球的图片。
    m_pBrush = CCSprite::create("Images/fire.png");
	//占用它,对其引用计数器加一。
    m_pBrush->retain();
	//设置精灵为红色
    m_pBrush->setColor(ccRED);
	//设置透明度为20%
    m_pBrush->setOpacity(20);
	//设置当前层响应触屏事件。
    this->setTouchEnabled(true);

    //创建一个菜单项文字的字体,大小为16。
    CCMenuItemFont::setFontSize(16);
	//创建两个菜单项文字,分别为“保存图像”和“清空图像”。
    CCMenuItem *item1 = CCMenuItemFont::create("Save Image", this, menu_selector(RenderTextureSave::saveImage));
    CCMenuItem *item2 = CCMenuItemFont::create("Clear", this, menu_selector(RenderTextureSave::clearImage));
	//由这两个菜单项创建菜单,并放入当前层下。
    CCMenu *menu = CCMenu::create(item1, item2, NULL);
    this->addChild(menu);
	//设置菜单项按纵向排列。
    menu->alignItemsVertically();
	//设置菜单的位置。
    menu->setPosition(ccp(s.width - 80, s.height - 30));
}
//取得标题。
string RenderTextureSave::title()
{
    return "Touch the screen";
}
//取得副标题。
string RenderTextureSave::subtitle()
{
    return "Press 'Save Image' to create an snapshot of the render texture";
}
//清除图片图像。
void RenderTextureSave::clearImage(cocos2d::CCObject *pSender)
{
	//对目标纹理调用clear即可以将其清空为相应的颜色。
	//这里对,R,G,B,A的色彩分量做了0~1间的随机,产生出随机的色彩。
    m_pTarget->clear(CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1(), CCRANDOM_0_1());
}
//保存图像。
void RenderTextureSave::saveImage(cocos2d::CCObject *pSender)
{
	//创建一个计数器,用来统计保存的次数。
    static int counter = 0;
	//要保存文件的名称。
    char png[20];
    sprintf(png, "image-%d.png", counter);
    char jpg[20];
    sprintf(jpg, "image-%d.jpg", counter);
	//对目标纹理调用saveToFile将纹理上的图像保存为PNG和JPG两种格式的图片。
    m_pTarget->saveToFile(png, kCCImageFormatPNG);
    m_pTarget->saveToFile(jpg, kCCImageFormatJPEG);
	//取得纹理对应的CCImage实例指针
    CCImage *pImage = m_pTarget->newCCImage();
	//将PNG图片和CCImage实例中的信息加载到纹理管理器中。
    CCTexture2D *tex = CCTextureCache::sharedTextureCache()->addUIImage(pImage, png);
	//删除pImage
    CC_SAFE_DELETE(pImage);
	//由纹理tex创建精灵sprite。
    CCSprite *sprite = CCSprite::createWithTexture(tex);
	//设置缩放值为原来0.3倍
    sprite->setScale(0.3f);
	//将精灵放到当前层下。
    addChild(sprite);
	//设置精灵位置。
    sprite->setPosition(ccp(40, 40));
    sprite->setRotation(counter * 3);
	//打印日志
    CCLOG("Image saved %s and %s", png, jpg);
	//计数器加一
    counter++;
}
//析构
RenderTextureSave::~RenderTextureSave()
{
	//对占用的目标纹得和刷子精灵的引用计数减一。
    m_pBrush->release();
    m_pTarget->release();
	//纹理管理器释放不用的纹理。
    CCTextureCache::sharedTextureCache()->removeUnusedTextures();
}
//响应触屏时移动事件
void RenderTextureSave::ccTouchesMoved(CCSet* touches, CCEvent* event)
{
	//取得触点
    CCTouch *touch = (CCTouch *)touches->anyObject();
	//取得触点的当前位置。
    CCPoint start = touch->getLocation();
	//取得触点的上一个位置。
    CCPoint end = touch->getPreviousLocation();
    // 开始渲染到目标纹理
    m_pTarget->begin();

    //取得当前位置和上一个位置的距离。 
    float distance = ccpDistance(start, end);
	//如果距离大于1像素。
    if (distance > 1)
    {
        int d = (int)distance;
		//则在上一个位置和当前位置间连一条线,每个像素渲染一下刷子精灵。
        for (int i = 0; i < d; i++)
        {
			//计算X和Y方向的偏移。
            float difx = end.x - start.x;
            float dify = end.y - start.y;
			//通过偏移计算斜率。
            float delta = (float)i / distance;
			//通过直线公式计数出连像上第i个位置的像素位置设置给精灵。
            m_pBrush->setPosition(ccp(start.x + (difx * delta), start.y + (dify * delta)));
			 //设置一个随机的旋转值给精灵。
            m_pBrush->setRotation(rand() % 360);
			 //在给一个限定范围的随机缩放大小给精灵。
            float r = (float)(rand() % 50 / 50.f) + 0.25f;
            m_pBrush->setScale(r);
            /*m_pBrush->setColor(ccc3(CCRANDOM_0_1() * 127 + 128, 255, 255));*/
            // Use CCRANDOM_0_1() will cause error when loading libtests.so on android, I don't know why.
			//设置一个随机的颜色给精灵。
            m_pBrush->setColor(ccc3(rand() % 127 + 128, 255, 255));
            // Call visit to draw the brush, don't call draw..
			//将精灵绘制出来。
            m_pBrush->visit();
        }
    }

    //结束渲染到纹理,则由精灵构成的连线图像就被渲染到纹理中了。
    m_pTarget->end();
}

第二个演示:渲染小球到目标纹理。

说明:将纵向排列的两个小球的精灵渲染到目标纹理上,在场景中左边一列显示两个小球,右边一列显示目标纹理。

截图

Cocos2d-x 2.0 渲染到纹理深入分析

代码:

//由上面的类派生出的类,。
class RenderTextureIssue937 : public RenderTextureTest
{
public:
	//构造
    RenderTextureIssue937();
	//取得标题
    virtual std::string title();
	//取得副 标题
    virtual std::string subtitle();
};

对应CPP:

//构造
RenderTextureIssue937::RenderTextureIssue937()
{
	//创建一个色层做为背景放入到当前层下。
    CCLayerColor *background = CCLayerColor::create(ccc4(200,200,200,255));
    addChild(background);
	//创建第一个精灵
    CCSprite *spr_premulti = CCSprite::create("Images/fire.png");
    spr_premulti->setPosition(ccp(16,48));
	//创建第二个精灵。
    CCSprite *spr_nonpremulti = CCSprite::create("Images/fire.png");
    spr_nonpremulti->setPosition(ccp(16,16));
    //创建一个渲染目标纹理。
    CCRenderTexture *rend = CCRenderTexture::create(32, 64, kCCTexture2DPixelFormat_RGBA8888);
	//如果失败则返回
    if (NULL == rend)
    {
        return;
    }
	//开始渲染图像到目标纹理。
    rend->begin();
	//先渲染第一个精灵。
    spr_premulti->visit();
	//再渲染第二个精灵。
    spr_nonpremulti->visit();
	//结束渲染到纹理。
    rend->end(); 
	//取得屏幕大小。
    CCSize s = CCDirector::sharedDirector()->getWinSize();
    //设置第一个精灵的位置。
    spr_premulti->setPosition(ccp(s.width/2-16, s.height/2+16));
    //设置第二个精灵的位置。
    spr_nonpremulti->setPosition(ccp(s.width/2-16, s.height/2-16));
	//设置目标纹理的位置。
    rend->setPosition(ccp(s.width/2+16, s.height/2));
	//将上面三个结点都放到当前层之下。
    addChild(spr_nonpremulti);
    addChild(spr_premulti);
    addChild(rend);
}
//取得标题。
std::string RenderTextureIssue937::title()
{
    return "Testing issue #937";
}
//取得副标题。
std::string RenderTextureIssue937::subtitle()
{
    return "All images should be equal...";
}

第三个演示:渲染不同Z值的精灵到目标纹理。

说明:在屏幕上创建了一系列不同Z值的文字标签和精灵,当触屏按下松开后将屏幕画面绘制到目标纹理,创建一个精灵使用这个目标纹理并在屏幕上运行一个显示到渐隐的动画。

截图

Cocos2d-x 2.0 渲染到纹理深入分析

代码:

//由上面的类派生出的类,。
class RenderTextureZbuffer : public RenderTextureTest
{
public:
	//构造
    RenderTextureZbuffer();
	//响应触屏的一些事件
	//按下并移动事件的响应
    virtual void ccTouchesMoved(CCSet* touches, CCEvent* event);
	//按下事件的响应
    virtual void ccTouchesBegan(CCSet* touches, CCEvent* event);
	//抬起事件的响应
    virtual void ccTouchesEnded(CCSet* touches, CCEvent* event);
	//取得标题。
    virtual std::string title();
	//取得副标题。
    virtual std::string subtitle();
	//渲染屏幕快照
    void renderScreenShot();

private:
	//使用的精灵批次优化处理结点。
    cocos2d::CCSpriteBatchNode *mgr;;
	//用到的若干精灵。
    cocos2d::CCSprite *sp1;
    cocos2d::CCSprite *sp2;
    cocos2d::CCSprite *sp3;
    cocos2d::CCSprite *sp4;
    cocos2d::CCSprite *sp5;
    cocos2d::CCSprite *sp6;
    cocos2d::CCSprite *sp7;
    cocos2d::CCSprite *sp8;
    cocos2d::CCSprite *sp9;
};

对应CPP:

//构造
RenderTextureZbuffer::RenderTextureZbuffer()
{
	//设置当前层响应触屏事悠扬。
    this->setTouchEnabled(true);
	//取得屏幕大小。
    CCSize size = CCDirector::sharedDirector()->getWinSize();
	//创建文字标签。
    CCLabelTTF *label = CCLabelTTF::create("vertexZ = 50", "Marker Felt", 64);
	//设置文字标签的位置并放入到当前层下。
    label->setPosition(ccp(size.width / 2, size.height * 0.25f));
    this->addChild(label);
	//创建第二个文字标签。
    CCLabelTTF *label2 = CCLabelTTF::create("vertexZ = 0", "Marker Felt", 64);
	//设置文字标签的位置并放入到当前层下。
    label2->setPosition(ccp(size.width / 2, size.height * 0.5f));
    this->addChild(label2);
	//创建第三个文字标签。
    CCLabelTTF *label3 = CCLabelTTF::create("vertexZ = -50", "Marker Felt", 64);
	//设置文字标签的位置并放入到当前层下。
    label3->setPosition(ccp(size.width / 2, size.height * 0.75f));
    this->addChild(label3);
	//分别对三个文字标签设置不同的顶点Z值。
    label->setVertexZ(50);
    label2->setVertexZ(0);
    label3->setVertexZ(-50);
	//由circle.plist 
	CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("Images/bugs/circle.plist");
	//创建相应的精灵批次优化结点。
    mgr = CCSpriteBatchNode::create("Images/bugs/circle.png", 9);
    this->addChild(mgr);
	//创建9个精灵
    sp1 = CCSprite::createWithSpriteFrameName("circle.png");
    sp2 = CCSprite::createWithSpriteFrameName("circle.png");
    sp3 = CCSprite::createWithSpriteFrameName("circle.png");
    sp4 = CCSprite::createWithSpriteFrameName("circle.png");
    sp5 = CCSprite::createWithSpriteFrameName("circle.png");
    sp6 = CCSprite::createWithSpriteFrameName("circle.png");
    sp7 = CCSprite::createWithSpriteFrameName("circle.png");
    sp8 = CCSprite::createWithSpriteFrameName("circle.png");
    sp9 = CCSprite::createWithSpriteFrameName("circle.png");
	//将其加入到批次优化结点中,给予不同的Z值。
    mgr->addChild(sp1, 9);
    mgr->addChild(sp2, 8);
    mgr->addChild(sp3, 7);
    mgr->addChild(sp4, 6);
    mgr->addChild(sp5, 5);
    mgr->addChild(sp6, 4);
    mgr->addChild(sp7, 3);
    mgr->addChild(sp8, 2);
    mgr->addChild(sp9, 1);
	//对这九个精灵设置不同的顶点Z值。
    sp1->setVertexZ(400);
    sp2->setVertexZ(300);
    sp3->setVertexZ(200);
    sp4->setVertexZ(100);
    sp5->setVertexZ(0);
    sp6->setVertexZ(-100);
    sp7->setVertexZ(-200);
    sp8->setVertexZ(-300);
    sp9->setVertexZ(-400);
	//设置第九个放大2倍,顶点色为黄色。
    sp9->setScale(2);
    sp9->setColor(ccYELLOW);
}
//取得标题。
string RenderTextureZbuffer::title()
{
    return "Testing Z Buffer in Render Texture";
}
//取得副标题。
string RenderTextureZbuffer::subtitle()
{
    return "Touch screen. It should be green";
}
//按下事件的响应
void RenderTextureZbuffer::ccTouchesBegan(cocos2d::CCSet *touches, cocos2d::CCEvent *event)
{
	//遍历所有的触点。
    CCSetIterator iter;
    CCTouch *touch;
    for (iter = touches->begin(); iter != touches->end(); ++iter)
    {
		//将九个精灵放在触点位置。
        touch = (CCTouch *)(*iter);
        CCPoint location = touch->getLocation();

        sp1->setPosition(location);
        sp2->setPosition(location);
        sp3->setPosition(location);
        sp4->setPosition(location);
        sp5->setPosition(location);
        sp6->setPosition(location);
        sp7->setPosition(location);
        sp8->setPosition(location);
        sp9->setPosition(location);
    }
}
//按下并移动事件的响应。
void RenderTextureZbuffer::ccTouchesMoved(CCSet* touches, CCEvent* event)
{
	//遍历所有的触点。
    CCSetIterator iter;
    CCTouch *touch;
    for (iter = touches->begin(); iter != touches->end(); ++iter)
    {//将九个精灵放在触点位置。

        touch = (CCTouch *)(*iter);
        CCPoint location = touch->getLocation();

        sp1->setPosition(location);
        sp2->setPosition(location);
        sp3->setPosition(location);
        sp4->setPosition(location);
        sp5->setPosition(location);
        sp6->setPosition(location);
        sp7->setPosition(location);
        sp8->setPosition(location);
        sp9->setPosition(location);
    }
}

//抬起事件的响应
void RenderTextureZbuffer::ccTouchesEnded(CCSet* touches, CCEvent* event)
{
	//渲染屏幕快照
    this->renderScreenShot();
}
//渲染屏幕快照
void RenderTextureZbuffer::renderScreenShot()
{
	//创建一个512,512大小的渲染目标纹理。
    CCRenderTexture *texture = CCRenderTexture::create(512, 512);
	//如果无效直接返回。
    if (NULL == texture)
    {
        return;
    } 
	//设置锚点为左下角。
    texture->setAnchorPoint(ccp(0, 0));
	//开始渲染到目标纹理。
    texture->begin();
	//将当前层渲染一遍。
    this->visit();
	//结束渲染到目标纹理。
    texture->end();
	//创建一个精灵。
    CCSprite *sprite = CCSprite::createWithTexture(texture->getSprite()->getTexture());
	//设置精灵的位置,透明度,Y方向翻转。
    sprite->setPosition(ccp(256, 256));
    sprite->setOpacity(182);
    sprite->setFlipY(1);
	//将精灵放入到当前层的最前面。
    this->addChild(sprite, 999999);
	//设置为绿色。
    sprite->setColor(ccGREEN);
	//让精灵运行一个动画序列,表现为渐渐消失。
    sprite->runAction(CCSequence::create(CCFadeTo::create(2, 0),
                                          CCHide::create(),
                                          NULL));
}

第四个演示:测试模版缓冲目标纹理。

说明

关于模版缓冲可以先看一下博文:

http://www.cnblogs.com/aokman/archive/2010/12/13/1904723.html

本例是开启模版测试,然后绘制球精灵,但只写A通过模版测试,对通过的像素位置设置模版值为1,之后向右上移动1/4大小球的位置,对模版值为0的区域设置通过来绘制球精灵,这样的效果约会有1/4缺失。

    注意:不幸的是,经测试所有版本似乎都无效,我不知道是不是哪里存在BUG,还是我的错误。

截图

现实是这样的:

Cocos2d-x 2.0 渲染到纹理深入分析

按代码的做法我用Photoshop做了一个结果图,本应是这样:

Cocos2d-x 2.0 渲染到纹理深入分析

代码:

//由上面的基类派生出的类,。
class RenderTextureTestDepthStencil : public RenderTextureTest
{
public:
	//构造
    RenderTextureTestDepthStencil();
	//取得标题。
    virtual std::string title();
	//取得副标题。
    virtual std::string subtitle();
};

对应CPP:

//构造
RenderTextureTestDepthStencil::RenderTextureTestDepthStencil()
{
	//取得屏幕大小。
    CCSize s = CCDirector::sharedDirector()->getWinSize();
	//创建一个精灵,设置其位置,10倍放大。
    CCSprite *sprite = CCSprite::create("Images/fire.png");
    sprite->setPosition(ccp(s.width * 0.25f, 0));
    sprite->setScale(10);
	//创建一个目标纹理,用于存储深度缓冲。
    CCRenderTexture *rend = CCRenderTexture::create(s.width, s.height, kCCTexture2DPixelFormat_RGBA4444, CC_GL_DEPTH24_STENCIL8);
	//设置模版缓冲的掩码值。
    glStencilMask(0xFF);
	//清空目标纹理的深度缓冲和模板缓冲为0。
    rend->beginWithClear(0, 0, 0, 0, 0, 0);
    //开启模版测试。
    glEnable(GL_STENCIL_TEST);
	//设置绘制图像的像素部分总是可以通过模版测试
    glStencilFunc(GL_ALWAYS, 1, 0xFF);
	//设置更新操作为:
	//1,模板测试失败时保持当前的模板值不变。
	//2,模板测试通过,深度测试失败时保持当前的模板值不变。
	//3,模板测试通过,深度测试通过时将当前的模板值设置为参考值。
    glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE);
	//设置对颜色缓冲区的R,G,B,A位是否执行写入操作。这里只写A。
    glColorMask(0, 0, 0, 1);
	//将精灵渲染一遍。
    sprite->visit();
    //设置精灵位置向右上偏移一些
    sprite->setPosition(ccpAdd(sprite->getPosition(), ccpMult(ccp(sprite->getContentSize().width * sprite->getScale(), sprite->getContentSize().height * sprite->getScale()), 0.5)));
	//设置比较条件,如果像素参考值不等于模板值通过测试。
    glStencilFunc(GL_NOTEQUAL, 1, 0xFF);
	//设置对颜色缓冲区的R,G,B,A位执行写入操作。
    glColorMask(1, 1, 1, 1);
	//精灵再渲染一遍。
    sprite->visit();
	//结束渲染到目标纹理。
    rend->end();
	//关闭模版缓冲测试。
    glDisable(GL_STENCIL_TEST);
	//设置目标纹理的位置。
    rend->setPosition(ccp(s.width * 0.5f, s.height * 0.5f));
	//将目标纹理放到当前层下。
    this->addChild(rend);
}
//标题。
std::string RenderTextureTestDepthStencil::title()
{
    return "Testing depthStencil attachment";
}
//取得副标题。
std::string RenderTextureTestDepthStencil::subtitle()
{
    return "Circle should be missing 1/4 of its region";
}

 

    上面这个例子如果真存在问题,希望Cocos2d-x开发组能有看到并做下修改。最后代码余下的就是场景了:

//演示用的场景
class RenderTextureScene : public TestScene
{
public:
	//运行场景时的处理
    virtual void runThisTest();
};

对应CPP:

//运行演示场景。
void RenderTextureScene::runThisTest()
{
	//创建下一个演示层放入到当前的场景中。
    CCLayer* pLayer = nextTestCase();
    addChild(pLayer);
	//运行当前场景。
    CCDirector::sharedDirector()->replaceScene(this);
}

     渲染到纹理,就是这么个东西了。很有用的技术,希望各位好好掌握。周末愉快,下课!

分类: cocos2d-x 标签:

Cocos2d-x 2.0 拖尾效果深入分析

2013年2月22日 没有评论

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

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

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


      Cocos2d-x 2.0 拖尾效果深入分析

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

cocos2d-2.0-x-2.0.2@ Aug 30
2012

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

 

         今天我们来学习一下Cocos2d-x中的拖尾效果。在游戏中,拖尾效果常常用来做刀光,火球法术等一些运动物体的效果。如图:

                                       Cocos2d-x 2.0 拖尾效果深入分析

        在Cocos2d-x中,拖尾效果有一个专门的类CCMotionStreak来实现。下面我们打开CCMotionStreak.h来看一下:

#ifndef __CCMOTION_STREAK_H__
#define __CCMOTION_STREAK_H__

#include "CCProtocols.h"
#include "textures/CCTexture2D.h"
#include "ccTypes.h"
#include "base_nodes/CCNode.h"
//使用Cocos2d命名空间
NS_CC_BEGIN

//条带由三个基类共同派生
class CC_DLL CCMotionStreak : public CCNode, public CCTextureProtocol, public CCRGBAProtocol
{
public:
	//构造
    CCMotionStreak();
	//析构
    virtual ~CCMotionStreak();
    //静态函数,创建一个拖尾效果,内部调用create实现,参一为消隐动画时长,参二为拖尾条带相邻顶点间的最小距离,参三为拖尾条带的宽度,参四为顶点颜色值,参五为所使用的纹理图片。
    CC_DEPRECATED_ATTRIBUTE static CCMotionStreak* streakWithFade(float fade, float minSeg, float stroke, ccColor3B color, const char* path);
    //静态函数,创建一个拖尾效果,内部调用create实现,参一为消隐动画时长,参二为拖尾条带相邻顶点间的最小距离,参三为拖尾条带的宽度,参四为顶点颜色值,参五为所使用的纹理对象指针。
    CC_DEPRECATED_ATTRIBUTE static CCMotionStreak* streakWithFade(float fade, float minSeg, float stroke, ccColor3B color, CCTexture2D* texture);

    //上面第一个创建函数的create实现。
    static CCMotionStreak* create(float fade, float minSeg, float stroke, ccColor3B color, const char* path);
    //上面第二个创建函数的create实现。
    static CCMotionStreak* create(float fade, float minSeg, float stroke, ccColor3B color, CCTexture2D* texture);

    //初始化拖尾效果
    bool initWithFade(float fade, float minSeg, float stroke, ccColor3B color, const char* path);
    //初始化拖尾效果
    bool initWithFade(float fade, float minSeg, float stroke, ccColor3B color, CCTexture2D* texture);

    //
    void tintWithColor(ccColor3B colors);

    //重置,删除所有的条带段
    void reset();

    //设置位置
    virtual void setPosition(const CCPoint& position);
	//绘制
    virtual void draw();
	//更新
    virtual void update(float delta);

    //取得纹理。
    virtual CCTexture2D* getTexture(void);
	//设置纹理。
    virtual void setTexture(CCTexture2D *texture);
	//设置
    virtual void setBlendFunc(ccBlendFunc blendFunc);
    virtual ccBlendFunc getBlendFunc(void);
	//设置颜色
    virtual void setColor(const ccColor3B& color);
    virtual const ccColor3B& getColor(void);
	//设置透明度
    virtual GLubyte getOpacity(void);
    virtual void setOpacity(GLubyte opacity);
	//设置修改A同时影响RGB
    virtual void setOpacityModifyRGB(bool bValue);
    virtual bool isOpacityModifyRGB(void);

    //是否是快速模式
    inline bool isFastMode() { return m_bFastMode; }
    inline void setFastMode(bool bFastMode) { m_bFastMode = bFastMode; }
	//起点是否初始化
    inline bool isStartingPositionInitialized() { return m_bStartingPositionInitialized; }
    inline void setStartingPositionInitialized(bool bStartingPositionInitialized) 
    { 
        m_bStartingPositionInitialized = bStartingPositionInitialized; 
    }
protected:
	//是否是快速模式
    bool m_bFastMode;
	//起点是否初始化
    bool m_bStartingPositionInitialized;
private:
    //所用的纹理对象指针
    CCTexture2D* m_pTexture;
	//ALPHA混合方案
    ccBlendFunc m_tBlendFunc;
	//当前拖尾起点位置
    CCPoint m_tPositionR;
	//颜色值
    ccColor3B m_tColor;
	//拖尾线条的宽度,越大当前越粗
    float m_fStroke;
	//每秒条带渐隐的alpha值减少量
    float m_fFadeDelta;
	//拖尾中用于划分条带的顶点的最小距离。
    float m_fMinSeg;
	//顶点最大数量
    unsigned int m_uMaxPoints;
	//当前的顶点数量
    unsigned int m_uNuPoints;
	//上次的顶点数量
    unsigned int m_uPreviousNuPoints;

    //顶点位置数组
    CCPoint* m_pPointVertexes;
	//顶点的状态值数组,这个状态值取值为0~1.0间,代表了消隐程度,其实就是alpha值。
    float* m_pPointState;

    // OPENGL所用的顶点各类数据绘冲
	//位置
    ccVertex2F* m_pVertices;
	//颜色
    GLubyte* m_pColorPointer;
	//纹理UV
    ccTex2F* m_pTexCoords;
};

// end of misc_nodes group
/// @}

NS_CC_END

#endif //__CCMOTION_STREAK_H__

对应的CPP:

#include "CCMotionStreak.h"
#include "textures/CCTextureCache.h"
#include "shaders/ccGLStateCache.h"
#include "shaders/CCGLProgram.h"
#include "shaders/CCShaderCache.h"
#include "ccMacros.h"

#include "support/CCVertex.h"
#include "support/CCPointExtension.h"
//使用Cocos2d命名空间
NS_CC_BEGIN
//构造
CCMotionStreak::CCMotionStreak()
: m_bFastMode(false)
, m_pTexture(NULL)
, m_tPositionR(CCPointZero)
, m_tColor(ccc3(0,0,0))
, m_fStroke(0.0f)
, m_fFadeDelta(0.0f)
, m_fMinSeg(0.0f)
, m_uMaxPoints(0)
, m_uNuPoints(0)
, m_uPreviousNuPoints(0)
, m_pPointVertexes(NULL)
, m_pPointState(NULL)
, m_pVertices(NULL)
, m_pColorPointer(NULL)
, m_pTexCoords(NULL)
, m_bStartingPositionInitialized(false)
{
    m_tBlendFunc.src = GL_SRC_ALPHA;
    m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;
}
//析构
CCMotionStreak::~CCMotionStreak()
{
	//释放纹理对象和申请的内存缓冲区
    CC_SAFE_RELEASE(m_pTexture);
    CC_SAFE_FREE(m_pPointState);
    CC_SAFE_FREE(m_pPointVertexes);
    CC_SAFE_FREE(m_pVertices);
    CC_SAFE_FREE(m_pColorPointer);
    CC_SAFE_FREE(m_pTexCoords);
}
//静态函数,创建一个拖尾效果,内部调用create实现,参一为消隐动画时长,参二为拖尾条带相邻顶点间的最小距离,参三为拖尾条带的宽度,参四为顶点颜色值,参五为所使用的纹理图片。
CCMotionStreak* CCMotionStreak::streakWithFade(float fade, float minSeg, float stroke, ccColor3B color, const char* path)
{
    return CCMotionStreak::create(fade, minSeg, stroke, color, path);
}
//上面的create实现。 
CCMotionStreak* CCMotionStreak::create(float fade, float minSeg, float stroke, ccColor3B color, const char* path)
{
	//创建一个CCMotionStreak的实例对象并初始化,如果成功交由内存管理器进行引用计数器的管理。
    CCMotionStreak *pRet = new CCMotionStreak();
    if (pRet && pRet->initWithFade(fade, minSeg, stroke, color, path))
    {
        pRet->autorelease();
        return pRet;
    }
	//如果创建或初始化失败,删除实例对象指针并置空,返回NULL。
    CC_SAFE_DELETE(pRet);
    return NULL;
}
//静态函数,创建一个拖尾效果,内部调用create实现,参一为消隐动画时长,参二为拖尾条带相邻顶点间的最小距离,参三为拖尾条带的宽度,参四为顶点颜色值,参五为所使用的纹理对象。
CCMotionStreak* CCMotionStreak::streakWithFade(float fade, float minSeg, float stroke, ccColor3B color, CCTexture2D* texture)
{
    return CCMotionStreak::create(fade, minSeg, stroke, color, texture);
}
//上面创建函数的create实现。
CCMotionStreak* CCMotionStreak::create(float fade, float minSeg, float stroke, ccColor3B color, CCTexture2D* texture)
{
	//创建一个CCMotionStreak的实例对象并初始化,如果成功交由内存管理器进行引用计数器的管理。
    CCMotionStreak *pRet = new CCMotionStreak();
    if (pRet && pRet->initWithFade(fade, minSeg, stroke, color, texture))
    {
        pRet->autorelease();
        return pRet;
    }
	//如果创建或初始化失败,删除实例对象指针并置空,返回NULL。
    CC_SAFE_DELETE(pRet);
    return NULL;
}
//初始化条带
bool CCMotionStreak::initWithFade(float fade, float minSeg, float stroke, ccColor3B color, const char* path)
{
	//有效性判断。
    CCAssert(path != NULL, "Invalid filename");
	//加载图片生成纹理,使用纹理指针调用相应的初始化函数。
    CCTexture2D *texture = CCTextureCache::sharedTextureCache()->addImage(path);
    return initWithFade(fade, minSeg, stroke, color, texture);
}
//初始化条带
bool CCMotionStreak::initWithFade(float fade, float minSeg, float stroke, ccColor3B color, CCTexture2D* texture)
{
	//将结点位置为零零点。
    CCNode::setPosition(CCPointZero);
	//设置锚点位置为零零点。
    setAnchorPoint(CCPointZero);
	//
    ignoreAnchorPointForPosition(true);
	//
    m_bStartingPositionInitialized = false;
	//初始化起点为零零点。
    m_tPositionR = CCPointZero;
	//使用快速模式
    m_bFastMode = true;
	//如果minSeg值设-1,则成员变量m_fMinSeg取默认值为宽度/5.0f,否则按照参数取值。
    m_fMinSeg = (minSeg == -1.0f) ? stroke/5.0f : minSeg;
	//为什么要乘积呢?因为后面判断距离时提高效率,两点间距离公式不是要开平方嘛?这样做就省掉开平方了。
    m_fMinSeg *= m_fMinSeg;
	//保存拖尾效果条带的宽度。
    m_fStroke = stroke;
	//计算出每秒条带渐隐的alpha值减少量
    m_fFadeDelta = 1.0f/fade;
	//顶点最大数量这里是按消隐动画时长来计算的,每秒60个顶点,再加上首尾两个顶点。
    m_uMaxPoints = (int)(fade*60.0f)+2;
	//当前顶点数量为0。
    m_uNuPoints = 0;
	//创建相应顶点数量的状态值数组和顶点位置数组。
    m_pPointState = (float *)malloc(sizeof(float) * m_uMaxPoints);
    m_pPointVertexes = (CCPoint*)malloc(sizeof(CCPoint) * m_uMaxPoints);
	//创建相应顶点缓冲所用的内存。
    m_pVertices = (ccVertex2F*)malloc(sizeof(ccVertex2F) * m_uMaxPoints * 2);
    m_pTexCoords = (ccTex2F*)malloc(sizeof(ccTex2F) * m_uMaxPoints * 2);
    m_pColorPointer =  (GLubyte*)malloc(sizeof(GLubyte) * m_uMaxPoints * 2 * 4);

    // 设置ALPHA混合方案
    m_tBlendFunc.src = GL_SRC_ALPHA;
    m_tBlendFunc.dst = GL_ONE_MINUS_SRC_ALPHA;

    // 设置所用的Shader代码片段
setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor));

	//设置所用的纹理
    setTexture(texture);
	//设置所用的颜色
    setColor(color);
	//
    scheduleUpdate();

    return true;
}
//设置位置
void CCMotionStreak::setPosition(const CCPoint& position)
{
    m_bStartingPositionInitialized = true;
    m_tPositionR = position;
}
//设置颜色
void CCMotionStreak::tintWithColor(ccColor3B colors)
{
	//
    setColor(colors);

    // Fast assignation
    for(unsigned int i = 0; i<m_uNuPoints*2; i++) 
    {
        *((ccColor3B*) (m_pColorPointer+i*4)) = colors;
    }
}
//取得纹理
CCTexture2D* CCMotionStreak::getTexture(void)
{
    return m_pTexture;
}
//设置纹理
void CCMotionStreak::setTexture(CCTexture2D *texture)
{
    if (m_pTexture != texture)
    {
        CC_SAFE_RETAIN(texture);
        CC_SAFE_RELEASE(m_pTexture);
        m_pTexture = texture;
    }
}
//设置ALPHA混合方案
void CCMotionStreak::setBlendFunc(ccBlendFunc blendFunc)
{
    m_tBlendFunc = blendFunc;
}
//取得ALPHA混合方案。
ccBlendFunc CCMotionStreak::getBlendFunc(void)
{
    return m_tBlendFunc;
}
//设置颜色
void CCMotionStreak::setColor(const ccColor3B& color)
{
    m_tColor = color;
}
//取得颜色
const ccColor3B& CCMotionStreak::getColor(void)
{
    return m_tColor;
}
//设置透明度
void CCMotionStreak::setOpacity(GLubyte opacity)
{
    CCAssert(false, "Set opacity no supported");
}
//取得透明度
GLubyte CCMotionStreak::getOpacity(void)
{
    CCAssert(false, "Opacity no supported");
    return 0;
}
//设置修改透明度是否影响RGB值。
void CCMotionStreak::setOpacityModifyRGB(bool bValue)
{
    CC_UNUSED_PARAM(bValue);
}

bool CCMotionStreak::isOpacityModifyRGB(void)
{
    return false;
}
//更新处理
void CCMotionStreak::update(float delta)
{
	//如果未初始化直接返回。
    if (!m_bStartingPositionInitialized)
    {
        return;
    }
	//通过时间音隔x每秒消隐值,取得当前时间片内要消隐的值。
    delta *= m_fFadeDelta;
	//定义临时变量
    unsigned int newIdx, newIdx2, i, i2;
	//定义临时变量保存消隐的顶点个数。
    unsigned int mov = 0;

    // 遍历更新当前所有的顶点。
    for(i = 0; i<m_uNuPoints; i++)
    {
		//状态数组对应值减去消隐值。
        m_pPointState[i]-=delta;
		//如果小于0,即完全消隐,mov++。
        if(m_pPointState[i] <= 0)
            mov++;
        else
        {
			//这里是通过mov计算出当前顶点数组在更新后移除完全消隐的顶点之后的当前顶点实际位置保存到变量newIdx。
            newIdx = i-mov;
			//如果mov大于0,即有完全消隐的顶点。
            if(mov>0)
            {
                // 将当前顶点原来的状态值保存到数组新位置中,这是一个前移操作。
                m_pPointState[newIdx] = m_pPointState[i];

                //将当前顶点原来的位置值保存到数组新位置中。
                m_pPointVertexes[newIdx] = m_pPointVertexes[i];

                //将当前顶点原来的位置顶点缓冲值保存到顶点缓冲新位置中。
                i2 = i*2;
                newIdx2 = newIdx*2;
                m_pVertices[newIdx2] = m_pVertices[i2];
                m_pVertices[newIdx2+1] = m_pVertices[i2+1];

                //将当前顶点原来的颜色顶点缓冲值保存到顶点缓冲新位置中。这里要注意,因为是GLubyte格式,所以A,R,G,B分别占一个数组元素。
                i2 *= 4;
                newIdx2 *= 4;
                m_pColorPointer[newIdx2+0] = m_pColorPointer[i2+0];
                m_pColorPointer[newIdx2+1] = m_pColorPointer[i2+1];
                m_pColorPointer[newIdx2+2] = m_pColorPointer[i2+2];
                m_pColorPointer[newIdx2+4] = m_pColorPointer[i2+4];
                m_pColorPointer[newIdx2+5] = m_pColorPointer[i2+5];
                m_pColorPointer[newIdx2+6] = m_pColorPointer[i2+6];
            }else
                newIdx2 = newIdx*8;//如果mov等于0,则R,G,B值不用变,只修改A值即可。
			//将当前顶点原来的颜色顶点缓冲中Alpha值保存到顶点缓冲新位置Alpha值中。
            const GLubyte op = (GLubyte)(m_pPointState[newIdx] * 255.0f);
            m_pColorPointer[newIdx2+3] = op;
            m_pColorPointer[newIdx2+7] = op;
        }
    }
	//当前的顶点数量减去完全消隐的顶点数量。
    m_uNuPoints-=mov;

    // 定义临时变量判断是否需要增加顶点。
    bool appendNewPoint = true;
	//如果当前顶点数量大于最大顶点数量,不能再新增顶点了。
    if(m_uNuPoints >= m_uMaxPoints)
    {
        appendNewPoint = false;
    }
	//如果当前顶点数量大于0,需要做一些计算来判断是否增加新顶点。
    else if(m_uNuPoints>0)
    {
		//取当前拖尾起点位置顶点与后面一个顶点的距离的平方,看是否小于m_fMinSeg。
        bool a1 = ccpDistanceSQ(m_pPointVertexes[m_uNuPoints-1], m_tPositionR) < m_fMinSeg;
		//取当前拖尾起点位置顶点与后面第二个顶点的距离的平方,看是否小于m_fMinSeg*2。
        bool a2 = (m_uNuPoints == 1) ? false : (ccpDistanceSQ(m_pPointVertexes[m_uNuPoints-2], m_tPositionR) < (m_fMinSeg * 2.0f));
		//如果在距离范围内,就不再增加顶点,这一段代码是为了避免拖尾间顶点过密过多,约定只有相邻顶点间距离达到一定范围以外才增加新的顶点。
        if(a1 || a2)
        {
            appendNewPoint = false;
        }
    }
	//如果需要增加新的顶点。
    if(appendNewPoint)
    {
		//将当前起点位置保存到相应数组元素中。
        m_pPointVertexes[m_uNuPoints] = m_tPositionR;
		//将相应状态数组元素值初始化为1.0。
        m_pPointState[m_uNuPoints] = 1.0f;

        // 颜色赋值
        const unsigned int offset = m_uNuPoints*8;
        *((ccColor3B*)(m_pColorPointer + offset)) = m_tColor;
        *((ccColor3B*)(m_pColorPointer + offset+4)) = m_tColor;

        // 透明度赋值
        m_pColorPointer[offset+3] = 255;
        m_pColorPointer[offset+7] = 255;

        //生成相应的四边形条带
        if(m_uNuPoints > 0 && m_bFastMode )
        {
            if(m_uNuPoints > 1)
            {//如果当前顶点数量大于1,则由上一个顶点到当前顶点位置生成相四边形存入到m_pVertices。
                ccVertexLineToPolygon(m_pPointVertexes, m_fStroke, m_pVertices, m_uNuPoints, 1);
            }
            else
            {//否则由起始位置到当前顶点位置生成相四边形存入到m_pVertices。
                ccVertexLineToPolygon(m_pPointVertexes, m_fStroke, m_pVertices, 0, 2);
            }
        }
		//顶点数量加一。
        m_uNuPoints ++;
    }
	//如果不是快速模式,则从起始位置到当前顶点位置生成相应四边形条带存入到m_pVertices。可见,快速模式比非快速模式要生成更少的次数。
    if( ! m_bFastMode )
    {
        ccVertexLineToPolygon(m_pPointVertexes, m_fStroke, m_pVertices, 0, m_uNuPoints);
    }

    // 更新纹理坐标
    if( m_uNuPoints  && m_uPreviousNuPoints != m_uNuPoints ) {
        float texDelta = 1.0f / m_uNuPoints;
        for( i=0; i < m_uNuPoints; i++ ) {
			//左右两边各一个顶点,故纹理U值分别为¬0,1
            m_pTexCoords[i*2] = tex2(0, texDelta*i);
            m_pTexCoords[i*2+1] = tex2(1, texDelta*i);
        }
		//记录当前粒子数量。
        m_uPreviousNuPoints = m_uNuPoints;
    }
}
//重置
void CCMotionStreak::reset()
{
    m_uNuPoints = 0;
}
//绘制
void CCMotionStreak::draw()
{
	//如果粒子数量<=1,不能形成条带,就不需要显示。
    if(m_uNuPoints <= 1)
        return;
	//设置使用shader,并设置相关矩阵。
    CC_NODE_DRAW_SETUP();
	//设置使用的顶点格式和ALPHA混合方案。
    ccGLEnableVertexAttribs(kCCVertexAttribFlag_PosColorTex );
    ccGLBlendFunc( m_tBlendFunc.src, m_tBlendFunc.dst );
	//设置使用的纹理。
    ccGLBindTexture2D( m_pTexture->getName() );
	//设置顶点缓冲区。
    glVertexAttribPointer(kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, 0, m_pVertices);
    glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, m_pTexCoords);
    glVertexAttribPointer(kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, 0, m_pColorPointer);
	//渲染三角形条带
    glDrawArrays(GL_TRIANGLE_STRIP, 0, (GLsizei)m_uNuPoints*2);
	//更新渲染调用次数统计
    CC_INCREMENT_GL_DRAWS(1);
}

NS_CC_END

拖尾效果类的实现原理了解了,现在我们看一下Cocos2d-x中的相关演示。

MotionStreakTest.h:

#ifndef _MOTION_STREAK_TEST_H_
#define _MOTION_STREAK_TEST_H_

#include "../testBasic.h"
//使用Cocos2d命名空间
//USING_NS_CC;
//用于演示的基类。
class MotionStreakTest : public CCLayer
{
public:
	//构造
    MotionStreakTest(void);
	//析构
    ~MotionStreakTest(void);
	//取得标题。
    virtual std::string title();
	//取得副标题。
    virtual std::string subtitle();
	//当前层被加载时的处理。
    virtual void onEnter();
	//响应菜单按钮项重启当前演示。
    void restartCallback(CCObject* pSender);
	//响应菜单按钮项进行下一个演示。
    void nextCallback(CCObject* pSender);
	//响应菜单按钮项进行上一个演示。
    void backCallback(CCObject* pSender);
	//响应菜单文字项进行模式切换。
    void modeCallback(CCObject* pSender);
protected:
	//拖尾对象指针。
    CCMotionStreak *streak;
};

对应CPP:

//枚举所用到的精灵的TAG值
enum {
	kTagLabel = 1,
	kTagSprite1 = 2,
	kTagSprite2 = 3,
};

//下一个效果演示
CCLayer* nextMotionAction();
//上一个效果演示
CCLayer* backMotionAction();
//重新演示当前的效果
CCLayer* restartMotionAction();
//场景索引
static int sceneIdx = -1; 
//最大层数
#define MAX_LAYER    3
//根据效果创建不同的拖尾效果
CCLayer* createMotionLayer(int nIndex)
{
    switch(nIndex)
    {
        case 0: return new MotionStreakTest1();
        case 1: return new MotionStreakTest2();
        case 2: return new Issue1358();
    }

    return NULL;
}
//下一个效果
CCLayer* nextMotionAction()
{
    sceneIdx++;
    sceneIdx = sceneIdx % MAX_LAYER;

    CCLayer* pLayer = createMotionLayer(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}
//上一个效果演示
CCLayer* backMotionAction()
{
    sceneIdx--;
    int total = MAX_LAYER;
    if( sceneIdx < 0 )
        sceneIdx += total;    
    
    CCLayer* pLayer = createMotionLayer(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}
//重新演示当前的效果
CCLayer* restartMotionAction()
{
    CCLayer* pLayer = createMotionLayer(sceneIdx);
    pLayer->autorelease();

    return pLayer;
} 

//构造
MotionStreakTest::MotionStreakTest(void)
{
}
//析构
MotionStreakTest::~MotionStreakTest(void)
{
}

//标题
std::string MotionStreakTest::title()
{
    return "No title";
}

//副标题。
std::string MotionStreakTest::subtitle()
{
    return "";
}

//当前层加载时的处理
void MotionStreakTest::onEnter()
{
	//调用基类的相应处理。
    CCLayer::onEnter();
	//获取屏幕大小
    CCSize s = CCDirector::sharedDirector()->getWinSize();
	//创建标题文字标签
    CCLabelTTF* label = CCLabelTTF::create(title().c_str(), "Arial", 32);
	//将文字标签加入当前层中
    addChild(label, 0, kTagLabel);
	//设置文字标签的位置 
    label->setPosition(CCPointMake(s.width/2, s.height-50));
    //取得副标题
    string subTitle = this->subtitle();
	//创建副标题文字标签
    if (subTitle.size() > 0)
    {
        CCLabelTTF *l = CCLabelTTF::create(subTitle.c_str(), "Thonburi", 16);
        addChild(l, 1);
        l->setPosition(ccp(s.width/2, s.height-80));
    }
	//创建菜单项
    CCMenuItemImage *item1 = CCMenuItemImage::create(s_pPathB1, s_pPathB2, this, menu_selector(MotionStreakTest::backCallback) );
    CCMenuItemImage *item2 = CCMenuItemImage::create(s_pPathR1, s_pPathR2, this, menu_selector(MotionStreakTest::restartCallback) );
    CCMenuItemImage *item3 = CCMenuItemImage::create(s_pPathF1, s_pPathF2, this, menu_selector(MotionStreakTest::nextCallback) );
	//创建菜单
    CCMenu *menu = CCMenu::create(item1, item2, item3, NULL);
	//设置菜单与各菜单项的位置
    menu->setPosition(CCPointZero);
    item1->setPosition(CCPointMake(s.width/2 - item2->getContentSize().width*2, item2->getContentSize().height/2));
    item2->setPosition(CCPointMake(s.width/2, item2->getContentSize().height/2));
    item3->setPosition(CCPointMake(s.width/2 + item2->getContentSize().width*2, item2->getContentSize().height/2));
	//将菜单加入当前层
    addChild(menu, 1);    
	//创建一个菜单切换文字项,其实就是有复选框效果的文字标签。这里创建的切换项为“质量优先模式”和“效率优先模式”,响应函数为modeCallback。
    CCMenuItemToggle *itemMode = CCMenuItemToggle::createWithTarget(this, menu_selector(MotionStreakTest::modeCallback),
        CCMenuItemFont::create("Use High Quality Mode"),
        CCMenuItemFont::create("Use Fast Mode"),
        NULL);
	//创建第二个菜单并加入当前层
    CCMenu *menuMode = CCMenu::create(itemMode, NULL);
    addChild(menuMode);
	//设置菜单位置
    menuMode->setPosition(ccp(s.width/2, s.height/4));
}
//切换文字项的响应函数。
void MotionStreakTest::modeCallback(CCObject *pSender)
{
	//来回切换模式
    bool fastMode = streak->isFastMode();
    streak->setFastMode(! fastMode);
}
//重新演示当前效果。
void MotionStreakTest::restartCallback(CCObject* pSender)
{
	//创建一个演示场景。
    CCScene* s = new MotionStreakTestScene();//CCScene::create();
    s->addChild(restartMotionAction()); 
	//使用当前场景。
    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
}

//下一个效果的演示
void MotionStreakTest::nextCallback(CCObject* pSender)
{
	//创建一个演示场景。
    CCScene* s = new MotionStreakTestScene();//CCScene::create();
	//将下一个演示层加入场景,并运行这个场景
    s->addChild( nextMotionAction() );
    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
}

//上一个效果的演示
void MotionStreakTest::backCallback(CCObject* pSender)
{	//创建一个演示场景。
    CCScene* s = new MotionStreakTestScene;//CCScene::create();
	//将上一个演示层加入场景,并运行这个场景
    s->addChild( backMotionAction() );
    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
} 

然后是派生的第一个拖尾效果演示类:

class MotionStreakTest1 : public MotionStreakTest
{
protected:
	//根结点
    CCNode*        m_root;
	//带拖尾的目标结点
    CCNode*        m_target;

public:
	//加载当前层时的处理
    virtual void onEnter();
	//更新函数
    void onUpdate(float delta);
	//取得标题
    virtual std::string title();
};

对应CPP:

//加载当前层时的处理
void MotionStreakTest1::onEnter()
{
	//调用基类的相应函数。
    MotionStreakTest::onEnter();
	//取和屏幕大小
    CCSize s = CCDirector::sharedDirector()->getWinSize();
  
    // 创建根精灵结点,放入当前层中的屏幕中心位置。
    m_root = CCSprite::create(s_pPathR1);
    addChild(m_root, 1);
    m_root->setPosition(ccp(s.width/2, s.height/2));
  
    //创建目标精灵结点,放入根结点下的右边1/4屏幕宽度位置。
    m_target = CCSprite::create(s_pPathR1);
    m_root->addChild(m_target);
    m_target->setPosition(ccp(s.width/4, 0));

    // 创建拖尾效果并放入到当前层下。
    streak = CCMotionStreak::create(2, 3, 32, ccGREEN, s_streak);
    addChild(streak);
    //每帧调用onUpdate函数。
    schedule(schedule_selector(MotionStreakTest1::onUpdate));
  	//创建一个旋转动画,2秒内自转360度。
    CCActionInterval* a1 = CCRotateBy::create(2, 360);
	//创建一个al1的无限循环动画。
    CCAction* action1 = CCRepeatForever::create(a1);
	//创建一个平移动画,2秒内向右移动100像素。
    CCActionInterval* motion = CCMoveBy::create(2, CCPointMake(100,0) );
//根结点运行一个无限循环的动画序列,动画序列为平移动画及其反向动画。 	m_root->runAction( CCRepeatForever::create((CCActionInterval*)(CCSequence::create(motion, motion->reverse(), NULL)) ) );
//同时也运行无限循环的自转动画。
    m_root->runAction( action1 );
	//创建一个无限循环的动画序列,动画序列为7个变色动画,哪个结点使用它就会不断的变色。
    CCActionInterval *colorAction = CCRepeatForever::create((CCActionInterval *)CCSequence::create(
        CCTintTo::create(0.2f, 255, 0, 0),
        CCTintTo::create(0.2f, 0, 255, 0),
        CCTintTo::create(0.2f, 0, 0, 255),
        CCTintTo::create(0.2f, 0, 255, 255),
        CCTintTo::create(0.2f, 255, 255, 0),
        CCTintTo::create(0.2f, 255, 0, 255),
        CCTintTo::create(0.2f, 255, 255, 255),
        NULL));
	//让拖尾运行这个变色动画序列。
    streak->runAction(colorAction);
}
//实时更新函数。
void MotionStreakTest1::onUpdate(float delta)
{
	//更新拖尾的位置,设置为目标精灵结点的位置。
    streak->setPosition( m_target->convertToWorldSpace(CCPointZero) );
}
//取得标题。
std::string MotionStreakTest1::title()
{
    return "MotionStreak test 1";
}

演示如图:

Cocos2d-x 2.0 拖尾效果深入分析


派生的第二个拖尾效果演示类:

class MotionStreakTest2 : public MotionStreakTest
{
	//根结点
    CCNode*        m_root;
	//带拖尾的目标结点
    CCNode*        m_target;
public:
	//加载当前层时的处理
    virtual void onEnter();
	//触屏并移动时响应处理
    void ccTouchesMoved(CCSet* touches, CCEvent* event);
	//取得标题
    virtual std::string title();
};

 
对应的CPP:

//加载当前层时的处理
void MotionStreakTest2::onEnter()
{
	//调用基类的相应函数。
    MotionStreakTest::onEnter();
	//这里设置打开触屏响应
    setTouchEnabled(true);
	//取得屏幕大小
    CCSize s = CCDirector::sharedDirector()->getWinSize();
        
    // 创建拖尾效果并放入到当前层下。
    streak = CCMotionStreak::create(3, 3, 64, ccWHITE, s_streak );
    addChild(streak);
    //设置拖尾的初始位置为屏幕中小。
    streak->setPosition( CCPointMake(s.width/2, s.height/2) ); 
}
//触屏并移动时响应处理
void MotionStreakTest2::ccTouchesMoved(CCSet* touches, CCEvent* event)
{
	//取得触点位置。
    CCSetIterator it = touches->begin();
    CCTouch* touch = (CCTouch*)(*it);

    CCPoint touchLocation = touch->getLocation();    
    //设置为拖层的位置。
    streak->setPosition( touchLocation );
}
//取得标题
std::string MotionStreakTest2::title()
{
    return "MotionStreak test";
}

效果如图:

Cocos2d-x 2.0 拖尾效果深入分析


派生的第三个拖尾效果演示类:

class Issue1358 : public MotionStreakTest
{
public:
	//取得标题
    virtual std::string title();
	//取得副标题
    virtual std::string subtitle();
	//加载当前层时的处理
    virtual void onEnter();
	//更新函数
    virtual void update(float dt);
private:
	//中心位置
    CCPoint m_center;
	//半径
    float m_fRadius;
	//角度
    float m_fAngle;
};

对应CPP:

//加载当前层时的处理
void Issue1358::onEnter()
{
	//调用基类的相应函数。
    MotionStreakTest::onEnter();
    
    //取得屏幕位置
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    // 创建拖尾效果并放入到当前层下。
    streak = CCMotionStreak::create(2.0f, 1.0f, 50.0f, ccc3(255, 255, 0), "Images/Icon.png");
    addChild(streak);
	//初始化中心位置,半径和角度。
    m_center  = ccp(size.width/2, size.height/2);
    m_fRadius = size.width/3;
    m_fAngle = 0.0f;
	//设置每帧的回调更新函数。
    schedule(schedule_selector(Issue1358::update), 0);
}
//每帧的回调更新函数。
void Issue1358::update(float dt)
{
	//角度增加1.0f
    m_fAngle += 1.0f;
	//计算拖尾的位置
    streak->setPosition(ccp(m_center.x + cosf(m_fAngle/180 * M_PI)*m_fRadius,
                            m_center.y + sinf(m_fAngle/ 180 * M_PI)*m_fRadius));
}
//取得标题。
std::string Issue1358::title()
{
    return "Issue 1358";
}
//取得副标题。
std::string Issue1358::subtitle()
{
    return "The tail should use the texture";
}


效果如图:

Cocos2d-x 2.0 拖尾效果深入分析

然后是演示用的场景:

class MotionStreakTestScene : public TestScene
{
public:
	//运行场景的处理
    virtual void runThisTest();
};


//运行场景的处理
void MotionStreakTestScene::runThisTest()
{
	//创建下一个要演示的效果并放入当前场景中。
    CCLayer* pLayer = nextMotionAction();
    addChild(pLayer);
	//使用当前场景。
    CCDirector::sharedDirector()->replaceScene(this);
}

      

总结一下:

    拖尾效果的原理就是在相应距离内动态生成条带,然后逐渐消隐,在Cocos2d-x中可以指定这个最小距离,以及消隐的速度,还有条带的粗细,相应的纹理对象,都是通过CCMotionStreak 类来实现的。

     又一篇结束了,看看时间,一点半了,为了今年的目标,我只能坚持下去。工作,写作,工具箱,一个都不能少!努力吧,骚年!

分类: cocos2d, cocos2d-x 标签:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

2013年2月14日 没有评论

[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中粒子系统的的基类,了解了粒子系统的构成与原理,本节我们来看一下在Cocos2d-x中如何具体展示粒子系统。

         我们打开libcocos2d工程下的particle_nodes目录,可以看到:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

这个目录是粒子系统相关的一些源码的列表,很明显,我们还有一个CCParticleExample.h/cpp没有学习。那么,这一节,我们将会从这两个文件开始。

         CCParticleSystem.h:

#ifndef __CCPARTICLE_EXAMPLE_H__
#define __CCPARTICLE_EXAMPLE_H__
//包含粒子系统基类头文件
#include "CCParticleSystemQuad.h"
//使用Cocos2d命名空间
NS_CC_BEGIN


//由粒子系统基类派生出CCParticleFire,表现的是火焰
class CC_DLL CCParticleFire : public CCParticleSystemQuad
{
public:
	//构造
    CCParticleFire(){}
	//析构
    virtual ~CCParticleFire(){}
	//初始化,创建250个粒子。
    bool init(){ return initWithTotalParticles(250); }
	//创建相应数量的粒子。
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
	//创建一个当前实例对象,内部调用create实现。
    static CCParticleFire * node()
    {
        return create();
    }
	//上面的create实现。
    static CCParticleFire * create()
    {
		//创建一个新的实例,初始化后交由内存管理器进行管理,返回实例指针。
        CCParticleFire *pRet = new CCParticleFire();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
		//如果失败释放置空并返回NULL。
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};

对应的CPP实现:

//创建相应数量的粒子。
bool CCParticleFire::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的初始化函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        this->m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度的值。
        this->modeA.gravity = ccp(0,0);

        // 设置半径变化值及其用于随机初始化的范围值。
        this->modeA.radialAccel = 0;
        this->modeA.radialAccelVar = 0;

        // 设置速度及其用于随机初始化的范围值。
        this->modeA.speed = 60;
        this->modeA.speedVar = 20;        

        // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 10;

        // 设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, 60));
        this->m_tPosVar = ccp(40, 20);

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 3;
        m_fLifeVar = 0.25f;

        //起始大小及其用于随机初始化的范围值。终止大小为枚举值,在粒子系统基类的update里有处理,即大小在更新时不变化。
        m_fStartSize = 54.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

        // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.76f;
        m_tStartColor.g = 0.25f;
        m_tStartColor.b = 0.12f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.0f;
        m_tStartColorVar.g = 0.0f;
        m_tStartColorVar.b = 0.0f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.0f;
        m_tEndColor.g = 0.0f;
        m_tEndColor.b = 0.0f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

        // 设置为加亮模式。
        this->setBlendAdditive(true);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleFire,表现的是焰火
class CC_DLL CCParticleFireworks : public CCParticleSystemQuad
{
public:
	//构造
    CCParticleFireworks(){}
	//析构
    virtual ~CCParticleFireworks(){}
	//初始化,创建1500个粒子。
    bool init(){ return initWithTotalParticles(1500); }
	//创建相应数量的粒子。
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
	//创建一个当前实例对象,内部调用create实现。
    static CCParticleFireworks * node()
    {
        return create();
    }
	//上面的create实现。
    static CCParticleFireworks * create()
    {
		//创建一个新的实例,初始化后交由内存管理器进行管理,返回实例指针。
        CCParticleFireworks *pRet = new CCParticleFireworks();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
		//如果失败释放置空并返回NULL。
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP实现:
//创建相应数量的粒子。
bool CCParticleFireworks::initWithTotalParticles(unsigned int numberOfParticles)
{
    //先调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        this->m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度值。
        this->modeA.gravity = ccp(0,-90);

        // 设置半径变化值及其用于随机初始化的范围值。
        this->modeA.radialAccel = 0;
        this->modeA.radialAccelVar = 0;

         // 设置速度及其用于随机初始化的范围值。
        this->modeA.speed = 180;
        this->modeA.speedVar = 50;

        //设置发射器的位置。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));

         //设置起始角度及其用于随机初始化的范围值。
        this->m_fAngle= 90;
        this->m_fAngleVar = 20;

        // 粒子的生命值及其用于随机初始化的范围值。
        this->m_fLife = 3.5f;
        this->m_fLifeVar = 1;

        // 发射器的发射速率。
        this->m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.5f;
        m_tStartColor.g = 0.5f;
        m_tStartColor.b = 0.5f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.5f;
        m_tStartColorVar.g = 0.5f;
        m_tStartColorVar.b = 0.5f;
        m_tStartColorVar.a = 0.1f;
        m_tEndColor.r = 0.1f;
        m_tEndColor.g = 0.1f;
        m_tEndColor.b = 0.1f;
        m_tEndColor.a = 0.2f;
        m_tEndColorVar.r = 0.1f;
        m_tEndColorVar.g = 0.1f;
        m_tEndColorVar.b = 0.1f;
        m_tEndColorVar.a = 0.2f;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 8.0f;
        m_fStartSizeVar = 2.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

        //不使用加亮模式。
        this->setBlendAdditive(false);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleSun,表现的是太阳
class CC_DLL CCParticleSun : public CCParticleSystemQuad
{
public:
	//构造
    CCParticleSun(){}
	//析构
    virtual ~CCParticleSun(){}
	//初始化,创建350个粒子。
    bool init(){ return initWithTotalParticles(350); }
	//创建相应数量的粒子。
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
	//创建一个当前实例,内部调用create实现。
    static CCParticleSun * node()
    {
        return create();
    }
	//上面的create实现。
    static CCParticleSun * create()
    {
        CCParticleSun *pRet = new CCParticleSun();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应的CPP实现:
//创建相应数量的粒子。
bool CCParticleSun::initWithTotalParticles(unsigned int numberOfParticles)
{
    //先调用基类的相应函数。if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置使用加亮模式。
        this->setBlendAdditive(true);

        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度值。
        modeA.gravity = ccp(0,0);

        // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 0;

        // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 20;
        modeA.speedVar = 5;


        //设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 360;

       //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));
        m_tPosVar = CCPointZero;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 1;
        m_fLifeVar = 0.5f;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 30.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

        // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.76f;
        m_tStartColor.g = 0.25f;
        m_tStartColor.b = 0.12f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.0f;
        m_tStartColorVar.g = 0.0f;
        m_tStartColorVar.b = 0.0f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.0f;
        m_tEndColor.g = 0.0f;
        m_tEndColor.b = 0.0f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleGalaxy,表现的是银河或一大堆星星。
class CC_DLL CCParticleGalaxy : public CCParticleSystemQuad
{
public:
	//构造与析构
    CCParticleGalaxy(){}
virtual ~CCParticleGalaxy(){}
//初始化,创建200个粒子。
bool init(){ return initWithTotalParticles(200); }
//创建相应数量的例子。
virtual bool initWithTotalParticles(unsigned int numberOfParticles);
//创建当前类的实例,内部调用create实现。
    static CCParticleGalaxy * node()
    {
        return create();
    }
	//上面的create实现。
    static CCParticleGalaxy * create()
    {
        CCParticleGalaxy *pRet = new CCParticleGalaxy();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
其对应的CPP:
bool CCParticleGalaxy::initWithTotalParticles(unsigned int numberOfParticles)
{
//调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度值。
        modeA.gravity = ccp(0,0);

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 60;
        modeA.speedVar = 10;

        // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = -80;
        modeA.radialAccelVar = 0;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 80;
        modeA.tangentialAccelVar = 0;

        //设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 360;

       //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));
        m_tPosVar = CCPointZero;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 4;
        m_fLifeVar = 1;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 37.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

        //发射器的发射速率
        m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.12f;
        m_tStartColor.g = 0.25f;
        m_tStartColor.b = 0.76f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.0f;
        m_tStartColorVar.g = 0.0f;
        m_tStartColorVar.b = 0.0f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.0f;
        m_tEndColor.g = 0.0f;
        m_tEndColor.b = 0.0f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

        // 设置为加亮模式。
        this->setBlendAdditive(true);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleFlower,表现的是花瓣洒落。
class CC_DLL CCParticleFlower : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleFlower(){}
    virtual ~CCParticleFlower(){}
    bool init(){ return initWithTotalParticles(250); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleFlower * node()
    {
        return create();
    }

    static CCParticleFlower * create()
    {
        CCParticleFlower *pRet = new CCParticleFlower();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应的CPP:
bool CCParticleFlower::initWithTotalParticles(unsigned int numberOfParticles)
{
    //先调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度的值。
        modeA.gravity = ccp(0,0);

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 80;
        modeA.speedVar = 10;

         // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = -60;
        modeA.radialAccelVar = 0;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 15;
        modeA.tangentialAccelVar = 0;

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 360;

        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));
        m_tPosVar = CCPointZero;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 4;
        m_fLifeVar = 1;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 30.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.50f;
        m_tStartColor.g = 0.50f;
        m_tStartColor.b = 0.50f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.5f;
        m_tStartColorVar.g = 0.5f;
        m_tStartColorVar.b = 0.5f;
        m_tStartColorVar.a = 0.5f;
        m_tEndColor.r = 0.0f;
        m_tEndColor.g = 0.0f;
        m_tEndColor.b = 0.0f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

         // 设置为加亮模式。
        this->setBlendAdditive(true);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleMeteor,表现的是流星
class CC_DLL CCParticleMeteor : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleMeteor(){}
    virtual ~CCParticleMeteor(){}
    bool init(){ return initWithTotalParticles(150); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleMeteor * node()
    {
        return create();
    }
    static CCParticleMeteor * create()
    {
        CCParticleMeteor *pRet = new CCParticleMeteor();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP:
bool CCParticleMeteor::initWithTotalParticles(unsigned int numberOfParticles)
{
    if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

         // 设置重力加速度的值。

        modeA.gravity = ccp(-200,200);

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 15;
        modeA.speedVar = 5;

         // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 0;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 0;
        modeA.tangentialAccelVar = 0;

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 360;

        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));
        m_tPosVar = CCPointZero;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 2;
        m_fLifeVar = 1;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 60.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.2f;
        m_tStartColor.g = 0.4f;
        m_tStartColor.b = 0.7f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.0f;
        m_tStartColorVar.g = 0.0f;
        m_tStartColorVar.b = 0.2f;
        m_tStartColorVar.a = 0.1f;
        m_tEndColor.r = 0.0f;
        m_tEndColor.g = 0.0f;
        m_tEndColor.b = 0.0f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

         // 设置为加亮模式。
        this->setBlendAdditive(true);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleSpiral,表现的是螺旋线
class CC_DLL CCParticleSpiral : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleSpiral(){}
    virtual ~CCParticleSpiral(){}
    bool init(){ return initWithTotalParticles(500); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleSpiral * node()
    {
        return create();
    }
    static CCParticleSpiral * create()
    {
        CCParticleSpiral *pRet = new CCParticleSpiral();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP:
//创建相应数量的粒子。
bool CCParticleSpiral::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) ) 
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

         // 设置重力加速度的值。

        modeA.gravity = ccp(0,0);

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 150;
        modeA.speedVar = 0;

        // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = -380;
        modeA.radialAccelVar = 0;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 45;
        modeA.tangentialAccelVar = 0;

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 0;

        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));
        m_tPosVar = CCPointZero;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 12;
        m_fLifeVar = 0;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 20.0f;
        m_fStartSizeVar = 0.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fLife;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.5f;
        m_tStartColor.g = 0.5f;
        m_tStartColor.b = 0.5f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.5f;
        m_tStartColorVar.g = 0.5f;
        m_tStartColorVar.b = 0.5f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.5f;
        m_tEndColor.g = 0.5f;
        m_tEndColor.b = 0.5f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.5f;
        m_tEndColorVar.g = 0.5f;
        m_tEndColorVar.b = 0.5f;
        m_tEndColorVar.a = 0.0f;

         // 设置不使用加亮模式。
        this->setBlendAdditive(false);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleExplosion,表现的是爆炸
class CC_DLL CCParticleExplosion : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleExplosion(){}
    virtual ~CCParticleExplosion(){}
    bool init(){ return initWithTotalParticles(700); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleExplosion * node()
    {
        return create();
    }
    static CCParticleExplosion * create()
    {
        CCParticleExplosion *pRet = new CCParticleExplosion();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP:
//创建相应数量的粒子。
bool CCParticleExplosion::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) ) 
    {
        // 设置为一直不断的发射。
        m_fDuration = 0.1f;

		// 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

         // 设置重力加速度的值。

        modeA.gravity = ccp(0,0);

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 70;
        modeA.speedVar = 40;

        // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 0;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 0;
        modeA.tangentialAccelVar = 0;

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 360;

        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height/2));
        m_tPosVar = CCPointZero;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 5.0f;
        m_fLifeVar = 2;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 15.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fDuration;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.7f;
        m_tStartColor.g = 0.1f;
        m_tStartColor.b = 0.2f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.5f;
        m_tStartColorVar.g = 0.5f;
        m_tStartColorVar.b = 0.5f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.5f;
        m_tEndColor.g = 0.5f;
        m_tEndColor.b = 0.5f;
        m_tEndColor.a = 0.0f;
        m_tEndColorVar.r = 0.5f;
        m_tEndColorVar.g = 0.5f;
        m_tEndColorVar.b = 0.5f;
        m_tEndColorVar.a = 0.0f;

         // 设置不使用加亮模式。
        this->setBlendAdditive(false);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleSmoke,表现的是烟雾
class CC_DLL CCParticleSmoke : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleSmoke(){}
    virtual ~CCParticleSmoke(){}
    bool init(){ return initWithTotalParticles(200); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleSmoke * node()
    {
        return create();
    }
    static CCParticleSmoke * create()
    {
        CCParticleSmoke *pRet = new CCParticleSmoke();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP:
//创建相应数量的粒子。
bool CCParticleSmoke::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度的值。
        modeA.gravity = ccp(0,0);

        // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 0;

         // 设置速度及其用于随机初始化的范围值。

        modeA.speed = 25;
        modeA.speedVar = 10;

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = 90;
        m_fAngleVar = 5;

        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, 0));
        m_tPosVar = ccp(20, 0);

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 4;
        m_fLifeVar = 1;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 60.0f;
        m_fStartSizeVar = 10.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = m_uTotalParticles/m_fLife;

       // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.8f;
        m_tStartColor.g = 0.8f;
        m_tStartColor.b = 0.8f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.02f;
        m_tStartColorVar.g = 0.02f;
        m_tStartColorVar.b = 0.02f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.0f;
        m_tEndColor.g = 0.0f;
        m_tEndColor.b = 0.0f;
        m_tEndColor.a = 1.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

         // 设置不使用加亮模式。
        this->setBlendAdditive(false);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleSnow,表现的是雪
class CC_DLL CCParticleSnow : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleSnow(){}
    virtual ~CCParticleSnow(){}
    bool init(){ return initWithTotalParticles(700); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleSnow * node()
    {
        return create();
    }

    static CCParticleSnow * create()
    {
        CCParticleSnow *pRet = new CCParticleSnow();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP:
//创建相应数量的粒子。
bool CCParticleSnow::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) ) 
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;

        // 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度的值。

        modeA.gravity = ccp(0,-1);

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 5;
        modeA.speedVar = 1;

         // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 1;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 0;
        modeA.tangentialAccelVar = 1;

        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height + 10));
        m_tPosVar = ccp( winSize.width/2, 0 );

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = -90;
        m_fAngleVar = 5;

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 45;
        m_fLifeVar = 15;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 10.0f;
        m_fStartSizeVar = 5.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = 10;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 1.0f;
        m_tStartColor.g = 1.0f;
        m_tStartColor.b = 1.0f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.0f;
        m_tStartColorVar.g = 0.0f;
        m_tStartColorVar.b = 0.0f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 1.0f;
        m_tEndColor.g = 1.0f;
        m_tEndColor.b = 1.0f;
        m_tEndColor.a = 0.0f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

         // 设置不使用加亮模式。
        this->setBlendAdditive(false);
        return true;
    }
    return false;
}

//由粒子系统基类派生出CCParticleRain,表现的是雨
class CC_DLL CCParticleRain : public CCParticleSystemQuad
{
public:
	//函数注释同上面的类,不再赘述。
    CCParticleRain(){}
    virtual ~CCParticleRain(){}
    bool init(){ return initWithTotalParticles(1000); }
    virtual bool initWithTotalParticles(unsigned int numberOfParticles);
    static CCParticleRain * node()
    {
        return create();
    }
    static CCParticleRain * create()
    {
        CCParticleRain *pRet = new CCParticleRain();
        if (pRet->init())
        {
            pRet->autorelease();
            return pRet;
        }
        CC_SAFE_DELETE(pRet);
        return NULL;
    }
};
对应CPP:
//创建相应数量的粒子。
bool CCParticleRain::initWithTotalParticles(unsigned int numberOfParticles)
{
    //调用基类的相应函数。
if( CCParticleSystemQuad::initWithTotalParticles(numberOfParticles) )
    {
        // 设置为一直不断的发射。
        m_fDuration = kCCParticleDurationInfinity;
		// 设置为重力加速模式。
        m_nEmitterMode = kCCParticleModeGravity;

        // 设置重力加速度的值。

        modeA.gravity = ccp(10,-10);

        // 设置半径变化值及其用于随机初始化的范围值。
        modeA.radialAccel = 0;
        modeA.radialAccelVar = 1;

        // 设置切角变化值及其用于随机初始化的范围值。
        modeA.tangentialAccel = 0;
        modeA.tangentialAccelVar = 1;

         // 设置速度及其用于随机初始化的范围值。
        modeA.speed = 130;
        modeA.speedVar = 30;

          // 设置起始角度及其用于随机初始化的范围值。
        m_fAngle = -90;
        m_fAngleVar = 5;


        //设置发射器的位置及其用于随机初始化的范围值。
        CCSize winSize = CCDirector::sharedDirector()->getWinSize();
        this->setPosition(ccp(winSize.width/2, winSize.height));
        m_tPosVar = ccp( winSize.width/2, 0 );

        // 粒子的生命值及其用于随机初始化的范围值。
        m_fLife = 4.5f;
        m_fLifeVar = 0;

        //起始大小及其用于随机初始化的范围值,终止大小指定与起始大小相同,即在更新时不变化。
        m_fStartSize = 4.0f;
        m_fStartSizeVar = 2.0f;
        m_fEndSize = kCCParticleStartSizeEqualToEndSize;

         // 发射器的发射速率。
        m_fEmissionRate = 20;

        // 起始和结束的粒子颜色。
        m_tStartColor.r = 0.7f;
        m_tStartColor.g = 0.8f;
        m_tStartColor.b = 1.0f;
        m_tStartColor.a = 1.0f;
        m_tStartColorVar.r = 0.0f;
        m_tStartColorVar.g = 0.0f;
        m_tStartColorVar.b = 0.0f;
        m_tStartColorVar.a = 0.0f;
        m_tEndColor.r = 0.7f;
        m_tEndColor.g = 0.8f;
        m_tEndColor.b = 1.0f;
        m_tEndColor.a = 0.5f;
        m_tEndColorVar.r = 0.0f;
        m_tEndColorVar.g = 0.0f;
        m_tEndColorVar.b = 0.0f;
        m_tEndColorVar.a = 0.0f;

         // 设置不使用加亮模式。
        this->setBlendAdditive(false);
        return true;
    }
    return false;
}

    CCParticleExample.h中都是典型的粒子系统的表现类。我们可以清楚的看到,粒子系统的基类已经将这些典型的粒子系统所需要的属性一一包含,想要使用粒子系统摸拟自然现象,所需要做的就是通过对其具有的粒子属性进行相应的设置,仅此而已。

 

在ParticleTest中,共有42个粒子系统的展示,它们均是基于ParticleDemo这个类进行派生实现的, ParticleDemo是基于CCLayerColor所派生的一个类,实现了对于粒子系统演示的管理控制,下面我们来看一下头文件:

//粒子系统演示所用的场景
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();
};

//演示表现烟火的粒子系统,由基类ParticleDemo派生。
class DemoFirework : public ParticleDemo
{
public:
    virtual void onEnter();
    virtual std::string title();
};
//演示表现火焰的粒子系统,由基类ParticleDemo派生。
class DemoFire : public ParticleDemo
{
public:
    virtual void onEnter();
    virtual std::string title();
};
后面雷同不再赘述…

    看上面的代码,很容易理解,每个粒子系统的演示都是一个层,创建一个场景用来管理这些演示层。在CPP中,我们可以找到以下函数的实现。

//场景运行时调用的函数处理。
void ParticleTestScene::runThisTest()
{
	//创建下一个粒子系统的演示层,放入当前场景。
    addChild(nextParticleAction());
	//调用设备的更新场景函数,运行当前场景。
    CCDirector::sharedDirector()->replaceScene(this);
}

    CPP中有三个函数,分别是运行下一个粒子系统的演示,运行上一个粒子系统的演示,重新进行当前粒子系统的演示。

//下一个粒子系统的演示
CCLayer* nextParticleAction()
{
	//演示的索引加一并对最大演示数量取模防止出现无效值。
    sceneIdx++;
    sceneIdx = sceneIdx % MAX_LAYER;
	//由索引创建相应的粒子系统的演示层并设为由存管理器进行引用计数器的管理。
    CCLayer* pLayer = createParticleLayer(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}
//上一个粒子系统的演示
CCLayer* backParticleAction()
{
	//演示的索引减一进行无效值处理。
    sceneIdx--;
    int total = MAX_LAYER;
    if( sceneIdx < 0 )
        sceneIdx += total;    
    //由索引创建相应的粒子系统的演示层并设为由存管理器进行引用计数器的管理。
    CCLayer* pLayer = createParticleLayer(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}
//重新创建当前的粒子系统的演示层。
CCLayer* restartParticleAction()
{
//由索引创建相应的粒子系统的演示层并设为由存管理器进行引用计数器的管理。
    CCLayer* pLayer = createParticleLayer(sceneIdx);
    pLayer->autorelease();

    return pLayer;
}

可以看到,就如TestCpp类似的管理方案,所有的演示都会放在createParticleLayer中,通过索引来进行相应的控制管理。

//没错,就是这样。
CCLayer* createParticleLayer(int nIndex)
{
    switch(nIndex)
    {
        case 0: return new ParticleReorder();
        case 1: return new ParticleBatchHybrid();
        case 2: return new ParticleBatchMultipleEmitters();
        case 3: return new DemoFlower();
        case 4: return new DemoGalaxy();
        case 5: return new DemoFirework();
        case 6: return new DemoSpiral();
        case 7: return new DemoSun();
        case 8: return new DemoMeteor();
        case 9: return new DemoFire();
        case 10: return new DemoSmoke();
        case 11: return new DemoExplosion();
        case 12: return new DemoSnow();
        case 13: return new DemoRain();
        case 14: return new DemoBigFlower();
        case 15: return new DemoRotFlower();
        case 16: return new DemoModernArt();
        case 17: return new DemoRing();
        case 18: return new ParallaxParticle();
        case 19: return new DemoParticleFromFile("BoilingFoam");
        case 20: return new DemoParticleFromFile("BurstPipe");
        case 21: return new DemoParticleFromFile("Comet");
        case 22: return new DemoParticleFromFile("debian");
        case 23: return new DemoParticleFromFile("ExplodingRing");
        case 24: return new DemoParticleFromFile("LavaFlow");
        case 25: return new DemoParticleFromFile("SpinningPeas");
        case 26: return new DemoParticleFromFile("SpookyPeas");
        case 27: return new DemoParticleFromFile("Upsidedown");
        case 28: return new DemoParticleFromFile("Flower");
        case 29: return new DemoParticleFromFile("Spiral");
        case 30: return new DemoParticleFromFile("Galaxy");
        case 31: return new DemoParticleFromFile("Phoenix");
        case 32: return new RadiusMode1();
        case 33: return new RadiusMode2();
        case 34: return new Issue704();
        case 35: return new Issue870();
        case 36: return new Issue1201();
        // v1.1 tests
        case 37: return new MultipleParticleSystems();
        case 38: return new MultipleParticleSystemsBatched();
        case 39: return new AddAndDeleteParticleSystems();
        case 40: return new ReorderParticleSystems();
        case 41: return new PremultipliedAlphaTest();
        case 42: return new PremultipliedAlphaTest2();
        default:
            break;
    }

    return NULL;
}

然后是演示粒子效果所用的层的基类ParticleDemo:

//析构函数。
ParticleDemo::~ParticleDemo(void)
{
	//释放粒子系统。
    CC_SAFE_RELEASE(m_emitter);
}
//加载当前层时调用的函数。
void ParticleDemo::onEnter(void)
{
	//调用基类的相应函数。
    CCLayer::onEnter();
    //初始化背景色。
    initWithColor( ccc4(127,127,127,255) );
    //粒子系统置空。
    m_emitter = NULL;
    //设置接受触屏消息。
    setTouchEnabled( true );
    //取得屏幕大小。
    CCSize s = CCDirector::sharedDirector()->getWinSize();
	//创建主标题文字标签并放到当前层中的相应位置。
    CCLabelTTF* label = CCLabelTTF::create(title().c_str(), "Arial", 28);
    addChild(label, 100, 1000);
    label->setPosition( CCPointMake(s.width/2, s.height-50) );
    //创建幅标题文字标签并放到当前层中的相应位置。
    CCLabelTTF *sub = CCLabelTTF::create(subtitle().c_str(), "Arial", 16);
    addChild(sub, 100);
    sub->setPosition(CCPointMake(s.width/2, s.height-80));
    
    //创建菜单按钮用于控制演示。
    CCMenuItemImage* item1 = CCMenuItemImage::create(s_pPathB1, s_pPathB2, this, menu_selector(ParticleDemo::backCallback) );
    CCMenuItemImage* item2 = CCMenuItemImage::create(s_pPathR1, s_pPathR2, this, menu_selector(ParticleDemo::restartCallback) );
    CCMenuItemImage* item3 = CCMenuItemImage::create(s_pPathF1, s_pPathF2,  this, menu_selector(ParticleDemo::nextCallback) );
    //创建一个菜单按钮用于切换运动时的受影响类型,响应函数为ParticleDemo::toggleCallback。
    CCMenuItemToggle* item4 = CCMenuItemToggle::createWithTarget(this, 
                                                               menu_selector(ParticleDemo::toggleCallback), 
                                                               CCMenuItemFont::create( "Free Movement" ),
                                                               CCMenuItemFont::create( "Relative Movement" ),
                                                               CCMenuItemFont::create( "Grouped Movement" ),
                                                               NULL );
    //创建菜单。
    CCMenu *menu = CCMenu::create(item1, item2, item3, item4, NULL);
    //设置各菜单按钮的位置。
    menu->setPosition( CCPointZero );
    item1->setPosition( ccp( s.width/2 - item2->getContentSize().width*2, item2->getContentSize().height/2) );
    item2->setPosition( ccp( s.width/2, item2->getContentSize().height/2) );
    item3->setPosition( ccp( s.width/2 + item2->getContentSize().width*2, item2->getContentSize().height/2) );
    item4->setPosition( ccp( 0, 100) );
    item4->setAnchorPoint( ccp(0,0) );
    //将菜单放入当前层中。
    addChild( menu, 100 );
    //创建一个文字标签显示FPS
    CCLabelAtlas* labelAtlas = CCLabelAtlas::create("0000", "fps_images.png", 12, 32, '.');
    addChild(labelAtlas, 100, kTagParticleCount);
    labelAtlas->setPosition(ccp(s.width-66,50));
    
    //创建背景图精灵并放在当前层的相应位置。
    m_background = CCSprite::create(s_back3);
    addChild(m_background, 5);
    m_background->setPosition( ccp(s.width/2, s.height-180) );
    //创建移动动画以及反向动画。
    CCActionInterval* move = CCMoveBy::create(4, ccp(300,0) );
    CCActionInterval* move_back = move->reverse();
	//将两个动画放到一个动画序列中。
    CCFiniteTimeAction* seq = CCSequence::create( move, move_back, NULL);
    //让背景图精录无限循环执行这个动画序列。
m_background->runAction( CCRepeatForever::create((CCActionInterval*)seq) );
    
    //让当前层响应Update函数。
    scheduleUpdate();
	//取得主标题并设置显示文字串。
    CCLabelTTF* pLabel = (CCLabelTTF*)(this->getChildByTag(1000));
    pLabel->setString(title().c_str());
}
//取得主标题。
std::string ParticleDemo::title()
{
    return "No title";
}
//取得幅标题。
std::string ParticleDemo::subtitle()
{
    return "No titile";
}
//开启触屏消息响应。
void ParticleDemo::registerWithTouchDispatcher()
{
    CCDirector* pDirector = CCDirector::sharedDirector();
    pDirector->getTouchDispatcher()->addTargetedDelegate(this, 0, false);
}
//当手指按下屏幕时
bool ParticleDemo::ccTouchBegan(CCTouch* touch, CCEvent* event)
{
    return true;
}
//当手指在按下屏幕状态并滑动时响应。
void ParticleDemo::ccTouchMoved(CCTouch* touch, CCEvent* event)
{
    return ccTouchEnded(touch, event);
}
//当手指从按下状态抬起时响应。
void ParticleDemo::ccTouchEnded(CCTouch* touch, CCEvent* event)
{
	//取得按下的屏幕位置
    CCPoint location = touch->getLocation();
	//创建临时变量取得背景精灵的位置。
    CCPoint pos = CCPointZero;
    if (m_background)
    {
        pos = m_background->convertToWorldSpace(CCPointZero);
    }
	//如果粒子系统发射器有效,设置其位置为触点位置减去背景精零的零点世界位置(即当前层的左下角世界位置),因为粒子系统是当前层的子结点,所以ccpSub(location, pos)结果即为触点与当前层的相对位置。。
    if (m_emitter != NULL)
    {
        m_emitter->setPosition( ccpSub(location, pos) );    
    }
}

//更新函数。
void ParticleDemo::update(float dt)
{
	//显示粒子的数量。
    if (m_emitter)
    {
        CCLabelAtlas* atlas = (CCLabelAtlas*)getChildByTag(kTagParticleCount);
        char str[5] = {0};
        sprintf(str, "%04d", m_emitter->getParticleCount());
        atlas->setString(str);
    }
}

//点击切换粒子系统的运动影响方式时的响应函数。
void ParticleDemo::toggleCallback(CCObject* pSender)
{
	//根据当前粒子系统运动影响方式类型,切换到下一种枚举的运动影响方式类型。
    if (m_emitter != NULL)
    {
        if( m_emitter->getPositionType() == kCCPositionTypeGrouped )
            m_emitter->setPositionType( kCCPositionTypeFree );
        else if (m_emitter->getPositionType() == kCCPositionTypeFree)
            m_emitter->setPositionType(kCCPositionTypeRelative);
        else if (m_emitter->getPositionType() == kCCPositionTypeRelative)
            m_emitter->setPositionType( kCCPositionTypeGrouped );
    }
}
//重新启动当前的粒子系统。
void ParticleDemo::restartCallback(CCObject* pSender)
{
    if (m_emitter != NULL)
    {
        m_emitter->resetSystem(); 
    }
}
//切换到下一个粒子系统演示。
void ParticleDemo::nextCallback(CCObject* pSender)
{
    CCScene* s = new ParticleTestScene();
    s->addChild( nextParticleAction() );
    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
}
//返回上一个粒子系统的演示。
void ParticleDemo::backCallback(CCObject* pSender)
{
    CCScene* s = new ParticleTestScene();
    s->addChild( backParticleAction() );
    CCDirector::sharedDirector()->replaceScene(s);
    s->release();
} 
//设置粒子系统发射器的位置为屏幕中心位置。
void ParticleDemo::setEmitterPosition()
{
    CCSize s = CCDirector::sharedDirector()->getWinSize();
    if (m_emitter != NULL)
    {
        m_emitter->setPosition( CCPointMake(s.width / 2, s.height / 2) );
    }
}

OK,这个基类搞定后,下面我们来一一把本演示DEMO中的演示实例进行解释:

(1)    ParticleReorder

描述:

粒子系统的排序。每秒对粒子系统进行一个不同的显示顺序的设定。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

 源码解析:

//当前层加载时的处理。
void ParticleReorder::onEnter()
{
	//调用基类的相应初始化函数。
    ParticleDemo::onEnter();
	//
    m_nOrder = 0;
	//设置层背景色为黑色。
    setColor(ccBLACK);
	//将背景精灵移除并置空
    removeChild(m_background, true);
    m_background = NULL;
	//从SmallSun.plist中加载创建一个粒子系统。
    CCParticleSystem* ignore = CCParticleSystemQuad::create("Particles/SmallSun.plist");
	//创建第一个父结点。
    CCNode *parent1 = CCNode::create();
	//由粒子系统取出的纹理对象创建一个粒子批次结点做为第二个父结点。
    CCNode *parent2 = CCParticleBatchNode::createWithTexture(ignore->getTexture());
	//粒子系统不调用Update函数。
    ignore->unscheduleUpdate();
	//
    for( unsigned int i=0; i<2;i++) 
    {
		//循环两次,第一次parent取parent1,第二次取parent2。
        CCNode *parent = ( i==0 ? parent1 : parent2 );
		//由SmallSun.plist文件创建第一个粒子系统。
        CCParticleSystemQuad *emitter1 = CCParticleSystemQuad::create("Particles/SmallSun.plist");
		//设置粒子系统的粒子的起始颜色。
        emitter1->setStartColor(ccc4f(1,0,0,1));
		//设置粒子系统不使用加亮模式。
        emitter1->setBlendAdditive(false);
		//由SmallSun.plist文件创建第二个粒子系统。
        CCParticleSystemQuad *emitter2 = CCParticleSystemQuad::create("Particles/SmallSun.plist");
		//设置粒子系统的粒子的起始颜色。
        emitter2->setStartColor(ccc4f(0,1,0,1));
		//设置粒子系统不使用加亮模式。
        emitter2->setBlendAdditive(false);
		//由SmallSun.plist文件创建第三个粒子系统。
        CCParticleSystemQuad *emitter3 = CCParticleSystemQuad::create("Particles/SmallSun.plist");
		//设置粒子系统的粒子的起始颜色。
        emitter3->setStartColor(ccc4f(0,0,1,1));
		//设置粒子系统不使用加亮模式。
        emitter3->setBlendAdditive(false);
		//取得屏幕大小。
        CCSize s = CCDirector::sharedDirector()->getWinSize();
		//根据遍历计数器值设定一个位置影响参数。
        int neg = (i==0 ? 1 : -1 );
		//设置三个粒子系统的位置。
        emitter1->setPosition(ccp( s.width/2-30,    s.height/2+60*neg));
        emitter2->setPosition(ccp( s.width/2,        s.height/2+60*neg));
        emitter3->setPosition(ccp( s.width/2+30,    s.height/2+60*neg));
		//将三个粒子系统放入相应的父结点。
        parent->addChild(emitter1, 0, 1);
        parent->addChild(emitter2, 0, 2);
        parent->addChild(emitter3, 0, 3);
		//将父结点做为子结点放入当前层。
        addChild(parent, 10, 1000+i);
    }
	//定时器每秒响应一次ParticleReorder::reorderParticles函数。
    schedule(schedule_selector(ParticleReorder::reorderParticles), 1.0f);
}
//取得标题。
std::string ParticleReorder::title()
{
    return "Reordering particles";
}
//取得幅标题。
std::string ParticleReorder::subtitle()
{
    return "Reordering particles with and without batches batches";
}
//每秒响应的回调函数。
void ParticleReorder::reorderParticles(float dt)
{
    for( int i=0; i<2;i++) {
		//取得不同的粒子系统父结点。
        CCNode *parent = getChildByTag(1000+i);
		//取得相应的粒子系统。
        CCNode *child1 = parent->getChildByTag(1);
        CCNode *child2 = parent->getChildByTag(2);
        CCNode *child3 = parent->getChildByTag(3);
		//对3取模后设置不同的排序。
        if( m_nOrder % 3 == 0 ) {
            parent->reorderChild(child1, 1);
            parent->reorderChild(child2, 2);
            parent->reorderChild(child3, 3);

        } else if (m_nOrder % 3 == 1 ) {
            parent->reorderChild(child1, 3);
            parent->reorderChild(child2, 1);
            parent->reorderChild(child3, 2);

        } else if (m_nOrder % 3 == 2 ) {
            parent->reorderChild(child1, 2);
            parent->reorderChild(child2, 3);
            parent->reorderChild(child3, 1);
        }
    }

    m_nOrder++;
}

(2)    ParticleBatchHybrid

描述:

表现使用批次结点

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码分析:

//加载当前层时的处理。
void ParticleBatchHybrid::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//设置背景色为黑色。
    setColor(ccBLACK);
	//删除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//由LavaFlow.plist文件创建粒子系统。
    m_emitter = CCParticleSystemQuad::create("Particles/LavaFlow.plist");
	//占用此粒子系统,对其引用计数器加一操作。
    m_emitter->retain();
	//由粒子系统的纹理对象创建一个粒子批次结点。
    CCParticleBatchNode *batch = CCParticleBatchNode::createWithTexture(m_emitter->getTexture());
	//将粒子系统加入到粒子批次结点中。
    batch->addChild(m_emitter);
	//将粒子批次结点加入到当前层中。
    addChild(batch, 10);
	//每2秒调用一次ParticleBatchHybrid::switchRender函数切换使用批次结点。
     schedule(schedule_selector(ParticleBatchHybrid::switchRender), 2.0f);
 	//创建一个结点做为子结点。
     CCNode *node = CCNode::create();
     addChild(node);
 	//第一父结点设为批次结点。
     m_pParent1 = batch;
	//第二父结点设为上面的子结点。
     m_pParent2 = node;
}
//切换使用或不使用批次结点的函数。
void ParticleBatchHybrid::switchRender(float dt)
{
	//取得是否使用了批次结点。
     bool usingBatch = ( m_emitter->getBatchNode() != NULL );
	//从父结点中移除。
     m_emitter->removeFromParentAndCleanup(false);
 	//根据上面的usingBatch来设置不同的父结点,这样就可以让粒子系统进行来回切换了。
     CCNode *newParent = (usingBatch ? m_pParent2  : m_pParent1 );
	//将粒子系统放入父结点中。
     newParent->addChild(m_emitter);
	//打印信息
     CCLog("Particle: Using new parent: %s", usingBatch ? "CCNode" : "CCParticleBatchNode");
}
//取得标题。
std::string ParticleBatchHybrid::title()
{
    return "Paticle Batch";
}
//取得幅标题。
std::string ParticleBatchHybrid::subtitle()
{
    return "Hybrid: batched and non batched every 2 seconds";
}

(3)    ParticleBatchMultipleEmitters

描述:

多个粒子系统应用一个批次结点。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码分析:

//加载当前层的处理。
void ParticleBatchMultipleEmitters::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并将指针置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建三个粒子系统,分别设置不同的初始颜色。
    CCParticleSystemQuad *emitter1 = CCParticleSystemQuad::create("Particles/LavaFlow.plist");
    emitter1->setStartColor(ccc4f(1,0,0,1));
    CCParticleSystemQuad *emitter2 = CCParticleSystemQuad::create("Particles/LavaFlow.plist");
    emitter2->setStartColor(ccc4f(0,1,0,1));
    CCParticleSystemQuad *emitter3 = CCParticleSystemQuad::create("Particles/LavaFlow.plist");
    emitter3->setStartColor(ccc4f(0,0,1,1));
	//取得屏幕大小
    CCSize s = CCDirector::sharedDirector()->getWinSize();
	//设置粒子系统的位置。
    emitter1->setPosition(ccp( s.width/1.25f, s.height/1.25f));
    emitter2->setPosition(ccp( s.width/2, s.height/2));
    emitter3->setPosition(ccp( s.width/4, s.height/4));
	//由粒子系统的纹理创建相应的批次结点。
    CCParticleBatchNode *batch = CCParticleBatchNode::createWithTexture(emitter1->getTexture());
	//将粒子系统都放入批次结点中。

    batch->addChild(emitter1, 0);
    batch->addChild(emitter2, 0);
    batch->addChild(emitter3, 0);
	//将批次结点放入当前层。
    addChild(batch, 10);
}
//标题。
std::string ParticleBatchMultipleEmitters::title()
{
    return "Paticle Batch";
}
//幅标题。
std::string ParticleBatchMultipleEmitters::subtitle()
{
    return "Multiple emitters. One Batch";
}

(4)    DemoFlower:

描述:

花?不像,倒不如说是星光闪烁。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码分析:

//加载当前层时的处理
void DemoFlower::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建粒子系统。
    m_emitter = CCParticleFlower::create();
    m_emitter->retain();
	//将粒子系统放入背景精灵结点下。
    m_background->addChild(m_emitter, 10);
	//设置粒子系统使用星星的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_stars1) );
    //调用基类的相应函数将粒子发射器置于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoFlower::title()
{
    return "ParticleFlower";
}

注:这里星星的纹理为:Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

这是一个PNG格式的ALPHA渐变图,本身色彩就是由白到黑的一个亮度变化。通过粒子的顶点着色就能出现五彩缤纷的星光了。

(5)    DemoGalaxy

描述:

星系。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码分析:

//加载当前层时的处理。
void DemoGalaxy::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleGalaxy类的实例。
    m_emitter = CCParticleGalaxy::create();
	//占用,则对其使用计数器加一。
    m_emitter->retain();
	//将粒子系统放入背景结点下。
    m_background->addChild(m_emitter, 10);
    //加载一个火焰的图片,设置为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    //调用基类的相应函数,设置粒子系统的位置居于屏幕中央。
    setEmitterPosition(); 
}
//取得标题
std::string DemoGalaxy::title()
{
    return "ParticleGalaxy";
}

注:这里火焰的纹理为:Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

这是一个PNG格式的ALPHA渐变图,本身色彩就是由白到黑的一个亮度变化。通过粒子的顶点着色就能出现五彩的火火焰了。

(6)     DemoFirework

描述:

烟火的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoFirework::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleFireworks类的实例。
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";
}

(7)     DemoSpiral

描述:

螺旋线的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层的处理。
void DemoSpiral::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleSpiral类的实例。
m_emitter = CCParticleSpiral::create();
//占用,对其引用计数器加一。
m_emitter->retain();
//将粒子系统放在精灵背景结点之下。
    m_background->addChild(m_emitter, 10);
    //加载火焰图片设置为粒子系统所用的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
   //设置粒子系统发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoSpiral::title()
{
    return "ParticleSpiral";
}

(8)    DemoSun

描述:

太阳的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoSun::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleSun类的实例
    m_emitter = CCParticleSun::create();
	//占用粒子系统,对其引用计数器加一。
    m_emitter->retain();
	//将粒子系统放在背景精灵结点之下。
    m_background->addChild(m_emitter, 10);
	//加载火焰图片做为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    //设置粒子系统发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题文字。
std::string DemoSun::title()
{
    return "ParticleSun";
}

(9)    DemoMeteor:

描述:

流星的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层的处理。
void DemoMeteor::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleMeteor类的实例。
    m_emitter = CCParticleMeteor::create();
	//占用它,对它的引用计数器加一。
    m_emitter->retain();
	//将粒子系统加入到当前背景精灵节点之下。
    m_background->addChild(m_emitter, 10);
    //加载火焰的图片做为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    //设置粒子系统居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoMeteor::title()
{
    return "ParticleMeteor";
}

(10)     DemoFire

描述:

火焰的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

void DemoFire::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleFire类的实例。
    m_emitter = CCParticleFire::create();
	//占用它,对其引用计数器加一。
    m_emitter->retain();
	//将粒子系统放在背景精灵结点之下。
    m_background->addChild(m_emitter, 10);
    //加载一个火焰图片做为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );//.pvr");
	//取得粒子系统的位置。设置高度在100。
    CCPoint p = m_emitter->getPosition();
    m_emitter->setPosition( CCPointMake(p.x, 100) );
    //将粒子系统居中。
    setEmitterPosition();
}
//取得标题。
std::string DemoFire::title()
{
    return "ParticleFire";
}

(11)    DemoSmoke

描述:

烟雾的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoSmoke::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleSmoke的实例。
    m_emitter = CCParticleSmoke::create();
	//占用它,对它的引用计数器加一。
    m_emitter->retain();
	//将粒子系统放入到背景精灵结点之下。
    m_background->addChild(m_emitter, 10);
	//加载一个火焰的图片设置为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    //取得粒子系统的位置并限制其高度在100。
    CCPoint p = m_emitter->getPosition();
    m_emitter->setPosition( CCPointMake( p.x, 100) );
    //将粒子系统居于屏幕中央。
    setEmitterPosition();
}
//取得粒子系统的标题。
std::string DemoSmoke::title()
{
    return "ParticleSmoke";
}

(12)    DemoExplosion

描述:

爆炸的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoExplosion::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleExplosion类的实例。
    m_emitter = CCParticleExplosion::create();
	//占用实例,对其引用计数器加一。
    m_emitter->retain();
	//将粒子发射器放到背景精灵结点之下。
    m_background->addChild(m_emitter, 10);
    //加载星星图片做为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_stars1) );
    //设置粒子释放完成后自动移除并释放,这样的话,不会连续不断的发射粒子。
    m_emitter->setAutoRemoveOnFinish(true);
    //设置粒子发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoExplosion::title()
{
    return "ParticleExplosion";
}

(13)    DemoSnow

描述:

雪的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoSnow::onEnter()
{
	//先调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleSnow的实例。
    m_emitter = CCParticleSnow::create();
	//占用它,对它的引用计数器加一。
    m_emitter->retain();
	//将其放在背景精灵结点之下。
    m_background->addChild(m_emitter, 10);
    //取得粒子系统的位置,设置为当前位置往下移110像素。具体位置请参看前面CCParticleExample中CCParticleSnow的initWithTotalParticles函数。
    CCPoint p = m_emitter->getPosition();
    m_emitter->setPosition( CCPointMake( p.x, p.y-110) );
	//设置生命值为3
    m_emitter->setLife(3);
	//设置生命随机加值范围为1,也就是生命值在2~4间初始化。
    m_emitter->setLifeVar(1);
    
    // 设置重力加速度值。
    m_emitter->setGravity(CCPointMake(0,-10));
        
    // 设置初始速度值及随机加值范围,生命值在100~160间初始化。
    m_emitter->setSpeed(130);
    m_emitter->setSpeedVar(30);
    
    //取得粒子系统的起始色彩值。
    ccColor4F startColor = m_emitter->getStartColor();
    startColor.r = 0.9f;
    startColor.g = 0.9f;
    startColor.b = 0.9f;
    m_emitter->setStartColor(startColor);
    //取得粒子系统的起始随机色彩加值范围。
    ccColor4F startColorVar = m_emitter->getStartColorVar();
    startColorVar.b = 0.1f;
    m_emitter->setStartColorVar(startColorVar);
    //设置粒子系统发射的速率。
    m_emitter->setEmissionRate(m_emitter->getTotalParticles()/m_emitter->getLife());
    //加载雪的图片并设置为当前粒子系统使用的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_snow) );
    //设置粒子系统发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoSnow::title()
{
    return "ParticleSnow";
}

(14)    DemoRain:

描述:

雨的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//当前层加载时的处理
void DemoRain::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleRain的实例,
    m_emitter = CCParticleRain::create();
	//占用,对其引用计数器加一。
    m_emitter->retain();
	//将粒子系统加入到背景精灵结点下。
    m_background->addChild(m_emitter, 10);
    //取得粒子系统的位置,设置为当前位置往下移110像素。具体位置请参看前面CCParticleExample中CCParticleRain的initWithTotalParticles函数。
    CCPoint p = m_emitter->getPosition();
    m_emitter->setPosition( CCPointMake( p.x, p.y-100) );
	//设置粒子的生命值为4
    m_emitter->setLife(4);
    //加载火焰图片做为当前粒子系统的纹理
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    //设置粒子系统发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoRain::title()
{
    return "ParticleRain";
}

(15)    DemoBigFlower

描述:

大颗星光闪烁的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoBigFlower::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleSystemQuad的实例。
    m_emitter = new CCParticleSystemQuad();
	//创建50个粒子。
    m_emitter->initWithTotalParticles(50);
    //m_emitter->autorelease();
	//将创建的粒子系统放入到背景精灵的结点之下。
    m_background->addChild(m_emitter, 10);
    ////m_emitter->release();    // win32 :  use this line or remove this line and use autorelease()
	//加载星星的图片做为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_stars1) );
	//设置粒子系统更新没有时长限制。
    m_emitter->setDuration(-1);
    
    //设置重力加速度。
    m_emitter->setGravity(CCPointZero);
    
    // 设置初始角度及角度初始随机变化范围
    m_emitter->setAngle(90);
    m_emitter->setAngleVar(360);
    
    // 设置初始速度及速度初始随机变化范围
    m_emitter->setSpeed(160);
    m_emitter->setSpeedVar(20);
    
    //设置初始半径长度及半径长度初始随机变化范围
    m_emitter->setRadialAccel(-120);
    m_emitter->setRadialAccelVar(0);
    
    //设置初始切角度及切角度初始随机变化范围
    m_emitter->setTangentialAccel(30);
    m_emitter->setTangentialAccelVar(0);
    
    //设置初始位置及切位置初始随机变化范围
    m_emitter->setPosition( CCPointMake(160,240) );
    m_emitter->setPosVar(CCPointZero);
    
    //设置初始生命值及生命值随机变化范围
    m_emitter->setLife(4);
    m_emitter->setLifeVar(1);
    
    // 设置粒子起始的旋转角度和随机的旋转角度加值范围,这里设置为不旋转。
    m_emitter->setStartSpin(0);
    m_emitter->setStartSizeVar(0);
    m_emitter->setEndSpin(0);
    m_emitter->setEndSpinVar(0);
    
    // 设置粒子的起始,结束色彩值和其相应的随机变化范围。
    ccColor4F startColor = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColor(startColor);
    
    ccColor4F startColorVar = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColorVar(startColorVar);
    
    ccColor4F endColor = {0.1f, 0.1f, 0.1f, 0.2f};
    m_emitter->setEndColor(endColor);
    
    ccColor4F endColorVar = {0.1f, 0.1f, 0.1f, 0.2f};    
    m_emitter->setEndColorVar(endColorVar);
    
    // 设置粒子的大小值及其随机变化范围。
    m_emitter->setStartSize(80.0f);
    m_emitter->setStartSizeVar(40.0f);
	//结束大小设为宏kParticleStartSizeEqualToEndSize,代表大小不变化。
    m_emitter->setEndSize(kParticleStartSizeEqualToEndSize);
    
    // 设置粒子系统的发射速率。
    m_emitter->setEmissionRate(m_emitter->getTotalParticles()/m_emitter->getLife());
    
    // 设置为加亮模式。
    m_emitter->setBlendAdditive(true);
	//设置粒子系统发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoBigFlower::title()
{
    return "ParticleBigFlower";
}

(16)    DemoRotFlower

描述:

旋转的星光的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)
源码解析:

void DemoRotFlower::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个粒子系统的实例。
m_emitter = new CCParticleSystemQuad();
//创建300个粒子。
    m_emitter->initWithTotalParticles(300);
    //m_emitter->autorelease();
	//将粒子系统加入到背景精灵结点之下。
    m_background->addChild(m_emitter, 10);
////m_emitter->release();    // win32 : Remove this line
//加载星星的图片做为粒子系统的纹理。
    m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_stars2) );
    
    // 设置粒子系统一直进行update
    m_emitter->setDuration(-1);
    
    // 设置无重力加速度。
    m_emitter->setGravity(CCPointZero);
    
    // 设置起始的角度以及初始化时的随机角度加值范围。
    m_emitter->setAngle(90);
    m_emitter->setAngleVar(360);
    
    //设置起始的速度以及初始化时的随机速度加值范围。
    m_emitter->setSpeed(160);
    m_emitter->setSpeedVar(20);
    
    //设置起始的半径以及初始化时的随机半径加值范围。
    m_emitter->setRadialAccel(-120);
    m_emitter->setRadialAccelVar(0);
    
//设置起始的切角以及初始化时的随机切角加值范围。
    m_emitter->setTangentialAccel(30);
    m_emitter->setTangentialAccelVar(0);
    
    //设置起始的位置以及初始化时的随机位置加值范围。
    m_emitter->setPosition( CCPointMake(160,240) );
    m_emitter->setPosVar(CCPointZero);
    
    //设置起始的生命值以及初始化时的随机生命值加值范围。
    m_emitter->setLife(3);
    m_emitter->setLifeVar(1);

//设置起始的旋转角度以及初始化时的随机旋转角度加值范围。
    m_emitter->setStartSpin(0);
    m_emitter->setStartSpinVar(0);
    m_emitter->setEndSpin(0);
    m_emitter->setEndSpinVar(2000);
    
    //设置起始的颜色以及初始化时起始颜色的随机颜色加值范围。
    ccColor4F startColor = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColor(startColor);
    ccColor4F startColorVar = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColorVar(startColorVar);
    //设置结束的颜色以及初始化时结束颜色的随机颜色加值范围。
    ccColor4F endColor = {0.1f, 0.1f, 0.1f, 0.2f};
    m_emitter->setEndColor(endColor);
    ccColor4F endColorVar = {0.1f, 0.1f, 0.1f, 0.2f};    
    m_emitter->setEndColorVar(endColorVar);

    //设置起始的粒子大小以及初始化时起始粒子大小的随机颜色加值范围。
    m_emitter->setStartSize(30.0f);
m_emitter->setStartSizeVar(00.0f);
//设置在Update过程中大小不变。
    m_emitter->setEndSize(kParticleStartSizeEqualToEndSize);
    
    // 设置粒子系统发射速率
    m_emitter->setEmissionRate(m_emitter->getTotalParticles()/m_emitter->getLife());

    // 设置不使用加亮模式。
    m_emitter->setBlendAdditive(false);
    //设置粒子系统发射器的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoRotFlower::title()
{
    return "ParticleRotFlower";
}

(17)    DemoModernArt

描述:

大小的变化

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载本层的源码
void DemoModernArt::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();

//创建一个粒子系统。
 glDrawArrays(GL_POINTS, 0, m_uParticleIdx);
//  m_emitter = new CCParticleSystemPoint();
    m_emitter = new CCParticleSystemQuad();
	//创建1000个粒子
    m_emitter->initWithTotalParticles(1000);
    //m_emitter->autorelease();
	//将粒子系统放入到背景精灵的结点下。
    m_background->addChild(m_emitter, 10);
    ////m_emitter->release();
    //取得屏幕大小。
    CCSize s = CCDirector::sharedDirector()->getWinSize();
    //设置一直更新粒子系统。
    m_emitter->setDuration(-1);
    //设置重力加速度。
    m_emitter->setGravity(CCPointMake(0,0));
    
    //设置角度初始值及角度初始值的随机加值范围 。
    m_emitter->setAngle(0);
    m_emitter->setAngleVar(360);
    
    //设置半径初始值及半径初始值的随机加值范围。
    m_emitter->setRadialAccel(70);
    m_emitter->setRadialAccelVar(10);
    
    //设置切角初始值及切角初始值的随机加值范围。
    m_emitter->setTangentialAccel(80);
    m_emitter->setTangentialAccelVar(0);
    
    //设置速度初始值及速度初始值的随机加值范围。
    m_emitter->setSpeed(50);
    m_emitter->setSpeedVar(10);
    
    //设置位置初始值在屏幕中央。
    m_emitter->setPosition( CCPointMake( s.width/2, s.height/2) );
    m_emitter->setPosVar(CCPointZero);
    
    //设置粒子的生命初始值及初始时生命值的随机加值范围。
    m_emitter->setLife(2.0f);
    m_emitter->setLifeVar(0.3f);
    
    //设置粒子系统的发射速率。
m_emitter->setEmissionRate(m_emitter->getTotalParticles()/m_emitter->getLife());
    
    //设置粒子系统的起始颜色和起始颜色的随机加值范围。
    ccColor4F startColor = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColor(startColor);
    
    ccColor4F startColorVar = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColorVar(startColorVar);

    //设置粒子系统的结束颜色和结束颜色的随机加值范围。
    ccColor4F endColor = {0.1f, 0.1f, 0.1f, 0.2f};
    m_emitter->setEndColor(endColor);
    
    ccColor4F endColorVar = {0.1f, 0.1f, 0.1f, 0.2f};    
    m_emitter->setEndColorVar(endColorVar);
    
    //设置粒子系统的起始大小和起始大小的随机加值范围。
    m_emitter->setStartSize(1.0f);
    m_emitter->setStartSizeVar(1.0f);
	//设置粒子系统的结束大小和结束大小的随机加值范围。
    m_emitter->setEndSize(32.0f);
    m_emitter->setEndSizeVar(8.0f);
    
    //加载火焰的图片做为粒子系统的纹理。
m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    
    //设置不使用加亮模式。
    m_emitter->setBlendAdditive(false);
    //设置粒子系统居中。
    setEmitterPosition();
}
//取得标题。
std::string DemoModernArt::title()
{
    return "Varying size";
}

(18)    DemoRing

描述:

光环的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void DemoRing::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//创建一个CCParticleFlower类的粒子系统实例。
    m_emitter = CCParticleFlower::create();
	//占用它,对其引用计数器加一。
    m_emitter->retain();
	//将粒子系统加入到背景精灵的结点之下。
    m_background->addChild(m_emitter, 10);

    //加载星星的图片做为粒子系统的纹理。
m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_stars1) );
	//设置粒子的生命值为10。
    m_emitter->setLifeVar(0);
    m_emitter->setLife(10);
	//设置粒子的速度。
    m_emitter->setSpeed(100);
    m_emitter->setSpeedVar(0);
	//设置粒子发射器的发射速率。
    m_emitter->setEmissionRate(10000);
    //设置粒子系统的位置居于屏幕中央。
    setEmitterPosition();
}
//取得标题。
std::string DemoRing::title()
{
    return "Ring Demo";
}

(19)    ParallaxParticle

描述:

视差的摸拟

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void ParallaxParticle::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
    //将背景精灵移除。
    m_background->getParent()->removeChild(m_background, true);
    m_background = NULL;
	//创建一个CCParallaxNode结点p。
    CCParallaxNode* p = CCParallaxNode::create(); 
	//将p放入当前层中。
    addChild(p, 5);
	//创建两个以s_back3为纹理的精灵p1,p2。
    CCSprite *p1 = CCSprite::create(s_back3);
    CCSprite *p2 = CCSprite::create(s_back3);
    //将p1,p2分别放入CCParallaxNode结点p中。
    p->addChild( p1, 1, CCPointMake(0.5f,1), CCPointMake(0,250) );
    p->addChild(p2, 2, CCPointMake(1.5f,1), CCPointMake(0,50) );
	//创建一个CCParticleFlower类的粒子系统实例。
    m_emitter = CCParticleFlower::create();
	//占用它,对其引用计数器加一。
    m_emitter->retain();
    //加载火焰的图片做为粒子系统的纹理。
m_emitter->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
	//将粒子系统emitter放入p1的结点下。
    p1->addChild(m_emitter, 10);
	//设置粒子系统的位置。
    m_emitter->setPosition( CCPointMake(250,200) );
    
	//创建一个CCParticleSun类的粒子系统实例。
    CCParticleSun* par = CCParticleSun::create();
	//将粒子系统par放入p2的结点下。
    p2->addChild(par, 10);
    //加载火焰的图片做为粒子系统的纹理。par->setTexture( CCTextureCache::sharedTextureCache()->addImage(s_fire) );
    //创建动画move和反向动画move_back,由两个动画创建动画序列seq.
    CCActionInterval* move = CCMoveBy::create(4, CCPointMake(300,0));
    CCActionInterval* move_back = move->reverse();
    CCFiniteTimeAction* seq = CCSequence::create( move, move_back, NULL);
	//让CCParallaxNode结点p运行这个序列动画。
    p->runAction(CCRepeatForever::create((CCActionInterval*)seq));    
}
//取得标题。
std::string ParallaxParticle::title()
{
    return "Parallax + Particles";
}

(20)    DemoParticleFromFile

描述:

从PLIST文件中创建一个粒子系统。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时进行的处理。
void DemoParticleFromFile::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//设置背景为黑色。
    setColor(ccBLACK);
	//将背景精灵移除并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建一个新的粒子系统。
    m_emitter = new CCParticleSystemQuad();
	//通过参数创建一个字符串做为PLIST文件名用于加载一个粒子系统。
    std::string filename = "Particles/" + m_title + ".plist";
    m_emitter->initWithFile(filename.c_str());
	//将粒子系统加入当前结点下。
    addChild(m_emitter, 10);
	//设置粒子系统居中。
    setEmitterPosition();
}

在createParticleLayer函数中有
        case 19: return new DemoParticleFromFile("BoilingFoam");
        case 20: return new DemoParticleFromFile("BurstPipe");
        case 21: return new DemoParticleFromFile("Comet");
        case 22: return new DemoParticleFromFile("debian");
        case 23: return new DemoParticleFromFile("ExplodingRing");
        case 24: return new DemoParticleFromFile("LavaFlow");
        case 25: return new DemoParticleFromFile("SpinningPeas");
        case 26: return new DemoParticleFromFile("SpookyPeas");
        case 27: return new DemoParticleFromFile("Upsidedown");
        case 28: return new DemoParticleFromFile("Flower");
        case 29: return new DemoParticleFromFile("Spiral");
        case 30: return new DemoParticleFromFile("Galaxy");
        case 31: return new DemoParticleFromFile("Phoenix");
大家可以一一查看,其实原理就是通过PLIST文件创建不同型态的粒子系统。

(21)    RadiusMode1

描述:

环形运动模式演示一。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理
void RadiusMode1::onEnter()
{
	//调用基类的相应处理
    ParticleDemo::onEnter();
	//设置背景色为黑色
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;

	//创建粒子系统,创建200个粒子。
    m_emitter = new CCParticleSystemQuad();
    m_emitter->initWithTotalParticles(200);
    addChild(m_emitter, 10);
    //加载图片做为粒子系统的纹理。
m_emitter->setTexture(CCTextureCache::sharedTextureCache()->addImage("Images/stars-grayscale.png"));

    //设置粒子系统永远更新,宏kCCParticleDurationInfinity值为-1。
    m_emitter->setDuration(kCCParticleDurationInfinity);

    // 设置粒子系统运动模式为环型模式。
    m_emitter->setEmitterMode(kCCParticleModeRadius);

    // 环型模式的起始和结束半径以及相应的半径随机加成范围值。
    m_emitter->setStartRadius(0);
    m_emitter->setStartRadiusVar(0);
    m_emitter->setEndRadius(160);
    m_emitter->setEndRadiusVar(0);

    // 设置环型运动模式的每秒旋转角度及相应的随机加成范围值。
    m_emitter->setRotatePerSecond(180);
    m_emitter->setRotatePerSecondVar(0);


    // 设置初始时粒子系统的角度以及相应的随机加成范围值。
    m_emitter->setAngle(90);
    m_emitter->setAngleVar(0);

    // 设置粒子系统的位置居于屏幕中央。
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    m_emitter->setPosition(ccp(size.width/2, size.height/2));
    m_emitter->setPosVar(CCPointZero);

    // 设置初始时粒子的生命值为5。
    m_emitter->setLife(5);
    m_emitter->setLifeVar(0);

    // 设置粒子本身起始和结束状态不旋转
    m_emitter->setStartSpin(0);
    m_emitter->setStartSpinVar(0);
    m_emitter->setEndSpin(0);
    m_emitter->setEndSpinVar(0);

    //设置粒子系统发射粒子的起始颜色和随机加值范围。
    ccColor4F startColor = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColor(startColor);

    ccColor4F startColorVar = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColorVar(startColorVar);

    //设置粒子系统发射粒子的终止颜色和随机加值范围。
    ccColor4F endColor = {0.1f, 0.1f, 0.1f, 0.2f};
    m_emitter->setEndColor(endColor);

    ccColor4F endColorVar = {0.1f, 0.1f, 0.1f, 0.2f};    
    m_emitter->setEndColorVar(endColorVar);

    // 设置粒子系统发射粒子的起始大小和随机加值范围,结束值指定为宏kCCParticleStartSizeEqualToEndSize代表大小在粒子运动时不变化。
    m_emitter->setStartSize(32);
    m_emitter->setStartSizeVar(0);
    m_emitter->setEndSize(kCCParticleStartSizeEqualToEndSize);

    // 设置粒子系统的发射速率。
    m_emitter->setEmissionRate(m_emitter->getTotalParticles() / m_emitter->getLife());

    // 设置不使用高亮模式。
    m_emitter->setBlendAdditive(false);
}
//取得标题。
std::string RadiusMode1::title()
{
    return "Radius Mode: Spiral";
}

(22)    RadiusMode2

描述:

环形运动模式演示一。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void RadiusMode2::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建粒子系统,创建200个粒子。
    m_emitter = new CCParticleSystemQuad();
    m_emitter->initWithTotalParticles(200);
    addChild(m_emitter, 10);
    //加载图片做为粒子系统的纹理。m_emitter->setTexture(CCTextureCache::sharedTextureCache()->addImage("Images/stars-grayscale.png"));

    //设置粒子系统永远更新,宏kCCParticleDurationInfinity值为-1。
    m_emitter->setDuration(kCCParticleDurationInfinity);

    // 设置粒子系统运动模式为环型模式。
    m_emitter->setEmitterMode(kCCParticleModeRadius);

    // 环型模式的起始和结束半径以及相应的半径随机加成范围值。
    m_emitter->setStartRadius(100);
    m_emitter->setStartRadiusVar(0);
    m_emitter->setEndRadius(kCCParticleStartRadiusEqualToEndRadius);
    m_emitter->setEndRadiusVar(0);

    // 设置环型运动模式的每秒旋转角度及相应的随机加成范围值。
    m_emitter->setRotatePerSecond(45);
    m_emitter->setRotatePerSecondVar(0);


    // 设置初始时粒子系统的角度以及相应的随机加成范围值。
    m_emitter->setAngle(90);
    m_emitter->setAngleVar(0);

    // 设置粒子系统的位置居于屏幕中央。
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    m_emitter->setPosition(ccp(size.width/2, size.height/2));
    m_emitter->setPosVar(CCPointZero);

    // 设置初始时粒子的生命值为4。
    m_emitter->setLife(4);
    m_emitter->setLifeVar(0);

    // 设置粒子本身起始和结束状态不旋转
    m_emitter->setStartSpin(0);
    m_emitter->setStartSpinVar(0);
    m_emitter->setEndSpin(0);
    m_emitter->setEndSpinVar(0);

//设置粒子系统发射粒子的起始颜色和随机加值范围。
    ccColor4F startColor = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColor(startColor);

    ccColor4F startColorVar = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColorVar(startColorVar);

    //设置粒子系统发射粒子的终止颜色和随机加值范围。
        ccColor4F endColor = {0.1f, 0.1f, 0.1f, 0.2f};
    m_emitter->setEndColor(endColor);

    ccColor4F endColorVar = {0.1f, 0.1f, 0.1f, 0.2f};    
    m_emitter->setEndColorVar(endColorVar);

    // 设置粒子系统发射粒子的起始大小和随机加值范围,结束值指定为宏kCCParticleStartSizeEqualToEndSize代表大小在粒子运动时不变化。
    m_emitter->setStartSize(32);
    m_emitter->setStartSizeVar(0);
    m_emitter->setEndSize(kCCParticleStartSizeEqualToEndSize);

    // 设置粒子系统的发射速率。
    m_emitter->setEmissionRate(m_emitter->getTotalParticles() / m_emitter->getLife());

    // 设置不使用高亮模式。
    m_emitter->setBlendAdditive(false);
}
//取得标题。
std::string RadiusMode2::title()
{
    return "Radius Mode: Semi Circle";
}

(23)    Issue704

描述:

从PLIST文件中创建一个粒子系统。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void Issue704::onEnter()
{
	//调用基类的相应函数
    ParticleDemo::onEnter();
	//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建粒子系统,创建100个粒子。
    m_emitter = new CCParticleSystemQuad();
    m_emitter->initWithTotalParticles(100);
    addChild(m_emitter, 10);
    //加载图片做为粒子系统的纹理。m_emitter->setTexture(CCTextureCache::sharedTextureCache()->addImage("Images/fire.png"));

    //设置粒子系统永远更新,宏kCCParticleDurationInfinity值为-1。
    m_emitter->setDuration(kCCParticleDurationInfinity);

    // 设置粒子系统运动模式为环型模式。
    m_emitter->setEmitterMode(kCCParticleModeRadius);

    // 环型模式的起始和结束半径以及相应的半径随机加成范围值。
    m_emitter->setStartRadius(50);
    m_emitter->setStartRadiusVar(0);
    m_emitter->setEndRadius(kCCParticleStartRadiusEqualToEndRadius);
    m_emitter->setEndRadiusVar(0);

    // 设置环型运动模式的每秒旋转角度及相应的随机加成范围值。    
m_emitter->setRotatePerSecond(0);
    m_emitter->setRotatePerSecondVar(0);


// 设置初始时粒子系统的角度以及相应的随机加成范围值。
    m_emitter->setAngle(90);
    m_emitter->setAngleVar(0);

    // 设置粒子系统的位置居于屏幕中央。
    CCSize size = CCDirector::sharedDirector()->getWinSize();
    m_emitter->setPosition(ccp(size.width/2, size.height/2));
    m_emitter->setPosVar(CCPointZero);

   // 设置初始时粒子的生命值为5。
    m_emitter->setLife(5);
    m_emitter->setLifeVar(0);

    // 设置粒子本身起始和结束状态不旋转
    m_emitter->setStartSpin(0);
    m_emitter->setStartSpinVar(0);
    m_emitter->setEndSpin(0);
    m_emitter->setEndSpinVar(0);

//设置粒子系统发射粒子的起始颜色和随机加值范围。
    ccColor4F startColor = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColor(startColor);

    ccColor4F startColorVar = {0.5f, 0.5f, 0.5f, 1.0f};
    m_emitter->setStartColorVar(startColorVar);

    //设置粒子系统发射粒子的终止颜色和随机加值范围。
        ccColor4F endColor = {0.1f, 0.1f, 0.1f, 0.2f};
    m_emitter->setEndColor(endColor);

    ccColor4F endColorVar = {0.1f, 0.1f, 0.1f, 0.2f};    
    m_emitter->setEndColorVar(endColorVar);

    // 设置粒子系统发射粒子的起始大小和随机加值范围,结束值指定为宏kCCParticleStartSizeEqualToEndSize代表大小在粒子运动时不变化。
    m_emitter->setStartSize(16);
    m_emitter->setStartSizeVar(0);
    m_emitter->setEndSize(kCCParticleStartSizeEqualToEndSize);

    // 设置粒子系统的发射速率。
    m_emitter->setEmissionRate(m_emitter->getTotalParticles() / m_emitter->getLife());

    // 设置不使用高亮模式。
    m_emitter->setBlendAdditive(false);
	// 让粒子系统的发射器运行一个无限旋转的动画。
    CCRotateBy* rot = CCRotateBy::create(16, 360);
    m_emitter->runAction(CCRepeatForever::create(rot));
}
//取得标题。
std::string Issue704::title()
{
    return "Issue 704. Free + Rot";
}
//取得幅标题。
std::string Issue704::subtitle()
{
    return "Emitted particles should not rotate";
}

(24)    Issue870

描述:

从PLIST文件中创建一个粒子系统。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void Issue870::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();

//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建一个粒子系统,由一个PLIST文件来创建相应的粒子。
    CCParticleSystemQuad *system = new CCParticleSystemQuad();
    system->initWithFile("Particles/SpinningPeas.plist");
    //加载纹得中的一个图块做为粒子的纹理。
system->setTextureWithRect(CCTextureCache::sharedTextureCache()->addImage("Images/particles.png"), CCRectMake(0,0,32,32));
    addChild(system, 10);
	//将粒子系统实例指针保存到成员变量中。在后面updateQuads使用。
    m_emitter = system;
//图片中的图块索引。
    m_nIndex = 0;
//让当前层每2秒调用一下更新粒子的绘制矩形函数。
    schedule(schedule_selector(Issue870::updateQuads), 2.0f);
}
//更新粒子的绘制矩形函数。
void Issue870::updateQuads(float dt)
{
	//图块索引加一,并限定在[0,4)间。
    m_nIndex = (m_nIndex + 1) % 4;
	//通过图块索引来取得相应的图块位置并设置为粒子使用的纹理。
    CCRect rect = CCRectMake(m_nIndex * 32, 0, 32, 32);
    CCParticleSystemQuad* system = (CCParticleSystemQuad*)m_emitter;
    system->setTextureWithRect(m_emitter->getTexture(), rect);
}
//取得标题
std::string Issue870::title()
{
    return "Issue 870. SubRect";
}
//取得幅标题
std::string Issue870::subtitle()
{
    return "Every 2 seconds the particle should change";
}

(25)    Issue1201

描述:

似乎从截图上看不到什么,仔细看,并移动鼠标。这个实例应该是一个类似慧尾的效果,只是很淡。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理
void Issue1201::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建一个RainbowEffect类的实例,创建5O个粒子。
    RainbowEffect *particle = new RainbowEffect();
    particle->initWithTotalParticles(50);
	//将其加入到当前层结点下。
    addChild(particle);
	//将粒子系统的位置居于屏幕中央。
    CCSize s = CCDirector::sharedDirector()->getWinSize();
    particle->setPosition(ccp(s.width/2, s.height/2));
    m_emitter = particle;
}
//取得标题。
std::string Issue1201::title()
{
    return "Issue 1201. Unfinished";
}
//取得幅标题。
std::string Issue1201::subtitle()
{
    return "Unfinished test. Ignore it";
}

(26)    MultipleParticleSystems

描述:

多个粒子系统。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理
void MultipleParticleSystems::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
    //加载纹得中的一个图块做为粒子的纹理。CCTextureCache::sharedTextureCache()->addImage("Images/particles.png"); 
	//创建5个粒子系统。
    for (int i = 0; i<5; i++) {
		//由相应的PLIST创建出粒子系统来。
        CCParticleSystemQuad *particleSystem = CCParticleSystemQuad::create("Particles/SpinningPeas.plist");
		//根据索引将粒子系统放在不同的位置。
        particleSystem->setPosition(ccp(i*50 ,i*50));
		//设置粒子系统运动影响方式类型为按组方式。
        particleSystem->setPositionType(kCCPositionTypeGrouped);
        addChild(particleSystem);
    }
	//粒子发射器指针置空。
    m_emitter = NULL;
}
//取得标题。
std::string MultipleParticleSystems::title()
{
    return "Multiple particle systems";
}
//取得幅标题。
std::string MultipleParticleSystems::subtitle()
{
    return "v1.1 test: FPS should be lower than next test";
}
//更新函数。
void MultipleParticleSystems::update(float dt)
{
	//取得用于标识粒子数量的文字标签。
    CCLabelAtlas *atlas = (CCLabelAtlas*) getChildByTag(kTagParticleCount);
	//定义一个计数器变量count
    unsigned int count = 0; 
    //遍历所有粒子系统子结点
    CCObject* pObj = NULL;
	//CCARRAY_FOREACH为遍历CArray数组的宏
    CCARRAY_FOREACH(m_pChildren, pObj)
    {
		 //通过dynamic_cast来将数组元素转换成为相应的粒子系统指针。
        CCParticleSystem* item = dynamic_cast<CCParticleSystem*>(pObj);
        if (item != NULL)
        {
			 //当前计数器变量count值加上当前粒子系统子结点的粒子数量,统计粒子总数。
            count += item->getParticleCount();    
        }
    }
	//让文字标签显示相应的粒子数量。
    char str[100] = {0};
    sprintf(str, "%4d", count);
    atlas->setString(str);
}

(27)    MultipleParticleSystemBatched

描述:

使用批次结点进行多外粒子系统的渲染管理。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//当前层加载时的处理
void MultipleParticleSystemsBatched::onEnter()
{
	//先调用基类的相应处理。
    ParticleDemo::onEnter();
//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;
	//创建一个新的粒子系统的批次结点。
    CCParticleBatchNode *batchNode = new CCParticleBatchNode();
	//初始化,渲染用的矩形信息容器的容量初始化为3000。
    batchNode->initWithTexture(NULL, 3000);
	//将粒子系统的批次结点放入当前结点之下。
    addChild(batchNode, 1, 2);
	//创建5个粒子系统。
    for (int i = 0; i<5; i++) {
		//由PLIST文件创建相应的粒子系统。
        CCParticleSystemQuad *particleSystem = CCParticleSystemQuad::create("Particles/SpinningPeas.plist");
		//设置粒子系统运动影响方式类型为按组方式。
        particleSystem->setPositionType(kCCPositionTypeGrouped);         
		//根据索引将粒子系统放在不同的位置。
        particleSystem->setPosition(ccp(i*50 ,i*50));
		//设置使用粒子系统的纹理。
        batchNode->setTexture(particleSystem->getTexture());
        batchNode->addChild(particleSystem);
    }
	//已经放入到当前结点下,不再占用粒子系统批次结点,所以对其引用计数器减一操作。
    batchNode->release();
	//使用批次结点,则原指向所用的粒子系统发射器的指针置空。
    m_emitter = NULL;
}
//更新处理。
void MultipleParticleSystemsBatched::update(float dt)
{   //取得用于标识粒子数量的文字标签。
    CCLabelAtlas *atlas = (CCLabelAtlas*) getChildByTag(kTagParticleCount);
	//定义一个计数器变量count
    unsigned count = 0; 
    //根据索引取得粒子系统的批次结点。
    CCNode* batchNode = getChildByTag(2);
    //遍历所有粒子系统子结点
    CCObject* pObj = NULL;
	//CCARRAY_FOREACH为遍历CArray数组的宏
    CCARRAY_FOREACH(batchNode->getChildren(), pObj)
    {
		 //通过dynamic_cast来将数组元素转换成为相应的粒子系统指针。
        CCParticleSystem* item = dynamic_cast<CCParticleSystem*>(pObj);
        if (item != NULL)
        {
			 //当前计数器变量count值加上当前粒子系统子结点的粒子数量,统计粒子总数。
            count += item->getParticleCount();    
        }
    }
	//让文字标签显示相应的粒子数量。

    char str[50] = {0};
    sprintf(str, "%4d", count);
    atlas->setString(str);
}
//取得标题。
std::string MultipleParticleSystemsBatched::title()
{
    return "Multiple particle systems batched";
}
//取得幅标题。
std::string MultipleParticleSystemsBatched::subtitle()
{
    return "v1.1 test: should perform better than previous test";
}

(28)    AddAndDeleteParticleSystems

描述:

动态的增加和删除粒子系统。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理
void AddAndDeleteParticleSystems::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();

//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background, true);
    m_background = NULL;

    //创建一个粒子系统的批次结点,渲染用的矩形信息容器的容量初始化为16000。
    m_pBatchNode = CCParticleBatchNode::createWithTexture((CCTexture2D*)NULL, 16000);
	//将批次结点加入当前层中。
    addChild(m_pBatchNode, 1, 2);
	//创建6个粒子系统。
    for (int i = 0; i<6; i++) {
	//由相应的PLIST文件创建出粒子系统。
        CCParticleSystemQuad *particleSystem = CCParticleSystemQuad::create("Particles/Spiral.plist");
		//取出粒子系统所用的纹理设置为批次结点的纹理。
        m_pBatchNode->setTexture(particleSystem->getTexture());
		//设置粒子系统的运动方式为组运动方式,最大粒子数量为200。
        particleSystem->setPositionType(kCCPositionTypeGrouped);         
        particleSystem->setTotalParticles(200);
		//设置每个粒子系统的位置。
        particleSystem->setPosition(ccp(i*15 +100,i*15+100));
		//将粒子系统放入到批次结点中。
        unsigned int randZ = rand() % 100; 
        m_pBatchNode->addChild(particleSystem, randZ, -1);

    }

   //让当前层每0.5秒触发一次函数removeSystem
chedule(schedule_selector(AddAndDeleteParticleSystems::removeSystem), 0.5f);
	//因为使用的是粒子系统的批次结点,所以指向粒子系统的指针置空。
    m_emitter = NULL;

}
//定时触发的函数。
void AddAndDeleteParticleSystems::removeSystem(float dt)
{
	//取得批次结点中的粒子系统的数量。
    int nChildrenCount = m_pBatchNode->getChildren()->count();
    if (nChildrenCount > 0) 
    {
        CCLOG("remove random system");
		//产生一个随机索引。
        unsigned int uRand = rand() % (nChildrenCount - 1);
        //将对应索引的粒子系统从批次结点中删除。
pBatchNode->removeChild((CCNode*)m_pBatchNode->getChildren()->objectAtIndex(uRand), true);
		//再由PLIST创建一个新的粒子系统。
        CCParticleSystemQuad *particleSystem = CCParticleSystemQuad::create("Particles/Spiral.plist");
        //add new
		//设置粒子系统运动方式为组方式,粒子总数量为200
        particleSystem->setPositionType(kCCPositionTypeGrouped);         
        particleSystem->setTotalParticles(200);
		//设置粒子系统处于一个随机的位置。
        particleSystem->setPosition(ccp(rand() % 300 ,rand() % 400));
		//打印日志,将新粒子系统放入到批次结点中。
        CCLOG("add a new system");
        unsigned int randZ = rand() % 100; 
        m_pBatchNode->addChild(particleSystem, randZ, -1);
    }
}
//更新处理
void AddAndDeleteParticleSystems::update(float dt)
{
	//取得用于显示粒子数量的文字标签。
    CCLabelAtlas *atlas = (CCLabelAtlas*) getChildByTag(kTagParticleCount);

    unsigned int count = 0; 
    //取得粒子系统的批次结点。
    CCNode* batchNode = getChildByTag(2);
	//定义临时指针变量pObj,用于遍历所有的粒子系统结点。
    CCObject* pObj = NULL;
    CCARRAY_FOREACH(batchNode->getChildren(), pObj)
    {
		//通过dynamic_cast将物体基类指针转化为粒子系统结点指针。
        CCParticleSystem* item = dynamic_cast<CCParticleSystem*>(pObj);
        if (item != NULL)
        {
			  //更新粒子的计数器值。
            count += item->getParticleCount();    
        }
}
//写字,显示数量。
    char str[100] = {0};
    sprintf(str, "%4d", count);
    atlas->setString(str);
}
//取得标题。
std::string AddAndDeleteParticleSystems::title()
{
    return "Add and remove Particle System";
}
//取得幅标题。
std::string AddAndDeleteParticleSystems::subtitle()
{
    return "v1.1 test: every 2 sec 1 system disappear, 1 appears";
}

(29)    ReorderParticleSystems

描述:

粒子系统的Z值重新排序。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void ReorderParticleSystems::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();

//设置背景色为黑色。
    setColor(ccBLACK);
	//移除背景精灵并置空。
    removeChild(m_background ,true);
    m_background = NULL;
	//创建一个新的粒子系统的批次结点,渲染用的矩形信息容器的容量初始化为3000。
    m_pBatchNode = CCParticleBatchNode::create("Images/stars-grayscale.png" ,3000);
	//将粒子系统的批次结点放入到当前层结点下。
    addChild(m_pBatchNode, 1, 2);

	//使用for循环创建三个粒子系统。
    for (int i = 0; i<3; i++) {

		//创建第i个粒子系统。
        CCParticleSystemQuad* particleSystem = new CCParticleSystemQuad();
		//创建200个粒子。
        particleSystem->initWithTotalParticles(200);
		//取得批次结点的纹理设置为粒子系统的纹理。
        particleSystem->setTexture(m_pBatchNode->getTexture());

        //设置粒子系统一直调用Update
        particleSystem->setDuration(kCCParticleDurationInfinity);

        //设置粒子的运动方式为环型运动方式。
        particleSystem->setEmitterMode(kCCParticleModeRadius);

        //设置粒子系统的粒子起始角度为100。
        particleSystem->setStartRadius(100);
        particleSystem->setStartRadiusVar(0);
        //指定粒子系统的粒子结束角度与起始角度相同,即角度不变化。
particleSystem->setEndRadius(kCCParticleStartRadiusEqualToEndRadius);
        particleSystem->setEndRadiusVar(0);    // not used when start == end

        // 设置粒子系统的环型运动速率为每秒45度。
        particleSystem->setRotatePerSecond(45);
        particleSystem->setRotatePerSecondVar(0);


        // 设置粒子系统的角度为90度。
        particleSystem->setAngle(90);
        particleSystem->setAngleVar(0);

        //设置粒子系统的位置为零点
        particleSystem->setPosVar(CCPointZero);

        //设置粒子初始化的生命值为4。
        particleSystem->setLife(4);
        particleSystem->setLifeVar(0);

        //设置粒子运动过程中自身不旋转。
        particleSystem->setStartSpin(0);
        particleSystem->setStartSpinVar(0);
        particleSystem->setEndSpin(0);
        particleSystem->setEndSpinVar(0);

        //设置粒子的起始和结束的颜色,这里通过一个float数组来表示R,G,B的值,通过索引设置相应色彩分量值表现红,绿,蓝三种颜色。
        float color[3] = {0,0,0};
        color[i] = 1;
        ccColor4F startColor = {color[0], color[1], color[2], 1.0f};
        particleSystem->setStartColor(startColor);

        ccColor4F startColorVar = {0, 0, 0, 0};
        particleSystem->setStartColorVar(startColorVar);

        ccColor4F endColor = startColor;
        particleSystem->setEndColor(endColor);

        ccColor4F endColorVar = startColorVar;
        particleSystem->setEndColorVar(endColorVar);

        // 设置粒子的起始大小。
        particleSystem->setStartSize(32);
        particleSystem->setStartSizeVar(0);
        particleSystem->setEndSize(kCCParticleStartSizeEqualToEndSize);

        //设置粒子系统发射粒子的速率。
        particleSystem->setEmissionRate(particleSystem->getTotalParticles()/particleSystem->getLife());

        // 设置粒子系统的位置,

        particleSystem->setPosition(ccp(i*10+120 ,200));

		//将粒子系统放入到批次结点下,设置粒子系统的运动方式为自由运动方式。
        m_pBatchNode->addChild(particleSystem);
        particleSystem->setPositionType(kCCPositionTypeFree);
		//使用批次结点进行渲染,粒子系统已经放入到批次结点之下,故不再占用粒子系统,所以可以在这里对其引用计数器减一操作。
        particleSystem->release();

        //[pBNode addChild:particleSystem z:10 tag:0);

    }
	//每2秒调用一下reorderSystem函数。
    schedule(schedule_selector(ReorderParticleSystems::reorderSystem), 2.0f);
    m_emitter = NULL;

}
//定时触发的函数。
void ReorderParticleSystems::reorderSystem(float time)
{
	//通过索引取得相应的批次系统结点,进行重新的Z值排序。
    CCParticleSystem* system = (CCParticleSystem*)m_pBatchNode->getChildren()->objectAtIndex(1);
    m_pBatchNode->reorderChild(system, system->getZOrder() - 1);     
}

//更新函数。
void ReorderParticleSystems::update(float dt)
{
	//取得用于显示粒子数量的文字标签。
    CCLabelAtlas *atlas = (CCLabelAtlas*) getChildByTag(kTagParticleCount);
	//创建临时变量用于统计粒子数量。
    unsigned int count = 0; 
    //取得批次结点
    CCNode* batchNode = getChildByTag(2);
	//定义临时CCObject指针变量用于遍历所有的粒子系统。
    CCObject* pObj = NULL;
    CCARRAY_FOREACH(batchNode->getChildren(), pObj)
    {
		//通过dynamic_cast将物体基类指针转化为粒子系统结点指针。
        CCParticleSystem* item = dynamic_cast<CCParticleSystem*>(pObj);
        if (item != NULL)
        {
			 //更新粒子数量的计数器。
            count += item->getParticleCount();    
        }
    }
	//写字。
    char str[100] = {0};
    sprintf(str, "%4d", count);
    atlas->setString(str);
}
//返回标题。
std::string ReorderParticleSystems::title()
{
    return "reorder systems";
}
//返回幅标题。
std::string ReorderParticleSystems::subtitle()
{
    return "changes every 2 seconds";
}

(30)    PremultipliedAlphaTest

描述:

预乘aplha通道效果演示之一。什么是预乘?假设一个像素点,用RGBA 四个分量来表示,记做(R,G,B,A),那预乘后的像素就是(R*A,G*A,B*A, A),这里A的取值范围是[0,1]。所以,预乘就是每个颜色分量都与该像素的alpha分量预先相乘。可以发现,对于一个没有透明度,或者说透明度为1 的像素来说,预乘不预乘结果都是一样的。为什么要用预乘方式来表示像素?主要是这样会使公式更简单。而且实际上,在实际运算过程中,使用预乘像素进行运算在某些情况下效率会更高。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//返回标题。
std::string PremultipliedAlphaTest::title()
{
    return "premultiplied alpha";
}
//返回副标题。
std::string PremultipliedAlphaTest::subtitle()
{
    return "no black halo, particles should fade out";
}
//当前层加载时调用的处理。
void PremultipliedAlphaTest::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//设置背景为蓝色。
    this->setColor(ccBLUE);
	//删除原来的背景精灵并置空。
    this->removeChild(m_background, true);
    m_background = NULL;
	//由PLIST文件创建一个粒子系统。
    m_emitter = CCParticleSystemQuad::create("Particles/BoilingFoam.plist");
	//占用它,所以对其引用计数器加一。
    m_emitter->retain();
    //设置ALPHA混合方案为方案。
    ccBlendFunc tBlendFunc = { GL_ONE, GL_ONE_MINUS_SRC_ALPHA };
    m_emitter->setBlendFunc(tBlendFunc);
	//提示纹理必须有ALPHA通道
    CCAssert(m_emitter->getOpacityModifyRGB(), "Particle texture does not have premultiplied alpha, test is useless");

    // Toggle next line to see old behavior
    //	this->emitter.opacityModifyRGB = NO;
	//设置粒子系统的粒子起始和结束的颜色。
    m_emitter->setStartColor(ccc4f(1, 1, 1, 1));
    m_emitter->setEndColor(ccc4f(1, 1, 1, 0));
    m_emitter->setStartColorVar(ccc4f(0, 0, 0, 0));
    m_emitter->setEndColorVar(ccc4f(0, 0, 0, 0));
	//将粒子系统加入到当前结点下。
    this->addChild(m_emitter, 10);
}

(31)    PremultipliedAlphaTest2:

描述:

预乘aplha通道效果演示之二。

截图:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

源码解析:

//加载当前层时的处理。
void PremultipliedAlphaTest2::onEnter()
{
	//调用基类的相应函数。
    ParticleDemo::onEnter();
	//设置背景为黑色。
    this->setColor(ccBLACK);
	//删除原来的背景精灵并置空。
    this->removeChild(m_background, true);
    m_background = NULL;
	//由PLIST文件创建一个粒子系统。具体的ALPHA混合方案都由PLIST文件中的数据进行读取设置。
m_emitter = CCParticleSystemQuad::create("Particles/TestPremultipliedAlpha.plist");
//占用粒子系统,对其引用计数器加一。
    m_emitter->retain();
	//将粒子系统放入到当前结点下。
    this->addChild(m_emitter ,10);
}
//取得标题。
std::string PremultipliedAlphaTest2::title()
{
    return "premultiplied alpha 2";
}
//取得副标题。
std::string PremultipliedAlphaTest2::subtitle()
{
    return "Arrows should be faded";
}

 

         我去~,累死哥了。不过经过这么痛苦的注释之后,我相信大家会跟我一样越来越清楚Cocos2d-x里的粒子系统是什么原理以及怎样使用的.我们可以看到,PLIST作为Cocos2d-x粒子系统数据的的存储文件,它可以很方便的产生出粒子系统。在实际的游戏开发过程中,我们一般都会为粒子系统开发一个专用的编辑器,它应该具有Cocos2d-x粒子系统中的所用属性并能编辑后保存为PLIST文件以方便游戏使用。比如这样:

Cocos2d-x 2.0 粒子系统深入分析三部曲(三)

      粒子系统深入分析三部曲结束了,恰逢新的一年到来之际,我衷心的祝愿各位同学能够2013身体健康,事业有成。下课!

分类: cocos2d, cocos2d-x 标签: