cocos2d-x-AnimationCache

| 分类 cocos2d-x  | 标签 cocos2d-x 源码学习 

本文为xiedantibu原创,转载请注明作者及出处!

###目录 —-     AnimationFrame
    Animation-序列帧动画信息
    AnimationCache
    Animate-帧动画处理类
    知识点补漏


cocos2d-x-SpriteFrameCache中的plist文件格式中我们谈到,plist文件还将用在CCAnimationCache中。所以今天接着前面的章节,继续学习CCAnimationCache,说说帧的用法。 ####AnimationFrame-动画帧 — AnimationFrame动画帧实际上就是精灵帧加上延迟时间就是所谓的动画帧了,其实也是啊。单独一张帧,给他一个时间就是动画了啊。。。
先看下AnimationFrame的主要属性把:

  1. SpriteFrame* _spriteFrame;//话说这就是精灵帧啊
  2. float _delayUnits;//单帧延迟时间,其实在目前的代码中,都是以1.0去处理他的,如果改为其他值,可能会出现问题,所以在AnimationFrame中帧的延迟时间,要以默认值去处理,话说放在这就是个多余的角色
  3. ValueMap _userInfo;//用户信息啊,这杂用呢,跟EventCustom合伙一起用

下面接着看下DisplayedEventInfo这个结构把,主要是动画帧在显示(display)的时候,把AnimationFrameDisplayedNotification这个消息分发出去,当然dispatchEvent的时候要设置setUserData,而这UserData就是DisplayedEventInfo。当然分发的消息,也就有接受消息的,具体还是学习ActionsTest中的Animation例子。下面就先看下DisplayedEventInfo:

  1. struct DisplayedEventInfo
  2. {
  3. Node* target;//目标精灵
  4. const ValueMap* userInfo;//用户信息
  5. }; DisplayedEventInfo主要在显示动画帧的时候,传递消息,在`Animate::update`中学习到,到时候具体再温习学习吧,现在先记下来。

动画帧的构造方法

  1. static AnimationFrame* create(SpriteFrame* spriteFrame, float delayUnits, const ValueMap& userInfo)//主要调用initWithSpriteFrame方法

初始化动画帧

  1. bool initWithSpriteFrame(SpriteFrame* spriteFrame, float delayUnits, const ValueMap& userInfo);//主要是给精灵帧,单帧延迟时间,以及用户消息赋值

设置获取精灵帧

  1. SpriteFrame* getSpriteFrame()//获取当前动画帧的精灵帧
  2. void setSpriteFrame(SpriteFrame* frame)//设置当前动画帧的精灵帧

设置获取单帧延迟时间

  1. float getDelayUnits() const { return _delayUnits; };
  2. void setDelayUnits(float delayUnits);

设置获取用户信息,用户信息主要是为了在显示动画帧的时候,给DisplayedEventInfo的ValueMap* userInfo赋值。

  1. const ValueMap& getUserInfo() const
  2. void setUserInfo(const ValueMap& userInfo) --- <a id='Animation' name='Animation'> </a> ####[Animation-动画帧信息类](#top) --- Animation英文解释就是动画,但其实他不是真正的执行者,他只能算是动画的本体。真正的动画执行是Animate这个类,如`sprite->runAction(Animate::create(animation));`有这代码我们可以看出Animate才是Action,而Animation其实就是保存了一堆的动画帧。要了解Animation首先得看它的属性:
  3. float _totalDelayUnits;//_totalDelayUnits这东西英文翻译是动画的总延迟单元,但理解成总帧数我觉得更恰当
  4. float _delayPerUnit;//在Animation中的动画帧的延迟时间都是一样的,就是这东东。延迟单元时间
  5. float _duration;//播放时间:_totalDelayUnits*_delayPerUnit
  6. Vector<AnimationFrame*> _frames;//该Vector是封装了std::vector<T> _data的容器,保存到Vector中的数据不需要管内存释放问题。所以_frames也就是一个保存了AnimationFrame*动画帧指针的数组。
  7. bool _restoreOriginalFrame;//当动画结束的时候,释放还原到原来的帧
  8. unsigned int _loops;//循环几次啊。。。 初始化相关方法:
  9. bool init();//该方法主要就初始化了_loops以及_delayPerUnit两个属性。
  10. bool initWithSpriteFrames(const Vector<SpriteFrame*>& arrayOfSpriteFrameNames, float delay = 0.0f, unsigned int loops = 1);//该方法名字就很突出,通过精灵帧来初始化,看参数第一个就是精灵帧数组,第二个参数有个默认值,延迟时间默认为0.0,第三个参数是默认的循环次数1。从第一个参数我们能明白,该方法主要是通过遍历数组,将精灵帧组装成动画帧,再把动画帧加入到_frames中,对了在加入到_frames的时候,要更新_totalDelayUnits++,该方法第二个参数是真正的赋值给单位延迟时间的。
  11. bool initWithAnimationFrames(const Vector<AnimationFrame*>& arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops);//该方法与上面的方法相似,但是他的第一个参数却是动画帧,所以他不需要转换,直接加入到_frames中,但在加入的同时也得更新_totalDelayUnits,不过这里却写成_totalDelayUnits += animFrame->getDelayUnits(),个人理解觉得还是得改成_totalDelayUnits++,应该是笔误吧。

