一、使用示例

  1. auto sp_frame_plist = "animations/grossini.plist";
  2. auto animationConf = "animations/animations-2.plist"
  3. auto aniCache = AnimationCache::getInstance();
  4. auto spFrameCache = SpriteFrameCache::getInstance();
  5. // *********************************************************
  6. // *********** 示例一,挨个创建SpriteFrame/AnimationFrame,加载plist大纹理
  7. // *********************************************************
  8. // 加载SpriteFrame
  9. spFrameCache->addSpriteFramesWithFile(sp_frame_plist);
  10. // 获取指定SpriteFrame序列
  11. Vector<SpriteFrame*> spFrames(15);
  12. for(......) {
  13. spFrames.pushBack(spFrameCache->getSpriteFrameByName(...));
  14. }
  15. // 创建animation
  16. auto aniData = Animation::createWithSpriteFrames(spFrames);
  17. // 缓存animation
  18. AnimationCache::getInstance()->addAnimation( aniData, "dance" );
  19. // 指定帧动画
  20. runAction(Animate::create(aniData));
  21. // *********************************************************
  22. // *********** 示例二,加载Animation配置文件,一步到位
  23. // *********************************************************
  24. aniCache->addAnimationsWithFile( animationConf );
  25. auto aniData1 = animCache->getAnimation("dance_1");
  26. auto aniData2 = animCache->getAnimation("dance_2");
  27. auto aniData3 = animCache->getAnimation("dance_3");
  28. runAction(Sequence::create(
  29. Animate::create(aniData1),
  30. Animate::create(aniData2),
  31. Animate::create(aniData3),
  32. nullptr));

上面涉及的资源文件如下:

