最近做项目,需要用到特效,首先我想到的是动画编辑器,目前用于cocos2d-x的动画编辑器不是很多,我曾经用过一款,
Spriter 网址 http://www.brashmonkey.com/
这是一款不错的编辑器,而且对资源的节省也是不错的,但是可惜的是,我们的美术没有用过。我看了看他们做特效用的是Flash或者AE,可以很轻松的导出序列帧动画,我想既可以导出序列帧动画,那么我们在程序里直接播放就可以了。cocos2d-x自带的动画播放类是Animate,不过很遗憾,我没有用过,并且对它也没有什么兴趣,毕竟一个简单的帧动画,实现起来还是很简单的,更何况我们需要的是更多自己的定制。所以我就决定自己写一个播放序列帧的类。
1.我们新建一个类,起名字叫做QY_AnimationPlayer,注意这里,我们让它继承CCNode,而不是CCSprite
首先给它加入两个参数
//帧速
CC_SYNTHESIZE(int, m_nFrameSpeed, FrameSpeed);
//是否循环播放
CC_SYNTHESIZE(bool, m_bIsLoop, IsLoop);
这里我采用的是cocos2d-x的访问器写法,不明白的同学请自己去看源代码,或者去看Objective-C的语法,因为这一写法是来自于OC的。我们在这里定义了一个整形变量帧速和一个布尔型变量是否循环播放。
2.添加一个精灵显示每帧动画,添加一个数组,用于存放整个动画序列的图片的名字
CCSprite *m_pAnimateSprite;
我们让这个CCSprite动态的显示不同的图片,那么它就会形成一个动画,动画就是这么来的。
CCArray *m_pSpriteArray;
在这个数组里存放每一帧的图片名称,通过顺序读取这个数组里的名字,从内存中获取响应的图片,显示在界面上
3.定时器
schedule(schedule_selector(QY_AnimationPlayer::update),framespeed/1000.0);
启动一个定时器,每隔一定的时间,调用一次update函数,进行一个刷新
void QY_AnimationPlayer::update(float time) { //帧数 m_nAnimateCounter ++; if(m_nAnimateCounter >= m_pSpriteArray->count()) { if(m_bIsLoop) { m_nAnimateCounter = 0; }else{ //如果不循环播放,那么播放完一遍,移除 this->unschedule(schedule_selector(QY_AnimationPlayer::update)); this->stop(); return; } } //绘制 CCString *fileNameStr = (CCString*)m_pSpriteArray->objectAtIndex(m_nAnimateCounter); // CCLog("绘制 %s %d",fileNameStr->getCString(),m_nAnimateCounter); CCSprite *sprite = CCSprite::createWithSpriteFrameName(fileNameStr->getCString()); if(sprite) { m_pAnimateSprite->setDisplayFrame(sprite->displayFrame()); } }
4.用texture packer 打包,我们读取plist文件
这里的plist文件名字,必须和图片的名字相同,比如说 aaa.plist 文件中图片的命名是 aaa_1.png
好啦,说的太多,大家也不明白,直接上代码就好了,一个完整的类,读取plist文件来初始化。
// // QY_AnimationPlayer.h // TestAnimate // // Created by 江南岸 on 13-6-13. // // #ifndef __TestAnimate__QY_AnimationPlayer__ #define __TestAnimate__QY_AnimationPlayer__ #include <iostream> #include "cocos2d.h" USING_NS_CC; class QY_AnimationPlayer : public CCNode { //动画帧数计数器 CC_SYNTHESIZE(int, m_nAnimateCounter, AnimateCounter); //帧速 CC_SYNTHESIZE(int, m_nFrameSpeed, FrameSpeed); //是否循环播放 CC_SYNTHESIZE(bool, m_bIsLoop, IsLoop); //plist文件的名字 CC_SYNTHESIZE_RETAIN(CCString*, m_pPlistFileName, PlistFileName); private: CCSprite *m_pAnimateSprite; CCArray *m_pSpriteArray; const char* parsePlistName(const char* plistFile); int parseFileName(const char* fileName); void constructArray(CCArray *array); public: QY_AnimationPlayer(); ~QY_AnimationPlayer(); static QY_AnimationPlayer *createWithPlistFile(const char *plistFile,bool isLoop=false,int frameSpeed=40); bool initWithPlistFile(const char *plistFile,bool isLoop=false,int frameSpeed=40); void update(float time); void viewDidLoad(); //获得整个动画的时间 float getAllAnimationTime(); //获得帧数总和 int getAllFrameCount(); //停止播放 void stop(); }; #endif /* defined(__TestAnimate__QY_AnimationPlayer__) */
// // QY_AnimationPlayer.cpp // TestAnimate // // Created by 江南岸 on 13-6-13. // // #include "QY_AnimationPlayer.h" QY_AnimationPlayer::QY_AnimationPlayer() { m_pSpriteArray = CCArray::create(); m_pSpriteArray->retain(); m_pAnimateSprite = NULL; m_pPlistFileName = NULL; m_nAnimateCounter = 0; m_nFrameSpeed = 40; m_bIsLoop =true; } QY_AnimationPlayer::~QY_AnimationPlayer() { CC_SAFE_RELEASE(m_pSpriteArray); } QY_AnimationPlayer* QY_AnimationPlayer::createWithPlistFile(const char *plistFile,bool isLoop,int frameSpeed) { QY_AnimationPlayer *animate = new QY_AnimationPlayer(); if(animate && animate->initWithPlistFile(plistFile,isLoop,frameSpeed)) { animate->autorelease(); return animate; } CC_SAFE_DELETE(animate); return NULL; } bool QY_AnimationPlayer::initWithPlistFile(const char *plistFile,bool isLoop,int frameSpeed) { if(!CCNode::init()) { return false; } //帧速 m_nFrameSpeed = frameSpeed; //是否循环 m_bIsLoop = isLoop; //保存文件名字 setPlistFileName(CCString::create(plistFile)); //解析 CCDictionary *dict = CCDictionary::createWithContentsOfFile(plistFile); CCDictionary *frames = (CCDictionary*)dict->objectForKey("frames"); CCArray* array = frames->allKeys(); //不能为空 if(0 == array->count()) { return NULL; } constructArray(array); viewDidLoad(); return true; } void QY_AnimationPlayer::constructArray(cocos2d::CCArray *array) { //得到最小值和最大值 int max = 0; int min = 0; for(int i=0;i<array->count();i++) { CCString *str = (CCString*)array->objectAtIndex(i); int num = parseFileName(str->getCString()); if(0 == i) { max = num; min = num; } if(num > max) { max = num; } if(num < min) { min = num; } } //掐头去尾后的名字 /* plist文件起名字是有规则的 比如说图片是 aa_1.png aa_2.png.... 那么plist文件的名字应该是 aa.plist */ const char*fileName = parsePlistName(m_pPlistFileName->getCString()); // printf("file name is %s\n",fileName); char filename[128] = {0}; strcpy(filename, fileName); //组织图片文件序列 int lastIndex = min; for(int i=min;i<=max;i++) { bool isHava = false; CCObject *obj = NULL; CCARRAY_FOREACH(array, obj) { CCString *str = (CCString*)obj; int num = parseFileName(str->getCString()); if(i == num) { //招到了对应的图片名字 CCString *str = CCString::createWithFormat("%s_%d.png",filename,i); m_pSpriteArray->addObject(str); lastIndex = i; isHava = true; break; } } //如果找不到这个图片名字 if(!isHava && i!=max) { CCString *str = CCString::createWithFormat("%s_%d.png",filename,lastIndex); m_pSpriteArray->addObject(str); } } } void QY_AnimationPlayer::viewDidLoad() { CCString *firstStr = (CCString*)m_pSpriteArray->objectAtIndex(0); m_pAnimateSprite = CCSprite::createWithSpriteFrameName(firstStr->getCString()); addChild(m_pAnimateSprite); float framespeed = m_nFrameSpeed; //开始播放 schedule(schedule_selector(QY_AnimationPlayer::update),framespeed/1000.0); } void QY_AnimationPlayer::update(float time) { //帧数 m_nAnimateCounter ++; if(m_nAnimateCounter >= m_pSpriteArray->count()) { if(m_bIsLoop) { m_nAnimateCounter = 0; }else{ //如果不循环播放,那么播放完一遍,移除 this->unschedule(schedule_selector(QY_AnimationPlayer::update)); this->stop(); return; } } //绘制 CCString *fileNameStr = (CCString*)m_pSpriteArray->objectAtIndex(m_nAnimateCounter); // CCLog("绘制 %s %d",fileNameStr->getCString(),m_nAnimateCounter); CCSprite *sprite = CCSprite::createWithSpriteFrameName(fileNameStr->getCString()); if(sprite) { m_pAnimateSprite->setDisplayFrame(sprite->displayFrame()); } } float QY_AnimationPlayer::getAllAnimationTime() { return m_nFrameSpeed * getAllFrameCount()*0.001; } int QY_AnimationPlayer::getAllFrameCount() { return m_pSpriteArray->count(); } void QY_AnimationPlayer::stop() { this->unschedule(schedule_selector(QY_AnimationPlayer::update)); this->removeFromParent(); } // animate/hq.plist 变成 hq const char* QY_AnimationPlayer::parsePlistName(const char* plistFile) { char buff[64] = {0}; for(int i=0;i<strlen(plistFile);i++) { if('/' == plistFile[i]) { strcpy(buff, &plistFile[i+1]); } } // printf("#animate plist file name is%s\n",buff); char buff2[64] = {0}; for(int i=0;i<strlen(buff);i++) { if('.' == buff[i]) { break; } buff2[i] = buff[i]; } const char* buffer = buff2; // printf("buffer is %s\n",buffer); return buffer; } //hq_1.png 得到1,也就是这个图片的索引 int QY_AnimationPlayer::parseFileName(const char* fileName) { char buff[64] = {0}; for(int i=0;i<strlen(fileName);i++) { if('_' == fileName[i]) { int index = i + 1; int buffIndex = 0; while (index < strlen(fileName) && '.' != fileName[index]) { buff[buffIndex] = fileName[index]; index ++; buffIndex++; } } } int num = atoi(buff); // printf("num == %d , buff == %s\n",num,buff); return num; }