Create相关方法:

  1. static Animation* create(void);//回调init方法初始化
  2. static Animation* createWithSpriteFrames(const Vector<SpriteFrame*>& arrayOfSpriteFrameNames, float delay = 0.0f, unsigned int loops = 1);//回调initWithSpriteFrames方法初始化,主要是精灵帧,还有delay
  3. static Animation* create(const Vector<AnimationFrame*>& arrayOfAnimationFrameNames, float delayPerUnit, unsigned int loops = 1);//回调initWithAnimationFrames方法初始化,注意该方法中的_totalDelayUnits可能有问题,只要加入的动画帧的_delayPerUnit不为1,就可能出问题

将精灵帧添加到动画帧数组_frames中:

  1. void Animation::addSpriteFrame(SpriteFrame* spriteFrame)
  2. {
  3. AnimationFrame *animFrame = AnimationFrame::create(spriteFrame, 1.0f, ValueMap());//精灵帧转换成动画帧
  4. _frames.pushBack(animFrame);//加入到数组中
  5. _totalDelayUnits++;//总帧数update
  6. } 将图片添加到动画帧数组_frames中:
  7. void Animation::addSpriteFrameWithFile(const std::string& filename)
  8. {
  9. Texture2D *texture = Director::getInstance()->getTextureCache()->addImage(filename);//先将图片转换成纹理
  10. Rect rect = Rect::ZERO;
  11. rect.size = texture->getContentSize();
  12. SpriteFrame *frame = SpriteFrame::createWithTexture(texture, rect);//把纹理转换成精灵帧
  13. addSpriteFrame(frame);//回调上一个方法
  14. }

将纹理添加到动画帧数组_frames中:

  1. void addSpriteFrameWithTexture(Texture2D* pobTexture, const Rect& rect);//直接添加纹理加入到动画帧数组_frames

获取播放时间:

  1. float Animation::getDuration(void) const
  2. {
  3. return _totalDelayUnits * _delayPerUnit;
  4. }

设置是否动画播放完,还原原始帧:

  1. bool getRestoreOriginalFrame()
  2. void setRestoreOriginalFrame(bool restoreOriginalFrame)

####AnimationCache

AnimationCache-动画缓存,当然就是保存Animation的东西了啊,下面我们来看看其相关属性啊。。。

  1. Map<std::string, Animation*> _animations; //就是这个Map保存了Animation*缓存
  2. static AnimationCache* s_sharedAnimationCache;//静态实例对象 下面就看其怎么实例化的啊
  3. AnimationCache* AnimationCache::getInstance()
  4. {
  5. if (! s_sharedAnimationCache)//判断AnimationCache*是否为空
  6. {
  7. s_sharedAnimationCache = new (std::nothrow) AnimationCache();
  8. s_sharedAnimationCache->init();
  9. }
  10. return s_sharedAnimationCache;
  11. } 直接添加动画到_animations
  12. void addAnimation(Animation *animation, const std::string& name);//传入Animation,还需要一个名字,名字可以随便搞啊

根据名字把缓存中的动画移除

  1. void removeAnimation(const std::string& name);//根据名字从Map中移除动画

通过读取plist文件,添加动画到Map中,由于这plist文件,不知道用的啥动画编辑器,所以就简单的说下啊

  1. void addAnimationsWithFile(const std::string& plist);//把plist文件名传进去,回调下面方法
  2. void addAnimationsWithDictionary(const ValueMap& dictionary,const std::string& plist);//紧接着上一个方法,传入ValueMap以及plist名字,跟进plist文件中format属性值不同解析,找到不同解析方法
  3. void parseVersion1(const ValueMap& animations);//format值为1,在这方法中真正的添加动画到Map中
  4. void parseVersion2(const ValueMap& animations);//format值为2

####Animate-帧动画处理类 — Animate继承于ActionInterval,那ActionInterval是啥呢,他就是一个持续性动作,那就是说Animate也是个持续性动作,而他的动作就是切换动画帧。

