标签:cocos2d-x
2D游戏经常有角色穿装备的情况,如下图角色手部加了一个武器.此外还有格斗游戏里常有的投技:
注意角色是处在站立状态下的,有Idle动画,手部武器也要随角色一起联动。我们是不是要让美术再画一套加手部动画的素材,那美术显然不干了,那要有脚呢,披风呢?不要画死了。他们只会给你一套纯武器的站立动画,让你自己去拼。
那我们要想让武器随角色一起联动,自然想到设定好位置和zorder后,调用CCSpawn同时动作的方法。可这有个大问题,就是独立执行两个不同的动画会有很大机率产生不同步的问题。为了解决这一问题,必须实现一种动画组的机制,就是让人物作为动画组的主动画,武器作为动画组的子成员,当主动画帧切换时子动画才切换。也就是我动你才你,要动一起动。
如这个机器人是由头上的烟和身体以及腰上的亮点组成的,攻击时机器人对攻击动画同时烟也有自己的动作,烟要随着机器人的每帧动作切换时它也要同步切换到下一帧,这时机器人作为动画组的主成员,而烟动画需要作为动画组子动画成员。
实现同步动画原理是CCAnimate的update方法是每执行一次就切换一次显示帧来实现动画效果,我们要重写这个update,让主动画update时也让动画组的所有成员也切换关键帧,这样就能实现绝对同步了。
首先实现动画组成员的方法 AnimateMember,继承于CCObject
#ifndef _AnimationMember_ #define _AnimationMember_ #include "cocos2d.h" class AnimationMember : public cocos2d::CCObject { public: AnimationMember(); ~AnimationMember(); static AnimationMember* memberWithAnimation(cocos2d::CCAnimation *animation, cocos2d::CCSprite *target); bool initWithAnimation(cocos2d::CCAnimation *animation, cocos2d::CCSprite *target); //用动画和播放对象来初始化 void start(); //开始播放动画 void stop(); //停止播放动画 void setFrame(int frameIndex); //设置播放动画的对象(_target)图片为动画中的某一帧 protected: cocos2d::CCSpriteFrame* _origFrame; //初始帧 cocos2d::CCAnimation* _animation; //动画 cocos2d::CCSprite *_target; //谁在播放动画 private: }; #endif这是头文件,有初始帧,动画和目标这几个关键方法。看下初始化的实现
AnimationMember* AnimationMember::memberWithAnimation(cocos2d::CCAnimation *animation, cocos2d::CCSprite *target) { AnimationMember* pRet = new AnimationMember(); if (pRet && pRet->initWithAnimation(animation, target)) { return pRet; } else { delete pRet; pRet = NULL; return pRet; } } bool AnimationMember::initWithAnimation(cocos2d::CCAnimation *animation, cocos2d::CCSprite *target) { bool bRet = false; do { //CC_BREAK_IF(!) this->_animation = animation; this->_target = target; this->_animation->retain(); this->_target->retain(); _origFrame = NULL; bRet = true; } while (0); return bRet; }初始化只不过将几个关键信息赋值,是非常简单的。
void AnimationMember::start() { _origFrame = _target->displayFrame(); //取得当前显示的帧作为初始帧 } void AnimationMember::stop() { bool bRestore = _animation->getRestoreOriginalFrame(); //播放完成后是否恢复第一帧 if (bRestore) { _target->setDisplayFrame(_origFrame); //恢复第一帧 } }start和stop函数只是设置下初始帧,跟播放没有关系。别急,接着往下看。
void AnimationMember::setFrame(int frameIndex) { CCArray* frames = _animation->getFrames(); int nCount = frames->count(); if (frameIndex>=nCount) { CCLog("AnimationMember setFrame frameindex is greater than framecount"); return; } //从动画里取得index帧 CCAnimationFrame *frame = (CCAnimationFrame *)(frames->objectAtIndex(frameIndex)); CCSpriteFrame *spriteFrame = frame->getSpriteFrame(); _target->setDisplayFrame(spriteFrame); }setFrame是从动画中取得想要播放的帧,然后设置成当前显示的帧。此方法在以后会用到.
AnimationMember::AnimationMember() { _target = NULL; _origFrame = NULL; _animation = NULL; } AnimationMember::~AnimationMember() { CC_SAFE_RELEASE_NULL(_animation); CC_SAFE_RELEASE_NULL(_target); }
再来看动画组AnimateGroup类,动画组是用来播放动画的,所以它要继承于CCAnimate类,因此它具有CCAnimate的一切功能,头文件如下
#ifndef _AnimateGroup_ #define _AnimateGroup_ #include "cocos2d.h" class AnimateGroup : public cocos2d::CCAnimate { public: AnimateGroup(); ~AnimateGroup(); //用数组来初始化函数 static AnimateGroup* actionWithAnimation(cocos2d::CCAnimation *animation, cocos2d::CCArray *members); bool initWithAnimation(cocos2d::CCAnimation *animation, cocos2d::CCArray *members); //用动画和数组来初始化 //用成员数来初始化 static AnimateGroup* actionWithAnimation(cocos2d::CCAnimation *animation, int memberCount); bool initWithAnimation(cocos2d::CCAnimation *animation, int memberCount); //用动画和数组数来初始化 void startWithTarget(cocos2d::CCNode *pTarget); void stop(); //所有动画停止 void update(float dt); cocos2d::CCArray* _members; //动画成员 protected: }; #endif可看到它的重要的成员变量是_members动画成员数组, 此外还有update方法, 看下初始化的实现
AnimateGroup::AnimateGroup() { _members = NULL; } AnimateGroup::~AnimateGroup() { CC_SAFE_RELEASE_NULL(_members); }根据数组初始化函数:
AnimateGroup* AnimateGroup::actionWithAnimation(cocos2d::CCAnimation *animation, cocos2d::CCArray *members) { AnimateGroup* pRet = new AnimateGroup(); if (pRet && pRet->initWithAnimation(animation, members)) { return pRet; } else { delete pRet; pRet = NULL; return pRet; } } bool AnimateGroup::initWithAnimation(cocos2d::CCAnimation *animation, cocos2d::CCArray *members) { bool bRet = false; do { CC_BREAK_IF(!CCAnimate::initWithAnimation(animation)); this->_members = members; this->_members->retain(); bRet = true; } while (0); return bRet; }可看出成员_members是直接传过来的,下面是另一个初始化函数
AnimateGroup* AnimateGroup::actionWithAnimation(cocos2d::CCAnimation *animation,int memberCount) { AnimateGroup* pRet = new AnimateGroup(); if (pRet && pRet->initWithAnimation(animation, memberCount)) { return pRet; } else { delete pRet; pRet = NULL; return pRet; } } bool AnimateGroup::initWithAnimation(cocos2d::CCAnimation *animation, int memberCount) { bool bRet = false; do { CC_BREAK_IF(!CCAnimate::initWithAnimation(animation)); this->_members = CCArray::createWithCapacity(memberCount); this->_members->retain(); bRet = true; } while (0); return bRet; }这里只是创建个容量为指定大小的空数组。
void AnimateGroup::startWithTarget(CCNode *pTarget) { CCAnimate::startWithTarget(pTarget); AnimationMember* aniMember = NULL; CCObject *member = NULL; CCARRAY_FOREACH(this->_members, member) { aniMember = (AnimationMember*)member; aniMember->start(); } } void AnimateGroup::stop() { CCAnimate::stop(); AnimationMember *aniMember = NULL; CCObject* member = NULL; CCARRAY_FOREACH(_members, member) { aniMember = (AnimationMember *)member; aniMember->stop(); } }可以看出开始播放和停止播放都是开始先调用基类的方法,再轮循调用每一个子成员的开始和停止方法, 由于 开始播放和停止播放都是基类来完成,所以子成员要作的工作仅仅是设置下当前显示的帧就行了。
void AnimateGroup::update(float dt) { CCAnimate::update(dt); int frameIndex = MAX(0, m_nNextFrame - 1); AnimationMember *aniMember = NULL; CCObject* member = NULL; CCARRAY_FOREACH(_members, member) { aniMember = (AnimationMember *)member; aniMember->setFrame(frameIndex); } }如果你查看CCAnimate的update源码实现,你会发现它也是在update里通过设置切换帧来实现动画效果,所以我们也如法泡制,在update里轮循每个动画成员,让它切换一下帧,注意m_nNextFrame是CCAnimate里的protected成员变量,表示要播放的下一帧索引。
AnimateGroup* Robot::animateGroupWithActionWord(const char* actionKeyWord, int frameCount, float delay) { //根据frame的前缀名来组建基本动画 CCAnimation* baseAnimation = this->animationWithPrefix(CCString::createWithFormat("robot_base_%s",actionKeyWord)->getCString(), 0, frameCount,delay); //腰带动画 AnimationMember *beltMember = this->animationMemberWithPrefix(CCString::createWithFormat("robot_belt_%s", actionKeyWord)->getCString(), 0, frameCount, delay, _belt); //头上的烟动画 AnimationMember *smokeMember = this->animationMemberWithPrefix(CCString::createWithFormat("robot_smoke_%s", actionKeyWord)->getCString(), 0, frameCount, delay, _smoke); <span style="white-space:pre"> </span>//组建动画组成员 将腰带动画和烟动画放进去 CCArray *animationMembers = CCArray::create(); animationMembers->addObject(beltMember); animationMembers->addObject(smokeMember); <span style="white-space:pre"> </span>//生成动画组 return AnimateGroup::actionWithAnimation(baseAnimation, animationMembers); }注释写的很清楚,就是生成三个基本动画,将机器人的动画作为主动画(actionWithAnimation作为第一个参数传入,非常重要),其他两个作为子成员塞进_members里。等等,那个讨厌的animationMemberWithPrefix是什么?其实也就是将动画生成的一些步骤封装了一下,代码如下:
AnimationMember* ActionSprite::animationMemberWithPrefix(const char* prefix, int startFrameIdx, int frameCount, float delay, cocos2d::CCSprite* target) { CCAnimation* animation = this->animationWithPrefix(prefix, startFrameIdx, frameCount, delay); return AnimationMember::memberWithAnimation(animation, target); }疑?怎么还有一层animationWithPrefix封装,烦不烦呀,没办法,源工程代码就是这样写的,我也是拿来主义,这个方法才是真正的动画封装,意思是从plist中取出帧名前缀为prefix的帧, 根据开始索引号和结束索引号在代码里拼出帧名,如"robot_idle_00.png", "robot_idle_01.png",结合每帧延时delay来生成动画。具体代码如下:
CCAnimation* ActionSprite::animationWithPrefix(const char* prefix, int startFrameIdx, int frameCount, float delay) { int idxCount = frameCount + startFrameIdx; //总帧数 CCArray *frames = CCArray::createWithCapacity(frameCount); CCSpriteFrame *frame; for (int i=startFrameIdx; i<idxCount; i++) { frame = CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(CCString::createWithFormat("%s_%02d.png", prefix, i)->getCString()); frames->addObject(frame); } return CCAnimation::createWithSpriteFrames(frames, delay); }这个代码您一定很熟悉,是cocos2d-x的标准的动画生成步骤,不多解释,它返回的是CCAnimation。
//idle动画 AnimateGroup *idleAnimationGroup = this->animateGroupWithActionWord("idle", 5, 1.0f/12.0f); this->_idleAction = CCRepeatForever::create(idleAnimationGroup) this->idleAction->retain();这个机器人Robot类里专门有个成员变量是_idleAction,用来存放站立动画,要播放时直接:
cocos2d-x实现多个精灵动画同步播放(一),布布扣,bubuko.com
标签:cocos2d-x
原文地址:http://blog.csdn.net/qiou2719/article/details/37737735