转自:http://blog.csdn.net/honghaier/article/details/8222401
序列帧动画主要有几个类:
CCSpriteFrame:精灵帧信息,序列帧动画是依靠多个精灵帧信息来显示相应的纹理图像,一个精灵帧信息包包含了所使用的纹理,对应纹理块的位置以及纹理块是否经过旋转和偏移,这些信息可以取得对应纹理中正确的纹理块区域做为精灵帧显示的图像。
CCAnimationFrame:序列帧动画单帧信息,它存储了对应的精灵帧信息。
CCAnimation:序列帧动画信息,它存储了所有的单帧信息,可以对单帧信息进行管理。
CCAnimate:序列帧动画处理类,它是真正完成动画表演的类。
先学习一下CCSpriteFrame:
#ifndef __SPRITE_CCSPRITE_FRAME_H__ #define __SPRITE_CCSPRITE_FRAME_H__ //相关头文件 #include "base_nodes/CCNode.h" #include "CCProtocols.h" #include "cocoa/CCObject.h" #include "cocoa/CCGeometry.h" //Cocos2d命名空间 NS_CC_BEGIN //要用到的类 class CCTexture2D; class CCZone; //精灵帧:精灵序列帧动画中的一帧。 class CC_DLL CCSpriteFrame : public CCObject { public:
// pixel大小 //取得精灵帧的大小(像素单位) inline const CCRect& getRectInPixels(void) { return m_obRectInPixels; } //设置精灵帧的大小(像素单位) void setRectInPixels(const CCRect& rectInPixels);
// 旋转 //取得当前帧所用的纹理块是否旋转了90度 inline bool isRotated(void) { return m_bRotated; } //设置当前帧所用的纹理块旋转90度 inline void setRotated(bool bRotated) { m_bRotated = bRotated; }
// rect //取得当前帧所用的纹理块在图集纹理的矩形(点单位) inline const CCRect& getRect(void) { return m_obRect; } //设置当前帧所用的纹理块在图集中的矩形(点单位) void setRect(const CCRect& rect);
// offset 像素单位 //取得当前帧所用的纹理块在图集中的位置(像素单位) const CCPoint& getOffsetInPixels(void); //设置当前帧所用的纹理块在图集中的位置(像素单位) void setOffsetInPixels(const CCPoint& offsetInPixels);
// offset 点单位 //取得纹理块在合并时去掉周围空白边后的锚点偏移(点单位). const CCPoint& getOffset(void); //设置纹理块在合并时去掉周围空白边后的锚点偏移(点单位). void setOffset(const CCPoint& offsets);
// originalSize 像素单位 //取得纹理块的原始大小(像素单位)。 inline const CCSize& getOriginalSizeInPixels(void) { return m_obOriginalSizeInPixels; } //设置纹理块的原始大小(像素单位)。 inline void setOriginalSizeInPixels(const CCSize& sizeInPixels) { m_obOriginalSizeInPixels = sizeInPixels; }
// originalSize 点单位 //取得纹理块的原始大小(点单位)。 inline const CCSize& getOriginalSize(void) { return m_obOriginalSize; } //设置纹理块的原始大小(点单位)。 inline void setOriginalSize(const CCSize& sizeInPixels) { m_obOriginalSize = sizeInPixels; }
// texture //取得当前帧所使用的图集纹理 CCTexture2D* getTexture(void); //设置当前帧所使用的图集纹理 void setTexture(CCTexture2D* pobTexture);
public: //析构 ~CCSpriteFrame(void); //创建一个当前实例的拷贝 virtual CCObject* copyWithZone(CCZone *pZone); //从一个图集纹理中创建出一帧,内部调用对应createWithTexture函数实现,参一为图集纹理,参二为纹理块矩形。 CC_DEPRECATED_ATTRIBUTE static CCSpriteFrame* frameWithTexture(CCTexture2D* pobTexture, const CCRect& rect); //从一张图集图片中创建出一帧,内部调用对应create函数实现,参一为图集图片名称,参二为纹理块矩形。 CC_DEPRECATED_ATTRIBUTE static CCSpriteFrame* frameWithTextureFilename(const char* filename, const CCRect& rect); //从一个图集纹理中创建出一帧,内部调用对应createWithTexture函数实现,参一为图集纹理,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。 CC_DEPRECATED_ATTRIBUTE static CCSpriteFrame* frameWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize); //从一个张图集图片中创建出一帧,内部调用对应create函数实现,参一为图集图片名称,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。 CC_DEPRECATED_ATTRIBUTE static CCSpriteFrame* frameWithTextureFilename(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize); //从一张图片文件中以指定的矩形创建一个精灵帧。参一为图集图片名称,参二为对应纹理块的矩形。 static CCSpriteFrame* create(const char* filename, const CCRect& rect); //从一个图集图片中创建出一帧,参一为图集图片名称,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。 static CCSpriteFrame* create(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize); //从一张图集纹理中以指定的矩形创建一个精灵帧。参一为图集纹理,参二为对应纹理块的矩形。 static CCSpriteFrame* createWithTexture(CCTexture2D* pobTexture, const CCRect& rect); //从一个图集纹理中创建出一帧,参一为图集纹理,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。 static CCSpriteFrame* createWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize); public: //初始化精灵帧:参一为图集纹理,参二为对应纹理块的矩形。 bool initWithTexture(CCTexture2D* pobTexture, const CCRect& rect); //初始化精灵帧:参一为图集图片名称,参二为对应纹理块的矩形。 bool initWithTextureFilename(const char* filename, const CCRect& rect); //初始化精灵帧:参一为图集纹理,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。 bool initWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize); //初始化精灵帧:参一为图集图片名称,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。 bool initWithTextureFilename(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize); protected: //去掉纹理块空白后所导致的锚点偏移。 CCPoint m_obOffset; //纹理块的原始大小(未去掉空白的大小)。 CCSize m_obOriginalSize; //纹理块的大小(像素单位)。 CCRect m_obRectInPixels; //矩形块是否旋转。 bool m_bRotated; //纹理块 CCRect m_obRect; //去掉纹理块空白后所导致的锚点偏移(像素单位)。 CCPoint m_obOffsetInPixels; //纹理块的原始大小(像素单位)。 CCSize m_obOriginalSizeInPixels; //图集纹理。 CCTexture2D *m_pobTexture; //对应的图集图片。 std::string m_strTextureFilename; }; NS_CC_END #endif //__SPRITE_CCSPRITE_FRAME_H__
再来看CCSpriteFrame.cpp:
#include "textures/CCTextureCache.h" #include "CCSpriteFrame.h" #include "CCDirector.h" //使用Cocos2d命名空间 NS_CC_BEGIN //从一个图集纹理中创建出一帧,内部调用后面的createWithTexture函数实现,参一为图集纹理,参二为纹理块矩形。 CCSpriteFrame* CCSpriteFrame::frameWithTexture(CCTexture2D *pobTexture, const CCRect& rect) { return CCSpriteFrame::createWithTexture(pobTexture, rect); } //从一个图集纹理中创建出一帧,参一为图集纹理,参二为纹理块矩形。 CCSpriteFrame* CCSpriteFrame::createWithTexture(CCTexture2D *pobTexture, const CCRect& rect) { //新帧一个精灵帧,初始化完毕后交由内存管理器进行释放管理。 CCSpriteFrame *pSpriteFrame = new CCSpriteFrame();; pSpriteFrame->initWithTexture(pobTexture, rect); pSpriteFrame->autorelease(); //返回新创建的精灵帧。 return pSpriteFrame; } //从一张图集图片中创建出一帧,内部调用后面的create函数实现,参一为图集图片名称,参二为纹理块矩形。 CCSpriteFrame* CCSpriteFrame::frameWithTextureFilename(const char* filename, const CCRect& rect) { return CCSpriteFrame::create(filename, rect); } //从一张图集图片中创建出一帧,参一为图集图片名称,参二为纹理块矩形。 CCSpriteFrame* CCSpriteFrame::create(const char* filename, const CCRect& rect) { //新帧一个精灵帧,初始化完毕后交由内存管理器进行释放管理。 CCSpriteFrame *pSpriteFrame = new CCSpriteFrame();; pSpriteFrame->initWithTextureFilename(filename, rect); pSpriteFrame->autorelease(); //返回新创建的精灵帧。 return pSpriteFrame; } //从一个图集纹理中创建出一帧,内部调用后面的createWithTexture函数实现,参一为图集纹理,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。 CCSpriteFrame* CCSpriteFrame::frameWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize) { return CCSpriteFrame::createWithTexture(pobTexture, rect, rotated, offset, originalSize); } //从一个图集纹理中创建出一帧,参一为图集纹理,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。 CCSpriteFrame* CCSpriteFrame::createWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize) { //新帧一个精灵帧,初始化完毕后交由内存管理器进行释放管理。 CCSpriteFrame *pSpriteFrame = new CCSpriteFrame();; pSpriteFrame->initWithTexture(pobTexture, rect, rotated, offset, originalSize); pSpriteFrame->autorelease(); //返回新创建的精灵帧。 return pSpriteFrame; } //从一个张图集图片中创建出一帧,内部调用后面的create函数实现,参一为图集图片名称,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。 CCSpriteFrame* CCSpriteFrame::frameWithTextureFilename(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize) { return CCSpriteFrame::create(filename, rect, rotated, offset, originalSize); }
//从一个张图集图片中创建出一帧,参一为图集图片名称,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小。 CCSpriteFrame* CCSpriteFrame::create(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize) { //新帧一个精灵帧,初始化完毕后交由内存管理器进行释放管理。 CCSpriteFrame *pSpriteFrame = new CCSpriteFrame();; pSpriteFrame->initWithTextureFilename(filename, rect, rotated, offset, originalSize); pSpriteFrame->autorelease(); //返回新创建的精灵帧。 return pSpriteFrame; } //初始化精灵帧:参一为图集纹理,参二为对应纹理块的矩形。 bool CCSpriteFrame::initWithTexture(CCTexture2D* pobTexture, const CCRect& rect) { //将矩形参数转为当前设备分辨率下的像素单位的大小。 CCRect rectInPixels = CC_RECT_POINTS_TO_PIXELS(rect); //调用参数更详细的初始化函数。 return initWithTexture(pobTexture, rectInPixels, false, CCPointZero, rectInPixels.size); }
//初始化精灵帧:参一为图集图片名称,参二为对应纹理块的矩形。 bool CCSpriteFrame::initWithTextureFilename(const char* filename, const CCRect& rect) { //将矩形参数转为当前设备分辨率下的像素单位的大小。 CCRect rectInPixels = CC_RECT_POINTS_TO_PIXELS( rect ); //调用参数更详细的初始化函数。 return initWithTextureFilename(filename, rectInPixels, false, CCPointZero, rectInPixels.size); }
//初始化精灵帧:参一为图集纹理,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉纹理块空白后所导致的锚点偏移,参五为纹理块的原始大小 bool CCSpriteFrame::initWithTexture(CCTexture2D* pobTexture, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize) { //保存图集纹理 m_pobTexture = pobTexture; //占用纹理资源,对其引用计数器加1 if (pobTexture) { pobTexture->retain(); } //保存像素单位的矩形大小 m_obRectInPixels = rect; //保存点单位的矩形大小 m_obRect = CC_RECT_PIXELS_TO_POINTS(rect); //保存像素单位的去掉纹理块空白后所导致的锚点偏移。 m_obOffsetInPixels = offset; //保存点单位的去掉纹理块空白后所导致的锚点偏移。 m_obOffset = CC_POINT_PIXELS_TO_POINTS( m_obOffsetInPixels ); //保存像素单位的纹理块原始大小 m_obOriginalSizeInPixels = originalSize; //保存点单位的纹理块原始大小 m_obOriginalSize = CC_SIZE_PIXELS_TO_POINTS( m_obOriginalSizeInPixels ); //保存纹理块是否旋转了90度。 m_bRotated = rotated; return true; } //初始化精灵帧:参一为图集图片名称,参二为对应纹理块的矩形,参三为矩形块是否旋转,参四为去掉空白后纹理块所导致的偏移,参五为纹理块的原始大小 bool CCSpriteFrame::initWithTextureFilename(const char* filename, const CCRect& rect, bool rotated, const CCPoint& offset, const CCSize& originalSize) { //图集纹理置空 m_pobTexture = NULL; //保存图集图片名称。 m_strTextureFilename = filename; //保存像素单位的矩形大小 m_obRectInPixels = rect; //保存转换为点单位的矩形大小 m_obRect = CC_RECT_PIXELS_TO_POINTS(rect); //保存像素单位的去掉纹理块空白后所导致的锚点偏移。 m_obOffsetInPixels = offset; //保存转换为点单位的去掉纹理块空白后所导致的锚点偏移。 m_obOffset = CC_POINT_PIXELS_TO_POINTS( m_obOffsetInPixels ); //保存像素单位的纹理块原始大小 m_obOriginalSizeInPixels = originalSize; //保存转换为点单位的纹理块原始大小 m_obOriginalSize = CC_SIZE_PIXELS_TO_POINTS( m_obOriginalSizeInPixels ); //保存纹理块是否旋转了90度。 m_bRotated = rotated; return true; }
//析构 CCSpriteFrame::~CCSpriteFrame(void) { CCLOGINFO("cocos2d: deallocing %p", this); CC_SAFE_RELEASE(m_pobTexture); }
//创建一个当前实例的拷贝 CCObject* CCSpriteFrame::copyWithZone(CCZone *pZone) { CC_UNUSED_PARAM(pZone); //新创一个CCSpriteFrame实例对象。 CCSpriteFrame *pCopy = new CCSpriteFrame(); //以当前实例的各属性值为参数对其进行初始化。 pCopy->initWithTextureFilename(m_strTextureFilename.c_str(), m_obRectInPixels, m_bRotated, m_obOffsetInPixels, m_obOriginalSizeInPixels); //设置同样的纹理图集。 pCopy->setTexture(m_pobTexture); //返回创建的CCSpriteFrame实例对象。 return pCopy; } //设置当前帧所用的纹理块在图集中的矩形 void CCSpriteFrame::setRect(const CCRect& rect) { //保存点单位的矩形大小 m_obRect = rect; //保存转换为像素单位的矩形大小 m_obRectInPixels = CC_RECT_POINTS_TO_PIXELS(m_obRect); } //设置当前帧所用的纹理块在图集中的矩形(像素单位) void CCSpriteFrame::setRectInPixels(const CCRect& rectInPixels) { //保存像素单位的矩形大小 m_obRectInPixels = rectInPixels; //保存转换为点单位的矩形大小 m_obRect = CC_RECT_PIXELS_TO_POINTS(rectInPixels); } //取得纹理块在合并时去掉周围空白边后的锚点偏移(点单位). const CCPoint& CCSpriteFrame::getOffset(void) { return m_obOffset; } //设置纹理块在合并时去掉周围空白边后的锚点偏移(点单位). void CCSpriteFrame::setOffset(const CCPoint& offsets) { //保存纹理块在合并时去掉周围空白边后的锚点偏移(点单位). m_obOffset = offsets; //保存转换为像素单位的纹理块在合并时去掉周围空白边后的锚点偏移。 m_obOffsetInPixels = CC_POINT_POINTS_TO_PIXELS( m_obOffset ); } //取得纹理块在合并时去掉周围空白边后的锚点偏移(像素单位). const CCPoint& CCSpriteFrame::getOffsetInPixels(void) { return m_obOffsetInPixels; } //设置纹理块在合并时去掉周围空白边后的锚点偏移(像素单位). void CCSpriteFrame::setOffsetInPixels(const CCPoint& offsetInPixels) { //保存像素单位的纹理块在合并时去掉周围空白边后的锚点偏移。 m_obOffsetInPixels = offsetInPixels; //保存转换为点单位的纹理块在合并时去掉周围空白边后的锚点偏移. m_obOffset = CC_POINT_PIXELS_TO_POINTS( m_obOffsetInPixels ); } //设置图集纹理 void CCSpriteFrame::setTexture(CCTexture2D * texture) { //删放旧图集纹理,使用新图集纹理。 if( m_pobTexture != texture ) { CC_SAFE_RELEASE(m_pobTexture); //对应CC_SAFE_RELEASE对引用计数器减一操作, CC_SAFE_RETAIN对引用计数器加一操作. CC_SAFE_RETAIN(texture); m_pobTexture = texture; } } //取得图集纹理。 CCTexture2D* CCSpriteFrame::getTexture(void) { //如果纹理指针有效,直接返回。 if( m_pobTexture ) { return m_pobTexture; } //否则查看纹理图片是否有效,如果有效,加载到纹理管理器中返回创建的纹理。 if( m_strTextureFilename.length() > 0 ) { return CCTextureCache::sharedTextureCache()->addImage(m_strTextureFilename.c_str()); } // 如果都无效,返回NULL。 return NULL; } NS_CC_END
现在来看CCAnimationFrame 和CCAnimation:
#ifndef __CC_ANIMATION_H__ #define __CC_ANIMATION_H__ //引入相关头文件。 #include "platform/CCPlatformConfig.h" #include "cocoa/CCObject.h" #include "cocoa/CCArray.h" #include "cocoa/CCDictionary.h" #include "cocoa/CCGeometry.h" #include "CCSpriteFrame.h" #include <string> //使用Cocos2d命名空间。 NS_CC_BEGIN //用到的类。 class CCTexture2D; class CCSpriteFrame; //序列帧动画单帧:序列帧动画中的一帧。 class CC_DLL CCAnimationFrame : public CCObject { public: //构造 CCAnimationFrame(); //析构 virtual ~CCAnimationFrame(); //创建一个当前实例的拷贝。 virtual CCObject* copyWithZone(CCZone* pZone); //初始化单帧:参一为精灵帧,参二为当前帧到下一帧的时间间隔?错,这里有问题,后面讲,参三为存储各帧所用的词典。 bool initWithSpriteFrame(CCSpriteFrame* spriteFrame, float delayUnits, CCDictionary* userInfo); //定义一个精灵帧成员指针变量m_pSpriteFrame,代表当前动画帧所对应的精灵帧。CC_SYNTHESIZE_RETAIN宏创建对变量的set 和 get 操作的函数,函数名分别为setSpriteFrame和getSpriteFrame, 相应的set函数会释放旧的指针变量,并将参数保存到指针变量,并自动的对其引用计数器加一。 CC_SYNTHESIZE_RETAIN(CCSpriteFrame*, m_pSpriteFrame, SpriteFrame) //定义一个float成员变量m_fDelayUnits,代表当前帧到下一帧的时间间隔?错,这个变量其实没有毛用处?看多了反而容易乱,先略过吧。最后统一分析。CC_SYNTHESIZE宏创建对变量的set 和 get 操作的函数,函数名分别为setDelayUnits和getDelayUnits。 CC_SYNTHESIZE(float, m_fDelayUnits, DelayUnits) //定义一个词典成员指针变量m_pUserInfo,CC_SYNTHESIZE_RETAIN宏创建对变量的set 和 get 操作的函数,函数名分别为set UserInfo和get UserInfo, 相应的set函数会释放旧的指针变量,并将参数保存到指针变量,并自动的对其引用计数器加一。 CC_SYNTHESIZE_RETAIN(CCDictionary*, m_pUserInfo, UserInfo) }; //CCAnimation:序列帧动画类,管理所有动画中的CCAnimationFrame。 class CC_DLL CCAnimation : public CCObject { public: //构造函数。 CCAnimation(); //析构函数。 ~CCAnimation(void); public: //创建一个序列帧动画,内部调用create实现。 CC_DEPRECATED_ATTRIBUTE static CCAnimation* animation(void); //创建一个序列帧动画,内部调用createWithSpriteFrames实现。参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔。 CC_DEPRECATED_ATTRIBUTE static CCAnimation* animationWithSpriteFrames(CCArray* arrayOfSpriteFrameNames, float delay = 0.0f); //创建一个序列帧动画,内部调用相应create实现。参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔,参三为循环次数。 CC_DEPRECATED_ATTRIBUTE static CCAnimation* animationWithAnimationFrames(CCArray *arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops); //创建一个序列帧动画。 static CCAnimation* create(void); //创建一个序列帧动画,参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔。 static CCAnimation* createWithSpriteFrames(CCArray* arrayOfSpriteFrameNames, float delay = 0.0f); //创建一个序列帧动画。参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔,参三为循环次数。 static CCAnimation* create(CCArray *arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops); //新加一帧到序列帧动画中,参数为CCSpriteFrame指针。 void addSpriteFrame(CCSpriteFrame *pFrame); //新加一帧加入到序列帧动画中,参数为单帧图片名称。 void addSpriteFrameWithFileName(const char *pszFileName); //新加一帧加入到序列帧动画中,参一为图集纹理,参二为图集纹理中的图块矩形。 void addSpriteFrameWithTexture(CCTexture2D* pobTexture, const CCRect& rect); //初始化一个空白的动画。 bool init(); //初始化序列帧动画,参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔。 bool initWithSpriteFrames(CCArray *pFrames, float delay = 0.0f); //初始化序列帧动画,参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔,参三为循环次数。 bool initWithAnimationFrames(CCArray* arrayOfAnimationFrames, float delayPerUnit, unsigned int loops); //创建一个当前类的实例对象。 virtual CCObject* copyWithZone(CCZone* pZone); //定义一个float成员变量m_ fTotalDelayUnits,代表总帧数。CC_SYNTHESIZE_READONLY宏创建对变量的get 操作的函数,函数为getTotalDelayUnits。 CC_SYNTHESIZE_READONLY(float, m_fTotalDelayUnits, TotalDelayUnits) //定义一个float成员变量m_fDelayPerUnit,代表每两帧间的时间间隔,CC_SYNTHESIZE宏创建对变量的set 和 get 操作的函数,函数名分别为setDelayPerUnit和getDelayPerUnit。 CC_SYNTHESIZE(float, m_fDelayPerUnit, DelayPerUnit) //定义一个float成员变量m_fDuration,这个变量没用,不知作者为什么要加这个变量~。CC_SYNTHESIZE_READONLY宏创建对变量的get 操作的函数,函数为getDuration。 CC_PROPERTY_READONLY(float, m_fDuration, Duration) //定义一个词典成员指针变量m_pFrames,用于保存每个动画帧,CC_SYNTHESIZE_RETAIN宏创建对变量的set 和 get 操作的函数,函数名分别为setFrames和getFrames, 相应的set函数会释放旧的指针变量,并将参数保存到指针变量,并自动的对其引用计数器加一。 CC_SYNTHESIZE_RETAIN(CCArray*, m_pFrames, Frames) //定义一个bool成员变量m_bRestoreOriginalFrame,用于指定是否在动画结束后释放各帧。CC_SYNTHESIZE宏创建对变量的set 和 get 操作的函数,函数名分别为setRestoreOriginalFrame和getRestoreOriginalFrame。 CC_SYNTHESIZE(bool, m_bRestoreOriginalFrame, RestoreOriginalFrame) //定义一个unsigned int成员变量m_uLoops,代表循环播放次数。CC_SYNTHESIZE宏创建对变量的set 和 get 操作的函数,函数名分别为setLoops和getLoops。 CC_SYNTHESIZE(unsigned int, m_uLoops, Loops) }; NS_CC_END #endif // __CC_ANIMATION_H__
再看CPP:
#include "CCAnimation.h" #include "textures/CCTextureCache.h" #include "textures/CCTexture2D.h" #include "ccMacros.h" #include "sprite_nodes/CCSpriteFrame.h" #include "cocoa/CCZone.h" //Cocos2d命名空间 NS_CC_BEGIN //构造 CCAnimationFrame::CCAnimationFrame() : m_pSpriteFrame(NULL) , m_fDelayUnits(0.0f) , m_pUserInfo(NULL) { } //初始化单帧:参一为精灵帧,参二为当前帧到下一帧的时间间隔,参三为存储各帧所用的词典。 bool CCAnimationFrame::initWithSpriteFrame(CCSpriteFrame* spriteFrame, float delayUnits, CCDictionary* userInfo) { //设置为当前单帧所用的各成员变量。 setSpriteFrame(spriteFrame); setDelayUnits(delayUnits); setUserInfo(userInfo); return true; } //析构 CCAnimationFrame::~CCAnimationFrame() { CCLOGINFO( "cocos2d: deallocing %s", this); //对占用的精灵帧和词典引用计数估减一操作。 CC_SAFE_RELEASE(m_pSpriteFrame); CC_SAFE_RELEASE(m_pUserInfo); } //创建当前单帧的实例拷贝。 CCObject* CCAnimationFrame::copyWithZone(CCZone* pZone) { //定义拷贝类变量指针 CCZone* pNewZone = NULL; //定义单帧类指针变量pCopy用于存储创建结果,置空。 CCAnimationFrame* pCopy = NULL; //如果参数pZone有效且已经有内部产生的实例拷贝。 if(pZone && pZone->m_pCopyObject) { //直接取得拷贝返回给pCopy. pCopy = (CCAnimationFrame*)(pZone->m_pCopyObject); } else { //如果参数pZone无值或者无内部产生的拷贝,重新生成单帧以及相应单帧的拷贝。 pCopy = new CCAnimationFrame(); pNewZone = new CCZone(pCopy); } //对pCopy进行初始化,注意相应成员变量也要调用copy以产生拷贝,并调用autorelease以交由内存管理器进行释放管理。 pCopy->initWithSpriteFrame((CCSpriteFrame*)m_pSpriteFrame->copy()->autorelease(), m_fDelayUnits, m_pUserInfo != NULL ? (CCDictionary*)m_pUserInfo->copy()->autorelease() : NULL); //释放pNewZone CC_SAFE_DELETE(pNewZone); //返回pCopy。 return pCopy; } //创建动画 CCAnimation* CCAnimation::animation(void) { return CCAnimation::create(); } //创建动画。 CCAnimation* CCAnimation::create(void) { //使用new创建一个动画,初始化并交由内存管理器进行释放管理。 CCAnimation *pAnimation = new CCAnimation(); pAnimation->init(); pAnimation->autorelease(); //返回创建成功的动画。 return pAnimation; } //创建一个序列帧动画,内部调用相应createWithSpriteFrames实现。参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔。 CCAnimation* CCAnimation::animationWithSpriteFrames(CCArray *frames, float delay/* = 0.0f*/) { return CCAnimation::createWithSpriteFrames(frames, delay); } //创建一个序列帧动画。参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔。 CCAnimation* CCAnimation::createWithSpriteFrames(CCArray *frames, float delay/* = 0.0f*/) { //使用new创建一个动画实例,初始化后交由内存管理器进行释放管理。 CCAnimation *pAnimation = new CCAnimation(); pAnimation->initWithSpriteFrames(frames, delay); pAnimation->autorelease(); //返回创建的动画实例。 return pAnimation; } //创建一个序列帧动画,内部调用相应create实现。参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔,参三为循环次数。 CCAnimation* CCAnimation::animationWithAnimationFrames(CCArray* arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops) { return CCAnimation::create(arrayOfAnimationFrameNames, delayPerUnit, loops); } //创建一个序列帧动画,参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔,参三为循环次数。 CCAnimation* CCAnimation::create(CCArray* arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops) { //使用new创建一个动画实例,初始化后交由内存管理器进行释放管理。 CCAnimation *pAnimation = new CCAnimation(); pAnimation->initWithAnimationFrames(arrayOfAnimationFrameNames, delayPerUnit, loops); pAnimation->autorelease(); //返回创建的动画实例。 return pAnimation; } //初始化一个空白的动画。 bool CCAnimation::init() { return initWithSpriteFrames(NULL, 0.0f); } //初始化序列帧动画,参数一为存储精灵帧的数组,参数二为动画的每一帧与下帧的时间间隔。 bool CCAnimation::initWithSpriteFrames(CCArray *pFrames, float delay/* = 0.0f*/) { //对pFrames是否是CCSpriteFrame指针数组进行检查。 CCARRAY_VERIFY_TYPE(pFrames, CCSpriteFrame*); //将循环次数置1. m_uLoops = 1; //保存每一帧与下一帧的时间间隔。 m_fDelayPerUnit = delay; //创建一个CArray,返回它的地址给指针pTmpFrames。 CCArray* pTmpFrames = CCArray::create(); //将pTmpFrames设置为当前动画的帧数组指针。 setFrames(pTmpFrames); //如果代表单帧数组的参数pFrames有值,遍历各帧存入pTmpFrames. if (pFrames != NULL) { //定义临时变量pObj用来存储遍历时的元素。 CCObject* pObj = NULL; //遍历pFrames,以pObj为每次循环的元素指针。 CCARRAY_FOREACH(pFrames, pObj) { //将pObj转为精灵帧指针。 CCSpriteFrame* frame = (CCSpriteFrame*)pObj; //创建一个动画单帧,并将精灵单帧做为参数对其进行初始化。 CCAnimationFrame *animFrame = new CCAnimationFrame(); animFrame->initWithSpriteFrame(frame, 1, NULL); //将初始化完成后的动画单帧放入动画帧容器。 m_pFrames->addObject(animFrame); //对动画单帧的引用计数器减1,不会被释放的,因为上面一行代码中addObject对它的引用计数器内部会做加1操作。这样“该接手的加1,该放手的减1”完成了交接工作。 animFrame->release(); //动画帧数加一。 m_fTotalDelayUnits++; } } return true; } //初始化序列帧动画,参数一为存储单帧的数组,参数二为动画的每一帧与下帧的时间间隔,参数三为循环播放次数。 bool CCAnimation::initWithAnimationFrames(CCArray* arrayOfAnimationFrames, float delayPerUnit, unsigned int loops) { //对pFrames是否是CCSpriteFrame指针数组进行检查。 CCARRAY_VERIFY_TYPE(arrayOfAnimationFrames, CCAnimationFrame*); //保存每一帧与下一帧的时间间隔。 m_fDelayPerUnit = delayPerUnit; //保存循环次数 m_uLoops = loops; //将arrayOfAnimationFrames设置为当前动画的帧数组指针。 setFrames(CCArray::createWithArray(arrayOfAnimationFrames)); //定义临时变量pObj用来存储遍历时的元素。 CCObject* pObj = NULL; //遍历pFrames,以pObj为每次循环的元素指针。 CCARRAY_FOREACH(m_pFrames, pObj) { //将pObj转为动画单帧指针。 CCAnimationFrame* animFrame = (CCAnimationFrame*)pObj; //这里写的有误,应该改成动画帧数加一,它加动画帧的相应函数干毛? m_fTotalDelayUnits += animFrame->getDelayUnits(); } //返回成功。 return true; } //构造 CCAnimation::CCAnimation() : m_fTotalDelayUnits(0.0f) , m_fDelayPerUnit(0.0f) , m_fDuration(0.0f) , m_pFrames(NULL) , m_bRestoreOriginalFrame(false) , m_uLoops(0) { } //析构 CCAnimation::~CCAnimation(void) { CCLOGINFO("cocos2d, deallocing %p", this); CC_SAFE_RELEASE(m_pFrames); } //新加一帧到序列帧动画中,参数为CCSpriteFrame指针。 void CCAnimation::addSpriteFrame(CCSpriteFrame *pFrame) { //利用new新建一个动画帧. CCAnimationFrame *animFrame = new CCAnimationFrame(); //使用精灵指参数对其进行初始化。 animFrame->initWithSpriteFrame(pFrame, 1.0f, NULL); //将生成的动画帧放入动画帧容器 m_pFrames->addObject(animFrame); //对动画单帧的引用计数器减1。(新建的animFrame,交给m_pFrames的addObject函数会对其引用计数器加1,后就要减1才对。交接也要保证引用计数的平衡) animFrame->release(); //动画帧数加一。 m_fTotalDelayUnits++; } //新加一帧加入到序列帧动画中,参数为单帧图片名称。 void CCAnimation::addSpriteFrameWithFileName(const char *pszFileName) { //先由图片名称取得创建的纹理。 CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addImage(pszFileName); //定义临时矩形保存图片大小。 CCRect rect = CCRectZero; rect.size = pTexture->getContentSize(); //由当前图片和对应大小创建出一个精灵帧 。 CCSpriteFrame *pFrame = CCSpriteFrame::createWithTexture(pTexture, rect); //使用创建的精灵帧来调用上一个函数增加一帧动画。 addSpriteFrame(pFrame); } //新加一帧加入到序列帧动画中,参数一为图集纹理,参数二为对应的纹理块矩形。 void CCAnimation::addSpriteFrameWithTexture(CCTexture2D *pobTexture, const CCRect& rect) { //由当前图片和对应大小创建出一个精灵帧 。 CCSpriteFrame *pFrame = CCSpriteFrame::createWithTexture(pobTexture, rect); //使用创建的精灵帧来增加一帧动画。 addSpriteFrame(pFrame); } //取得序列帧动画总时长。 float CCAnimation::getDuration(void) { //总时长 = 帧数x帧间隔时间。 return m_fTotalDelayUnits * m_fDelayPerUnit; } //创建当前序列帧动画实例的拷贝。 CCObject* CCAnimation::copyWithZone(CCZone* pZone) { //定义拷贝类变量指针 CCZone* pNewZone = NULL; //定义序列帧动画类指针变量pCopy用于存储创建结果,置空。 CCAnimation* pCopy = NULL; //如果参数pZone有效且已经有内部产生的实例拷贝。 if(pZone && pZone->m_pCopyObject) { //直接取得拷贝返回给pCopy. pCopy = (CCAnimation*)(pZone->m_pCopyObject); } else { //如果参数pZone无值或者无内部产生的拷贝,重新生成单帧以及相应单帧的拷贝。 pCopy = new CCAnimation(); pNewZone = new CCZone(pCopy); } //初始化序列序动画。 pCopy->initWithAnimationFrames(m_pFrames, m_fDelayPerUnit, m_uLoops); //设置相应变理。 pCopy->setRestoreOriginalFrame(m_bRestoreOriginalFrame); //释放pNewZone. CC_SAFE_DELETE(pNewZone); return pCopy; } NS_CC_END
最后我们看一下CCAnimate:
class CCTexture2D; //序列帧动画 class CC_DLL CCAnimate : public CCActionInterval { public: //构造与析构。 CCAnimate(); ~CCAnimate(); //初始化动画。 bool initWithAnimation(CCAnimation *pAnimation); //重载基类的相应函数。 virtual CCObject* copyWithZone(CCZone* pZone); virtual void startWithTarget(CCNode *pTarget); virtual void stop(void); virtual void update(float t); virtual CCActionInterval* reverse(void); public: //创建一个序列帧动画,内部调用create实现。参数为序列帧动画信息结构指针。 CC_DEPRECATED_ATTRIBUTE static CCAnimate* actionWithAnimation(CCAnimation *pAnimation); //创建一个序列帧动画。 static CCAnimate* create(CCAnimation *pAnimation); //定义一个序列帧动画信息结构指针变量以及存取此变量的函数。 CC_SYNTHESIZE_RETAIN(CCAnimation*, m_pAnimation, Animation) protected: //保存每一帧要切换时的进度的容器指针,由动画信息结构指针取得。 std::vector<float>* m_pSplitTimes; //当前要播放的下一帧序号。 int m_nNextFrame; //各帧中保存的精灵信息。 CCSpriteFrame* m_pOrigFrame; //循环次数。 unsigned int m_uExecutedLoops; }; 具体实现: //创建一个序列帧动画,内部调用create实现。参数为动画信息结构。 CCAnimate* CCAnimate::actionWithAnimation(CCAnimation *pAnimation) { return CCAnimate::create(pAnimation); } //创建一个序列帧动画。 CCAnimate* CCAnimate::create(CCAnimation *pAnimation) { //使用new创建一个CCAnimate实例对象,初始化,交由内存管理器。 CCAnimate *pAnimate = new CCAnimate(); pAnimate->initWithAnimation(pAnimation); pAnimate->autorelease(); //返回创建的实例对象。 return pAnimate; } //初始化序列动画。 bool CCAnimate::initWithAnimation(CCAnimation *pAnimation) { //有效性判断。 CCAssert( pAnimation!=NULL, "Animate: argument Animation must be non-NULL"); //取得序列的时长。 float singleDuration = pAnimation->getDuration(); //乘以循环次数做为当前动画总时长来进行初始化。 if ( CCActionInterval::initWithDuration(singleDuration * pAnimation->getLoops() ) ) { //初始化变量。 m_nNextFrame = 0; //将参数pAnimation保存到动画信息结构指针变量m_pAnimation. setAnimation(pAnimation); m_pOrigFrame = NULL; m_uExecutedLoops = 0; //设置容器大小。这里我认为应该写成resize而不是reserver!!! m_pSplitTimes->reserve(pAnimation->getFrames()->count()); //初始化变量。 float accumUnitsOfTime = 0; //序列播放一轮的时间/ float newUnitOfTimeValue = singleDuration / pAnimation->getTotalDelayUnits(); //取得序列信息中的帧信息数组。 CCArray* pFrames = pAnimation->getFrames(); //判断pFrames是否是CCAnimationFrame指针类型,确保是。 CCARRAY_VERIFY_TYPE(pFrames, CCAnimationFrame*); //定义临时变量pObj来遍历取得帧信息中的单帧信息。 CCObject* pObj = NULL; CCARRAY_FOREACH(pFrames, pObj) { //取得单帧信息。 CCAnimationFrame* frame = (CCAnimationFrame*)pObj; //计算播放当前单帧时的进度,这里又用到了单帧信息的接口getDelayUnits。后面讲一下。 float value = (accumUnitsOfTime * newUnitOfTimeValue) / singleDuration; accumUnitsOfTime += frame->getDelayUnits(); //将当前单帧的进度存入容器。 m_pSplitTimes->push_back(value); } //返回成功。 return true; } //如果初始化失败,返回false。 return false; } //创建拷贝。 CCObject* CCAnimate::copyWithZone(CCZone *pZone) { CCZone* pNewZone = NULL; CCAnimate* pCopy = NULL; if(pZone && pZone->m_pCopyObject) { //in case of being called at sub class pCopy = (CCAnimate*)(pZone->m_pCopyObject); } else { pCopy = new CCAnimate(); pZone = pNewZone = new CCZone(pCopy); } CCActionInterval::copyWithZone(pZone); pCopy->initWithAnimation((CCAnimation*)m_pAnimation->copy()->autorelease()); CC_SAFE_DELETE(pNewZone); return pCopy; } //构造,注意在这里申请了一个vector<float>容器,并将成员指针变量m_pSplitTimes指向它的。 CCAnimate::CCAnimate() : m_pAnimation(NULL) , m_pSplitTimes(new std::vector<float>) , m_nNextFrame(0) , m_pOrigFrame(NULL) , m_uExecutedLoops(0) { } //析构。 CCAnimate::~CCAnimate() { CC_SAFE_RELEASE(m_pAnimation); CC_SAFE_RELEASE(m_pOrigFrame); CC_SAFE_DELETE(m_pSplitTimes); } //设置演示当前序列动画的演员。 void CCAnimate::startWithTarget(CCNode *pTarget) { //先调用基类的相应函数。 CCActionInterval::startWithTarget(pTarget); //序列帧动画必须是个精灵。 CCSprite *pSprite = (CCSprite*)(pTarget); //释放上一个 CC_SAFE_RELEASE(m_pOrigFrame); //如果有帧数据。 if (m_pAnimation->getRestoreOriginalFrame()) { //取得精灵的帧信息。 m_pOrigFrame = pSprite->displayFrame(); //对帧信息占用,引用数加一。 m_pOrigFrame->retain(); } m_nNextFrame = 0; m_uExecutedLoops = 0; } //停止当前动画。 void CCAnimate::stop(void) { //如果动画有帧数据且有演员。 if (m_pAnimation->getRestoreOriginalFrame() && m_pTarget) { //设置演员显示当前帧。 ((CCSprite*)(m_pTarget))->setDisplayFrame(m_pOrigFrame); } //停止当前动画。 CCActionInterval::stop(); } //更新动画。 void CCAnimate::update(float t) { // 如果整个动画未播放,先计算循环是否够次数。 if( t < 1.0f ) { //计算出当前进度播放的循环数。 t *= m_pAnimation->getLoops(); // 通过先取整再判断是否大于当前的已经循环次数来判断是否是新的循环,如果是将下一帧置零,已经循环的次数加1 。 unsigned int loopNumber = (unsigned int)t; if( loopNumber > m_uExecutedLoops ) { m_nNextFrame = 0; m_uExecutedLoops++; } // 对t进行浮点取模值,将其限制在0~1之间,这样等于又转换成为了当前动画播放的进度值。 t = fmodf(t, 1.0f); } //取得动画的帧信息容器和帧数。 CCArray* frames = m_pAnimation->getFrames(); unsigned int numberOfFrames = frames->count(); //精灵图片信息。 CCSpriteFrame *frameToDisplay = NULL; //找出要播放的帧图片设置为精灵要显示的图片,这里用了一个for循环。从下一帧开始到结束帧进行遍历,判断是否到了这一帧。 for( unsigned int i=m_nNextFrame; i < numberOfFrames; i++ ) { //取出循环中的当前帧的播出时间进度。 float splitTime = m_pSplitTimes->at(i); //如果这一帧的进度小于当前动画的播放进度,即代表进入了这一帧。 if( splitTime <= t ) { //取得对应的动画帧信息。 CCAnimationFrame* frame = (CCAnimationFrame*)frames->objectAtIndex(i); //取得当前帧的精灵图片信息。 frameToDisplay = frame->getSpriteFrame(); //这才是显示动画的关键,找到相应的精灵帧并设置为演员要显示的当前帧。 ((CCSprite*)m_pTarget)->setDisplayFrame(frameToDisplay); //通过动画帧信息取得其附加的用户词典信息,这个词典存储的是用于需要通知的目标。 CCDictionary* dict = frame->getUserInfo(); if( dict ) { //暂忽略了。 //TODO: [[NSNotificationCenter defaultCenter] postNotificationName:CCAnimationFrameDisplayedNotification object:target_ userInfo:dict]; } //帧数加一。 m_nNextFrame = i+1; break; } } } //创建一个反向播放的序列帧动画。 CCActionInterval* CCAnimate::reverse(void) { CCArray* pOldArray = m_pAnimation->getFrames(); CCArray* pNewArray = CCArray::createWithCapacity(pOldArray->count()); CCARRAY_VERIFY_TYPE(pOldArray, CCAnimationFrame*); if (pOldArray->count() > 0) { CCObject* pObj = NULL; CCARRAY_FOREACH_REVERSE(pOldArray, pObj) { CCAnimationFrame* pElement = (CCAnimationFrame*)pObj; if (! pElement) { break; } pNewArray->addObject((CCAnimationFrame*)(pElement->copy()->autorelease())); } } CCAnimation *newAnim = CCAnimation::create(pNewArray, m_pAnimation->getDelayPerUnit(), m_pAnimation->getLoops()); newAnim->setRestoreOriginalFrame(m_pAnimation->getRestoreOriginalFrame()); return create(newAnim); }
源码看完了,来看一个小示例 ,以ActionsTest为例,我将ActionAnimate做了稍许修改,只有一个演员演示一个14帧的序列帧动画。我们来看一下实际的动作生成过程。
void ActionAnimate::onEnter() { ActionsDemo::onEnter(); //创建一个演员,站在屏幕中央。 centerSprites(1); //创建一个空白的序列帧动画信息。 CCAnimation* animation = CCAnimation::create(); //共有14帧,这里用for循环将对应的序列图加入到动画中。 for( int i=1;i<15;i++) { char szName[100] = {0}; sprintf(szName, "Images/grossini_dance_%02d.png", i); animation->addSpriteFrameWithFileName(szName); } //设置每两帧间时间间隔为1秒。 animation->setDelayPerUnit(1.0f); //设置动画结束后仍保留动画帧信息。 animation->setRestoreOriginalFrame(true); //由这个动画信息创建一个序列帧动画。 CCAnimate* action = CCAnimate::create(animation); //让演员演示这个动画。 m_grossini->runAction(action); }
流程是先创建动画帧信息,然后由动画帧信息生成动画信息,最后由动画信息创建出序列帧动画供精灵演示。
这里面的关健函数是addSpriteFrameWithFileName,我们来看一下在调用它时的程序流程,当我们把图片名称赋给它后,它创建出纹理并调用createWithTexture函数来进行创建一个精灵单帧,初始化后使用这个精灵单帧信息创建一帧动画放到动画信息容器中。
void CCAnimation::addSpriteFrameWithFileName(const char *pszFileName) { CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addImage(pszFileName); CCRect rect = CCRectZero; rect.size = pTexture->getContentSize(); CCSpriteFrame *pFrame = CCSpriteFrame::createWithTexture(pTexture, rect); addSpriteFrame(pFrame); }
在createWithTexture函数中,传入的是纹理和纹理大小。其内部实现了创建精灵帧并初始化的功能。
CCSpriteFrame* CCSpriteFrame::createWithTexture(CCTexture2D *pobTexture, const CCRect& rect) { CCSpriteFrame *pSpriteFrame = new CCSpriteFrame();; pSpriteFrame->initWithTexture(pobTexture, rect); pSpriteFrame->autorelease(); return pSpriteFrame; }
这里的关帧是initWithTexture函数,传入的是纹理和纹理大小。
bool CCSpriteFrame::initWithTexture(CCTexture2D* pobTexture, const CCRect& rect) { CCRect rectInPixels = CC_RECT_POINTS_TO_PIXELS(rect); return initWithTexture(pobTexture, rectInPixels, false, CCPointZero, rectInPixels.size); }
我们看,此函数内部以不旋转,不偏移,原始大小为当前大小等参数来进行了初始化精灵单帧的处理。这样做说明使用的就是单图集的纹理。
如果我们使用的是图集纹理,需要在调用addSpriteFrameWithFileName函数时加上相应的纹理块的相关设置参数。
最后,我来解答一下CCAnimationFrame类中的成员变量m_fDelayUnits的。这个变量其实是无用的,在整个动画处理过程中它出现过二次。一次是CCAnimation::initWithAnimationFrames函数中
m_fTotalDelayUnits+= animFrame->getDelayUnits();
另一次是CCAnimate::initWithAnimation函数中在for循环中的
float value = (accumUnitsOfTime* newUnitOfTimeValue) / singleDuration; accumUnitsOfTime+= frame->getDelayUnits();
先看第一次,我们知道m_fTotalDelayUnits代表的是帧的数量,其实对于单帧来说,它本身只有一帧,也就是说animFrame->getDelayUnits()的结果应该是1,所以m_fTotalDelayUnits += animFrame->getDelayUnits();应该改为m_fTotalDelayUnits ++;就好了。第二次中也一样,frame->getDelayUnits();也应该为一,因为float value =(accumUnitsOfTime * newUnitOfTimeValue) / singleDuration;这一句是用来进算当前帧在整个动画播放中的进度,float newUnitOfTimeValue = singleDuration /pAnimation->getTotalDelayUnits();这一句中singleDuration代表动画的时长,pAnimation->getTotalDelayUnits()代表总帧数,那newUnitOfTimeValue即是每两帧间的时间时间隔。既然newUnitOfTimeValue的意义是每两帧间的时间时间隔,则accumUnitsOfTime应该代表的就是当前遍历到第几帧了,它是帧索引。所以我认为CCAnimationFrame类中的成员变量m_fDelayUnits本身没有必要存在,就是1,这个变量的存在反而会影响到大家对于动画的理解,如果说CCAnimationFrame本身又支持存储多个精灵帧,那倒是需要一个变量来代表精灵帧的数量,但目前看并不是。