先看看他存了啥货色啊-属性

  1. std::vector<float>* _splitTimes;//话说这东西就是个指针,废话,他不是指针是啥!!!它保存的就是一个个的(0,1/_totalDelayUnits,2/_totalDelayUnits,。。。),每一帧的进度
  2. int _nextFrame;//当前要播放的下一帧序号
  3. SpriteFrame* _origFrame;//各帧中保存的精灵信息
  4. unsigned int _executedLoops;//循环次数
  5. Animation* _animation;//保存动画帧信息的指针
  6. EventCustom* _frameDisplayedEvent;//普通分发事件
  7. AnimationFrame::DisplayedEventInfo _frameDisplayedEventInfo;//这东西很熟悉把,分发信息封装类,前面讲到的啊,有个sender,还有用户信息。

初始化方法

  1. bool Animate::initWithAnimation(Animation* animation)//传了一个动画帧信息类进来
  2. {
  3. float singleDuration = animation->getDuration();//获取播放事件-_totalDelayUnits * _delayPerUnit(帧数*单帧延迟时间)
  4. if ( ActionInterval::initWithDuration(singleDuration * animation->getLoops() ) )//这里要注意初始化持续性动作的时间,不是单纯的singleDuration,还需要Loops,这才是真正的总播放时间
  5. {
  6. _nextFrame = 0;//下一帧序号为0
  7. setAnimation(animation);//初始化_animation,对啦,在这里讲下如果,单独回调setAnimation着方法的时候可能会出现问题,因为并没有改变赋值其他属性的值,所以在此注意
  8. _origFrame = nullptr;//后面赋值,通过精灵获取其原始帧。
  9. _executedLoops = 0;//当前循环次数
  10. _splitTimes->reserve(animation->getFrames().size());//初始化_splitTimes存储的大小
  11. float accumUnitsOfTime = 0;
  12. float newUnitOfTimeValue = singleDuration / animation->getTotalDelayUnits();//就是_delayPerUnit
  13. auto frames = animation->getFrames();//获取动画帧Vector
  14. for (auto& frame : frames)迭代该Vector,给每一个动画帧,设置相应的进度
  15. {
  16. float value = (accumUnitsOfTime * newUnitOfTimeValue) / singleDuration;//其实就是accumUnitsOfTime/_totalDelayUnits
  17. accumUnitsOfTime += frame->getDelayUnits();//其实frame->getDelayUnits()就是为1,如果不为1,可能出现问题啊
  18. _splitTimes->push_back(value);//加入到vector中
  19. }
  20. return true;
  21. }
  22. return false;
  23. }

创建一个Animate

  1. Animate* Animate::create(Animation *animation)//直接传入一个动画帧信息类指针
  2. {
  3. Animate *animate = new (std::nothrow) Animate();
  4. animate->initWithAnimation(animation);//回调初始化方法
  5. animate->autorelease();
  6. return animate;
  7. } 开始并设置执行动画Node,该方法继承于Action
  8. void Animate::startWithTarget(Node *target)
  9. {
  10. ActionInterval::startWithTarget(target);//给target赋值,执行动画的Node
  11. Sprite *sprite = static_cast<Sprite*>(target);//看到这里我们应该明白,Animate的动画其实是针对精灵的
  12. CC_SAFE_RELEASE(_origFrame);//回收原始帧
  13. if (_animation->getRestoreOriginalFrame())//_restoreOriginalFrame为true,_restoreOriginalFrame表示如果动画播放结束,还原到原始帧
  14. {
  15. _origFrame = sprite->getSpriteFrame();//设置原始帧,直接获取精灵本身的帧
  16. _origFrame->retain();//计数+1
  17. }
  18. _nextFrame = 0;//设置下一帧为0
  19. _executedLoops = 0;//循环次数为0
  20. } Animate停止播放
  21. void Animate::stop()
  22. {
  23. if (_animation->getRestoreOriginalFrame() && _target)//_restoreOriginalFrame为true
  24. {
  25. static_cast<Sprite*>(_target)->setSpriteFrame(_origFrame);//精灵显示成初始化帧
  26. }
  27. ActionInterval::stop();
  28. } 讲解update方法,在讲Animateupdate之前,得先学习ActionIntervalstep方法,这个step啥时候回调啊?在`ActionManager::update(float dt)`这方法回调。在update方法中我们看到`_currentTarget->currentAction->step(dt)`,看这就是Actionstep方法。
  29. void ActionInterval::step(float dt)
  30. {
  31. if (_firstTick)//_firstTick是一个控制变量,_firstTick为true只有在ActionInterval::initWithDuration的时候才设置为true
  32. {
  33. _firstTick = false;
  34. _elapsed = 0;//_elapsed为从动作开始起逝去的时间
  35. }
  36. else
  37. {
  38. _elapsed += dt;//逝去的时间
  39. }
  40. this->update(MAX (0, // needed for rewind. elapsed could be negative
  41. MIN(1, _elapsed /
  42. MAX(_duration, FLT_EPSILON) // division by 0
  43. )
  44. )
  45. );//MIN(1, _elapsed /MAX(_duration, FLT_EPSILON) )//从这看出不管_elapsed的时间是否大于1,则update(t)中的t都在(0,1)之间。
  46. }

