[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]
红孩儿Cocos2d-X学习园地QQ2群:44208467 加群写:Cocos2d-x
红孩儿Cocos2d-X学习园地QQ群:249941957 [暂满]加群写:Cocos2d-x
本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!
Cocos2d-x 2.0 网格动画深入分析
另:本章所用Cocos2d-x版本为:
cocos2d-2.0-x-2.0.2 @ Aug 30 2012
http://cn.cocos2d-x.org/download
写在前面:请各位大大在转载博文时注明来源,红孩儿每天熬夜写博实属不易,但屡有一些同学拿来主义,把本博的文章去头掐尾,抹掉作者,然后赤裸裸的改成自已的 " 原创 ",实在令人气愤。在此特声明:本博博文,欢迎您收藏,转载,如果有需要,也可以借用段落或插图,请您经我同意,我不是一个刻板吝啬的人。但如果直接剽窃,我将奋起反击!
在TestCpp中的EffectsTest示例中展示了一些屏幕特效,它是将屏幕划分为多个格子,并对这些格子进行了动画处理从而产生出相应的屏幕特效。今天我们来学习一下这些动画。
主要是涉及到以下几个文件:
CCGrid.h /cpp :网格数据及渲染,包括基本网格数据和3D网格数据,这是数据的基础。
CCActionGrid.h /cpp :网格基本动画,这是动画的基础。
CCActionGrid3D.h/cpp: 3D网格基本动画,这是3D网格动画的基础。
CCActionTiledGrid.h / cpp :网格衍生动画,这是最终的特效实现。
咱们首先来看 关于网格数据及渲染的文件:CCGrid.h
#ifndef __EFFECTS_CCGRID_H__ #define __EFFECTS_CCGRID_H__ #include "cocoa/CCObject.h" #include "base_nodes/CCNode.h" #include "CCCamera.h" #include "ccTypes.h" #include "textures/CCTexture2D.h" #include "CCDirector.h" #include "kazmath/mat4.h" //使用Cocos2d命名空间 NS_CC_BEGIN //当前.h文件要用到以下三个类的指针。 class CCTexture2D; class CCGrabber; class CCGLProgram; //网格基类。由CCObject派生。 class CC_DLL CCGridBase : public CCObject { public: //析构 virtual ~CCGridBase(void); //是否激活状态。 inline bool isActive(void) { return m_bActive; } //设置为激活状态。 void setActive(bool bActive); //取得 inline int getReuseGrid(void) { return m_nReuseGrid; } inline void setReuseGrid(int nReuseGrid) { m_nReuseGrid = nReuseGrid; } //取得网格的大小。 inline const ccGridSize& getGridSize(void) { return m_sGridSize; } //设置网格的大小。 inline void setGridSize(const ccGridSize& gridSize) { m_sGridSize = gridSize; } //取得每个格子占用的图像点数。即图像大于与格子大小的比值。 inline const CCPoint& getStep(void) { return m_obStep; } inline void setStep(const CCPoint& step) { m_obStep = step; } //取得纹理是否上下反转 inline bool isTextureFlipped(void) { return m_bIsTextureFlipped; } //设置纹理是否上下反转。 void setTextureFlipped(bool bFlipped); //初始化网格,参一为网格大小,参二为对应的纹理,参三为设置纹理是否反转。 bool initWithSize(const ccGridSize& gridSize, CCTexture2D *pTexture, bool bFlipped); ////初始化网格,参数为网格大小。 bool initWithSize(const ccGridSize& gridSize); //在渲染之前要做的处理。 void beforeDraw(void); //在渲染之后要做的处理。 void afterDraw(CCNode *pTarget); //渲染处理 virtual void blit(void); //还原到原顶点位置 virtual void reuse(void); //计算顶点缓冲 virtual void calculateVertexPoints(void); public: //详细创建函数,参一为网格大小,参二为对应的纹理,参三为设置纹理是否反转。内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCGridBase* gridWithSize(const ccGridSize& gridSize, CCTexture2D *texture, bool flipped); //简单创建函数,参数为网格大小。内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCGridBase* gridWithSize(const ccGridSize& gridSize); //对应详细创建函数的create实现。 static CCGridBase* create(const ccGridSize& gridSize, CCTexture2D *texture, bool flipped); //对应简单创建函数的create实现。 static CCGridBase* create(const ccGridSize& gridSize); //设置2D投影矩阵。 void set2DProjection(void); protected: //是否被激活。 bool m_bActive; int m_nReuseGrid; //格子大小 ccGridSize m_sGridSize; //所用纹理 CCTexture2D *m_pTexture; //每格的图像点数 CCPoint m_obStep; //将屏幕画面渲染到纹理的处理器。 CCGrabber *m_pGrabber; //纹理是否上下反转。 bool m_bIsTextureFlipped; //Shader代码片段指针。 CCGLProgram* m_pShaderProgram; //投影方式 ccDirectorProjection m_directorProjection; }; 对应CPP: //简单创建函数,参一为网格大小,参二为对应的纹理,参三为设置纹理是否反转。内部调用create实现。CCGridBase* CCGridBase::gridWithSize(const ccGridSize& gridSize) { return CCGridBase::create(gridSize); } //对应简单创建函数的create实现。 CCGridBase* CCGridBase::create(const ccGridSize& gridSize) { //先new出一个当前类实例。 CCGridBase *pGridBase = new CCGridBase(); //如果成功 if (pGridBase) { //进行初始化。 if (pGridBase->initWithSize(gridSize)) { //如果初始化成功交由内存管理器进行管理。 pGridBase->autorelease(); } else { //如果失败释放并置空。 CC_SAFE_RELEASE_NULL(pGridBase); } } //如果成功返回创建的类实例,否则反回NULL。 return pGridBase; } //详细创建函数,参数为网格大小。内部调用create实现。 CCGridBase* CCGridBase::gridWithSize(const ccGridSize& gridSize, CCTexture2D *texture, bool flipped) { return CCGridBase::create(gridSize, texture, flipped); } //对应详细创建函数的create实现。 CCGridBase* CCGridBase::create(const ccGridSize& gridSize, CCTexture2D *texture, bool flipped) { //先new出一个当前类实例。 CCGridBase *pGridBase = new CCGridBase(); //如果成功 if (pGridBase) { //进行初始化。 if (pGridBase->initWithSize(gridSize, texture, flipped)) { //如果初始化成功交由内存管理器进行管理。 pGridBase->autorelease(); } else { //如果失败释放并置空。 CC_SAFE_RELEASE_NULL(pGridBase); } } //如果成功返回创建的类实例,否则反回NULL。 return pGridBase; } //初始化函数。 bool CCGridBase::initWithSize(const ccGridSize& gridSize, CCTexture2D *pTexture, bool bFlipped) { bool bRet = true; //未激活。 m_bActive = false; m_nReuseGrid = 0; //保存格子大小。 m_sGridSize = gridSize; //保存纹理。 m_pTexture = pTexture; //占用纹理,对其引用计数器加一操作。 CC_SAFE_RETAIN(m_pTexture); //保存是否进行纹理反转的变量。 m_bIsTextureFlipped = bFlipped; //取得纹理中的图像大小。 const CCSize& texSize = m_pTexture->getContentSize(); //计算出一个格子所占的纹理寻址长度。 m_obStep.x = texSize.width / m_sGridSize.x; m_obStep.y = texSize.height / m_sGridSize.y; //新建一个屏幕图像获取器,这家伙是干嘛的?其实就是一个获取屏幕图像的类,可以将屏幕上渲染出的图像输出到一张指定的纹理中,类似DX中的RTT。 m_pGrabber = new CCGrabber(); if (m_pGrabber) { //将纹理设置为输出纹理。 m_pGrabber->grab(m_pTexture); } else { //如果图像获取器创建失败,做下返回值处理。 bRet = false; } //取得对应的Shader代码片段。 m_pShaderProgram = CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTexture); //计算所要用到的顶点数据并创建顶点缓冲区。 calculateVertexPoints(); return bRet; } //初始化。 bool CCGridBase::initWithSize(const ccGridSize& gridSize) { //取得设备指针,并通过它取得屏幕大小。 CCDirector *pDirector = CCDirector::sharedDirector(); CCSize s = pDirector->getWinSizeInPixels(); //计算相应大小对应的2次幂大小。 unsigned long POTWide = ccNextPOT((unsigned int)s.width); unsigned long POTHigh = ccNextPOT((unsigned int)s.height); // 使用ARGB8888格式。 CCTexture2DPixelFormat format = kCCTexture2DPixelFormat_RGBA8888; //创建相应的像素绘冲区。 void *data = calloc((int)(POTWide * POTHigh * 4), 1); if (! data) { CCLOG("cocos2d: CCGrid: not enough memory."); this->release(); return false; } //创建一个纹理。 CCTexture2D *pTexture = new CCTexture2D(); //使用像素数据初始化纹理。 pTexture->initWithData(data, format, POTWide, POTHigh, s); //释放像素数据。 free(data); //如果失败,返回false。 if (! pTexture) { CCLOG("cocos2d: CCGrid: error creating texture"); delete this; return false; } //使用创建的纹理初始化 initWithSize(gridSize, pTexture, false); //释放纹理。 pTexture->release(); return true; } //析构。 CCGridBase::~CCGridBase(void) { CCLOGINFO("cocos2d: deallocing %p", this); //释放纹理。 CC_SAFE_RELEASE(m_pTexture); //释放屏幕 CC_SAFE_RELEASE(m_pGrabber); } //设置激活。 void CCGridBase::setActive(bool bActive) { //保存激活设置 m_bActive = bActive; if (! bActive) { //如果未激活,复原投影矩阵。 CCDirector *pDirector = CCDirector::sharedDirector(); ccDirectorProjection proj = pDirector->getProjection(); pDirector->setProjection(proj); } } //设置纹理反转。 void CCGridBase::setTextureFlipped(bool bFlipped) { //如果参数与原设置不同,更新设置并计算顶点缓冲。 if (m_bIsTextureFlipped != bFlipped) { m_bIsTextureFlipped = bFlipped; calculateVertexPoints(); } } //设置2D投影。 void CCGridBase::set2DProjection() { //取得窗口大小 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()) ); //开始设置投影矩阵。 kmGLMatrixMode(KM_GL_PROJECTION); //先将投影矩阵单位化。 kmGLLoadIdentity(); //创建一个矩阵变量。 kmMat4 orthoMatrix; //通过参数生成出投影矩阵变量的值,此处使用的是正交投影矩阵。 kmMat4OrthographicProjection(&orthoMatrix, 0, size.width * CC_CONTENT_SCALE_FACTOR(), 0, size.height * CC_CONTENT_SCALE_FACTOR(), -1, 1); //将原单位化矩阵与正交投影矩阵相乘,当然还是正交投影矩阵了,做为投影矩阵的结果。 kmGLMultMatrix( &orthoMatrix ); //下面开始设置模型观察矩阵。 kmGLMatrixMode(KM_GL_MODELVIEW); //将矩阵单位化。 kmGLLoadIdentity(); //设置投影矩阵。 ccSetProjectionMatrixDirty(); } //在开始渲染前的处理。 void CCGridBase::beforeDraw(void) { // 先取得当前的投影矩阵保存到变量中 CCDirector *director = CCDirector::sharedDirector(); m_directorProjection = director->getProjection(); // 设置新的投影矩阵。 set2DProjection(); // 设置开启屏幕渲染到纹理。 m_pGrabber->beforeRender(m_pTexture); } //在渲染结束后的处理。 void CCGridBase::afterDraw(cocos2d::CCNode *pTarget) { //设置完成屏幕渲染到纹理。 m_pGrabber->afterRender(m_pTexture); //恢复原投影矩阵。 CCDirector *director = CCDirector::sharedDirector(); director->setProjection(m_directorProjection); // if (pTarget->getCamera()->isDirty()) { //取得演示的锚点做为摄像机的矩阵转换中心点。 const CCPoint& offset = pTarget->getAnchorPointInPoints(); //注意:下面三句的意义是对摄像机进行矩阵变换,这个矩阵变换是相对于自身中心点位置,俗称自身矩阵变换。 //先将当前模型矩阵偏移到这个中心点。 kmGLTranslatef(offset.x, offset.y, 0); //进行本地矩阵的转换 pTarget->getCamera()->locate(); //恢复原位置。 kmGLTranslatef(-offset.x, -offset.y, 0); } //设置使用纹理。 ccGLBindTexture2D(m_pTexture->getName()); // restore projection for default FBO .fixed bug #543 #544 //TODO: CCDirector::sharedDirector()->setProjection(CCDirector::sharedDirector()->getProjection()); //TODO: CCDirector::sharedDirector()->applyOrientation(); //进行渲染 blit(); } //进行渲染 void CCGridBase::blit(void) { CCAssert(0, ""); } //还原到原来顶点位置。 void CCGridBase::reuse(void) { CCAssert(0, ""); } //计算顶点缓冲区。 void CCGridBase::calculateVertexPoints(void) { CCAssert(0, ""); } //3D网格类,派生于格子基类, class CC_DLL CCGrid3D : public CCGridBase { public: //构造 CCGrid3D(); //析构 ~CCGrid3D(void); //返回对应位置的顶点。 ccVertex3F vertex(const ccGridSize& pos); //返回对应位置的原始顶点。 ccVertex3F originalVertex(const ccGridSize& pos); //设置对应位置的顶点。 void setVertex(const ccGridSize& pos, const ccVertex3F& vertex); //重载基类的函数。 //渲染。 virtual void blit(void); //还原到原来顶点位置 virtual void reuse(void); //计算顶点缓冲。 virtual void calculateVertexPoints(void); public: //静态函数:详细参数的创建3D网格,参一为格子大小,参二为纹理指针,参三为是否进行纹理反转,内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCGrid3D* gridWithSize(const ccGridSize& gridSize, CCTexture2D *pTexture, bool bFlipped); //静态函数:简单参数的创建3D网格,参数为格子大小,内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCGrid3D* gridWithSize(const ccGridSize& gridSize); //详细参数的创建3D网格 static CCGrid3D* create(const ccGridSize& gridSize, CCTexture2D *pTexture, bool bFlipped); //简单参数的创建3D网格 static CCGrid3D* create(const ccGridSize& gridSize); protected: //顶点缓冲指针_纹理UV GLvoid *m_pTexCoordinates; //顶点缓冲指针_位置 GLvoid *m_pVertices; //原始顶点缓冲指针_位置 GLvoid *m_pOriginalVertices; //索引缓冲指针 GLushort *m_pIndices; }; 对应CPP: //静态函数:详细参数的创建3D网格,参一为格子大小,参二为纹理指针,参三为是否进行纹理反转,内部调用create实现。 CCGrid3D* CCGrid3D::gridWithSize(const ccGridSize& gridSize, CCTexture2D *pTexture, bool bFlipped) { return CCGrid3D::create(gridSize, pTexture, bFlipped); } //上面的create实现 CCGrid3D* CCGrid3D::create(const ccGridSize& gridSize, CCTexture2D *pTexture, bool bFlipped) { //先new出一个当前类实例 CCGrid3D *pRet= new CCGrid3D(); //如果成功 if (pRet) { //初始化并交由内存管理器进行引用计数器的管理 if (pRet->initWithSize(gridSize, pTexture, bFlipped)) { pRet->autorelease(); } else { //如果失败,则释放并置空。 delete pRet; pRet = NULL; } } //返回结果。 return pRet; } //简单参数的创建3D网格,内部调用create实现。 CCGrid3D* CCGrid3D::gridWithSize(const ccGridSize& gridSize) { return CCGrid3D::create(gridSize); } //上面函数的create实现。 CCGrid3D* CCGrid3D::create(const ccGridSize& gridSize) { //先new出一个当前类实例 CCGrid3D *pRet= new CCGrid3D(); //如果成功 if (pRet) { //初始化并交由内存管理器进行引用计数器的管理 if (pRet->initWithSize(gridSize)) { pRet->autorelease(); } else { //如果失败,则释放并置空。 delete pRet; pRet = NULL; } } //返回结果。 return pRet; } //构造 CCGrid3D::CCGrid3D() : m_pTexCoordinates(NULL) , m_pVertices(NULL) , m_pOriginalVertices(NULL) , m_pIndices(NULL) { } //析构 CCGrid3D::~CCGrid3D(void) { //释放各缓冲区 CC_SAFE_FREE(m_pTexCoordinates); CC_SAFE_FREE(m_pVertices); CC_SAFE_FREE(m_pIndices); CC_SAFE_FREE(m_pOriginalVertices); } //进行渲染处理 void CCGrid3D::blit(void) { //计算格子的数量 int n = m_sGridSize.x * m_sGridSize.y; //设置渲染顶点的格式 ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords ); //开启使用Shader m_pShaderProgram->use(); //设置Shader中的 顶点矩阵,其值 = 投影矩阵X模型矩阵X观察矩阵的结果,用于在Shader中进行顶点的最终位置计算。 m_pShaderProgram->setUniformForModelViewProjectionMatrix();; //顶点缓冲中位置属性的指定。 glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, 0, m_pVertices); //顶点缓冲中纹理贴图UV属性的指定。 glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, m_pTexCoordinates); //绘制三角形,每个格子两个三角形,使用GL_TRIANGLES方式进行绘制,则每个格子需要6个顶点。所以顶点数为n*6,渲染时按照索引绘冲m_pIndices所指定的三角形顶点索引来进行绘制。 glDrawElements(GL_TRIANGLES, (GLsizei) n*6, GL_UNSIGNED_SHORT, m_pIndices); //增加一次DP统计值。 CC_INCREMENT_GL_DRAWS(1); } //计算顶点缓冲数据 void CCGrid3D::calculateVertexPoints(void) { //取得纹理的宽高。 float width = (float)m_pTexture->getPixelsWide(); float height = (float)m_pTexture->getPixelsHigh(); //取得纹理中图像区域的高度。 float imageH = m_pTexture->getContentSizeInPixels().height; //定义临时变量。 int x, y, i; //如果相应的顶点缓冲数据有值,先释放干净并置空。 CC_SAFE_FREE(m_pVertices); CC_SAFE_FREE(m_pOriginalVertices); CC_SAFE_FREE(m_pTexCoordinates); CC_SAFE_FREE(m_pIndices); //计算总顶点数,每一行的顶点数为格子列数+1,每一列的顶点数为格子行数+1。 unsigned int numOfPoints = (m_sGridSize.x+1) * (m_sGridSize.y+1); //为相应的顶点缓冲区申请相应大小的内存。 //m_pVertices中存入的是顶点位置,使用ccVertex3F来存储。所申请的内存大小即为顶点数乘以ccVertex3F的字节数大小。 m_pVertices = malloc(numOfPoints * sizeof(ccVertex3F)); //同上。 m_pOriginalVertices = malloc(numOfPoints * sizeof(ccVertex3F)); //纹理UV使用ccVertex2F来存储。 m_pTexCoordinates = malloc(numOfPoints * sizeof(ccVertex2F)); //索引缓冲m_pIndices使用GLushort来存储绘制三角形所用的顶点的索引值,因为每个面3个索引,一个格子就需要6个索引。 m_pIndices = (GLushort*)malloc(m_sGridSize.x * m_sGridSize.y * sizeof(GLushort) * 6); //为了方便填充数据,将缓冲区地址转换成相应的指针变量。 GLfloat *vertArray = (GLfloat*)m_pVertices; GLfloat *texArray = (GLfloat*)m_pTexCoordinates; GLushort *idxArray = m_pIndices; //双循环遍历每个格子。 for (x = 0; x < m_sGridSize.x; ++x) { for (y = 0; y < m_sGridSize.y; ++y) { //取得格子索引。 int idx = (y * m_sGridSize.x) + x; //取得格子的左上角和右下角在图像中的相应位置。m_obStep是每个格子占用的图像点数。 GLfloat x1 = x * m_obStep.x; GLfloat x2 = x1 + m_obStep.x; GLfloat y1 = y * m_obStep.y; GLfloat y2= y1 + m_obStep.y; //取得格子的四个顶点的索引。 GLushort a = (GLushort)(x * (m_sGridSize.y + 1) + y); GLushort b = (GLushort)((x + 1) * (m_sGridSize.y + 1) + y); GLushort c = (GLushort)((x + 1) * (m_sGridSize.y + 1) + (y + 1)); GLushort d = (GLushort)(x * (m_sGridSize.y + 1) + (y + 1)); //按照三角形的绘制方式将格子顶点的索引组成两个三角形的顶点索引。 GLushort tempidx[6] = {a, b, d, b, c, d}; //填充到相应的索引缓冲中。 memcpy(&idxArray[6*idx], tempidx, 6*sizeof(GLushort)); //格子四个顶点的位置。 int l1[4] = {a*3, b*3, c*3, d*3}; ccVertex3F e = {x1, y1, 0}; ccVertex3F f = {x2, y1, 0}; ccVertex3F g = {x2, y2, 0}; ccVertex3F h = {x1, y2, 0}; ccVertex3F l2[4] = {e, f, g, h}; int tex1[4] = {a*2, b*2, c*2, d*2}; CCPoint tex2[4] = {ccp(x1, y1), ccp(x2, y1), ccp(x2, y2), ccp(x1, y2)}; //遍历放入相应的顶点位置和纹理UV中。 for (i = 0; i < 4; ++i) { //顶点的x,y,z vertArray[l1[i]] = l2[i].x; vertArray[l1[i] + 1] = l2[i].y; vertArray[l1[i] + 2] = l2[i].z; //纹理U值 texArray[tex1[i]] = tex2[i].x / width; //纹理V值 if (m_bIsTextureFlipped) { //上下反转 texArray[tex1[i] + 1] = (imageH - tex2[i].y) / height; } else { //正常 texArray[tex1[i] + 1] = tex2[i].y / height; } } } } //将顶点位置数据填充到保存原始顶点位置的内存中。 memcpy(m_pOriginalVertices, m_pVertices, (m_sGridSize.x+1) * (m_sGridSize.y+1) * sizeof(ccVertex3F)); } //返回对应网格位置的顶点。 ccVertex3F CCGrid3D::vertex(const ccGridSize& pos) { //通过网格位置计算出起始顶点的索引。 int index = (pos.x * (m_sGridSize.y+1) + pos.y) * 3; //为方便获取顶点数据,将顶点缓冲地址转换为相应的指针变量。 float *vertArray = (float*)m_pVertices; //通过索引取得相应的顶点x,y,z。 ccVertex3F vert = {vertArray[index], vertArray[index+1], vertArray[index+2]}; //返回顶点 return vert; } //返回网格对应位置的原始顶点。 ccVertex3F CCGrid3D::originalVertex(const ccGridSize& pos) { //通过格子位置计算出起始顶点的索引。 int index = (pos.x * (m_sGridSize.y+1) + pos.y) * 3; //为方便获取顶点数据,将顶点缓冲地址转换为相应的指针变量。 float *vertArray = (float*)m_pOriginalVertices; //通过索引取得相应的顶点x,y,z。 ccVertex3F vert = {vertArray[index], vertArray[index+1], vertArray[index+2]}; //返回顶点 return vert; } //设置对应网格位置的顶点。 void CCGrid3D::setVertex(const ccGridSize& pos, const ccVertex3F& vertex) { //通过格子位置计算出起始顶点的索引。 int index = (pos.x * (m_sGridSize.y + 1) + pos.y) * 3; //为方便获取顶点数据,将顶点缓冲地址转换为相应的指针变量。 float *vertArray = (float*)m_pVertices; 设置对应索引位置的顶点的x,y,z。 vertArray[index] = vertex.x; vertArray[index+1] = vertex.y; vertArray[index+2] = vertex.z; } //还原原来位置。 void CCGrid3D::reuse(void) { //如果还原格子的变量大于0。 if (m_nReuseGrid > 0) { //将原始顶点位置拷贝到顶点缓冲中。 memcpy(m_pOriginalVertices, m_pVertices, (m_sGridSize.x+1) * (m_sGridSize.y+1) * sizeof(ccVertex3F)); //还原格子的变量做减1操作。 --m_nReuseGrid; } } //这也是一个网格类,但它在网格的每个位置点存的不是一个顶点,而是一个四边形格子。 class CC_DLL CCTiledGrid3D : public CCGridBase { public: //构造 CCTiledGrid3D(); //析构 ~CCTiledGrid3D(void); //返回相应位置的格子数据 ccQuad3 tile(const ccGridSize& pos); //返回原始位置的格子数据。 ccQuad3 originalTile(const ccGridSize& pos); //设置相应位置的格子数据。 void setTile(const ccGridSize& pos, const ccQuad3& coords); //重载相应基类函数。 virtual void blit(void); virtual void reuse(void); virtual void calculateVertexPoints(void); public: //静态函数:创建一个当前类的实例。参一为格子大小,参二为对应的纹理,参三为是否进行纹理反转。内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCTiledGrid3D* gridWithSize(const ccGridSize& gridSize, CCTexture2D *pTexture, bool bFlipped); //上面函数的简化版本。 CC_DEPRECATED_ATTRIBUTE static CCTiledGrid3D* gridWithSize(const ccGridSize& gridSize); //对应创建函数的实现。 static CCTiledGrid3D* create(const ccGridSize& gridSize, CCTexture2D *pTexture, bool bFlipped); //对应创建函数的实现。 static CCTiledGrid3D* create(const ccGridSize& gridSize); protected: //所要用到的顶点数据缓冲区。 GLvoid *m_pTexCoordinates; GLvoid *m_pVertices; GLvoid *m_pOriginalVertices; GLushort *m_pIndices; }; 对应CPP: //构造函数。 CCTiledGrid3D::CCTiledGrid3D() : m_pTexCoordinates(NULL) , m_pVertices(NULL) , m_pOriginalVertices(NULL) , m_pIndices(NULL) { } //析构函数。 CCTiledGrid3D::~CCTiledGrid3D(void) { //释放各顶点所用的数据缓冲区。 CC_SAFE_FREE(m_pTexCoordinates); CC_SAFE_FREE(m_pVertices); CC_SAFE_FREE(m_pOriginalVertices); CC_SAFE_FREE(m_pIndices); } //静态函数:创建一个当前类的实例。参一为格子大小,参二为对应的纹理,参三为是否进行纹理反转。内部调用create实现。 CCTiledGrid3D* CCTiledGrid3D::gridWithSize(const ccGridSize& gridSize, CCTexture2D *pTexture, bool bFlipped) { return CCTiledGrid3D::create(gridSize, pTexture, bFlipped); } //上面函数的具体实现。 CCTiledGrid3D* CCTiledGrid3D::create(const ccGridSize& gridSize, CCTexture2D *pTexture, bool bFlipped) { //先用new创建一个当前类的实例。 CCTiledGrid3D *pRet= new CCTiledGrid3D(); //如果成功。 if (pRet) { //进行初始化并交由内存管理器进行引用计数器的管理。 if (pRet->initWithSize(gridSize, pTexture, bFlipped)) { pRet->autorelease(); } else { //如果失败,释放new出来的实例指针并置空。 delete pRet; pRet = NULL; } } //返回实例指针。 return pRet; } //静态函数:创建一个当前类的实例。简化版本。 CCTiledGrid3D* CCTiledGrid3D::gridWithSize(const ccGridSize& gridSize) { return CCTiledGrid3D::create(gridSize); } //上面函数的具体实现。 CCTiledGrid3D* CCTiledGrid3D::create(const ccGridSize& gridSize) { //先用new创建一个当前类的实例。 CCTiledGrid3D *pRet= new CCTiledGrid3D(); if (pRet) { /进行初始化并交由内存管理器进行引用计数器的管理。 if (pRet->initWithSize(gridSize)) { pRet->autorelease(); } else { //如果失败,释放new出来的实例指针并置空。 delete pRet; pRet = NULL; } } //返回实例指针。 return pRet; } //实际渲染处理。 void CCTiledGrid3D::blit(void) { //取得格子的数量。 int n = m_sGridSize.x * m_sGridSize.y; //开启使用shader片段 m_pShaderProgram->use(); //设置Shader中的 顶点矩阵,其值 = 投影矩阵X模型矩阵X观察矩阵的结果,用于在Shader中进行顶点的最终位置计算。 m_pShaderProgram->setUniformForModelViewProjectionMatrix(); //设置顶点缓冲的结构,由位置和纹理坐构构成。 ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position | kCCVertexAttribFlag_TexCoords ); //顶点缓冲中位置属性的指定 glVertexAttribPointer(kCCVertexAttrib_Position, 3, GL_FLOAT, GL_FALSE, 0, m_pVertices); //纹理坐标通道 glVertexAttribPointer(kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, 0, m_pTexCoordinates); //绘制图形 glDrawElements(GL_TRIANGLES, (GLsizei)n*6, GL_UNSIGNED_SHORT, m_pIndices); //更新DP统计 CC_INCREMENT_GL_DRAWS(1); } //计算顶点缓冲的数据 void CCTiledGrid3D::calculateVertexPoints(void) { //取得纹理的宽高 float width = (float)m_pTexture->getPixelsWide(); float height = (float)m_pTexture->getPixelsHigh(); //取得纹理中图像区域的高度。 float imageH = m_pTexture->getContentSizeInPixels().height; //定义临时变量。 int numQuads = m_sGridSize.x * m_sGridSize.y; //如果相应的顶点缓冲数据有值,先释放干净并置空。 CC_SAFE_FREE(m_pVertices); CC_SAFE_FREE(m_pOriginalVertices); CC_SAFE_FREE(m_pTexCoordinates); CC_SAFE_FREE(m_pIndices); //为相应的顶点缓冲区申请相应大小的内存。 m_pVertices = malloc(numQuads*4*sizeof(ccVertex3F)); m_pOriginalVertices = malloc(numQuads*4*sizeof(ccVertex3F)); m_pTexCoordinates = malloc(numQuads*4*sizeof(ccVertex2F)); m_pIndices = (GLushort*)malloc(numQuads*6*sizeof(GLushort)); //为了方便填充数据,将缓冲区地址转换成相应的指针变量。 GLfloat *vertArray = (GLfloat*)m_pVertices; GLfloat *texArray = (GLfloat*)m_pTexCoordinates; GLushort *idxArray = m_pIndices; int x, y; //双循环遍历每个格子。 for (x = 0; x < m_sGridSize.x; ++x) { for (y = 0; y < m_sGridSize.y; ++y) { //取得格子的左上角和右下角在图像中的位置。 float x1 = x * m_obStep.x; float x2 = x1 + m_obStep.x; float y1 = y * m_obStep.y; float y2 = y1 + m_obStep.y; //填充数据 *vertArray++ = x1; *vertArray++ = y1; *vertArray++ = 0; *vertArray++ = x2; *vertArray++ = y1; *vertArray++ = 0; *vertArray++ = x1; *vertArray++ = y2; *vertArray++ = 0; *vertArray++ = x2; *vertArray++ = y2; *vertArray++ = 0; float newY1 = y1; float newY2 = y2; //如果纹理V值上下反转,则做下处理。 if (m_bIsTextureFlipped) { newY1 = imageH - y1; newY2 = imageH - y2; } //填充数据。 *texArray++ = x1 / width; *texArray++ = newY1 / height; *texArray++ = x2 / width; *texArray++ = newY1 / height; *texArray++ = x1 / width; *texArray++ = newY2 / height; *texArray++ = x2 / width; *texArray++ = newY2 / height; } } //索引缓冲数据填充。 for (x = 0; x < numQuads; x++) { idxArray[x*6+0] = (GLushort)(x * 4 + 0); idxArray[x*6+1] = (GLushort)(x * 4 + 1); idxArray[x*6+2] = (GLushort)(x * 4 + 2); idxArray[x*6+3] = (GLushort)(x * 4 + 1); idxArray[x*6+4] = (GLushort)(x * 4 + 2); idxArray[x*6+5] = (GLushort)(x * 4 + 3); } //填充原始位置顶点缓冲 memcpy(m_pOriginalVertices, m_pVertices, numQuads * 12 * sizeof(GLfloat)); } //设置相应位置的格子数据。 void CCTiledGrid3D::setTile(const ccGridSize& pos, const ccQuad3& coords) { //先计算出对应格子的顶点索引 int idx = (m_sGridSize.y * pos.x + pos.y) * 4 * 3; //为了方便填充数据,将缓冲区地址转换成相应的指针变量。 float *vertArray = (float*)m_pVertices; //填充对应的数据 memcpy(&vertArray[idx], &coords, sizeof(ccQuad3)); } //返回原始位置的格子数据。 ccQuad3 CCTiledGrid3D::originalTile(const ccGridSize& pos) { //先计算出对应格子的顶点索引 int idx = (m_sGridSize.y * pos.x + pos.y) * 4 * 3; //为了方便取得数据,将缓冲区地址转换成相应的指针变量。 float *vertArray = (float*)m_pOriginalVertices; //定义变量值做为返回结果。 ccQuad3 ret; //由索引取得数据填充到返回值中。 memcpy(&ret, &vertArray[idx], sizeof(ccQuad3)); return ret; } //返回相应位置的格子数据 ccQuad3 CCTiledGrid3D::tile(const ccGridSize& pos) { //先计算出对应格子的顶点索引 int idx = (m_sGridSize.y * pos.x + pos.y) * 4 * 3; //为了方便取得数据,将缓冲区地址转换成相应的指针变量。 float *vertArray = (float*)m_pVertices; //定义变量值做为返回结果。 ccQuad3 ret; //由索引取得数据填充到返回值中。 memcpy(&ret, &vertArray[idx], sizeof(ccQuad3)); return ret; } //恢复为原始顶点 void CCTiledGrid3D::reuse(void) { //如果还原格子的变量大于0。 if (m_nReuseGrid > 0) { int numQuads = m_sGridSize.x * m_sGridSize.y; //将原始顶点位置拷贝到顶点缓冲中。 memcpy(m_pOriginalVertices, m_pVertices, numQuads * 12 * sizeof(GLfloat)); //还原格子的变量做减1操作。 --m_nReuseGrid; } }
上面讲的是网格的的数据和渲染基类CCGridBase及其派生CCGrid3D和CCTiledGrid3D,CCGridBase的关键是处理了将屏幕渲染到纹理这个功能。
它有两个重要的函数:
//在渲染之前要做的处理。
voidbeforeDraw(void);
//在渲染之后要做的处理。
voidafterDraw(CCNode *pTarget);
我们来看一下这两个函数是在哪里调用的。
经过搜索,我们可以在CCNode.h中发现这一句:
/** A CCGrid object that is used when applying effects */
CC_PROPERTY(CCGridBase *, m_pGrid, Grid)
即每个CCNode有一个CCGridBase类型的成员指针变量。在其cpp的visit中发现了要找的东西。
void CCNode::visit() { if (!m_bIsVisible) { return; } kmGLPushMatrix(); //如果m_pGrid有值并且被激活则开启渲染到纹理。 if (m_pGrid && m_pGrid->isActive()) { m_pGrid->beforeDraw(); } //相应结点的渲染处理。 … //如果m_pGrid有值并且被激活则关闭渲染到纹理,这样当前结点上所有绘制的图像都被输出到m_pGrid中对应的纹理中了。 if (m_pGrid && m_pGrid->isActive()) { m_pGrid->afterDraw(this); } kmGLPopMatrix(); }
而CCGrid3D是一个基本的网格渲染类,他内部有所需要的顶点缓冲区。可以实现带纹理贴图的网格渲染功能。
CCTiledGrid3D比较有趣,它与CCGrid3D的不同之处是它的网格不是由一个个顶点构成,而是由一个个四边形构成,CCGrid3D所表现的是网格各顶点的动画,而CCTiledGrid3D是表现每个格子中的四边形的动画。
OK,下面我们来看一下操控网格数据表现一些基本动画的类。
打开CCActionGrid.h:
#ifndef __ACTION_CCGRID_ACTION_H__ #define __ACTION_CCGRID_ACTION_H__ #include "CCActionInterval.h" #include "CCActionInstant.h" //使用Cocos2d命名空间 NS_CC_BEGIN //用到CCGridBase类的指针。 class CCGridBase; //网格动画基类 class CC_DLL CCGridAction : public CCActionInterval { public: //产生一个当前类的实例拷贝 virtual CCObject* copyWithZone(CCZone* pZone); //设定演示当前动画的演员。 virtual void startWithTarget(CCNode *pTarget); //创建一个反向播放的动画。 virtual CCActionInterval* reverse(void); //初始化网格的大小和动画的时长。 virtual bool initWithSize(const ccGridSize& gridSize, float duration); //返回网格 virtual CCGridBase* getGrid(void); public: //静态创建函数。参数一为格子大小,参数二为动画时长,内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCGridAction* actionWithSize(const ccGridSize& gridSize, float duration); //同上 static CCGridAction* create(const ccGridSize& gridSize, float duration); protected: //格子大小。 ccGridSize m_sGridSize; }; 对应CPP: //静态创建函数。参数一为格子大小,参数二为动画时长,内部调用create实现。 CCGridAction* CCGridAction::actionWithSize(const ccGridSize& gridSize, float duration) { return CCGridAction::create(gridSize, duration); } //同上 CCGridAction* CCGridAction::create(const ccGridSize& gridSize, float duration) { //先new出一个CCGridAction CCGridAction *pAction = new CCGridAction(); if (pAction) { //初始化并放入内存管理器中。 if (pAction->initWithSize(gridSize, duration)) { pAction->autorelease(); } else { //如果初始化失败,释放并置空。 CC_SAFE_DELETE(pAction); } } //返回创建的实例指针,当然,也可能返回NULL。 return pAction; } //初始化函数。 bool CCGridAction::initWithSize(const ccGridSize& gridSize, float duration) { //调用时间动画基类的初始化函数保存动画时长。 if (CCActionInterval::initWithDuration(duration)) { //保存格子大小。 m_sGridSize = gridSize; return true; } return false; } //设置演示当前动画的演员,注意这个演员是什么?不是精灵,而是 void CCGridAction::startWithTarget(CCNode *pTarget) { //调用时间动画基类的相应函数。 CCActionInterval::startWithTarget(pTarget); //取得网格数据类。 CCGridBase *newgrid = this->getGrid(); CCNode *t = m_pTarget; //取得演员的对应网格数据。 CCGridBase *targetGrid = t->getGrid(); //有效性判断,如果演员有网格数据并且已经填充了数据。 if (targetGrid && targetGrid->getReuseGrid() > 0) { //如果网格数据被激活并且其格子大小与本类实例的网格大小相同。 if (targetGrid->isActive() && targetGrid->getGridSize().x == m_sGridSize.x && targetGrid->getGridSize().y == m_sGridSize.y /*&& dynamic_cast<CCGridBase*>(targetGrid) != NULL*/) { //演员的网格数据恢复为原始数据。 targetGrid->reuse(); } else { CCAssert(0, ""); } } else { //如果演员没有创建网格数据或者网格数据已经恢复为原始顶点。 //如果演员的网格数据有效并且是激活状态。 if (targetGrid && targetGrid->isActive()) { //将其设为未激活。 targetGrid->setActive(false); } //将当前类实例的网格数据设置给演员之后激活。 t->setGrid(newgrid); t->getGrid()->setActive(true); } } //纯虚函数:取得网格。做为基类没做任何处理。 CCGridBase* CCGridAction::getGrid(void) { // Abstract class needs implementation CCAssert(0, ""); return NULL; } //创建一个反向播放的动画。 CCActionInterval* CCGridAction::reverse(void) { return CCReverseTime::create(this); } //创建一个当前类实例的拷贝。 CCObject* CCGridAction::copyWithZone(CCZone *pZone) { CCZone* pNewZone = NULL; CCGridAction* pCopy = NULL; //如果pZone有效且已有拷贝数据。 if(pZone && pZone->m_pCopyObject) { //直接取用 pCopy = (CCGridAction*)(pZone->m_pCopyObject); } else { //否则新建一个当前类实例并设为pNewZone的拷贝数据。 pCopy = new CCGridAction(); pZone = pNewZone = new CCZone(pCopy); } //调用基类的相应函数。 CCActionInterval::copyWithZone(pZone); //初始化拷贝 pCopy->initWithSize(m_sGridSize, m_fDuration); //释放临时变量 CC_SAFE_DELETE(pNewZone); return pCopy; } //3D网格动画基类 class CC_DLL CCGrid3DAction : public CCGridAction { public: //取得网格数据。 virtual CCGridBase* getGrid(void); //返回对应网格位置的顶点。 ccVertex3F vertex(const ccGridSize& pos); //返回对应网格位置的原始顶点。 ccVertex3F originalVertex(const ccGridSize& pos); //设置对应网格位置的顶点。 void setVertex(const ccGridSize& pos, const ccVertex3F& vertex); public: //静态创建函数:参一为格子大小,参二为动画时长,内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCGrid3DAction* actionWithSize(const ccGridSize& gridSize, float duration); //上面的create实现。 static CCGrid3DAction* create(const ccGridSize& gridSize, float duration); }; 对应CPP: //取得网格数据。 CCGridBase* CCGrid3DAction::getGrid(void) { //创建一个对应大小的3D网格数据。 return CCGrid3D::create(m_sGridSize); } //返回对应网格位置的顶点。 ccVertex3F CCGrid3DAction::vertex(const ccGridSize& pos) { //取得演员的网格数据,调用网格数据的相应函数。 CCGrid3D *g = (CCGrid3D*)m_pTarget->getGrid(); return g->vertex(pos); } //返回对应网格位置的原始顶点。 ccVertex3F CCGrid3DAction::originalVertex(const ccGridSize& pos) { //取得演员的网格数据,调用网格数据的相应函数。 CCGrid3D *g = (CCGrid3D*)m_pTarget->getGrid(); return g->originalVertex(pos); } //设置对应网格位置的顶点。 void CCGrid3DAction::setVertex(const ccGridSize& pos, const ccVertex3F& vertex) { //取得演员的网格数据,调用网格数据的相应函数。 CCGrid3D *g = (CCGrid3D*)m_pTarget->getGrid(); g->setVertex(pos, vertex); } //派生于CCGridAction的3D格子动画类。 class CC_DLL CCTiledGrid3DAction : public CCGridAction { public: //返回对应位置的格子顶点数据。 ccQuad3 tile(const ccGridSize& pos); //返回对应位置的原始格子顶点数据。 ccQuad3 originalTile(const ccGridSize& pos); //设置对应位置的格子数据。 void setTile(const ccGridSize& pos, const ccQuad3& coords); //返回网格数据。 virtual CCGridBase* getGrid(void); public: //静态创建函数:参一为格子大小,参二为动画时长,内部调用creat来实现。 CC_DEPRECATED_ATTRIBUTE static CCTiledGrid3DAction* actionWithSize(const ccGridSize& gridSize, float duration); //上面的create实现。 static CCTiledGrid3DAction* create(const ccGridSize& gridSize, float duration); }; //取得网格数据。 CCGridBase* CCTiledGrid3DAction::getGrid(void) { return CCTiledGrid3D::create(m_sGridSize); } //返回对应位置的格子顶点数据。 ccQuad3 CCTiledGrid3DAction::tile(const ccGridSize& pos) { //取得演员的网格数据,调用网格数据的相应函数。 CCTiledGrid3D *g = (CCTiledGrid3D*)m_pTarget->getGrid(); return g->tile(pos); } //返回对应位置的原始格子顶点数据。 ccQuad3 CCTiledGrid3DAction::originalTile(const ccGridSize& pos) { //取得演员的网格数据,调用网格数据的相应函数。 CCTiledGrid3D *g = (CCTiledGrid3D*)m_pTarget->getGrid(); return g->originalTile(pos); } //设置对应位置的格子数据。 void CCTiledGrid3DAction::setTile(const ccGridSize& pos, const ccQuad3& coords) { //取得演员的网格数据,调用网格数据的相应函数。 CCTiledGrid3D *g = (CCTiledGrid3D*)m_pTarget->getGrid(); return g->setTile(pos, coords); } //网格所用的变速动画基类:先加速再减速。 class CC_DLL CCAccelDeccelAmplitude : public CCActionInterval { public: //析构 virtual ~CCAccelDeccelAmplitude(void); //初始化 bool initWithAction(CCAction *pAction, float duration); //设置演示动画的演员。 virtual void startWithTarget(CCNode *pTarget); //更新动画。 virtual void update(float time); //创建一个当前类的反向动画实例。 virtual CCActionInterval* reverse(void); //取得速率 inline float getRate(void) { return m_fRate; } //设置速率 inline void setRate(float fRate) { m_fRate = fRate; } public: //静态创建函数:参一为动画实例指针,参二为动画时长,内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCAccelDeccelAmplitude* actionWithAction(CCAction *pAction, float duration); //上面函数的create实现。 static CCAccelDeccelAmplitude* create(CCAction *pAction, float duration); protected: //速率 float m_fRate; //所控制的时间动画。 CCActionInterval *m_pOther; }; 对应CPP: //静态创建函数:参一为动画实例指针,参二为动画时长,内部调用create实现。 CCAccelDeccelAmplitude* CCAccelDeccelAmplitude::actionWithAction(CCAction *pAction, float duration) { return CCAccelDeccelAmplitude::create(pAction, duration); } //上面函数的create实现。 CCAccelDeccelAmplitude* CCAccelDeccelAmplitude::create(CCAction *pAction, float duration) { //先new出一个当前类的实例。 CCAccelDeccelAmplitude *pRet = new CCAccelDeccelAmplitude(); if (pRet) { //初始化并交由内存管理器去进行引用计数器的管理。 if (pRet->initWithAction(pAction, duration)) { pRet->autorelease(); } else { //如果失败,释放并置空。 CC_SAFE_DELETE(pRet); } } //返回创建的当前类实例,当然可能为NULL。 return pRet; } //初始化处理。 bool CCAccelDeccelAmplitude::initWithAction(CCAction *pAction, float duration) { //调用基类的初始化函数。 if (CCActionInterval::initWithDuration(duration)) { //保存相应的变量值,占用控制的动画故对其引用计数加一。 m_fRate = 1.0f; m_pOther = (CCActionInterval*)(pAction); pAction->retain(); return true; } return false; } //析构 CCAccelDeccelAmplitude::~CCAccelDeccelAmplitude(void) { //不再占用控制的时间动画,对其引用计数器减一。 CC_SAFE_RELEASE(m_pOther); } //设置演示当前动画的演员。 void CCAccelDeccelAmplitude::startWithTarget(CCNode *pTarget) { //先调用基类的相应函数。 CCActionInterval::startWithTarget(pTarget); //设置控制动画使用此演员。 m_pOther->startWithTarget(pTarget); } //更新动画。 void CCAccelDeccelAmplitude::update(float time) { //创建一个f值变为进度的2倍。 float f = time * 2; //如果动画时间大于一半,让进度由1再变为0。 if (f > 1) { f -= 1; f = 1 - f; } //使用powf处理来形成一个变速曲线公式来达到变速目的。 ((CCAccelDeccelAmplitude*)(m_pOther))->setAmplitudeRate(powf(f, m_fRate)); } //创建当前类的反向动画实例。 CCActionInterval* CCAccelDeccelAmplitude::reverse(void) { return CCAccelDeccelAmplitude::create(m_pOther->reverse(), m_fDuration); } //网格所用的变速动画基类:加速动画。 class CC_DLL CCAccelAmplitude : public CCActionInterval { public: //析构 ~CCAccelAmplitude(void); //初始化处理。 bool initWithAction(CCAction *pAction, float duration); //取得速率 inline float getRate(void) { return m_fRate; } //设置速率 inline void setRate(float fRate) { m_fRate = fRate; } //设置演示当前动画的演员。 virtual void startWithTarget(CCNode *pTarget); //更新动画 virtual void update(float time); //创建一个当前动画的反向动画实例。 virtual CCActionInterval* reverse(void); public: //静态创建函数:参一为一个时间动画,参二为时长。内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCAccelAmplitude* actionWithAction(CCAction *pAction, float duration); //上面函数的create实现。 static CCAccelAmplitude* create(CCAction *pAction, float duration); protected: //速率 float m_fRate; //控制动画。 CCActionInterval *m_pOther; }; 对应CPP: //静态创建函数:参一为一个时间动画,参二为时长。内部调用create实现。 CCAccelAmplitude* CCAccelAmplitude::actionWithAction(CCAction *pAction, float duration) { return CCAccelAmplitude::create(pAction, duration); } //上面函数的create实现。 CCAccelAmplitude* CCAccelAmplitude::create(CCAction *pAction, float duration) { //创建三部曲,new,初始化,autorelease,一个不少。 CCAccelAmplitude *pRet = new CCAccelAmplitude(); if (pRet) { if (pRet->initWithAction(pAction, duration)) { pRet->autorelease(); } else { //当然,要有失败处理。 CC_SAFE_DELETE(pRet); } } return pRet; } //初始化 bool CCAccelAmplitude::initWithAction(CCAction *pAction, float duration) { //先调用基类的初始化函数。 if (CCActionInterval::initWithDuration(duration)) { //速率设为1.0。 m_fRate = 1.0f; //保存动画。占用对其引用计数加一。 m_pOther = (CCActionInterval*)(pAction); pAction->retain(); return true; } return false; } //析构函数。 CCAccelAmplitude::~CCAccelAmplitude(void) { //不占用,对其引用计数减一。 CC_SAFE_DELETE(m_pOther); } //设置演示当前动画的演员。 void CCAccelAmplitude::startWithTarget(CCNode *pTarget) { //调用基类的相应函数。 CCActionInterval::startWithTarget(pTarget); //设置控制动画使用此演员。 m_pOther->startWithTarget(pTarget); } //动画更新处理。 void CCAccelAmplitude::update(float time) { //创建变速曲线来设置控制动画的速率。 ((CCAccelAmplitude*)(m_pOther))->setAmplitudeRate(powf(time, m_fRate)); //更新控制动画。 m_pOther->update(time); } //创建一个当前类的反向动画实例。 CCActionInterval* CCAccelAmplitude::reverse(void) { return CCAccelAmplitude::create(m_pOther->reverse(), m_fDuration); } //网格所用的变速动画基类:减速动画。 class CC_DLL CCDeccelAmplitude : public CCActionInterval { public: //析构函数。 ~CCDeccelAmplitude(void); //初始化 bool initWithAction(CCAction *pAction, float duration); //取得速率 inline float getRate(void) { return m_fRate; } //设置速率 inline void setRate(float fRate) { m_fRate = fRate; } //设置演示当前动画的演员。 virtual void startWithTarget(CCNode *pTarget); //更新处理。 virtual void update(float time); //创建一个当前类的反向播放的实例。 virtual CCActionInterval* reverse(void); public: //静态创建函数,参一为一个动画,参二为动画时长。内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCDeccelAmplitude* actionWithAction(CCAction *pAction, float duration); //上面的create实现。 static CCDeccelAmplitude* create(CCAction *pAction, float duration); protected: //速率 float m_fRate; //控制动画。 CCActionInterval *m_pOther; }; 对应CPP: //静态创建函数,参一为一个动画,参二为动画时长。内部调用create实现。 CCDeccelAmplitude* CCDeccelAmplitude::actionWithAction(CCAction *pAction, float duration) { return CCDeccelAmplitude::create(pAction, duration); } //上面的create实现。 CCDeccelAmplitude* CCDeccelAmplitude::create(CCAction *pAction, float duration) { //new, initWithAction, autorelease三部曲,当然别忘了失败处理。 CCDeccelAmplitude *pRet = new CCDeccelAmplitude(); if (pRet) { if (pRet->initWithAction(pAction, duration)) { pRet->autorelease(); } else { CC_SAFE_DELETE(pRet); } } return pRet; } //初始化处理。 bool CCDeccelAmplitude::initWithAction(CCAction *pAction, float duration) { //先调用基类的初始化处理。 if (CCActionInterval::initWithDuration(duration)) { //速率置1。 m_fRate = 1.0f; //占用控制动画,对其引用计数器加一。 m_pOther = (CCActionInterval*)(pAction); pAction->retain(); return true; } return false; } //析构 CCDeccelAmplitude::~CCDeccelAmplitude(void) { //不再占用控制动画,对其引用计数器减一。 CC_SAFE_RELEASE(m_pOther); } //设置演示当前动画的演员。 void CCDeccelAmplitude::startWithTarget(CCNode *pTarget) { //调用基类的相应函数。 CCActionInterval::startWithTarget(pTarget); //调用控制动画的相应函数。 m_pOther->startWithTarget(pTarget); } //更新动画。 void CCDeccelAmplitude::update(float time) { //与加速动画有什么不同?很容易明白。 ((CCDeccelAmplitude*)(m_pOther))->setAmplitudeRate(powf((1 - time), m_fRate)); m_pOther->update(time); } //创建一个当前类的反向动画实例。 CCActionInterval* CCDeccelAmplitude::reverse(void) { return CCDeccelAmplitude::create(m_pOther->reverse(), m_fDuration); } //停止网格动画。 class CC_DLL CCStopGrid : public CCActionInstant { public: //设置演示该动画的演员。 virtual void startWithTarget(CCNode *pTarget); public: //静态函数:创建一个当前类的实例动画,内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCStopGrid* action(void); //上面的create实现。 static CCStopGrid* create(void); }; 对应CPP: //设置演示当前动画的演员。 void CCStopGrid::startWithTarget(CCNode *pTarget) { //调用基类的相应函数。 CCActionInstant::startWithTarget(pTarget); //取得演员所用的网格数据。 CCGridBase *pGrid = m_pTarget->getGrid(); if (pGrid && pGrid->isActive()) { //如果是激活状态,取消激活。 pGrid->setActive(false); } } //静态函数:创建一个当前类的实例动画,内部调用create实现。 CCStopGrid* CCStopGrid::action(void) { return CCStopGrid::create(); } //上面的create实现。 CCStopGrid* CCStopGrid::create(void) { CCStopGrid* pAction = new CCStopGrid(); pAction->autorelease(); return pAction; } //恢复网格动画。 class CC_DLL CCReuseGrid : public CCActionInstant { public: //初始化 bool initWithTimes(int times); //设置演示当前动画的演员。 virtual void startWithTarget(CCNode *pTarget); public: //静态函数:创建一个当前类的实例。参数为,内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCReuseGrid* actionWithTimes(int times); //上面函数的create实现。 static CCReuseGrid* create(int times); protected: // int m_nTimes; }; 对应CPP: // CCReuseGrid* CCReuseGrid::actionWithTimes(int times) { return CCReuseGrid::create(times); } //上面函数的create实现。 CCReuseGrid* CCReuseGrid::create(int times) { //先new出一个当前类的实例。 CCReuseGrid *pAction = new CCReuseGrid(); if (pAction) { //进行初始化,成功后交由内存管理器进行引用计数器的管理。 if (pAction->initWithTimes(times)) { pAction->autorelease(); } else { //初始化失败则释放并置空。 CC_SAFE_DELETE(pAction); } } //返回创建的动画实例,当然,也可能为NULL。 return pAction; } //初始化。 bool CCReuseGrid::initWithTimes(int times) { m_nTimes = times; return true; } //设置演示当前动画的演员。 void CCReuseGrid::startWithTarget(CCNode *pTarget) { //调用基类的相应函数。 CCActionInstant::startWithTarget(pTarget); //如果演员有正在使用的网格动画。 if (m_pTarget->getGrid() && m_pTarget->getGrid()->isActive()) { //取得这个网格动画并激活。 m_pTarget->getGrid()->setReuseGrid(m_pTarget->getGrid()->getReuseGrid() + m_nTimes); } }
有了网格基本动画,我们再来看一下3D网格基本动画
CCActionGrid3D.h/cpp:
#include "CCActionGrid.h" NS_CC_BEGIN //波浪动画 class CC_DLL CCWaves3D : public CCGrid3DAction public: //取得和设置振幅 inline float getAmplitude(void) { return m_fAmplitude; } inline void setAmplitude(float fAmplitude) { m_fAmplitude = fAmplitude; } //取得和设置频率 inline float getAmplitudeRate(void) { return m_fAmplitudeRate; } inline void setAmplitudeRate(float fAmplitudeRate) { m_fAmplitudeRate = fAmplitudeRate; } //初始化 bool initWithWaves(int wav, float amp, const ccGridSize& gridSize, float duration); //产生一个实例的拷贝。 virtual CCObject* copyWithZone(CCZone* pZone); //动画更新。 virtual void update(float time); public: //创建一个波浪动画,参一为波浪动画摇动的次数,参二为波浪的振幅,参三为格子的大小,参四为动画时长。 CC_DEPRECATED_ATTRIBUTE static CCWaves3D* actionWithWaves(int wav, float amp, const ccGridSize& gridSize, float duration); //上面的create实现。 static CCWaves3D* create(int wav, float amp, const ccGridSize& gridSize, float duration); protected: //波浪摇动的次数。 int m_nWaves; //摇动的振幅。 float m_fAmplitude; //摇动的频率。 float m_fAmplitudeRate; }; CPP实现: //创建一个波浪动画,参一为波浪动画摇动的次数,参二为波浪的振幅,参三为格子的大小,参四为动画时长。 CCWaves3D* CCWaves3D::actionWithWaves(int wav, float amp, const ccGridSize& gridSize, float duration) { return CCWaves3D::create(wav, amp, gridSize, duration); } //上面的create实现。 CCWaves3D* CCWaves3D::create(int wav, float amp, const ccGridSize& gridSize, float duration) { //new,初始化,autorelease,失败处理。 CCWaves3D *pAction = new CCWaves3D(); if (pAction) { if (pAction->initWithWaves(wav, amp, gridSize, duration)) { pAction->autorelease(); } else { CC_SAFE_RELEASE_NULL(pAction); } } return pAction; } //初始化。 bool CCWaves3D::initWithWaves(int wav, float amp, const ccGridSize& gridSize, float duration) { //先调用基类的初始化函数。 if (CCGrid3DAction::initWithSize(gridSize, duration)) { //保存相应的参数。 m_nWaves = wav; m_fAmplitude = amp; m_fAmplitudeRate = 1.0f; return true; } return false; } //产生一个当前类的实例。 CCObject* CCWaves3D::copyWithZone(CCZone *pZone) { CCZone* pNewZone = NULL; CCWaves3D* pCopy = NULL; if(pZone && pZone->m_pCopyObject) { //in case of being called at sub class pCopy = (CCWaves3D*)(pZone->m_pCopyObject); } else { pCopy = new CCWaves3D(); pZone = pNewZone = new CCZone(pCopy); } CCGrid3DAction::copyWithZone(pZone); pCopy->initWithWaves(m_nWaves, m_fAmplitude, m_sGridSize, m_fDuration); CC_SAFE_DELETE(pNewZone); return pCopy; } //更新动画。 void CCWaves3D::update(float time) { //双循环遍历格子。 int i, j; for (i = 0; i < m_sGridSize.x + 1; ++i) { for (j = 0; j < m_sGridSize.y + 1; ++j) { //取出当前格子原始顶点,对z值进行sin曲线的变化加值。形成来回摇动的效果。 ccVertex3F v = originalVertex(ccg(i ,j)); v.z += (sinf((float)M_PI * time * m_nWaves * 2 + (v.y+v.x) * .01f) * m_fAmplitude * m_fAmplitudeRate); CCLog("v.z offset is %f\n", (sinf((float)M_PI * time * m_nWaves * 2 + (v.y+v.x) * .01f) * m_fAmplitude * m_fAmplitudeRate)); //将顶点设置到格子中。 setVertex(ccg(i, j), v); } } } 对应图:
//绕X轴翻转。
class CC_DLL CCFlipX3D : public CCGrid3DAction
{
public:
//初始化动画。
bool initWithDuration(float duration);
//初始化格子大小。
virtual bool initWithSize(const ccGridSize& gridSize, float duration);
//产生一个当前类的实例拷贝。
virtual CCObject* copyWithZone(CCZone* pZone);
//动画更新。
virtual void update(float time);public:
//创建一个绕X轴翻转的3D网格动画。参数为动画时长。
CC_DEPRECATED_ATTRIBUTE static CCFlipX3D* actionWithDuration(float duration);
//上面的create实现。
static CCFlipX3D* create(float duration);
};对应的CPP实