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


1.jpg

其下还继承了很多的持续动作类,这里只截取了一部分。

1、成员变量:

protected:

float _elapsed; // 动作已执行了多长时间。

/* 这个是为了防jerk而设定的,不过jerk是什么我还不是特别清楚,图像抖动吗?

* 下面说到step()成员方法的源码实现时还会说到。

*/

bool _firstTick;

2、成员方法:

(1) inline float getElapsed(void) { return _elapsed; }

该方法用于获得从动作开始执行后过去了多少秒。

(2) void setAmplitudeRate(float amp);

float getAmplitudeRate(void);

这两个方法用于设置格子动画的振幅,不过好像还没有实现。

(3) virtual bool isDone(void) const override;

该方法用于判断动作是否执行完毕。

实现源码:

bool ActionInterval::isDone() const

{

return _elapsed >= _duration;

}

很简单,当发现动作的执行时间大于等于指定的时间,则说明动作已经执行完成。

(4) bool initWithDuration(float d);

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

d:动作持续时间。

实例:

该函数不需要应用程序中主动调用,一般由ActionInterval的派生类调用create()时内部调用,例如MoveBy::create()。

实现源码:

bool ActionInterval::initWithDuration(float d)

{

_duration = d; // 初始化动作的持续时间,_duration属于ActionInterval的父类。

// prevent division by 0

// This comparison could be in step:, but it might decrease the performance

// by 3% in heavy based action games.

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) 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) 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) virtual ActionInterval* reverse() const override

virtual ActionInterval *clone() const override

这两个函数在ActionInterval中不能被调用,需要其派生来实现。