下面就来看看Animate::update这方法

  1. void Animate::update(float t)//从上面方法我们就能看出t只能在(0-1)之间,当t=1的时候,说明动画已经放完了
  2. {
  3. // if t==1, ignore. Animation should finish with t==1
  4. if( t < 1.0f ) {//表明动画还没放完
  5. t *= _animation->getLoops();//t我们这里写成_elapsed /_duration,而_duration为一个循环的播放时间*_animation->getLoops(),所以t就是_elapsed/单次循环的播放时间*_animation->getLoops(),所以t*=_animation->getLoops(),最后的值t=_elapsed/单次循环的播放时间
  6. // new loop? If so, reset frame counter
  7. unsigned int loopNumber = (unsigned int)t;//这时候t可能大于1啊,而它的整数其实就是相当于实际播放的循环数
  8. if( loopNumber > _executedLoops ) {//如果大于当前的循环播放值,则得更新当前的播放值
  9. _nextFrame = 0;
  10. _executedLoops++;
  11. }
  12. // new t for animations
  13. t = fmodf(t, 1.0f);//// 对t进行浮点取模值,将其限制在0~1之间,这样等于又转换成为了当前动画播放的进度值
  14. }
  15. auto frames = _animation->getFrames();//获取动画帧Vector
  16. auto numberOfFrames = frames.size();//有多少帧数
  17. SpriteFrame *frameToDisplay = nullptr;//将要播放的帧
  18. for( int i=_nextFrame; i < numberOfFrames; i++ ) {//注意这里是从_nextFrame开始迭代的
  19. float splitTime = _splitTimes->at(i);//获取每一帧的播放进度
  20. if( splitTime <= t ) {///如果这一帧的进度小于当前动画的播放进度,即代表进入了这一帧。,否则还是执行上一帧
  21. AnimationFrame* frame = frames.at(i);//取得对应的动画帧信息
  22. frameToDisplay = frame->getSpriteFrame();//取得当前帧的精灵图片信息。
  23. static_cast<Sprite*>(_target)->setSpriteFrame(frameToDisplay);//找到精灵,设置显示的精灵帧,动画就是这样。。。
  24. const ValueMap& dict = frame->getUserInfo();//用户信息
  25. if ( !dict.empty() )
  26. {
  27. if (_frameDisplayedEvent == nullptr)
  28. _frameDisplayedEvent = new (std::nothrow) EventCustom(AnimationFrameDisplayedNotification);//初始化分发事件
  29. _frameDisplayedEventInfo.target = _target;
  30. _frameDisplayedEventInfo.userInfo = &dict;
  31. _frameDisplayedEvent->setUserData(&_frameDisplayedEventInfo);
  32. Director::getInstance()->getEventDispatcher()->dispatchEvent(_frameDisplayedEvent);//分发时间
  33. }
  34. _nextFrame = i+1;//更新下一帧的播放序号
  35. }
  36. else {
  37. break;
  38. }
  39. }
  40. }

创建反向播放序列帧

Animate* Animate::reverse() const
  1. {
  2. auto oldArray = _animation->getFrames();//获取全部的帧
  3. Vector<AnimationFrame*> newArray(oldArray.size());//初始化大小为oldArray.size()的Vector
  4. if (oldArray.size() > 0)//赋值了
  5. {
  6. for (auto iter = oldArray.crbegin(); iter != oldArray.crend(); ++iter)
  7. {
  8. AnimationFrame* animFrame = *iter;
  9. if (!animFrame)
  10. {
  11. break;
  12. }
  13. newArray.pushBack(animFrame->clone());
  14. }
  15. }
  16. Animation *newAnim = Animation::create(newArray, _animation->getDelayPerUnit(), _animation->getLoops());
  17. newAnim->setRestoreOriginalFrame(_animation->getRestoreOriginalFrame());
  18. return Animate::create(newAnim);
  19. }

####知识点补漏

  1. std::nothrow:在内存不足时,new (std::nothrow)并不抛出异常,而是将指针置NULL

    1. Animation * animat = new (std::nothrow) Animation;
    2. if (!animat)
    3. {
    4. }

青春是一场大雨,即使感冒了,还盼回头再淋一次...image...微笑永远是一个人身上最好看的东西...


PREVIOUS     NEXT