cocos2d-x播放序列幀動畫
最近做項目,需要用到特效,首先我想到的是動畫編輯器,目前用於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());
}
}
我們每一次調用update就會取下一幀的圖片名字,然後把它顯示在精靈上面。
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;
}