前言
帧动画是以序列帧轮放的方式来表现一个动画,就像胶片电影一样,一张张画面进行切换,当切换的间隔足够小时,人眼就看不出中间的间隔,而是一个流畅的视频。cocos2d-x 中的帧动画涉及到三个类 AnimationFrame,Animation 和 Animate。AnimationFrame 是对精灵帧 SpriteFrame 的再次封装,保存了一帧画面的信息;Animation 是一个动画的数据集合,保存了所有动画帧及其它数据;Animate 是在 Animation 上封装的一个动作,播放动画时是精灵执行这个动作的过程。
AnimationFrame
AnimationFrame 是动画帧,它只是对 SpriteFrame 的简单封装,它定义三个属性
SpriteFrame *_spriteFrame;
float _delayUnits;
ValueMap _userInfo;
_delayUnits 姑且称之为延迟单元数,表示这一帧画面将持续多少单元时间,一般给它赋值 1,表示一个单元时间,一个单元时间指每一帧的间隔时间;_userInfo 表示这一帧画面显示时要广播的数据,这个属性暂时没用到,传一个空的字典 ValueMap 就行。
AnimationFrame 最重要的函数是 initWithSpriteFrame,用于设置上面那三个属性的值
bool AnimationFrame::initWithSpriteFrame(SpriteFrame* spriteFrame, float delayUnits, const ValueMap& userInfo)
{
setSpriteFrame(spriteFrame);
setDelayUnits(delayUnits);
setUserInfo(userInfo);
return true;
}
Animation
Animation 是一个动画数据类,它定义了下面六个属性
float _totalDelayUnits;
float _delayPerUnit;
float _duration;
Vector<AnimationFrame *> _frames;
bool _restoreOriginalFrame;
unsigned int _loops;
这六个属性中比较重要的三个属性是 _frames,_delayPerUnit 和 _loops;_frames 是动画帧集合,这是一个 Vector,保存了所有的 AnimateFrame;_delayPerUnit 是一帧画面的间隔时间,也就是上面提到的一个单元时间;_loops 是动画播放时的循环次数。
另外三个属性并不是不重要,而是一般不需要调用者关心而已;_restoreOriginalFrame 表示动画播放完是否恢复到第一帧的画面,默认是 false,可以通过接口来修改它的值;另外两个属性一般不需要调用者去设置或读取它的值,_totalDelayUnits 是指动画一共需要多少个单元时间,这个数是添加动画帧的时候自动计算的,前面说到动画帧有一个属性延迟单元个数 _delayUnits 表示这个动画帧需要的单元时间个数,把所有动画帧的延迟单元个数加起来就是这个动画需要的总延迟单元个数,动画帧的延迟单元个数一般为 1,所以动画的总延迟单元个数一般等于动画帧的个数;_duration 是指动画持续的时间,这个时间的计算也很简单,由总延迟单元个数乘以一个单元时间即可。
Animation 最重要的函数是初始化函数,这个函数设置三个对外属性 动画帧、单元时间 和 循环次数 的值,然后通过遍历动画帧计算出 总延迟单元个数。
bool Animation::initWithAnimationFrames(const Vector<AnimationFrame*>& arrayOfAnimationFrames, float delayPerUnit, unsigned int loops)
{
_delayPerUnit = delayPerUnit;
_loops = loops;
setFrames(arrayOfAnimationFrames);
for (auto& animFrame : _frames)
{
_totalDelayUnits += animFrame->getDelayUnits();
}
return true;
}
获取动画持续时间 _duration 的时候通过 总延迟单元个数 和 单元时间 计算出总持续时间,所以,其实上面的 _duration 定义之后并没有使用,cocos2d-x 的源代码质量其实并不高~
float Animation::getDuration(void) const
{
return _totalDelayUnits * _delayPerUnit;
}
还有一个初始化函数是直接传精灵帧集合进来,然后根据精灵帧创建动画帧保存下来,同样也会计算 总延迟单元个数 _totalDelayUnits
bool Animation::initWithSpriteFrames(const Vector<SpriteFrame*>& frames, float delay/* = 0.0f*/, unsigned int loops/* = 1*/)
{
_delayPerUnit = delay;
_loops = loops;
for (auto& spriteFrame : frames)
{
auto animFrame = AnimationFrame::create(spriteFrame, 1, ValueMap());
_frames.pushBack(animFrame);
_totalDelayUnits++;
}
return true;
}
上面两个初始化函数都是一次性初始化动画帧数据,还有一个函数是一次添加一帧数据;这个函数平时用得比较多,不用去创建一个 Vector,也不用等创建完所有动画帧或精灵帧时再初始化动画,可以每得到一个精灵帧就添加进来;另外,上面两个批量初始化函数导出的 lua 函数好像有问题,所以我一般都是用这个函数来设置动画数据
void Animation::addSpriteFrame(SpriteFrame* spriteFrame)
{
AnimationFrame *animFrame = AnimationFrame::create(spriteFrame, 1.0f, ValueMap());
_frames.pushBack(animFrame);
// update duration
_totalDelayUnits++;