// 一个动画帧,包含: // 一个精灵帧,就是一帧的画面 // 帧时延 // 播放到此帧的广播数据 class CC_DLL AnimationFrame : public Ref, public Clonable { protected:

  1. SpriteFrame* _spriteFrame; // 一个动画帧 对应 一个精灵帧,就是一个画面
  2. float _delayUnits; // 这一帧有几个delay unit
  3. // 如果_userInfo != null,则播放到这一帧时,
  4. // 默认eventDispatcher会广播一个EventCustom(AnimationFrameDisplayedNotification)
  5. // 事件的userData就是这个userinfo
  6. ValueMap _userInfo;

};

  1. <a name="2l0jf"></a>
  2. ## 2、创建
  3. ```cpp
  4. class CC_DLL AnimationFrame : public Ref, public Clonable
  5. {
  6. public:
  7. // spriteFrame: 这一帧的画面
  8. // delayUnits: 这一帧有几个delay unit
  9. // userInfo: 播放到此帧时广播的event custom的userdata
  10. static AnimationFrame* create( SpriteFrame* spriteFrame,
  11. float delayUnits,
  12. const ValueMap& userInfo);
  13. virtual AnimationFrame *clone() const override;
  14. };

3、重要操作

  1. class CC_DLL AnimationFrame : public Ref, public Clonable
  2. {
  3. // 更改这一帧的画面
  4. void setSpriteFrame(SpriteFrame* frame);
  5. SpriteFrame* getSpriteFrame() const;
  6. // 设置这一帧的delay unit数量,决定这一帧的持续时间(时延delaytime)
  7. // delaytime = delayUnits * delayPerUnit
  8. // 在这里设置每一帧的持续时间。
  9. void setDelayUnits(float delayUnits);
  10. float getDelayUnits() const;
  11. // 播放到此帧时广播的event custom的userdata
  12. void setUserInfo(const ValueMap& userInfo);
  13. const ValueMap& getUserInfo() const;
  14. ValueMap& getUserInfo();
  15. }

四、Animation:一个帧动画

1、数据结构

  1. // 包含了一个帧动画的所有数据:我觉得叫做AnimationData更贴切一点。
  2. // 帧序列
  3. // total delay unit
  4. // delay per unit
  5. // duration,播放一次的动画总时长
  6. // loop,动画播放次数
  7. class CC_DLL Animation : public Ref, public Clonable
  8. {
  9. float _totalDelayUnits; // 总共的delay unit数量
  10. float _delayPerUnit; // 一个 delay unit的时长
  11. float _duration; // 动画总时长,totalDelayUnits * delayPerUnit
  12. Vector<AnimationFrame*> _frames; // 动画帧
  13. bool _restoreOriginalFrame; // 动画结束时候是否恢复到第一帧。
  14. unsigned int _loops; // 播放次数
  15. }

2、创建

  1. class CC_DLL Animation : public Ref, public Clonable
  2. {
  3. static Animation* create(void); // 空Animation
  4. // 创建一个“匀速”的帧动画,每一帧一个delay unit。
  5. // arrayOfSpriteFrameNames: 用于创建AnimationFrame
  6. // delayPerUnit: 一个delay unit的时长
  7. // loops: 播放次数
  8. static Animation* createWithSpriteFrames(
  9. const Vector<SpriteFrame*>& arrayOfSpriteFrameNames,
  10. float delayPerUnit = 0.0f,
  11. unsigned int loops = 1);
  12. // 创建一个自定义帧的帧动画,每一帧的delay unit数量可能不同,也就可能是个“变速”帧动画
  13. // arrayOfAnimationFrameNames: 动画帧
  14. // delayPerUnit: 一个delay unit的时长
  15. // loops: 播放次数
  16. static Animation* create(
  17. const Vector<AnimationFrame*>& arrayOfAnimationFrameNames,
  18. float delayPerUnit,
  19. unsigned int loops = 1);
  20. // 拷贝一个完全相同的帧动画。
  21. virtual Animation *clone() const override;
  22. }

3、重要操作

  1. class CC_DLL Animation : public Ref, public Clonable
  2. {
  3. // 末尾添加动画帧
  4. void addSpriteFrame(SpriteFrame *frame);
  5. void addSpriteFrameWithFile(const std::string& filename);
  6. void addSpriteFrameWithTexture(Texture2D* pobTexture, const Rect& rect);
  7. // 更换所有帧,注意会清空并release之前的动画帧。
  8. void setFrames(const Vector<AnimationFrame*>& frames){
  9. _frames = frames; // Vector的拷贝复制
  10. Vector<T>& operator=(const Vector<T>& other) // Vector的拷贝复制逻辑
  11. {
  12. if (this != &other) {
  13. clear(); // 先release每个item,然后并清空vector
  14. _data = other._data; // std::vector的拷贝赋值,拷贝每个item
  15. addRefForAllObjects(); // 每个item都retain一次。
  16. }
  17. return *this;
  18. }
  19. }
  20. // 动画结束,恢复到第一帧
  21. void setRestoreOriginalFrame(bool restoreOriginalFrame);
  22. }

五、Animate:执行帧动画

1、数据结构

  1. // 是一个延时Action
  2. class CC_DLL Animate : public ActionInterval
  3. {
  4. protected:
  5. // 为啥要动态分配_splitTimes?没看懂。
  6. std::vector<float>* _splitTimes; // 播放一次时,每一帧的进度,0.0 ~ 1.0,1.0表示播放到这一帧就播放完了。
  7. int _nextFrame; // next frame index,第一帧index = 0
  8. SpriteFrame* _origFrame; // sprite的sprite frame
  9. int _currFrameIndex; // current frame index
  10. unsigned int _executedLoops; // 已经播放次数
  11. Animation* _animation; // 帧动画数据
  12. EventCustom* _frameDisplayedEvent; // 播放每一帧时候都会广播一个eventcustom,event name是AnimationFrameDisplayedNotification
  13. AnimationFrame::DisplayedEventInfo _frameDisplayedEventInfo; // 这个event的userdata
  14. };

2、重要逻辑

  • Animate::initWithAnimation
    • 初始化Action,比如action的时长
  • Animate::update
    • 每一渲染帧的动画更新。 ```cpp

bool Animate::initWithAnimation(Animation* animation) { …… float singleDuration = animation->getDuration(); // 播放一次的时长

  1. // 设置action的总时长,才能计算出每一渲染帧时的执行进度。
  2. if ( ActionInterval::initWithDuration(singleDuration * animation->getLoops() ) )
  3. {
  4. ......
  5. float accumUnitsOfTime = 0; // 累计delay unit
  6. // delaytime per unit,这一步没看懂,为什么不能直接animation->getDelayPerUnit();
  7. float newUnitOfTimeValue = singleDuration / animation->getTotalDelayUnits();
  8. // 下面主要是为了计算出每一帧的执行进度(播放一次的)
  9. auto& frames = animation->getFrames();
  10. for (auto& frame : frames)
  11. {
  12. float value = (accumUnitsOfTime * newUnitOfTimeValue) / singleDuration;
  13. accumUnitsOfTime += frame->getDelayUnits();
  14. _splitTimes->push_back(value);
  15. }
  16. return true;
  17. }
  18. return false;

}

