cocos2dx をアニメーション化する方法はたくさんありますが、今日はそのうちの 1 つを紹介します AnimatePacker と TexturePacker を組み合わせて完成するアニメーション、実はもう 1 つ zwoptex という texturepacker と同じツールがあり、plist と統合された png を生成することができます, ただし、texturepackerは必須です 有料のものは無料版を使っていて、ずっと良いと思っていたのですが、生成された画像に大きな赤いマークを描いてしまうことが後で分かりました ここが、のチートな場所かもしれません無料版なのでクラック版を使用するのが最善です。
そして、texturepacker は非常に便利なツールで、基本的には一目で使い方がわかります。これはもう紹介されません。すべての小さな画像を 1 つの大きな画像に生成できます。plist ファイルには、各小さな画像の位置とサイズ、およびそれを生成したツール (texturepacker または zwoptex) が記録されます。しかし、最も重要なことは、plist ファイル内の画像プロパティの配列です。
plist 全体にすべての画像が必要な場合は、この plist と png を直接使用してアニメーション全体を実現できますが、この plist 内のアニメーションをバッチに分割したり、グループ化する必要がある場合は、animatepacker ツールを使用する必要があります。これも非常に便利です。animatepacker の左上の plist エリアに plist を直接ドラッグすると、右側のスプライト エリアにすべてのスプライトがリストされ、texturepacker によって生成された plist が表示されます。 Zwoptexだとわかりにくいので、texturepackerを使い、各アクションの名前、時間、方向を左下に直接設定できるようにしています。スペースなどの名前は、コード。
今度はコードが出てきます。バージョンの問題で少し修正しましたが、cocos2dx 2.2を使用しており、名前を使用する必要があるため、少し修正しました。
次のように、AnimatePacker クラスを使用する必要があります。
char anuPath[ 256 ];
sprintf (anuPath, "%d" , skillId);
文字列アクションタイプ = anuPath;
sprintf (anuPath, "assets/skill/icon/%d/%d.xml" ,skillId, skillId);
CCDictionary *dict = AnimatePacker :: getInstance ()-> loadAnimations (anuPath);
CCString *str = ( CCString *)dict-> objectForKey (actionType);
CCSprite *sprite = CCSprite :: createWithSpriteFrameName ( getFirstPictureNameByType (actionType, anuPath). c_str ());
m_skillShow -> setTexture (スプライト -> getTexture ());
CCCallFunc *endFun = CCCallFunc :: create ( this , callfunc_selector ( RoleSprite :: showSkillEnd ));
CCSequence *seq = CCSequence :: create ( AnimatePacker :: getInstance ()-> getAnimate (actionType. c_str ()), endFun, NULL );
m_skillShow -> runAction (シーケンス);
AnimatePacker :: getInstance ()-> freeAnimations (anuPath);
説明すると、animatepacker に最初にすべてのアクションをロードさせ、すべてのアクションの最初の画像の名前の辞書を取得し、次に spriteframename メソッドを使用してスプライトを作成し、次に通常のスプライトを使用します。
そして、loadAnimationsで使用される原理は、zwoptex の方法を使用することもできます。つまり、plist ファイルと png ファイルのみがある場合、loadAnimations 関数のパラメータは XML の相対パスであり、XML には plist の名前が含まれており、ファイルの相対パスを見つけて追加します。次のように、 CCSpriteFrameCache :: sharedSpriteFrameCache ()を順に実行します。
string plistPath = CCFileUtils :: sharedFileUtils ()-> fullPathFromRelativeFile (plist[ i ]. c_str (), pszPath. c_str ());
CCSpriteFrameCache :: sharedSpriteFrameCache ()-> addSpriteFramesWithFile ( plistPath.c_str ());
スプライト用のアニメーションスプライトを書きました。
//
// フレームムーブスプライト.h
// シェンシャオ
//
// liuyun によって 10/25/13 に作成されました。
//
//
#ifndef __shengxiao__frameMoveSprite__
#define __shengxiao__frameMoveSprite__
#include "cocos2d.h"
名前空間cocos2dを使用します。
クラスLYFrameMoveSprite: public CCSprite {
virtual bool initWithFile( const char *pszFilename, CCPoint位置, intカウント, float時間);
仮想~LYFrameMoveSprite();
int imgsCount;
浮動小数点変更時間;
CCSize最大サイズ;
パブリック:
CCSize getMaxSize();
LYFrameMoveSprite();
static LYFrameMoveSprite * create( const char *pszFileName, CCPoint位置, intカウント, float時間);
};
#endif /* 定義済み(__shengxiao__frameMoveSprite__) */
//
// フレームムーブスプライト.cpp
// シェンシャオ
//
// liuyun によって 10/25/13 に作成されました。
//
//
#include "フレームムーブスプライト.h"
名前空間stdを使用します。
/*
*パラメータ:
* fileName:相対パスファイル名plistとimageは同じ名前です
* position:图片展示位置点,中心为锚点
* count:item image count
* time:图片切换时间 second
*/
LYFrameMoveSprite*LYFrameMoveSprite::create(constchar *fileName, CCPoint position,int count,float time)
{
LYFrameMoveSprite *pobSprite =newLYFrameMoveSprite();
if (pobSprite && pobSprite->initWithFile(fileName, position, count, time))
{
pobSprite->autorelease();
return pobSprite;
}
CC_SAFE_DELETE(pobSprite);
return NULL;
}
LYFrameMoveSprite::LYFrameMoveSprite()
{
}
LYFrameMoveSprite::~LYFrameMoveSprite()
{
}
CCSize LYFrameMoveSprite::getMaxSize()
{
returnmaxSize;
}
boolLYFrameMoveSprite::initWithFile(constchar *fileName, CCPoint position,int count,float time)
{
CCAssert(fileName !=NULL,"Invalid filename for sprite");
if (!CCSprite::init()) {
return false;
}
changeTime = time;
imgsCount = count;
//xianhe animation :
//step 1: create spriteFrameCach by loading png picture and plist;
string imgFile = fileName;
imgFile.append(".png");
string plistFile = fileName;
plistFile.append(".plist");
CCTexture2D *xianheTexture =CCTextureCache::sharedTextureCache()->addImage(imgFile.c_str());
CCSpriteFrameCache *cache =CCSpriteFrameCache::sharedSpriteFrameCache();
cache->addSpriteFramesWithFile(plistFile.c_str(), xianheTexture);
// step 2: ceate spriteSheet obj,and set new position
CCSpriteBatchNode *xianheSheet=CCSpriteBatchNode::createWithTexture(xianheTexture);
xianheSheet->setPosition(position);
addChild(xianheSheet);
// step 3: create animation frame, load sub-picture of animation frame from cache, and add it as animation frame
//then run with sprite interface;
CCArray* animFrames =CCArray::create();
string fPngName = fileName;
int pos = fPngName.find_last_of("/");
fPngName = fPngName.substr(pos +1);
string midName = fPngName;
midName.append("_0000.png");
// string midName = "n_0000.png";
CCSprite* xianheSprite =CCSprite::createWithSpriteFrameName(midName.c_str());
char xianheStr[64] = { 0};
for(int k =0; k <imgsCount; k++)
{
sprintf(xianheStr,"%s_%04d.png", fPngName.c_str(), k);
CCSpriteFrame *frame = cache->spriteFrameByName(xianheStr);
CCSize frameSize = frame->getTexture()->getContentSize();
if ((frameSize.width * frameSize.height) > (maxSize.width *maxSize.height)) {
maxSize = frameSize;
}
animFrames->addObject(frame);
}
CCAnimation *animation =CCAnimation::createWithSpriteFrames(animFrames,changeTime);
xianheSprite->runAction(CCRepeatForever::create(CCAnimate::create(animation) ));
xianheSheet->addChild(xianheSprite);
// don't release here.
// when load texture failed, it's better to get a "transparent" sprite then a crashed program
// this->release();
return true;
}
看代码即可知道,如何实现动画了。
下面是animatepacker库的代码:
SingletonAni.h
#ifndef __SINGLETON_H__
#define __SINGLETON_H__
template <typename T>
class SingletonAnimate
{
public:
inlinestatic T* getInstance();
inlinestatic void release();
private:
static T* t;
};
template <typename T>
inline T*SingletonAnimate<T>::getInstance()
{
if (!t)
{
t =new T;
}
returnt;
}
template<typename T>
inlinevoid SingletonAnimate<T>::release()
{
if (t)
{
deletet;
t =0;
}
}
template <typename T>
T* SingletonAnimate<T>::t =0;
#endif // __SINGLE_H__
AnimatePacker.h
#ifndef _ANIMATE_PACKER_H_
#define _ANIMATE_PACKER_H_
#include <string>
#include <map>
#include <vector>
#include <set>
#include "cocos2d.h"
#include "SingletonAni.h"
struct Animate{
std::string name;
float delay;
bool flipX;
bool flipY;
std::vector<std::string> spriteFrames;
};
class AnimatePacker:publicSingletonAnimate<AnimatePacker>
{
public:
cocos2d::CCDictionary * loadAnimations(constchar *path);
void freeAnimations(constchar *path);
//Using this function to getting original animate(without FilpX and FlipY).
cocos2d::CCAnimate* getAnimate(constchar *name);
//This function supports FlipX and FlipY.
cocos2d::CCSequence* getSequence(constchar *name);
private:
//The two functions is came from Timothy Zhang. Thank him for his share.
//Original Tip Link:http://www.cocos2d-x.org/boards/6/topics/7219
cocos2d::CCSequence *createSequence(cocos2d::CCArray *actions);
cocos2d::CCSequence *createSequence(cocos2d::CCFiniteTimeAction *pAction1, cocos2d::CCFiniteTimeAction *pAction2, ...);
//From animate name to CCAnimates
std::map<std::string,Animate> nameToAnimateMap;
//From xml path to plist names
std ::マップ< std ::文字列, std ::ベクトル< std :: string > > pathToPlistsMap;
// XML パスからアニメーション名まで
std :: map < std :: string , std :: set < std :: string > > pathToNameMap;
};
#endif //_ANIMATE_PACKER_H_
#include "AnimatePacker.h"
#include "プラットフォーム/CCSAXParser.h"
名前空間stdを使用します。
using namespace cocos2d;
class AnimateSaxDelegator :public CCSAXDelegator
{
public:
enum{
STATE_NONE,
STATE_PLIST,
STATE_ANIMATION,
STATE_NAME,
STATE_DELAY,
STATE_FLIP_X,
STATE_FLIP_Y,
STATE_SPRITE_FRAME
}state;
void startElement(void *ctx,const char *name,const char **atts) ;
void endElement(void *ctx,const char *name) ;
void textHandler(void *ctx,const char *s,int len) ;
vector<string> plists;//All plist name
vector<Animate> animates;//All animate data
};
voidAnimateSaxDelegator::startElement( void *ctx, const char *name, const char **atts )
{
string tag((char*)name);
if (tag=="plist")
{
state=STATE_PLIST;
}
elseif (tag=="animation")
{
state=STATE_ANIMATION;
animates.push_back(Animate());
}
elseif (tag=="name")
{
state=STATE_NAME;
}
elseif (tag=="delay")
{
state=STATE_DELAY;
}
elseif (tag=="spriteFrame")
{
state=STATE_SPRITE_FRAME;
}
elseif (tag=="flipX")
{
state=STATE_FLIP_X;
}
elseif (tag=="flipY")
{
state=STATE_FLIP_Y;
}
else
{
state=STATE_NONE;
}
}
voidAnimateSaxDelegator::endElement( void *ctx, const char *name )
{
string tag((char*)name);
if (tag=="plist")
{
}
elseif (tag=="animation")
{
}
elseif (tag=="name")
{
}
elseif (tag=="delay")
{
}
elseif (tag=="spriteFrame")
{
}
elseif (tag=="flipX")
{
}
elseif (tag=="flipY")
{
}
else
{
}
state =STATE_NONE;
}
voidAnimateSaxDelegator::textHandler( void *ctx, const char *ch, int len )
{
if (state ==STATE_NONE)
{
return;
}
string text((char*)ch,0,len);
int index;
float delay;
switch (state)
{
caseSTATE_PLIST:
plists.push_back(text);
break;
caseSTATE_ANIMATION:
break;
caseSTATE_NAME:
index=animates.size()-1;
animates[index].name=text;
break;
caseSTATE_DELAY:
index=animates.size()-1;
delay=atof(text.c_str());
animates[index].delay=delay;
break;
caseSTATE_SPRITE_FRAME:
index=animates.size()-1;
animates[index].spriteFrames.push_back(text);
break;
caseSTATE_FLIP_X:
index=animates.size()-1;
animates[index].flipX=(text=="true");
break;
caseSTATE_FLIP_Y:
index=animates.size()-1;
animates[index].flipY=(text=="true");
break;
default:
break;
}
}
CCDictionary *AnimatePacker::loadAnimations(constchar *path )
{
CCDictionary *ret =CCDictionary::create();
string pszPath =CCFileUtils::sharedFileUtils()->fullPathForFilename(path);
//CCFileUtils::sharedFileUtils()->fullPathForFilename(path);
CCSAXParser parser;
AnimateSaxDelegator delegator;
if (false == parser.init("UTF-8"))
{
//TODO
return ret;
}
parser.setDelegator(&delegator);
parser.parse(pszPath.c_str());
//load plist
vector<string> plists=delegator.plists;
for (unsignedint i=0;i<plists.size();i++)
{
string plistPath =CCFileUtils::sharedFileUtils()->fullPathFromRelativeFile(plists[i].c_str(), pszPath.c_str());//CCFileUtils::sharedFileUtils()->fullPathFromRelativeFile(plists[i].c_str(), pszPath);
CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(plistPath.c_str());
}
//load animate
vector<Animate> animates=delegator.animates;
CCArray *spriteFramesArray =new CCArray();
set<string> animateNames;
for (unsignedint i=0;i<animates.size();i++)
{
Animate animate=animates[i];
vector<string> spriteFrames=animate.spriteFrames;
CCString *str = CCString::create(animate.spriteFrames[0]);
CCLog("str->getCString() = %s, animate.name.c_str() = %s", str->getCString(), animate.name.c_str());
if (animate.spriteFrames[0].length() >0) {
ret->setObject(str, animate.name.c_str());
}
for (unsignedint j=0;j<spriteFrames.size();j++)
{
animateNames.insert(spriteFrames[j]);
CCLOG("spriteFrames[j] = %s", spriteFrames[j].c_str());
CCSpriteFrame *spriteFrame=CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(spriteFrames[j].c_str());
// CCLOG("spriteFrame = %p", spriteFrame);
spriteFramesArray->addObject(spriteFrame);
}
CCAnimation *animation =CCAnimation::createWithSpriteFrames(spriteFramesArray, animate.delay);//CCAnimation::createWithSpriteFrames(spriteFramesArray,animate.delay);
CCAnimationCache::sharedAnimationCache()->addAnimation(animation,animate.name.c_str());
spriteFramesArray->removeAllObjects();
}
//record animate
for(unsignedint i=0;i<animates.size();i++){
Animate animate=animates[i];
nameToAnimateMap[animate.name]=animate;
}
//record plist
pathToPlistsMap[path]=plists;
//record CCAnimate name
pathToNameMap[path]=animateNames;
return ret;
}
CCAnimate*AnimatePacker::getAnimate(constchar *name )
{
CCAnimation* animation=CCAnimationCache::sharedAnimationCache()->animationByName(name);
if(animation)
{
returnCCAnimate::create(animation);
}
returnNULL;
}
voidAnimatePacker::freeAnimations(constchar *path)
{
string pszPath =CCFileUtils::sharedFileUtils()->fullPathForFilename(path);
vector<string> plists=pathToPlistsMap[path];
for (unsignedint i=0;i<plists.size();i++)
{
string plistPath =CCFileUtils::sharedFileUtils()->fullPathFromRelativeFile(plists[i].c_str(), pszPath.c_str());
CCSpriteFrameCache::sharedSpriteFrameCache()->removeSpriteFramesFromFile(plistPath.c_str());
}
pathToPlistsMap.erase(path);
set<string> animateNames=pathToNameMap[path];
for (set<string>::iterator strItr=animateNames.begin();strItr!=animateNames.end();++strItr)
{
CCAnimationCache::sharedAnimationCache()->removeAnimationByName((*strItr).c_str());
nameToAnimateMap.erase((*strItr));
}
pathToNameMap.erase(path);
}
CCSequence*AnimatePacker::getSequence(constchar *name){
CCAnimation* animation=CCAnimationCache::sharedAnimationCache()->animationByName(name);
if(animation)
{
Animate animate=nameToAnimateMap[name];
CCArray *actions=CCArray::create();
actions->addObject(CCFlipX::create(animate.flipX));
actions->addObject(CCFlipY::create(animate.flipY));
actions->addObject(CCAnimate::create(animation));
CCSequence *sequence=createSequence(actions);
actions->removeAllObjects();
return sequence;
}
returnNULL;
}
CCSequence *AnimatePacker::createSequence(CCArray *actions)
{
CC_ASSERT(actions->count()>1);
CCSequence *seq =CCSequence::createWithTwoActions((CCFiniteTimeAction*)actions->objectAtIndex(0),
(CCFiniteTimeAction*)actions->objectAtIndex(1));
for (unsignedint i = 2; i < actions->count(); ++i) {
seq =CCSequence::createWithTwoActions(seq, (CCFiniteTimeAction*)actions->objectAtIndex(i));
}
return seq;
}
CCSequence *AnimatePacker::createSequence(CCFiniteTimeAction *pAction1,CCFiniteTimeAction *pAction2, ...)
{
va_list params;
va_start(params, pAction2);
CCSequence *pPrev =CCSequence::createWithTwoActions(pAction1, pAction2);
CCFiniteTimeAction *pNow =NULL;
while( pPrev ) {
pNow =va_arg(params, CCFiniteTimeAction*);
if (pNow)
{
pPrev =CCSequence::createWithTwoActions(pPrev, pNow);
}
else
{
break;
}
}
va_end(params);
return pPrev;
}
至于已经有sprite了,为什么还要添加CCSpriteBatchNode,可以参看参考资料,
CCSpriteBatchNode 中的所有CCSprite只会被渲染1次,因此可以提高游戏的FPS。
限制:加入到 CCSpriteBatchNode 中的CCSprite必须使用同一张纹理图,尺寸应该相同,否则会显示不出来。