在Cocos2d-x 2.0中有一个工程例子叫"ActionsProgressTest",故明思义是进度动画的演示,应该说在几乎所有的RPG类游戏中我们都会看到进度条,所以进度动画的作用之大是无须我多言。我们本节就来学习一下Cocos2d-x 2.0的进度动画。
首先,先把那个"ActionsProgressTest"放一边,我要做的不是让大家去知其然,而是要知其所以然。所以我们要学习一下进度动画的原理与实现,在此我们向大家介绍一下所要用到的三个关键功能类:
1.CCProgressTimer : 进度动画的渲染器,核心实现了进度动画的显示功能,本身是一个结点,通过渲染一个精灵来表现进度的变化。
2.CCProgressTo: TO进度控制器,控制进度从当前进度变化到某个值.
3.CCProgressFromTo: FromTo进度控制器,控制进度从一个指定值到另一个值的变化.
咱们先来看一下CCProgressTimer,这个类是整个进度动画的重中之重。
打开CCProgressTimer .h:
[cpp]
#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文件:
[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;
}
}
}
//更新进度。
v