void Animate::update(float t) { // if t==1, ignore. Animation should finish with t==1 if( t < 1.0f )
{ t *= _animation->getLoops();

  1. // new loop? If so, reset frame counter
  2. unsigned int loopNumber = (unsigned int)t;
  3. if( loopNumber > _executedLoops ) {
  4. _nextFrame = 0;
  5. _executedLoops++;
  6. }
  7. // 整数模除的浮点数版本,
  8. // 整数版:5 mod 3 = 2
  9. // 浮点版:5.1 mod 3 = 2.1
  10. t = fmodf(t, 1.0f); // 得到当前时刻在一个loop中的进度。
  11. // 比如loop = 5,当前进度0.5,得到t=0.5,表示当前在一个循环的中间,这样才好决定显示哪一帧。
  12. }
  13. auto& frames = _animation->getFrames();
  14. SpriteFrame* frameToDisplay = nullptr;
  15. for( int i = _nextFrame; i < frames.size(); i++ )
  16. {
  17. // 第i帧对应的进度,0 ~ 1
  18. float splitTime = _splitTimes->at(i);
  19. // 一直循环到t进度对应的那一帧。
  20. if( splitTime <= t )
  21. {
  22. auto blend = static_cast<Sprite*>(_target)->getBlendFunc();
  23. _currFrameIndex = i;
  24. AnimationFrame* frame = frames.at(_currFrameIndex);
  25. frameToDisplay = frame->getSpriteFrame();
  26. static_cast<Sprite*>(_target)->setSpriteFrame(frameToDisplay);
  27. static_cast<Sprite*>(_target)->setBlendFunc(blend);
  28. // 发送播放第i帧的广播。
  29. const ValueMap& dict = frame->getUserInfo();
  30. if ( !dict.empty() )
  31. {
  32. if (_frameDisplayedEvent == nullptr)
  33. _frameDisplayedEvent = new (std::nothrow) EventCustom(AnimationFrameDisplayedNotification);
  34. _frameDisplayedEventInfo.target = _target;
  35. _frameDisplayedEventInfo.userInfo = &dict;
  36. _frameDisplayedEvent->setUserData(&_frameDisplayedEventInfo);
  37. Director::getInstance()->getEventDispatcher()->dispatchEvent(_frameDisplayedEvent);
  38. }
  39. _nextFrame = i+1;
  40. }
  41. // Issue 1438. Could be more than one frame per tick, due to low frame rate or frame delta < 1/FPS
  42. else {
  43. break;
  44. }
  45. }

}

  1. <a name="szHY5"></a>
  2. # 六、AnimationCache:帧动画缓存
  3. <a name="gZKji"></a>
  4. ## 1、数据结构
  5. ```cpp
  6. class CC_DLL AnimationCache : public Ref
  7. {
  8. // key: animation name,见下面的plist配置,
  9. // value: animation
  10. Map<std::string, Animation*> _animations;
  11. static AnimationCache* s_sharedAnimationCache; // 全局单例
  12. };

2、重要操作

  1. class CC_DLL AnimationCache : public Ref
  2. {
  3. public:
  4. // 销毁所有缓存数据
  5. static void destroyInstance();
  6. // animation 缓存在一个map中,name就是key
  7. void addAnimation(Animation *animation, const std::string& name);
  8. void removeAnimation(const std::string& name);
  9. // 注意cache随时可能会销毁animation,因此返回之后要retain,确保使用期间不会被销毁。
  10. Animation* getAnimation(const std::string& name);
  11. // 根据帧动画配置文件,批量加载并缓存帧动画数据。
  12. // 配置文件格式见下面,实际调用的是下面的方法。
  13. void addAnimationsWithFile(const std::string& plist);
  14. // 这个方法设计的很奇怪,还不如不要public,初衷是这样的: plist是帧动画配置文件的路径
  15. // 而帧动画使用到的精灵表规定和这个plist同目录,即配置和数据在一起。
  16. //
  17. // dictionary: 帧动画的配置,文件格式见下面
  18. // plist: 配置文件路径,规定:精灵表和配置文件同目录。
  19. void addAnimationsWithDictionary(const ValueMap& dictionary,const std::string& plist);
  20. };

3、帧动画配置文件

是一个plist文件,内容样板如下:
总的来说,有两个标签:

  • anamations:保存所有的animation,以及每个animation下的每一帧animationFrame。
  • properties:保存这些帧动画使用到的精灵表、配置文件版本(format) ```xml

<?xml version=”1.0” encoding=”UTF-8”?> <!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd">

animations dance_1 delayPerUnit 0.2 restoreOriginalFrame loops 2 frames spriteframe grossini_dance_01.png
delayUnits 1 notification firstframe
……
properties spritesheets grossini.plist grossini_blue.plist grossini_family.plist format 2

```