一、使用示例
auto sp_frame_plist = "animations/grossini.plist";auto animationConf = "animations/animations-2.plist"auto aniCache = AnimationCache::getInstance();auto spFrameCache = SpriteFrameCache::getInstance();// *********************************************************// *********** 示例一,挨个创建SpriteFrame/AnimationFrame,加载plist大纹理// *********************************************************// 加载SpriteFramespFrameCache->addSpriteFramesWithFile(sp_frame_plist);// 获取指定SpriteFrame序列Vector<SpriteFrame*> spFrames(15);for(......) {spFrames.pushBack(spFrameCache->getSpriteFrameByName(...));}// 创建animationauto aniData = Animation::createWithSpriteFrames(spFrames);// 缓存animationAnimationCache::getInstance()->addAnimation( aniData, "dance" );// 指定帧动画runAction(Animate::create(aniData));// *********************************************************// *********** 示例二,加载Animation配置文件,一步到位// *********************************************************aniCache->addAnimationsWithFile( animationConf );auto aniData1 = animCache->getAnimation("dance_1");auto aniData2 = animCache->getAnimation("dance_2");auto aniData3 = animCache->getAnimation("dance_3");runAction(Sequence::create(Animate::create(aniData1),Animate::create(aniData2),Animate::create(aniData3),nullptr));
上面涉及的资源文件如下:
- sprite frame plist
- 动画配置文件
- animations-2.plist.txt
- 包含了上面的3个sprite frame plist
二、UML
点击查看【processon】三、AnimationFrame:一个动画帧
1、数据结构
```cpp
// 一个动画帧,包含: // 一个精灵帧,就是一帧的画面 // 帧时延 // 播放到此帧的广播数据 class CC_DLL AnimationFrame : public Ref, public Clonable { protected:
SpriteFrame* _spriteFrame; // 一个动画帧 对应 一个精灵帧,就是一个画面float _delayUnits; // 这一帧有几个delay unit// 如果_userInfo != null,则播放到这一帧时,// 默认eventDispatcher会广播一个EventCustom(AnimationFrameDisplayedNotification)// 事件的userData就是这个userinfoValueMap _userInfo;
};
<a name="2l0jf"></a>## 2、创建```cppclass CC_DLL AnimationFrame : public Ref, public Clonable{public:// spriteFrame: 这一帧的画面// delayUnits: 这一帧有几个delay unit// userInfo: 播放到此帧时广播的event custom的userdatastatic AnimationFrame* create( SpriteFrame* spriteFrame,float delayUnits,const ValueMap& userInfo);virtual AnimationFrame *clone() const override;};
3、重要操作
class CC_DLL AnimationFrame : public Ref, public Clonable{// 更改这一帧的画面void setSpriteFrame(SpriteFrame* frame);SpriteFrame* getSpriteFrame() const;// 设置这一帧的delay unit数量,决定这一帧的持续时间(时延delaytime)// delaytime = delayUnits * delayPerUnit// 在这里设置每一帧的持续时间。void setDelayUnits(float delayUnits);float getDelayUnits() const;// 播放到此帧时广播的event custom的userdatavoid setUserInfo(const ValueMap& userInfo);const ValueMap& getUserInfo() const;ValueMap& getUserInfo();}
四、Animation:一个帧动画
1、数据结构
// 包含了一个帧动画的所有数据:我觉得叫做AnimationData更贴切一点。// 帧序列// total delay unit// delay per unit// duration,播放一次的动画总时长// loop,动画播放次数class CC_DLL Animation : public Ref, public Clonable{float _totalDelayUnits; // 总共的delay unit数量float _delayPerUnit; // 一个 delay unit的时长float _duration; // 动画总时长,totalDelayUnits * delayPerUnitVector<AnimationFrame*> _frames; // 动画帧bool _restoreOriginalFrame; // 动画结束时候是否恢复到第一帧。unsigned int _loops; // 播放次数}
2、创建
class CC_DLL Animation : public Ref, public Clonable{static Animation* create(void); // 空Animation// 创建一个“匀速”的帧动画,每一帧一个delay unit。// arrayOfSpriteFrameNames: 用于创建AnimationFrame// delayPerUnit: 一个delay unit的时长// loops: 播放次数static Animation* createWithSpriteFrames(const Vector<SpriteFrame*>& arrayOfSpriteFrameNames,float delayPerUnit = 0.0f,unsigned int loops = 1);// 创建一个自定义帧的帧动画,每一帧的delay unit数量可能不同,也就可能是个“变速”帧动画// arrayOfAnimationFrameNames: 动画帧// delayPerUnit: 一个delay unit的时长// loops: 播放次数static Animation* create(const Vector<AnimationFrame*>& arrayOfAnimationFrameNames,float delayPerUnit,unsigned int loops = 1);// 拷贝一个完全相同的帧动画。virtual Animation *clone() const override;}
3、重要操作
class CC_DLL Animation : public Ref, public Clonable{// 末尾添加动画帧void addSpriteFrame(SpriteFrame *frame);void addSpriteFrameWithFile(const std::string& filename);void addSpriteFrameWithTexture(Texture2D* pobTexture, const Rect& rect);// 更换所有帧,注意会清空并release之前的动画帧。void setFrames(const Vector<AnimationFrame*>& frames){_frames = frames; // Vector的拷贝复制Vector<T>& operator=(const Vector<T>& other) // Vector的拷贝复制逻辑{if (this != &other) {clear(); // 先release每个item,然后并清空vector_data = other._data; // std::vector的拷贝赋值,拷贝每个itemaddRefForAllObjects(); // 每个item都retain一次。}return *this;}}// 动画结束,恢复到第一帧void setRestoreOriginalFrame(bool restoreOriginalFrame);}
五、Animate:执行帧动画
1、数据结构
// 是一个延时Actionclass CC_DLL Animate : public ActionInterval{protected:// 为啥要动态分配_splitTimes?没看懂。std::vector<float>* _splitTimes; // 播放一次时,每一帧的进度,0.0 ~ 1.0,1.0表示播放到这一帧就播放完了。int _nextFrame; // next frame index,第一帧index = 0SpriteFrame* _origFrame; // sprite的sprite frameint _currFrameIndex; // current frame indexunsigned int _executedLoops; // 已经播放次数Animation* _animation; // 帧动画数据EventCustom* _frameDisplayedEvent; // 播放每一帧时候都会广播一个eventcustom,event name是AnimationFrameDisplayedNotificationAnimationFrame::DisplayedEventInfo _frameDisplayedEventInfo; // 这个event的userdata};
2、重要逻辑
- Animate::initWithAnimation
- 初始化Action,比如action的时长
- Animate::update
- 每一渲染帧的动画更新。 ```cpp
bool Animate::initWithAnimation(Animation* animation) { …… float singleDuration = animation->getDuration(); // 播放一次的时长
// 设置action的总时长,才能计算出每一渲染帧时的执行进度。if ( ActionInterval::initWithDuration(singleDuration * animation->getLoops() ) ){......float accumUnitsOfTime = 0; // 累计delay unit// delaytime per unit,这一步没看懂,为什么不能直接animation->getDelayPerUnit();float newUnitOfTimeValue = singleDuration / animation->getTotalDelayUnits();// 下面主要是为了计算出每一帧的执行进度(播放一次的)auto& frames = animation->getFrames();for (auto& frame : frames){float value = (accumUnitsOfTime * newUnitOfTimeValue) / singleDuration;accumUnitsOfTime += frame->getDelayUnits();_splitTimes->push_back(value);}return true;}return false;
}
void Animate::update(float t)
{
// if t==1, ignore. Animation should finish with t==1
if( t < 1.0f )
{
t *= _animation->getLoops();
// new loop? If so, reset frame counterunsigned int loopNumber = (unsigned int)t;if( loopNumber > _executedLoops ) {_nextFrame = 0;_executedLoops++;}// 整数模除的浮点数版本,// 整数版:5 mod 3 = 2// 浮点版:5.1 mod 3 = 2.1t = fmodf(t, 1.0f); // 得到当前时刻在一个loop中的进度。// 比如loop = 5,当前进度0.5,得到t=0.5,表示当前在一个循环的中间,这样才好决定显示哪一帧。}auto& frames = _animation->getFrames();SpriteFrame* frameToDisplay = nullptr;for( int i = _nextFrame; i < frames.size(); i++ ){// 第i帧对应的进度,0 ~ 1float splitTime = _splitTimes->at(i);// 一直循环到t进度对应的那一帧。if( splitTime <= t ){auto blend = static_cast<Sprite*>(_target)->getBlendFunc();_currFrameIndex = i;AnimationFrame* frame = frames.at(_currFrameIndex);frameToDisplay = frame->getSpriteFrame();static_cast<Sprite*>(_target)->setSpriteFrame(frameToDisplay);static_cast<Sprite*>(_target)->setBlendFunc(blend);// 发送播放第i帧的广播。const ValueMap& dict = frame->getUserInfo();if ( !dict.empty() ){if (_frameDisplayedEvent == nullptr)_frameDisplayedEvent = new (std::nothrow) EventCustom(AnimationFrameDisplayedNotification);_frameDisplayedEventInfo.target = _target;_frameDisplayedEventInfo.userInfo = &dict;_frameDisplayedEvent->setUserData(&_frameDisplayedEventInfo);Director::getInstance()->getEventDispatcher()->dispatchEvent(_frameDisplayedEvent);}_nextFrame = i+1;}// Issue 1438. Could be more than one frame per tick, due to low frame rate or frame delta < 1/FPSelse {break;}}
}
<a name="szHY5"></a># 六、AnimationCache:帧动画缓存<a name="gZKji"></a>## 1、数据结构```cppclass CC_DLL AnimationCache : public Ref{// key: animation name,见下面的plist配置,// value: animationMap<std::string, Animation*> _animations;static AnimationCache* s_sharedAnimationCache; // 全局单例};
2、重要操作
class CC_DLL AnimationCache : public Ref{public:// 销毁所有缓存数据static void destroyInstance();// animation 缓存在一个map中,name就是keyvoid addAnimation(Animation *animation, const std::string& name);void removeAnimation(const std::string& name);// 注意cache随时可能会销毁animation,因此返回之后要retain,确保使用期间不会被销毁。Animation* getAnimation(const std::string& name);// 根据帧动画配置文件,批量加载并缓存帧动画数据。// 配置文件格式见下面,实际调用的是下面的方法。void addAnimationsWithFile(const std::string& plist);// 这个方法设计的很奇怪,还不如不要public,初衷是这样的: plist是帧动画配置文件的路径// 而帧动画使用到的精灵表规定和这个plist同目录,即配置和数据在一起。//// dictionary: 帧动画的配置,文件格式见下面// plist: 配置文件路径,规定:精灵表和配置文件同目录。void addAnimationsWithDictionary(const ValueMap& dictionary,const std::string& plist);};
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">
```
