Cocos2d-x 3.3中的ActionInterval用法讲解

2015年03月20日 14:32 1 点赞 0 评论 更新于 2017-05-09 17:38

在Cocos2d-x 3.3中,ActionInterval 主要负责记录持续动作已执行的时长。它与 ActionInstant(瞬时动作)相对应。ActionInterval 的继承关系如下:

其下还衍生出了许多持续动作类,这里仅展示了一部分。

成员变量

1. _elapsed

protected:
float _elapsed; // 动作已执行的时长

_elapsed 用于记录动作从开始执行到当前时刻所经过的时间。

2. _firstTick

bool _firstTick;

_firstTick 是为了防止 jerk(抖动现象)而设定的变量,后续在 step() 成员方法的源码实现部分会详细说明。

成员方法

1. getElapsed()

inline float getElapsed(void) { return _elapsed; }

此方法用于获取从动作开始执行后所经过的秒数。

2. setAmplitudeRate()getAmplitudeRate()

void setAmplitudeRate(float amp);
float getAmplitudeRate(void);

这两个方法用于设置格子动画的振幅,但目前似乎尚未实现。

3. isDone()

virtual bool isDone(void) const override;

该方法用于判断动作是否执行完毕。其实现源码如下:

bool ActionInterval::isDone() const
{
return _elapsed >= _duration;
}

当动作的执行时间大于或等于指定的持续时间时,表明动作已经执行完成。

4. initWithDuration()

bool initWithDuration(float d);

此函数用于初始化动作的持续时间 _duration 以及动作已执行的时间 _elapsed

  • d:动作持续的时间。

实例

该函数无需在应用程序中主动调用,通常由 ActionInterval 的派生类在调用 create() 时内部调用,例如 MoveBy::create()

实现源码

bool ActionInterval::initWithDuration(float d)
{
_duration = d; // 初始化动作的持续时间,_duration 属于 ActionInterval 的父类
// 防止除数为 0
// 此比较放在 step() 中可能会使基于动作的重度游戏性能降低约 3%
if (_duration == 0)
{
/* 防止 _duration 作为除数时为 0
* FLT_EPSILON 是计算机所能识别的最小正数
* _duration 仅在 ActionInterval::step() 中作为除数,
* 但那里有 MAX(_duration, FLT_EPSILON) 作为保障机制,
* 因此这里不特别将 _duration 赋值为 FLT_EPSILON 也可
* 上述注释的理解是否是由于影响游戏性能,
* step() 中可以不用 MAX() 对 _duration 的值进行保障,而放在这里?
*/
_duration = FLT_EPSILON;
}
_elapsed = 0; // 初始化动作已持续时间
_firstTick = true; // 为防止 jerk 而设置的变量
return true;
}

关键点总结

该函数需结合 ActionInterval 派生类的 create() 方法来看,如 MoveBy::create()。对于 _duration 作为除数需要非零的保障,在 ActionInterval::step() 中使用 MAX() 或者在 ActionInterval::initWithDuration() 中直接赋值均可。考虑到游戏运行过程中 step() 会频繁调用,在 step() 中总是使用 MAX() 会影响游戏性能,所以可在 initWithDuration() 中对 _duration 进行赋值,step() 中直接使用即可。

5. startWithTarget()

virtual void startWithTarget(Node *target) override;

该函数用于将动作与精灵进行绑定。

  • target:待执行动作的精灵。

实例

该函数无需在应用程序中主动调用,通常由精灵调用 runAction() 关联动作时内部调用,例如 mySprite->runAction(myRotateBy)

实现源码

void ActionInterval::startWithTarget(Node *target)
{
FiniteTimeAction::startWithTarget(target); // 其父类将精灵与动作进行绑定
_elapsed = 0.0f; // 初始化动作已持续时间
_firstTick = true; // 防止 jerk 而设置的变量
}

关键点总结

该函数需结合精灵的 runAction() 方法来看,如 mySprite->runAction(myRotateBy)

6. step()

virtual void step(float dt) override;

该函数根据流逝的时间计算出动作的进度(以百分比形式表示),然后将这个进度传递给动作的 update() 方法进行更新。

  • dt:从上一帧到这一帧所流逝的时间。

实例

暂无相关实例。

实现源码

void ActionInterval::step(float dt)
{
if (_firstTick) // 防止 jerk,step() 第一次被调用时会进入这里
{
_firstTick = false;
_elapsed = 0;
}
else
{
_elapsed += dt; // _elapsed 存储动作已执行的时间
}
/* 首先使用 MAX() 保障 _duration 非 0;之后用动作已执行时间除以动作总的持续时间
* 得到动作已完成的进度(理解为百分比)。当动作的进度为 1 时相当于 100%,
* 这里使用 MIN() 保障动作的执行进度不会超过 100%。
* 最后一个 MAX() 不太理解,难道进度会有负值的情况?
*/
this->update(MAX (0, // needed for rewind. elapsed could be negative
MIN(1, _elapsed /
MAX(_duration, FLT_EPSILON) // division by 0
)
));
}

关键点总结

该函数由引擎内部调用,动作执行期间会不断被调用。函数首先累计动作已执行的时间,然后根据该时间和动作的总持续时间计算出当前动作的执行进度,最后将进度传递给 update() 方法以更新精灵的状态。函数中 update() 参数里的最后一个 MAX() 不太容易理解,是否存在进度为负数的情况有待进一步研究。

7. reverse()clone()

virtual ActionInterval* reverse() const override;
virtual ActionInterval *clone() const override;

这两个函数在 ActionInterval 中不能直接调用,需要其派生类进行实现。

作者信息

boke

boke

共发布了 1025 篇文章