[Cocos2d-x相关教程来源于红孩儿的游戏编程之路CSDN博客地址:http://blog.csdn.net/honghaier]
红孩儿Cocos2d-X学习园地QQ2群:44208467 加群写:Cocos2d-x
红孩儿Cocos2d-X学习园地QQ群:249941957 [暂满]加群写:Cocos2d-x
本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!
另:本章所用Cocos2d-x版本为:
cocos2d-2.0-x-2.0.2 @ Aug 30 2012
http://cn.cocos2d-x.org/download
在Cocos2d-x 2.0中有一个工程例子叫"ActionsProgressTest",故明思义是进度动画的演示,应该说在几乎所有的RPG类游戏中我们都会看到进度条,所以进度动画的作用之大是无须我多言。我们本节就来学习一下Cocos2d-x 2.0的进度动画。
首先,先把那个"ActionsProgressTest"放一边,我要做的不是让大家去知其然,而是要知其所以然。所以我们要学习一下进度动画的原理与实现,在此我们向大家介绍一下所要用到的三个关键功能类:
1.CCProgressTimer : 进度动画的渲染器,核心实现了进度动画的显示功能,本身是一个结点,通过渲染一个精灵来表现进度的变化。
2.CCProgressTo: TO进度控制器,控制进度从当前进度变化到某个值.
3.CCProgressFromTo: FromTo进度控制器,控制进度从一个指定值到另一个值的变化.
咱们先来看一下CCProgressTimer,这个类是整个进度动画的重中之重。
打开CCProgressTimer .h:
#ifndef __MISC_NODE_CCPROGRESS_TIMER_H__ #define __MISC_NODE_CCPROGRESS_TIMER_H__ //使用精灵类 #include "sprite_nodes/CCSprite.h" //使用Cocos2d命名空间 NS_CC_BEGIN //定义一个枚举,代表两种不同的进度动画目标渲染类型。 typedef enum { ///绕圆心转动的进度动画 kCCProgressTimerTypeRadial, ///条形的进度动画。 kCCProgressTimerTypeBar, } CCProgressTimerType; //由CCNode和CCRGBAProtocol派生出CCProgressTimer类。 class CC_DLL CCProgressTimer : public CCNode, public CCRGBAProtocol { public: //构造函数。 CCProgressTimer(); //析构函数。 ~CCProgressTimer(void); //取得当前的进度动画目标渲染类型 inline CCProgressTimerType getType(void) { return m_eType; } //返回当前时刻的进度值。 inline float getPercentage(void) {return m_fPercentage; } //取得当前进度动画所渲染的精灵。 inline CCSprite* getSprite(void) { return m_pSprite; } //设置当前进度动画所渲染的精灵,并初始化动画。 bool initWithSprite(CCSprite* sp); //设置当前时刻的进度值。 void setPercentage(float fPercentage); //设置当前进度动画所渲染的精灵 void setSprite(CCSprite *pSprite); //设置当前的进度动画目标渲染类型 void setType(CCProgressTimerType type); //设置是否反向播放进度动画。 void setReverseProgress(bool reverse); //渲染当前结点。 virtual void draw(void); //设置锚点位置。 void setAnchorPoint(CCPoint anchorPoint); //设置颜色。 virtual void setColor(const ccColor3B& color); //取得颜色。 virtual const ccColor3B& getColor(void); //取得透明度。 virtual GLubyte getOpacity(void); //设置透明度。 virtual void setOpacity(GLubyte opacity); //设置透明度是否按颜色值变动而变动。 virtual void setOpacityModifyRGB(bool bValue); //取得透明度是否按颜色值变动而变动。 virtual bool isOpacityModifyRGB(void); //取得是否是按反方向播放动画。 inline bool isReverseDirection() { return m_bReverseDirection; }; //设置是否按反方向播放动画。 inline void setReverseDirection(bool value) { m_bReverseDirection = value; }; public: //静态函数,创建一个进度时间控制器,内部调用create实现. CC_DEPRECATED_ATTRIBUTE static CCProgressTimer* progressWithSprite(CCSprite* sp); //同上. static CCProgressTimer* create(CCSprite* sp); protected: //由一个二维插值参数值计算出纹理坐标U,V。 ccTex2F textureCoordFromAlphaPoint(CCPoint alpha); //由一个二维插值参数值计算出顶点坐标X,Y。 ccVertex2F vertexFromAlphaPoint(CCPoint alpha); //更新进度 void updateProgress(void); //更新条形进度的状态 void updateBar(void); //更新绕圆心转动的状态 void updateRadial(void); //更新颜色 void updateColor(void); // CCPoint boundaryTexCoord(char index); protected: //进度动画目标渲染类型 CCProgressTimerType m_eType; //当前的进度值 float m_fPercentage; //所渲染的精灵. CCSprite *m_pSprite; //顶点数量 int m_nVertexDataCount; //顶点数据指针。 ccV2F_C4B_T2F *m_pVertexData; //这里是一个称为“中点”的成员变量值,但它的意义绝不是这么简单,如果是绕中心旋转的进度动画,它就是中心,如果是条形动画,它代表了UV随进度变化的起始位置,如果是在最左边,其值为(0,y),如果是在最右边,其值为(1,y),如果在最上面(x,0),如果在最下面(x,1);如果是在中心(0.5,0.5)。 CC_PROPERTY(CCPoint, m_tMidpoint, Midpoint); //设里是一个表示动画方向的成员变量值,如果是横向方向,则设为(1,0),如果是纵向方向,则设为(0,1); CC_SYNTHESIZE(CCPoint, m_tBarChangeRate, BarChangeRate); //bool变量,代表是否反方向播放动画。 bool m_bReverseDirection; }; NS_CC_END #endif
再来看CPP文件:
#include "CCProgressTimer.h" #include "ccMacros.h" #include "textures/CCTextureCache.h" #include "support/CCPointExtension.h" #include "shaders/CCGLProgram.h" #include "shaders/CCShaderCache.h" #include "shaders/ccGLStateCache.h" #include "CCDirector.h" #include "support/TransformUtils.h" #include "CCDrawingPrimitives.h" // extern #include "kazmath/GL/matrix.h" #include <float.h> //使用Cocos2d命名空间 NS_CC_BEGIN //定义动画的四边形的四个角的纹理UV数量 #define kProgressTextureCoordsCount 4 // 将动画的四边形的四个角的纹理UV按位压缩到一个char中,从左下角{0,1}起,然后左上角{0,0},右上角{1,0},最后右下角{1,1},对应为0x4b,也就是01001011啊,貌似很方便,其实很坑爹。 const char kCCProgressTextureCoords = 0x4b; //构造函数 CCProgressTimer::CCProgressTimer() :m_eType(kCCProgressTimerTypeRadial) ,m_fPercentage(0.0f) ,m_pSprite(NULL) ,m_nVertexDataCount(0) ,m_pVertexData(NULL) ,m_tMidpoint(0,0) ,m_tBarChangeRate(0,0) ,m_bReverseDirection(false) {} //静态函数,创建一个进度时间控制器,内部调用create实现.CCProgressTimer* CCProgressTimer::progressWithSprite(CCSprite* sp) { return CCProgressTimer::create(sp); } //同上 CCProgressTimer* CCProgressTimer::create(CCSprite* sp) { //先用new创建一个CCProgressTimer 实例对象。 CCProgressTimer *pProgressTimer = new CCProgressTimer(); //对其进行初始化,这里应该先做一个有效性判断。 if (pProgressTimer->initWithSprite(sp)) { //交由内存管理器进行释放。 pProgressTimer->autorelease(); } else { //如果初始化失败,删除实例并置空。 delete pProgressTimer; pProgressTimer = NULL; } //返回新创建的CCProgressTimer 实例对象指针。 return pProgressTimer; } //初始化。 bool CCProgressTimer::initWithSprite(CCSprite* sp) { //设置当前进度为0 setPercentage(0.0f); //设置顶点BUF的指针为空,顶点数量为0,。 m_pVertexData = NULL; m_nVertexDataCount = 0; //设置锚点。 setAnchorPoint(ccp(0.5f,0.5f)); //设置动画形式为绕圆心旋转。 m_eType = kCCProgressTimerTypeRadial; //正方向播放动画。 m_bReverseDirection = false; //设置圆心位置。 setMidpoint(ccp(0.5f, 0.5f)); // setBarChangeRate(ccp(1,1)); //设置渲染的精灵。 setSprite(sp); // 设置使用的Shader代码片段.动画的效果由Shader来渲染。 setShaderProgram(CCShaderCache::sharedShaderCache()->programForKey(kCCShader_PositionTextureColor)); return true; } //析构函数。 CCProgressTimer::~CCProgressTimer(void) { //释放顶点BUF,并对所占用的精灵进行引用计数减1操作。 CC_SAFE_FREE(m_pVertexData); CC_SAFE_RELEASE(m_pSprite); } //设置当前的进度值。 void CCProgressTimer::setPercentage(float fPercentage) { //如果进度值有更新。 if (m_fPercentage != fPercentage) { //先将进度值限定在0~100间,然后更新进度。 m_fPercentage = clampf(fPercentage, 0, 100); updateProgress(); } } //设置演示当前动画的精灵。 void CCProgressTimer::setSprite(CCSprite *pSprite) { //先做个判断,防止重复进行设置。 if (m_pSprite != pSprite) { //占用新的精灵,对其引用计数器加1 CC_SAFE_RETAIN(pSprite); //释放老的精灵,对其引用计数器减1 CC_SAFE_RELEASE(m_pSprite); //将新的精灵设置为当前演示动画的精灵。 m_pSprite = pSprite; //设置绘制当前精灵的区域大小 setContentSize(m_pSprite->getContentSize()); // 如果顶点BUF有值, if (m_pVertexData) { //释放并置空 CC_SAFE_FREE(m_pVertexData); //设置顶点数量为0 m_nVertexDataCount = 0; } } } //设置当前的进度动画目标渲染类型 void CCProgressTimer::setType(CCProgressTimerType type) { //做个判断,避免重复设置。 if (type != m_eType) { // 如果顶点BUF有值, if (m_pVertexData) { //释放并置空 CC_SAFE_FREE(m_pVertexData); m_pVertexData = NULL; //设置顶点数量为0 m_nVertexDataCount = 0; } //设置进度动画目标渲染类型. m_eType = type; } } //设置是否按反方向播放动画。 void CCProgressTimer::setReverseProgress(bool reverse) { //做个判断,避免重复设置。 if( m_bReverseDirection != reverse ) { m_bReverseDirection = reverse; //如果顶点BUF有值,释放并置空并置顶点数量为0 CC_SAFE_FREE(m_pVertexData); m_nVertexDataCount = 0; } } //设置颜色。 void CCProgressTimer::setColor(const ccColor3B& color) { //对所控制的精灵进行设置颜色。 m_pSprite->setColor(color); //更新颜色。 updateColor(); } //取得颜色。 const ccColor3B& CCProgressTimer::getColor(void) { //由所控制的精灵取得颜色。 return m_pSprite->getColor(); } //设置透明度。 void CCProgressTimer::setOpacity(GLubyte opacity) { //由所控制的精灵设置透明度。 m_pSprite->setOpacity(opacity); //更新颜色。 updateColor(); } //取得透明度。 GLubyte CCProgressTimer::getOpacity(void) { //由所控制的精灵取得透明度。 return m_pSprite->getOpacity(); } //设置透明度是否按颜色值变动而变动。 void CCProgressTimer::setOpacityModifyRGB(bool bValue) { CC_UNUSED_PARAM(bValue); } //取得透明度是否按颜色值变动而变动。 bool CCProgressTimer::isOpacityModifyRGB(void) { return false; } //根据一个二维插值参数值计算当前进度的纹理UV。 ccTex2F CCProgressTimer::textureCoordFromAlphaPoint(CCPoint alpha) { //定义一个2维纹理坐标结构,初始化u,v为0,0 ccTex2F ret = {0.0f, 0.0f}; //如果没有操控的精灵,返回. if (!m_pSprite) { return ret; } //取得精灵的绘制四边形的顶点 ccV3F_C4B_T2F_Quad quad = m_pSprite->getQuad(); //左下角的纹理坐标存入min CCPoint min = ccp(quad.bl.texCoords.u,quad.bl.texCoords.v); //右上角的纹理坐标存入max CCPoint max = ccp(quad.tr.texCoords.u,quad.tr.texCoords.v); //如果精灵切换需要旋转90度,这里交换一下x,y值. if (m_pSprite->isTextureRectRotated()) { CC_SWAP(alpha.x, alpha.y, float); } //通过插值计算出对应的UV坐标位置存入一个ccTex2F 值返回. return tex2(min.x * (1.f - alpha.x) + max.x * alpha.x, min.y * (1.f - alpha.y) + max.y * alpha.y); } //根据一个二维插值参数值计算当前进度的顶点X,Y。 ccVertex2F CCProgressTimer::vertexFromAlphaPoint(CCPoint alpha) { //定义一个2维顶点值,初始化x,y为0,0 ccVertex2F ret = {0.0f, 0.0f}; //如果没有操控的精灵,返回. if (!m_pSprite) { return ret; } //取得精灵的绘制四边形的顶点 ccV3F_C4B_T2F_Quad quad = m_pSprite->getQuad(); //左下角的位置存入min CCPoint min = ccp(quad.bl.vertices.x,quad.bl.vertices.y); //右上角的位置存入max CCPoint max = ccp(quad.tr.vertices.x,quad.tr.vertices.y); //通过插值计算出对应的顶点位置存入ret. ret.x = min.x * (1.f - alpha.x) + max.x * alpha.x; ret.y = min.y * (1.f - alpha.y) + max.y * alpha.y; return ret; } //更新颜色 void CCProgressTimer::updateColor(void) { //如果没有操控的精灵,返回. if (!m_pSprite) { return; } //如果有顶点数据. if (m_pVertexData) { //取得左上角顶点的色彩存入临时色彩变量sc,然后遍历所有顶点设置为sc。 ccColor4B sc = m_pSprite->getQuad().tl.colors; for (int i = 0; i < m_nVertexDataCount; ++i) { m_pVertexData[i].colors = sc; } } } //更新进度。 void CCProgressTimer::updateProgress(void) { switch (m_eType) { //绕圆心转动的进度动画。 case kCCProgressTimerTypeRadial: //更新旋转 updateRadial(); break; //条形进度动画。 case kCCProgressTimerTypeBar: //更新条形进度。 updateBar(); break; default: break; } } //设置锚点。 void CCProgressTimer::setAnchorPoint(CCPoint anchorPoint) { CCNode::setAnchorPoint(anchorPoint); } //取得中点。 CCPoint CCProgressTimer::getMidpoint(void) { return m_tMidpoint; } //设置中点。 void CCProgressTimer::setMidpoint(CCPoint midPoint) { //将x,y限定到0~1间。 m_tMidpoint = ccpClamp(midPoint, CCPointZero, ccp(1,1)); } //绕中心旋转的进度动画的更新 void CCProgressTimer::updateRadial(void) { //如果没有操控的精灵直接返回。 if (!m_pSprite) { return; } //将进度值计算成百分比. float alpha = m_fPercentage / 100.f; //根据百分比计算出旋转的角度. float angle = 2.f*((float)M_PI) * ( m_bReverseDirection ? alpha : 1.0f - alpha); //将顶点中间的位置存入topMid. CCPoint topMid = ccp(m_tMidpoint.x, 1.f); //将topMid绕m_tMidpoint旋转angle度,将结果存入percentagePt。 CCPoint percentagePt = ccpRotateByAngle(topMid, m_tMidpoint, angle); //定义一个索引值 int index = 0; //定义一个点存来存储由中心点向旋转位置发射线与精灵四边形边缘的碰撞位置点。 CCPoint hit = CCPointZero; if (alpha == 0.f) { //如果百度百分比为0,则碰撞点在顶部中间位置,索引设为0。 hit = topMid; index = 0; } else if (alpha == 1.f) { //如果百度百分比为1,则碰撞点也在顶部中间位置,索引设为4。 hit = topMid; index = 4; } else { //运行一个循环来判断 float min_t = FLT_MAX; //顺时针遍历所有的拐角顶点。 for (int i = 0; i <= kProgressTextureCoordsCount; ++i) { //取得顺时针方向的上一个四边形拐角点的索引。 int pIndex = (i + (kProgressTextureCoordsCount - 1))%kProgressTextureCoordsCount; // 取得当前索引对应的四边形拐角点的UV坐标。 CCPoint edgePtA = boundaryTexCoord(i % kProgressTextureCoordsCount); //取得顺时针方向的上一个四边形拐角点的UV坐标。 CCPoint edgePtB = boundaryTexCoord(pIndex); //在循环中,以底部中点为分隔,做为起始和结束的时候对应的边缘段。 if(i == 0) { //底部中点至左下角为起始边缘段。 edgePtB = ccpLerp(edgePtA, edgePtB, 1-m_tMidpoint.x); } else if(i == 4) { //右下角至底部中点为结束边缘段。 edgePtA = ccpLerp(edgePtA, edgePtB, 1-m_tMidpoint.x); } // 定义FLOAT临时变量s,t。 // s用来存放中心点按旋转角度方向的直线与边缘直线相交时交点与中心点的距离。 // t用来存放中心点按旋转角度方向的直线与边缘直线相交时交点与边缘段起点的距离。 float s = 0, t = 0; //判断以中心点和旋转点percentagePt所在的直线与edgePtA,edgePtB所在的直线是否相交,如果相交,返回s和t。 if(ccpLineIntersect(edgePtA, edgePtB, m_tMidpoint, percentagePt, &s, &t)) { //如果有交点。 if ((i == 0 || i == 4)) { // 第一和最末次循环时s值的有效性判断。 if (!(0.f <= s && s <= 1.f)) { continue; } } //计录每次循环进行碰撞检测时,有效的最短t值和对应的循环索引。 if (t >= 0.f) { if (t < min_t) { min_t = t; index = i; } } } } // 由min_t和方向计算出碰撞点存入hit。 hit = ccpAdd(m_tMidpoint, ccpMult(ccpSub(percentagePt, m_tMidpoint),min_t)); } // 根据碰撞点所在的拐角象限计算需要多少顶点来绘制,加3的意义是要加上中点,12点位置和碰撞点三个点。 bool sameIndexCount = true; if(m_nVertexDataCount != index + 3){ //如果顶点有变化,释放顶点缓冲。 sameIndexCount = false; CC_SAFE_FREE(m_pVertexData); m_nVertexDataCount = 0; } //如果在上面的判断中释放了顶点缓冲,这里需要重建顶点缓冲。 if(!m_pVertexData) { m_nVertexDataCount = index + 3; m_pVertexData = (ccV2F_C4B_T2F*)malloc(m_nVertexDataCount * sizeof(ccV2F_C4B_T2F)); CCAssert( m_pVertexData, "CCProgressTimer. Not enough memory"); } //更新顶点的颜色. updateColor(); //设置顶点缓冲区的各顶点位置和纹理UV坐标。 if (!sameIndexCount) { //设置中点位置和纹理UV坐标。 m_pVertexData[0].texCoords = textureCoordFromAlphaPoint(m_tMidpoint); m_pVertexData[0].vertices = vertexFromAlphaPoint(m_tMidpoint); //12点位置和纹理UV坐标。 m_pVertexData[1].texCoords = textureCoordFromAlphaPoint(topMid); m_pVertexData[1].vertices = vertexFromAlphaPoint(topMid); //当前时刻所要绘制的拐角点的位置和UV坐标. for(int i = 0; i < index; ++i){ CCPoint alphaPoint = boundaryTexCoord(i); m_pVertexData[i+2].texCoords = textureCoordFromAlphaPoint(alphaPoint); m_pVertexData[i+2].vertices = vertexFromAlphaPoint(alphaPoint); } } //碰撞点的位置和UV坐标. m_pVertexData[m_nVertexDataCount - 1].texCoords = textureCoordFromAlphaPoint(hit); m_pVertexData[m_nVertexDataCount - 1].vertices = vertexFromAlphaPoint(hit); } //条形的进度动画的更新. void CCProgressTimer::updateBar(void) { //如果没有操控的精灵直接返回。 if (!m_pSprite) { return; } //将进度值计算成百分比. float alpha = m_fPercentage / 100.0f; //根据百分比计算x,y方向上的偏移值. CCPoint alphaOffset = ccpMult(ccp(1.0f * (1.0f - m_tBarChangeRate.x) + alpha * m_tBarChangeRate.x, 1.0f * (1.0f - m_tBarChangeRate.y) + alpha * m_tBarChangeRate.y), 0.5f); //计算中点位置在受到偏移后的最小和最大值。 CCPoint min = ccpSub(m_tMidpoint, alphaOffset); CCPoint max = ccpAdd(m_tMidpoint, alphaOffset); //将最小值和最大值取在有效范围内。 if (min.x < 0.f) { max.x += -min.x; min.x = 0.f; } if (max.x > 1.f) { min.x -= max.x - 1.f; max.x = 1.f; } if (min.y < 0.f) { max.y += -min.y; min.y = 0.f; } if (max.y > 1.f) { min.y -= max.y - 1.f; max.y = 1.f; } //根据方向来设置相应的四边形的顶点数据。 if (!m_bReverseDirection) { //如果正方向. //如果顶点缓冲为空,申请四个顶点的数据缓冲区。 if(!m_pVertexData) { m_nVertexDataCount = 4; m_pVertexData = (ccV2F_C4B_T2F*)malloc(m_nVertexDataCount * sizeof(ccV2F_C4B_T2F)); CCAssert( m_pVertexData, "CCProgressTimer. Not enough memory"); } //左上角 m_pVertexData[0].texCoords = textureCoordFromAlphaPoint(ccp(min.x,max.y)); m_pVertexData[0].vertices = vertexFromAlphaPoint(ccp(min.x,max.y)); //左下角 m_pVertexData[1].texCoords = textureCoordFromAlphaPoint(ccp(min.x,min.y)); m_pVertexData[1].vertices = vertexFromAlphaPoint(ccp(min.x,min.y)); //右上角 m_pVertexData[2].texCoords = textureCoordFromAlphaPoint(ccp(max.x,max.y)); m_pVertexData[2].vertices = vertexFromAlphaPoint(ccp(max.x,max.y)); //右下角 m_pVertexData[3].texCoords = textureCoordFromAlphaPoint(ccp(max.x,min.y)); m_pVertexData[3].vertices = vertexFromAlphaPoint(ccp(max.x,min.y)); } else { //如果是反方向,这里用了8个顶点,以使能够正确显示相应的四边形。我个人感觉这段代码写的比较恶心,而且很难理解,在ActionsProgressTest中并没有相应的演示,我经过测试后发现它是为了当你设置m_bReverseDirection为true后如果方向不符无法正确显示而做的一个算法,以致在后面需要调用两次渲染才能确保渲染正确,个人感觉这一段应可以改造为更简单和通俗易懂的四个顶点的填充,所以深深的建议你略过这一段代码。 if(!m_pVertexData) { m_nVertexDataCount = 8; m_pVertexData = (ccV2F_C4B_T2F*)malloc(m_nVertexDataCount * sizeof(ccV2F_C4B_T2F)); CCAssert( m_pVertexData, "CCProgressTimer. Not enough memory"); // 左上角 m_pVertexData[0].texCoords = textureCoordFromAlphaPoint(ccp(0,1)); m_pVertexData[0].vertices = vertexFromAlphaPoint(ccp(0,1)); //左下角 m_pVertexData[1].texCoords = textureCoordFromAlphaPoint(ccp(0,0)); m_pVertexData[1].vertices = vertexFromAlphaPoint(ccp(0,0)); //右上角 m_pVertexData[6].texCoords = textureCoordFromAlphaPoint(ccp(1,1)); m_pVertexData[6].vertices = vertexFromAlphaPoint(ccp(1,1)); //右下角 m_pVertexData[7].texCoords = textureCoordFromAlphaPoint(ccp(1,0)); m_pVertexData[7].vertices = vertexFromAlphaPoint(ccp(1,0)); } // 左上角2 m_pVertexData[2].texCoords = textureCoordFromAlphaPoint(ccp(min.x,max.y)); m_pVertexData[2].vertices = vertexFromAlphaPoint(ccp(min.x,max.y)); //左下角2 m_pVertexData[3].texCoords = textureCoordFromAlphaPoint(ccp(min.x,min.y)); m_pVertexData[3].vertices = vertexFromAlphaPoint(ccp(min.x,min.y)); // 右上角2 m_pVertexData[4].texCoords = textureCoordFromAlphaPoint(ccp(max.x,max.y)); m_pVertexData[4].vertices = vertexFromAlphaPoint(ccp(max.x,max.y)); // 右下角2 m_pVertexData[5].texCoords = textureCoordFromAlphaPoint(ccp(max.x,min.y)); m_pVertexData[5].vertices = vertexFromAlphaPoint(ccp(max.x,min.y)); } //更新颜色 updateColor(); } //根据索引取得相应对应四边形的四个角的纹理UV。 CCPoint CCProgressTimer::boundaryTexCoord(char index) { //既然之前将四个角的UV都压缩到一个char中,这里就是通过位运算将其取出来的过程。 if (index < kProgressTextureCoordsCount) { //因为这四个角的UV值就是0或1,所以各占一位,并且四个角是顺时针顺序放入到char中的,这里通过索引参数稍做计算可以取得对应位的值。 if (m_bReverseDirection) { //如果是反方向,如何如何计算。 return ccp((kCCProgressTextureCoords>>(7-(index<<1)))&1,(kCCProgressTextureCoords>>(7-((index<<1)+1)))&1); } else { //如果是正方向,如何如何计算。 return ccp((kCCProgressTextureCoords>>((index<<1)+1))&1,(kCCProgressTextureCoords>>(index<<1))&1); } } return CCPointZero; } //绘制 void CCProgressTimer::draw(void) { //如果顶点数组为空或要绘制的精灵为空,直接返回. if( ! m_pVertexData || ! m_pSprite) return; //设置使用shader. CC_NODE_DRAW_SETUP(); //设置精灵的混合状态. ccGLBlendFunc( m_pSprite->getBlendFunc().src, m_pSprite->getBlendFunc().dst ); //设置顶点的格式. ccGLEnableVertexAttribs(kCCVertexAttribFlag_PosColorTex ); //设置纹理 ccGLBindTexture2D( m_pSprite->getTexture()->getName() ); //设置渲染使用的顶点缓冲区中的数据结构格式. //数据结构首先是顶点信息. glVertexAttribPointer( kCCVertexAttrib_Position, 2, GL_FLOAT, GL_FALSE, sizeof(m_pVertexData[0]) , &m_pVertexData[0].vertices); //然后是纹理坐标信息. glVertexAttribPointer( kCCVertexAttrib_TexCoords, 2, GL_FLOAT, GL_FALSE, sizeof(m_pVertexData[0]), &m_pVertexData[0].texCoords); //最后是色彩信息. glVertexAttribPointer( kCCVertexAttrib_Color, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(m_pVertexData[0]), &m_pVertexData[0].colors); //如果是绕圆心旋转的进度动画,使用扇型方式进行三角形的绘制。 if(m_eType == kCCProgressTimerTypeRadial) { glDrawArrays(GL_TRIANGLE_FAN, 0, m_nVertexDataCount); } else if (m_eType == kCCProgressTimerTypeBar) { //如果是条形的进度动画,根据相应的方向进行三角形的绘制。 if (!m_bReverseDirection) { //正方向只需要进行一次绘制。 glDrawArrays(GL_TRIANGLE_STRIP, 0, m_nVertexDataCount); } else { //如果是反方向,需要进行两次绘制,这个原因嘛,就在前面那段8个顶点的BUF创建所决定,我在此建议Cocos2d-x的开发者重新考虑一下算法的修改,不行就干掉这个条形动画的反向处理,因为实际上是没什么用处的。 glDrawArrays(GL_TRIANGLE_STRIP, 0, m_nVertexDataCount/2); glDrawArrays(GL_TRIANGLE_STRIP, 4, m_nVertexDataCount/2); // 渲染次数统计多增加一次渲染调用。 CC_INCREMENT_GL_DRAWS(1); } } //渲染次数统计增加一次渲染调用。 CC_INCREMENT_GL_DRAWS(1); } NS_CC_END
我们看: CCProgressTimer 类实现了进度动画表现方案的核心算法处理,进度动画的更新和渲染都由它来完成,有了这个类,我们就可以根据进度值来绘制相应的绕圆心旋转或是条形的进度动画了。
但具体进度的变化控制则是由 CCProgressTo 和 CCProgressFromTo 来完成的。我们接着来看一下,打开CCActionProgressTimer.h:
#ifndef __ACTION_CCPROGRESS_TIMER_H__ #define __ACTION_CCPROGRESS_TIMER_H__ //用到时间动画类. #include "CCActionInterval.h" //开始Cocos2d命名空间 NS_CC_BEGIN //============= CCProgressTo ============================== //由时间动画派生出进度动画,对进度进行从0到另一个值的变化控制. class CC_DLL CCProgressTo : public CCActionInterval { public: //初始化进度动画,参数一为动画时长,参数二为目标百分比值。 bool initWithDuration(float duration, float fPercent); //产生一个拷贝 virtual CCObject* copyWithZone(CCZone *pZone); //设置演示当前动画的演员,启动动画。 virtual void startWithTarget(CCNode *pTarget); //每帧更新动画的处理。 virtual void update(float time); public: //静态函数,创建一个进度动画,内部调用create来实现,参一为动画时长,参二为结束进度值。 CC_DEPRECATED_ATTRIBUTE static CCProgressTo* actionWithDuration(float duration, float fPercent); //同上 static CCProgressTo* create(float duration, float fPercent); protected: //起始进度值 float m_fTo; //结束进度值 float m_fFrom; };
对应的CPP实现:
//定义一个宏 #define kProgressTimerCast CCProgressTimer* // 静态函数,创建一个进度动画,内部调用create来实现。 CCProgressTo* CCProgressTo::actionWithDuration(float duration, float fPercent) { return CCProgressTo::create(duration, fPercent); } //同上 CCProgressTo* CCProgressTo::create(float duration, float fPercent) { //先new出一个进度动画。 CCProgressTo *pProgressTo = new CCProgressTo(); //初始化这个进度动画。 pProgressTo->initWithDuration(duration, fPercent); //交由内存管理器进行释放管理。 pProgressTo->autorelease(); //返回创建的进度动画。 return pProgressTo; } //初始化进度动画,参数一为动画时长,参数二为目标百分比值。 bool CCProgressTo::initWithDuration(float duration, float fPercent) { //调用基类的初始化函数。 if (CCActionInterval::initWithDuration(duration)) { //保存目标进度值。 m_fTo = fPercent; return true; } return false; } //产生一个拷贝,讲的太多,参看动画三板斧。 CCObject* CCProgressTo::copyWithZone(CCZone *pZone) { CCZone* pNewZone = NULL; CCProgressTo* pCopy = NULL; if(pZone && pZone->m_pCopyObject) { pCopy = (CCProgressTo*)(pZone->m_pCopyObject); } else { pCopy = new CCProgressTo(); pZone = pNewZone = new CCZone(pCopy); } CCActionInterval::copyWithZone(pZone); pCopy->initWithDuration(m_fDuration, m_fTo); CC_SAFE_DELETE(pNewZone); return pCopy; } //设置演示当前动画的演员。 void CCProgressTo::startWithTarget(CCNode *pTarget) { //调用基类的相应函数。 CCActionInterval::startWithTarget(pTarget); //取得演示当前进度动画的渲染器中记录的进度百分比做为起始进度。 m_fFrom = ((kProgressTimerCast)(pTarget))->getPercentage(); //重置起始进度。 if (m_fFrom == 100) { m_fFrom = 0; } } //每帧更新动画。 void CCProgressTo::update(float time) { //根据时间计算出插值进度设置为目标演员的当前进度。 ((kProgressTimerCast)(m_pTarget))->setPercentage(m_fFrom + (m_fTo - m_fFrom) * time); }
CCProgressFromTo :
//由时间动画派生出进度动画,对进度进行从一个值到另一个值的变化控制. class CC_DLL CCProgressFromTo : public CCActionInterval { public: //初始化进度动画,参数一为动画时长,参数二为目标百分比值。 bool initWithDuration(float duration, float fFromPercentage, float fToPercentage); //产生一个拷贝 virtual CCObject* copyWithZone(CCZone *pZone); //生成一个反向动画。 virtual CCActionInterval* reverse(void); //设置演示当前动画的演员,启动动画。 virtual void startWithTarget(CCNode *pTarget); //每帧更新动画的处理。 virtual void update(float time); public: //静态函数,创建一个进度动画,内部调用create来实现,参一为动画时长,参二为起始进度值,参三为结束进度值。 CC_DEPRECATED_ATTRIBUTE static CCProgressFromTo* actionWithDuration(float duration, float fFromPercentage, float fToPercentage); //同上 static CCProgressFromTo* create(float duration, float fFromPercentage, float fToPercentage); protected: //进度进起值 float m_fTo; //进度结束值 float m_fFrom; };
其对应的CPP实现:
//静态函数,创建一个进度动画,内部调用create来实现。 CCProgressFromTo* CCProgressFromTo::actionWithDuration(float duration, float fFromPercentage, float fToPercentage) { return CCProgressFromTo::create(duration, fFromPercentage, fToPercentage); } //同上 CCProgressFromTo* CCProgressFromTo::create(float duration, float fFromPercentage, float fToPercentage) { CCProgressFromTo *pProgressFromTo = new CCProgressFromTo(); pProgressFromTo->initWithDuration(duration, fFromPercentage, fToPercentage); pProgressFromTo->autorelease(); return pProgressFromTo; } //初始化。 bool CCProgressFromTo::initWithDuration(float duration, float fFromPercentage, float fToPercentage) { //调用基类的初始化函数。保存进度动画的起止值。 if (CCActionInterval::initWithDuration(duration)) { m_fTo = fToPercentage; m_fFrom = fFromPercentage; return true; } return false; } //产生一个进度动画的拷贝。 CCObject* CCProgressFromTo::copyWithZone(CCZone *pZone) { CCZone* pNewZone = NULL; CCProgressFromTo* pCopy = NULL; if(pZone && pZone->m_pCopyObject) { //in case of being called at sub class pCopy = (CCProgressFromTo*)(pZone->m_pCopyObject); } else { pCopy = new CCProgressFromTo(); pZone = pNewZone = new CCZone(pCopy); } CCActionInterval::copyWithZone(pZone); pCopy->initWithDuration(m_fDuration, m_fFrom, m_fTo); CC_SAFE_DELETE(pNewZone); return pCopy; } //产生一个反向的进度动画。 CCActionInterval* CCProgressFromTo::reverse(void) { //这里只是交换了一下起止值就搞定了。 return CCProgressFromTo::create(m_fDuration, m_fTo, m_fFrom); } //设置演示动画的目标。 void CCProgressFromTo::startWithTarget(CCNode *pTarget) { CCActionInterval::startWithTarget(pTarget); } //更新进度动画 void CCProgressFromTo::update(float time) { //通过时间插值来计算出当前的进度值。 ((kProgressTimerCast)(m_pTarget))->setPercentage(m_fFrom + (m_fTo - m_fFrom) * time); }
现在我们知道了,当要实现一个进度动画时,我们使用CCProgressTimer 来设定相应的动画形式和操纵的精灵,使用CProgressTo和CCProgressFromTo来设定进度动画的进度变化形式.之后运行这个进度动画就可以了。
这三个类的功能讲解可以使我们深入的理解进度动画的内部构成。我们现在来看一下具体的应用,打开
ActionsProgressTest.h。
//为什么#ifndef 和#define 不一样,大坑啊~!各位注意改正。 #ifndef _ACTIONS__PROGRESS_TEST_H_ #define _ACTIONS_PROGRESS_TEST_H_ //测试工程的头文件,包含测试场景的定义 #include "../testBasic.h" //这里定义一个精灵显示层,做为进度动画的显示主体。 class SpriteDemo : public CCLayer { public: //构造函数与析构函数 SpriteDemo(void); ~SpriteDemo(void); //取得主标题,副标题 virtual std::string title(); virtual std::string subtitle(); //重载当前层被加载时的处理函数。 virtual void onEnter(); //声明重启,下一个演示,上一个演示的回调函数。 void restartCallback(CCObject* pSender); void nextCallback(CCObject* pSender); void backCallback(CCObject* pSender); };
其对应的CPP实现:
//构造 SpriteDemo::SpriteDemo(void) { } //析构 SpriteDemo::~SpriteDemo(void) { } //取得主标题std::string SpriteDemo::title() { return "ActionsProgressTest"; } //取得副标题 std::string SpriteDemo::subtitle() { return ""; } //重载当前层被加载时的处理函数。 void SpriteDemo::onEnter() { //调用基类的相应函数对CCLayer进行初始化。 CCLayer::onEnter(); //取得窗口的大小。 CCSize s = CCDirector::sharedDirector()->getWinSize(); //创建一个文字标签,显示标题,字体为Arial,字号为18。 CCLabelTTF* label = CCLabelTTF::create(title().c_str(), "Arial", 18); //将文字标签放入当前层 addChild(label, 1); //设置文字标签的位置 label->setPosition( CCPointMake(s.width/2, s.height-50) ); //取得幅标题 std::string strSubtitle = subtitle(); if( ! strSubtitle.empty() ) { //如果副标题不为空,则创建副标题,字体为Thonburi,字号为22。 CCLabelTTF* l = CCLabelTTF::create(strSubtitle.c_str(), "Thonburi", 22); //将副标题文字标签放入当前层 addChild(l, 1); //设置副标题文字标签的位置 l->setPosition( CCPointMake(s.width/2, s.height-80) ); } //创建三个菜单按钮,用于用户选择控制动画的演示。 //返回上一个演示。 CCMenuItemImage *item1 = CCMenuItemImage::create(s_pPathB1, s_pPathB2, this, menu_selector(SpriteDemo::backCallback) ); //重启当前演示。 CCMenuItemImage *item2 = CCMenuItemImage::create(s_pPathR1, s_pPathR2, this, menu_selector(SpriteDemo::restartCallback) ); //进行下一个演示。 CCMenuItemImage *item3 = CCMenuItemImage::create(s_pPathF1, s_pPathF2, this, menu_selector(SpriteDemo::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); //设置一个填充为红色的层。 CCLayerColor *background = CCLayerColor::create(ccc4(255,0,0,255)); //将这个实体色填充的层放入当前层中做为背景层。 addChild(background, -10); } //定义重启的回调函数。 void SpriteDemo::restartCallback(CCObject* pSender) { //创建一个进度动画的场景实例 CCScene* s = new ProgressActionsTestScene(); //将新创建的当前的演示效果CCLayer放入这个场景。 s->addChild(restartAction()); //运行此场景 CCDirector::sharedDirector()->replaceScene(s); //因新new的场景计数器为1,在交由设备运行时会再加1,这里完成场景的交接,故对其引用计数器减1。 s->release(); } //定义下一个演示的回调函数。 void SpriteDemo::nextCallback(CCObject* pSender) { //创建一个进度动画的场景实例 CCScene* s = new ProgressActionsTestScene(); //将一个新的演示效果CCLayer放入这个场景。 s->addChild( nextAction() ); //运行此场景 CCDirector::sharedDirector()->replaceScene(s); //完成场景的交接,对其引用计数器减1。 s->release(); } //定义上一个演示的回调函数。 void SpriteDemo::backCallback(CCObject* pSender) { CCScene* s = new ProgressActionsTestScene(); s->addChild( backAction() ); CCDirector::sharedDirector()->replaceScene(s); s->release(); }
和动画三板斧演示中的ActionsDemo一样,这个类是为了演示进度动画而做的一个基类,后面的各种演示会由此类派生实现。
SpriteProgressToRadial:
//============================================================ //圆形进度动画:圆形进度动画是一个绕中心将精灵边缘扫描一圈在这个过程中消失的动画消果。 class SpriteProgressToRadial : public SpriteDemo { public: //重载当前层被加载时的处理函数。 virtual void onEnter(); //取得副标题 virtual std::string subtitle(); };
对应的CPP实现:
//重载当前层被加载时的处理函数。 void SpriteProgressToRadial::onEnter() { //基类的相应函数. SpriteDemo::onEnter(); //取得窗口大小 CCSize s = CCDirector::sharedDirector()->getWinSize(); //创建两个进度动画的控制器.2秒进度值变化到100。 CCProgressTo *to1 = CCProgressTo::create(2, 100); CCProgressTo *to2 = CCProgressTo::create(2, 100); //创建一个进度动画的渲染器.使用女一号演员精灵来渲染。 CCProgressTimer *left = CCProgressTimer::create(CCSprite::create(s_pPathSister1)); //设置动画表现为绕圆心转动。 left->setType( kCCProgressTimerTypeRadial ); //将渲染器放入当前层. addChild(left); //设置渲染器的位置在屏幕左边 left->setPosition(CCPointMake(100, s.height/2)); //让它运行一个无限循环的进度动画,进度变化由控制器1来控制。 left->runAction( CCRepeatForever::create(to1)); //创建另一个进度动画的渲染器.使用色块图精灵来渲染。 CCProgressTimer *right = CCProgressTimer::create(CCSprite::create(s_pPathBlock)); right->setType(kCCProgressTimerTypeRadial); // 设置逆时针旋转。 right->setReverseProgress(true); //将渲染器放入当前层. addChild(right); //设置渲染器的位置在屏幕右边 right->setPosition(CCPointMake(s.width-100, s.height/2)); //让它运行一个无限循环的进度动画,进度变化由控制器2来控制。 right->runAction( CCRepeatForever::create(to2)); } //取得副标题。 std::string SpriteProgressToRadial::subtitle() { return "ProgressTo Radial"; }
SpriteProgressToHorizontal:
//===============SpriteProgressToHorizontal ================= //条形的横向进度动画演示 class SpriteProgressToHorizontal : public SpriteDemo { public: //重载当前层被加载时的处理函数。 virtual void onEnter(); //取得副标题 virtual std::string subtitle(); };
对应的CPP实现:
//当前层被加载时的处理函数。 void SpriteProgressToHorizontal::onEnter() { //基类的相应函数。 SpriteDemo::onEnter(); //取得窗口的大小 CCSize s = CCDirector::sharedDirector()->getWinSize(); //创建两个进度动画的控制器.2秒进度值变化到100。 CCProgressTo *to1 = CCProgressTo::create(2, 100); CCProgressTo *to2 = CCProgressTo::create(2, 100); //创建一个进度动画的渲染器.使用女演员精灵来渲染。 CCProgressTimer *left = CCProgressTimer::create(CCSprite::create(s_pPathSister1)); //设置表现为条形进度动画。 left->setType(kCCProgressTimerTypeBar); //设置进度起点在最左边 left->setMidpoint(ccp(0,0)); //设置进度动画方向为从左到右随进度增长而显现。 left->setBarChangeRate(ccp(1, 0)); //将渲染器放入当前层。 addChild(left); //设置渲染器的位置在屏幕左边 left->setPosition(CCPointMake(100, s.height/2)); //让它运行一个无限循环的进度动画,进度变化由控制器1来控制。 left->runAction( CCRepeatForever::create(to1)); //创建另一个进度动画的渲染器.使用女二号演员精灵来渲染。 CCProgressTimer *right = CCProgressTimer::create(CCSprite::create(s_pPathSister2)); //设置表现为条形进度动画。 right->setType(kCCProgressTimerTypeBar); //设置进度起点在最右边 right->setMidpoint(ccp(1, 0)); //设置进度动画方向为从右到左随进度增长而显现。 right->setBarChangeRate(ccp(1, 0)); //将渲染器放入当前层。 addChild(right); //设置渲染器的位置在屏幕右边 right->setPosition(CCPointMake(s.width-100, s.height/2)); //让它运行一个无限循环的进度动画,进度变化由控制器2来控制。 right->runAction( CCRepeatForever::create(to2)); } //取得幅标题。 std::string SpriteProgressToHorizontal::subtitle() { return "ProgressTo Horizontal"; }
SpriteProgressToVertical:
//==========SpriteProgressToVertical ============================= //纵向的条形进度动画演示 class SpriteProgressToVertical : public SpriteDemo { public: //重载当前层被加载时的处理函数。 virtual void onEnter(); //取得副标题 virtual std::string subtitle(); };
对应的CPP实现:
//当前层被加载时的处理函数。 void SpriteProgressToVertical::onEnter() { //基类的相应函数。 SpriteDemo::onEnter(); //取得窗口的大小 CCSize s = CCDirector::sharedDirector()->getWinSize(); //创建两个进度动画的控制器.2秒进度值变化到100。 CCProgressTo *to1 = CCProgressTo::create(2, 100); CCProgressTo *to2 = CCProgressTo::create(2, 100); //创建一个进度动画的渲染器.使用女演员精灵来渲染。 CCProgressTimer *left = CCProgressTimer::create(CCSprite::create(s_pPathSister1)); //设置表现为条形进度动画。 left->setType(kCCProgressTimerTypeBar); //设置进度起点在最上边 left->setMidpoint(ccp(0,0)); //设置进度动画方向为从上到下随进度增长而显现。 left->setBarChangeRate(ccp(0, 1)); //将渲染器放入当前层。 addChild(left); //设置渲染器的位置在屏幕左边 left->setPosition(CCPointMake(100, s.height/2)); //让它运行一个无限循环的进度动画,进度变化由控制器1来控制。 left->runAction( CCRepeatForever::create(to1)); //创建另一个进度动画的渲染器.使用女二号演员精灵来渲染。 CCProgressTimer *right = CCProgressTimer::create(CCSprite::create(s_pPathSister2)); //设置表现为条形进度动画。 right->setType(kCCProgressTimerTypeBar); //设置进度起点在最下边 right->setMidpoint(ccp(0, 1)); //设置进度动画方向为从下到上随进度增长而显现。 right->setBarChangeRate(ccp(0, 1)); //将渲染器放入当前层。 addChild(right); //设置渲染器的位置在屏幕右边 right->setPosition(CCPointMake(s.width-100, s.height/2)); //让它运行一个无限循环的进度动画,进度变化由控制器2来控制。 right->runAction( CCRepeatForever::create(to2)); } //取得幅标题。 std::string SpriteProgressToVertical::subtitle() { return "ProgressTo Vertical"; }
SpriteProgressToRadialMidpointChanged:
//===========SpriteProgressToRadialMidpointChanged ============= //中点可改变的绕中点旋转的进度动画。 class SpriteProgressToRadialMidpointChanged : public SpriteDemo { public: //重载当前层被加载时的处理函数。 virtual void onEnter(); //取得副标题 virtual std::string subtitle(); };
对应的CPP实现:
//当前层被加载时的处理函数。 void SpriteProgressToRadialMidpointChanged::onEnter() { //基类的相应函数。 SpriteDemo::onEnter(); //取得窗口的大小 CCSize s = CCDirector::sharedDirector()->getWinSize(); //创建进度动画的控制器.2秒进度值变化到100。 CCProgressTo *action = CCProgressTo::create(2, 100); //创建一个进度动画的渲染器.使用块色图精灵来渲染。 CCProgressTimer *left = CCProgressTimer::create(CCSprite::create(s_pPathBlock)); //设置表现为绕圆心旋转的进度动画。 left->setType(kCCProgressTimerTypeRadial); //将渲染器放入当前层。 addChild(left); //设置旋转的中心点在横向靠左1/4位置,纵向靠上1/4位置。 left->setMidpoint(ccp(0.25f, 0.75f)); //设置渲染器的位置在屏幕左边。 left->setPosition(ccp(100, s.height/2)); //让它运行一个无限循环的进度动画,进度变化由控制器1的拷贝来控制。 left->runAction(CCRepeatForever::create((CCActionInterval *)action->copy()->autorelease())); //创建另一个进度动画的渲染器.以下与上基本相同,只是中心点位置和精灵位置不同。 CCProgressTimer *right = CCProgressTimer::create(CCSprite::create(s_pPathBlock)); right->setType(kCCProgressTimerTypeRadial); right->setMidpoint(ccp(0.75f, 0.25f)); addChild(right); right->setPosition(ccp(s.width-100, s.height/2)); right->runAction(CCRepeatForever::create((CCActionInterval *)action->copy()->autorelease())); } //取得幅标题。 std::string SpriteProgressToRadialMidpointChanged::subtitle() { return "Radial w/ Different Midpoints"; }
SpriteProgressBarVarious:
//=============SpriteProgressBarVarious ======================= // class SpriteProgressBarVarious : public SpriteDemo { public: //重载当前层被加载时的处理函数。 virtual void onEnter(); //取得副标题 virtual std::string subtitle(); };
对应的CPP:
void SpriteProgressBarVarious::onEnter() { //基类的相应函数。 SpriteDemo::onEnter(); //取得窗口的大小 CCSize s = CCDirector::sharedDirector()->getWinSize(); //创建进度动画的控制器.2秒进度值变化到100。 CCProgressTo *to = CCProgressTo::create(2, 100); //创建一个进度动画的渲染器.使用女演员精灵来渲染。 CCProgressTimer *left = CCProgressTimer::create(CCSprite::create(s_pPathSister1)); //设置表现为条形的进度动画。 left->setType(kCCProgressTimerTypeBar); //设置起始位置在中间。 left->setMidpoint(ccp(0.5f, 0.5f)); //设置方向为横向向两边扩散。 left->setBarChangeRate(ccp(1, 0)); //将渲染器放入当前层 addChild(left); //设置渲染器的位置在屏幕左边。 left->setPosition(ccp(100, s.height/2)); //运行进度动画,动画表现为根据进度的递增精灵会横向由中心向两边显现。 left->runAction(CCRepeatForever::create((CCActionInterval *)to->copy()->autorelease())); //创建另一个渲染器.使用女二号演员精灵来渲染。 CCProgressTimer *middle = CCProgressTimer::create(CCSprite::create(s_pPathSister2)); //设置表现为条形的进度动画。 middle->setType(kCCProgressTimerTypeBar); //设置起始位置在屏幕中间。 middle->setMidpoint(ccp(0.5f, 0.5f)); //设置方向为向四周扩散。 middle->setBarChangeRate(ccp(1,1)); //将渲染器放入当前层 addChild(middle); //设置渲染器的位置在中央。 middle->setPosition(ccp(s.width/2, s.height/2)); //运行进度动画,动画表现为根据进度的递增精灵会横向由中心向四周显现。 middle->runAction(CCRepeatForever::create((CCActionInterval *)to->copy()->autorelease())); //创建第三个渲染器。 CCProgressTimer *right = CCProgressTimer::create(CCSprite::create(s_pPathSister2)); //设置表现为条形的进度动画。 right->setType(kCCProgressTimerTypeBar); //设置起始位置在中间。 right->setMidpoint(ccp(0.5f, 0.5f)); //设置方向为纵向向两边扩散。 right->setBarChangeRate(ccp(0, 1)); //将渲染器放入当前层 addChild(right); //设置渲染器的位置在屏幕右边。 right->setPosition(ccp(s.width-100, s.height/2)); //运行进度动画,动画表现为根据进度的递增精灵会纵向由中心向两边显现。 right->runAction(CCRepeatForever::create((CCActionInterval *)to->copy()->autorelease())); } //取得幅标题。 std::string SpriteProgressBarVarious::subtitle() { return "ProgressTo Bar Mid"; }
SpriteProgressBarTintAndFace:
//==========SpriteProgressBarTintAndFade =================== //进度动画之颜色,透明度的变化。 class SpriteProgressBarTintAndFade : public SpriteDemo { public: //重载当前层被加载时的处理函数。 virtual void onEnter(); //取得副标题 virtual std::string subtitle(); };
CPP实现:
void SpriteProgressBarTintAndFade::onEnter() { //基类的相应函数。 SpriteDemo::onEnter(); //取得窗口的大小 CCSize s = CCDirector::sharedDirector()->getWinSize(); //创建进度动画的控制器.6秒进度值变化到100。 CCProgressTo *to = CCProgressTo::create(6, 100); //创建一个动画序列,先是1秒内变化到红色,再经1秒变化到绿色,最后1秒变化到蓝色。 CCAction *tint = CCSequence::create(CCTintTo::create(1, 255, 0, 0), CCTintTo::create(1, 0, 255, 0), CCTintTo::create(1, 0, 0, 255), NULL); //创建一个动画序列,先是1秒内透明度变化到0而消失,再经1秒变化到255显现。 CCAction *fade = CCSequence::create(CCFadeTo::create(1.0f, 0), CCFadeTo::create(1.0f, 255), NULL); //创建一个进度动画渲染器,由女演员来表演这个进度动画效果。 CCProgressTimer *left = CCProgressTimer::create(CCSprite::create(s_pPathSister1)); //设置进度动画的形式为条形动画。 left->setType(kCCProgressTimerTypeBar); //设置起点为中间位置。 left->setMidpoint(ccp(0.5f, 0.5f)); //设置动画的方向为精灵根据进度的递增横向渐渐扩大显示。 left->setBarChangeRate(ccp(1, 0)); //将渲染器放入当前层。 addChild(left); //设置渲染器的位置放在屏幕左边。 left->setPosition(ccp(100, s.height/2)); //让渲染器运行一个无限循环进度动画,由上面进度动画控制器的拷贝来进行控制。 left->runAction(CCRepeatForever::create((CCActionInterval *)to->copy()->autorelease())); //让渲染器运行一个无限循环动画序列,不断的进行上面的色彩变化。 left->runAction(CCRepeatForever::create((CCActionInterval *)tint->copy()->autorelease())); //将一个文字标签放入到渲染器的结点之下。 left->addChild(CCLabelTTF::create("Tint", "Marker Felt", 20.0f)); //创建第二个进度动画的渲染器。 CCProgressTimer *middle = CCProgressTimer::create(CCSprite::create(s_pPathSister2)); //设置其表现形式为条形动画。 middle->setType(kCCProgressTimerTypeBar); //设置动画的起点在中心点位置。 middle->setMidpoint(ccp(0.5f, 0.5f)); //设置动画的方向为精灵根据进度的递增向四周渐渐扩大显示。 middle->setBarChangeRate(ccp(1, 1)); //将渲染器放入当前层。 addChild(middle); //设置渲染器的位置在屏幕中央。 middle->setPosition(ccp(s.width/2, s.height/2)); //让渲染器运行一个无限循环的进度动画,由上面进度动画控制器的拷贝来进行控制。 middle->runAction(CCRepeatForever::create((CCActionInterval *)to->copy()->autorelease())); //让渲染器运行一个无限循环动画序列,不断的进行上面的透明度变化。 middle->runAction(CCRepeatForever::create((CCActionInterval *)fade->copy()->autorelease())); //将一个文字标签放入到渲染器的结点之下。 middle->addChild(CCLabelTTF::create("Fade", "Marker Felt", 20.0f)); //创建第三个进度动画的渲染器。 CCProgressTimer *right = CCProgressTimer::create(CCSprite::create(s_pPathSister2)); //设置其表现形式为条形动画。 right->setType(kCCProgressTimerTypeBar); //设置动画的起点在中心点位置。 right->setMidpoint(ccp(0.5f, 0.5f)); //设置动画的方向为精灵根据进度的递增纵向渐渐扩大显示。 right->setBarChangeRate(ccp(0, 1)); addChild(right); //设置渲染器的位置在屏幕右边。 right->setPosition(ccp(s.width-100, s.height/2)); //让渲染器运行一个无限循环的进度动画,由上面进度动画控制器的拷贝来进行控制。 right->runAction(CCRepeatForever::create((CCActionInterval *)to->copy()->autorelease())); //让渲染器运行一个无限循环动画序列,不断的进行上面的颜色变化。 right->runAction(CCRepeatForever::create((CCActionInterval *)tint->copy()->autorelease())); //让渲染器运行一个无限循环动画序列,不断的进行上面的透明度变化。 right->runAction(CCRepeatForever::create((CCActionInterval *)fade->copy()->autorelease())); //将一个文字标签放入到渲染器的结点之下。 right->addChild(CCLabelTTF::create("Tint and Fade", "Marker Felt", 20.0f)); } //取得幅标题。 std::string SpriteProgressBarTintAndFade::subtitle() { return "ProgressTo Bar Mid"; }
SpriteProgressWithSpriteFrame:
//==========SpriteProgressWithSpriteFrame =============== //进度动画之使用序列帧中的单帧精灵来演示。 class SpriteProgressWithSpriteFrame : public SpriteDemo { public: //重载当前层被加载时的处理函数。 virtual void onEnter(); //取得副标题 virtual std::string subtitle(); };
CPP实现:
void SpriteProgressWithSpriteFrame::onEnter() { //基类的相应函数。 SpriteDemo::onEnter(); //取得窗口的大小 CCSize s = CCDirector::sharedDirector()->getWinSize(); //创建进度动画的控制器.6秒进度值变化到100。 CCProgressTo *to = CCProgressTo::create(6, 100); //读取一个plist导出一个序列帧动画。 CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile("zwoptex/grossini.plist"); //创建一个进度动画渲染器,使用序列帧中的grossini_dance_01.png图像来进行动画的渲染。 CCProgressTimer *left = CCProgressTimer::create(CCSprite::createWithSpriteFrameName("grossini_dance_01.png")); //将进度动画的表现形式设置为条形动画。 left->setType(kCCProgressTimerTypeBar); //设置动画的起点在中心点位置。 left->setMidpoint(ccp(0.5f, 0.5f)); //设置进度动画为精灵根据进度的递增横向渐渐扩大显示。 left->setBarChangeRate(ccp(1, 0)); //将渲染器放入当前层。 addChild(left); //设置渲染器的位置放在屏幕左边。 left->setPosition(ccp(100, s.height/2)); //让渲染器运行一个无限循环的进度动画,由上面进度动画控制器的拷贝来进行控制。 left->runAction(CCRepeatForever::create((CCActionInterval *)to->copy()->autorelease())); //创建第二个进度动画的渲染器。使用序列帧中的grossini_dance_02.png图像来进行动画的渲染。 CCProgressTimer *middle = CCProgressTimer::create(CCSprite::createWithSpriteFrameName("grossini_dance_02.png")); //设置其表现形式为条形动画。 middle->setType(kCCProgressTimerTypeBar); //设置动画的起点在中心点位置。 middle->setMidpoint(ccp(0.5f, 0.5f)); //设置动画的方向为精灵根据进度的递增向四周渐渐扩大显示。 middle->setBarChangeRate(ccp(1, 1)); //将渲染器放入当前层。 addChild(middle); //设置渲染器的位置放在屏幕中央。 middle->setPosition(ccp(s.width/2, s.height/2)); //让渲染器运行一个无限循环的进度动画,由上面进度动画控制器的拷贝来进行控制。 middle->runAction(CCRepeatForever::create((CCActionInterval *)to->copy()->autorelease())); //创建第三个进度动画的渲染器使用序列帧中的grossini_dance_03.png图像来进行动画的渲染。 CCProgressTimer *right = CCProgressTimer::create(CCSprite::createWithSpriteFrameName("grossini_dance_03.png")); //设置其表现形式为条形动画。 right->setType(kCCProgressTimerTypeRadial); //设置动画的起点在中心点位置。 right->setMidpoint(ccp(0.5f, 0.5f)); //设置动画的方向为精灵根据进度的递增纵向渐渐扩大显示。 right->setBarChangeRate(ccp(0, 1)); //将渲染器放入当前层。 addChild(right); //设置渲染器的位置放在屏幕右边。 right->setPosition(ccp(s.width-100, s.height/2)); //让渲染器运行一个无限循环的进度动画,由上面进度动画控制器的拷贝来进行控制。 right->runAction(CCRepeatForever::create((CCActionInterval *)to->copy()->autorelease())); } //取得幅标题。 std::string SpriteProgressWithSpriteFrame::subtitle() { return "Progress With Sprite Frame"; }
这些演示类都讲完了,最后是在场景中通过按钮把这些演示实例联系起来让用户可以控制演示哪一个进度动画。
//============================================================ //TestBasic.h中定义了基础的演示场景类,这里由它派生一个场景类用于演示本节的进度动画。 class ProgressActionsTestScene : public TestScene { public: //重载运行场景时的处理。 virtual void runThisTest(); };
对应的CPP:
#include "ActionsProgressTest.h" #include "../testResource.h" //场景索引。 static int sceneIdx = -1; //演示实例的数量。 #define MAX_LAYER 7 //创建相应的演示动画的CCLayer。 //下一个动画演示。 CCLayer* nextAction(); //上一个动画演示。 CCLayer* backAction(); //重新启动当前的动画演示。 CCLayer* restartAction(); //创建对应的进度动画演示CCLayer实例。 CCLayer* createLayer(int nIndex) { switch(nIndex) { case 0: return new SpriteProgressToRadial(); case 1: return new SpriteProgressToHorizontal(); case 2: return new SpriteProgressToVertical(); case 3: return new SpriteProgressToRadialMidpointChanged(); case 4: return new SpriteProgressBarVarious(); case 5: return new SpriteProgressBarTintAndFade(); case 6: return new SpriteProgressWithSpriteFrame(); } return NULL; } //下一个动画演示。 CCLayer* nextAction() { //索引加1,并与最大数量取模,限定在有效范围内。 sceneIdx++; sceneIdx = sceneIdx % MAX_LAYER; //创建对应的进度动画演示实例CCLayer。 CCLayer* pLayer = createLayer(sceneIdx); //将其交由内存管理器来进行释放处理。 pLayer->autorelease(); //返回对应进度动画演示实例CCLayer。 return pLayer; } //上一个动画演示。 CCLayer* backAction() { //索引减1,并限定在有效范围内。 sceneIdx--; int total = MAX_LAYER; if( sceneIdx < 0 ) sceneIdx += total; //创建对应的进度动画演示实例CCLayer。 CCLayer* pLayer = createLayer(sceneIdx); //将其交由内存管理器来进行释放处理。 pLayer->autorelease(); //返回对应进度动画演示实例CCLayer。 return pLayer; } //重新启动当前的动画演示。 CCLayer* restartAction() { //创建对应的进度动画演示实例CCLayer。 CCLayer* pLayer = createLayer(sceneIdx); //将其交由内存管理器来进行释放处理。 pLayer->autorelease(); //返回对应进度动画演示实例CCLayer。 return pLayer; } //启动当前场景。 void ProgressActionsTestScene::runThisTest() { //将第一个进度动画演示的CCLayer实例创建出来并放入当前场景。 addChild(nextAction()); //运行当前场景。 CCDirector::sharedDirector()->replaceScene(this); }
终于把这些东东都解释完了,我想大家不会只是知其然,而更是知其所以然。这几个月的辛苦努力,能换来大家对于博客的关注,我很开心,只是目前仍然对Cocos2d-x处于学习状态中,暂时只想尽快把它分析完了,然后再考虑出一些实例和视频教程,如果当前源码大家看起来有点头疼,可以多动手操作一下,相信我,对于基本功的提高,读代码能力的提高都是有极大的好处的。