cocos2dx动画

cocos2dx的动画有很多种方式,不过今天介绍的就是其中一种,AnimatePacker结合TexturePacker完成的动画,其实还有一种zwoptex是和texturepacker一样的工具,都可以生成一个plist和整合的png,但是texturepacker是个要收费的东西,我使用免费版本,一直以为不错,结果后来发现,他会在生成的图片上画一个很大的红印,这也许就是免费版的坑爹之处了,所以最好使用破解版。

而texturepacker是个很好用的工具,一看基本就知道怎么用了。也就不再介绍了,它可以将所有的小图片生成一个大的图片,以及一个plist文件,plist文件中纪录各个小图片的位置和尺寸,以及是什么工具生成的,是texturepacker还是zwoptex。不过最重要的还是plist文件中图片属性的数组。

如果你需要整个plist里面的所有图片,那么就可以直接使用这个plist和png就可以实现整个动画了,但是如果你需要将这个plist里面的动画分批次,或者分组的话,那么就需要使用animatepacker这个工具了,这也是一个很好用的东西,直接将plist拖入animatepacker左上方的plist区域,然后可以看到右方sprite区域里面列出了所有的sprite,texturepacker生成的plist能很好的辨认,zwoptex会比较混乱,所以使用了texturepacker,左下方可以直接设置每个动作的名字,时间,还有方向,最好不要有空格之类的名字,因为代码里面要用到。

这个时候该代码出场了。因为版本问题稍微有些修改,我使用的cocos2dx 2.2,而且由于需要使用的名字,另外还使用了一些修改。

需要使用到一个AnimatePacker类,使用方法如下:

    char anuPath[256];

   sprintf(anuPath,"%d", skillId);

   string actionType = 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(sprite->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(seq);

    AnimatePacker::getInstance()->freeAnimations(anuPath);


解释一下,就是先让animatepacker去load所有的动作,会得到所有动作的第一张图片的名字的一个dictionary,然后使用spriteframename的方式创建一个sprite,接着就象使用普通的sprite那样就可以了。

loadAnimations里面所使用的原理,我们也可以使用zwoptex的那种方式。就是光有plist和png文件的时候,loadAnimations函数的参数是那个xml的相对路径,而xml里面有plist的名字,然后找到该文件的相对路径,依次添加到CCSpriteFrameCache::sharedSpriteFrameCache()里面,如下方式:

string plistPath =CCFileUtils::sharedFileUtils()->fullPathFromRelativeFile(plists[i].c_str(), pszPath.c_str());

CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(plistPath.c_str());

我写了一个sprite的动画sprite:

//

//  frameMoveSprite.h

//  shengxiao

//

//  Created by liuyun on 10/25/13.

//

//


#ifndef __shengxiao__frameMoveSprite__

#define __shengxiao__frameMoveSprite__


#include "cocos2d.h"


using namespace cocos2d;


class LYFrameMoveSprite:publicCCSprite {

    

   virtualbool initWithFile(constchar *pszFilename,CCPoint position, int count, float time);

   virtual ~LYFrameMoveSprite();

   int imgsCount;

   float changeTime;

    

   CCSize maxSize;

    

public:

   CCSize getMaxSize();

    LYFrameMoveSprite();

   staticLYFrameMoveSprite* create(constchar *pszFileName,CCPoint position, int count, float time);

};


#endif /* defined(__shengxiao__frameMoveSprite__) */


cpp文件:

//

//  frameMoveSprite.cpp

//  shengxiao

//

//  Created by liuyun on 10/25/13.

//

//


#include "frameMoveSprite.h"


using namespace std;


/*

 *param:

 *  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::map<std::string,std::vector<std::string> > pathToPlistsMap;

//From xml path to animate names

std::map<std::string,std::set<std::string> > pathToNameMap;

};


#endif//_ANIMATE_PACKER_H_


AnimatePacker.cpp

#include "AnimatePacker.h"

#include "platform/CCSAXParser.h"



using namespace 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必须使用同一张纹理图,尺寸应该相同,否则会显示不出来。


参考资料:
cocos2d - CCSpriteBatchNode的使用

你可能感兴趣的:(动画,cocos2dx)