ActionInterval延时动作

路径

1
2
2d/actions/ActionInterval.h
2d/actions/ActionInterval.cpp

源码

根据分析ActionInstant的经验,

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
class CC_DLL ActionInterval : public FiniteTimeAction
{
public:
inline float getElapsed(void) { return _elapsed; }

/* 在GridAction中使用 */
void setAmplitudeRate(float amp);
float getAmplitudeRate(void);

virtual bool isDone(void) const override;
virtual void step(float dt) override;
virtual void startWithTarget(Node *target) override;
virtual ActionInterval* reverse() const override = 0;
virtual ActionInterval *clone() const override = 0;

protected:
/* 初始化动作 */
bool initWithDuration(float d);

/* 动作执行了多少时间 */
float _elapsed;

/* 是否是第一次执行step */
bool _firstTick;
};

/****************CPP**********************/

bool ActionInterval::initWithDuration(float d)
{
_duration = d;

if (_duration == 0) {
_duration = FLT_EPSILON;
}

_elapsed = 0;
_firstTick = true;

return true;
}

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

void ActionInterval::step(float dt)
{
if (_firstTick) {
_firstTick = false;
_elapsed = 0;
} else {
_elapsed += dt;
}

/* 这段代码主要是计算任务执行百分比 [0.0f, 1.0f] */
this->update(MAX(0,
MIN(1,
_elapsed / MAX(_duration, FLT_EPSILON) // 防止除0
)
)
);
}

void ActionInterval::startWithTarget(Node *target)
{
FiniteTimeAction::startWithTarget(target);
_elapsed = 0.0f;
_firstTick = true;
}
  • 和ActionInstant类似,ActionInterval也是在step中调用update。

有些功能性延时动作十分常用:

  • Sequence
  • Repeat
  • RepeatForever
  • Spawn (直接多次runAction)
  • Animate

除此之外,就是视觉性延时动作

  • MoveTo
  • MoveBy
  • RotateTo
  • RotateBy
  • SnewTo
  • SnewBy
  • JumpTo
  • JumpBy
  • BezielTo
  • BezierBy
  • ScaleTo
  • ScaleBy
  • Blink
  • FadeIn
  • FadeOut
  • FadeTo
  • TintTo
  • TintBy
  • DelayTime
  • ReverseTime

  • TargetedAction

ExtraAction

继承自FiniteTimeAction,什么都没实现,在Sequence,Spawn里作为一个结束动作。

PS : 默认Action的isDone返回的是true

Sequence

首先,我们来看看一个Sequence是怎么执行的?

例如有如下一个Sequence:

1
[[[[A0, A1], A2] A3] ExtraEnd]

S0 = [A0, A1]
S1 = [S0, A2]
S2 = [S1, A3]
S3 = [S2, ExtraEnd]
  • 其中An表示延时任务
  • ExtraEnd表示ExtraAction
  • Sn表示Sequence

所以执行S3的执行流程如下

  1. 执行S3的第一个Action (S2)
  2. 执行S3的第二个Action (ExtarEnd)

执行过程有点递归的意思。
执行S2的时候需要执行S1和A3。然后执行S1的时候需要执行S0和A2。执行S0的时候需要执行A0和A1。
所以,执行的顺序还是A0 -> A1 -> A2 -> A3 -> ExtraEnd


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/** @brief Runs actions sequentially, one after another
*/

class CC_DLL Sequence : public ActionInterval
{
public:

/************************/
/* 此处略去wpf8相关东西 */
/************************/

static Sequence* create(FiniteTimeAction *action1, ...) CC_REQUIRES_NULL_TERMINATION;

/** helper constructor to create an array of sequenceable actions given an array
* @code
* When this funtion bound to the js or lua,the input params changed
* in js :var create(var object1,var object2, ...)
* in lua :local create(local object1,local object2, ...)
* @endcode
*/

static Sequence* create(const Vector<FiniteTimeAction*>& arrayOfActions);


static Sequence* createWithVariableList(FiniteTimeAction *action1, va_list args);

/* 这个是最基础的类方法,其他类方法都是基于此 */
static Sequence* createWithTwoActions(FiniteTimeAction *actionOne, FiniteTimeAction *actionTwo);


virtual Sequence* clone() const override;
virtual Sequence* reverse() const override;
virtual void startWithTarget(Node *target) override;
virtual void stop(void) override;
virtual void update(float t) override;

CC_CONSTRUCTOR_ACCESS:
Sequence() {}
virtual ~Sequence(void);

bool initWithTwoActions(FiniteTimeAction *pActionOne, FiniteTimeAction *pActionTwo);

protected:
FiniteTimeAction *_actions[2];
/* 第一个Action所占的时间比例 */
float _split;
/* 上一次执行的Action序号 */
int _last;

private:
CC_DISALLOW_COPY_AND_ASSIGN(Sequence);
};

先看看初始化方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool Sequence::initWithTwoActions(FiniteTimeAction *actionOne, FiniteTimeAction *actionTwo)
{
CCASSERT(actionOne != nullptr, "");
CCASSERT(actionTwo != nullptr, "");

float d = actionOne->getDuration() + actionTwo->getDuration();
ActionInterval::initWithDuration(d);

_actions[0] = actionOne;
actionOne->retain();

_actions[1] = actionTwo;
actionTwo->retain();

return true;
}

给Action增加引用计数,设置了Sequence的时间。


当我们让一个对象runAction(Sequence)的时候,最先调用以下代码

1
2
3
4
5
6
void Sequence::startWithTarget(Node *target)
{
ActionInterval::startWithTarget(target);
_split = _actions[0]->getDuration() / _duration;
_last = -1;
}

这里计算出_split的值,也就是两个Action之间的分割线 =。=

注意在startWithTarget中没有调用两个Action的startWithTarget(在update中调用)


然后再来看看update方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
void Sequence::update(float t)
{
int found = 0;
float new_t = 0.0f;

/* 先判断是那个Action需要执行 */
if( t < _split ) {
/* action[0] */
found = 0;
if( _split != 0 )
/* 计算新的比率给该Action使用 */
/* 即调用 action->update(new_t) */
new_t = t / _split;
else
new_t = 1;

} else {
/* action[1] */
found = 1;
if ( _split == 1 )
new_t = 1;
else
/* 计算新的比率给该Action使用 */
/* 即调用 action->update(new_t) */
new_t = (t-_split) / (1 - _split );
}


/* 如果是第二个需要执行 */
if ( found==1 ) {
/* 则需要先把第一个Action给结束掉 */
if( _last == -1 ) {
/* 第一个Action还没开始执行过 */
_actions[0]->startWithTarget(_target);
_actions[0]->update(1.0f);
_actions[0]->stop();
}
else if( _last == 0 )
{
/* 第一个Action已经执行过 */
_actions[0]->update(1.0f);
_actions[0]->stop();
}
}
/* 如果是第一个需要执行,而且上一个执行的动作是第二个 */
else if(found==0 && _last==1 )
{
/* reverse导致的问题 */
_actions[1]->update(0); /* 这里的update(0) 表示结束,因为reverse了嘛*/
_actions[1]->stop();
}

/* 若当前动作已经执行完毕 */
if( found == _last && _actions[found]->isDone() )
{
return;
}

/* 开始新的动作 */
/* 一般是从 0 到 1 */
if( found != _last )
{
_actions[found]->startWithTarget(_target);
}

/* 每一帧的更新 */
_actions[found]->update(new_t);
_last = found;
}

通过分析以上代码,我们可以知道update在管理着这两个Action的开始和结束。

Repeat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class CC_DLL Repeat : public ActionInterval
{
public:
/****************/
/* 略去所有函数 */
/****************/

protected:
/* 重复次数 */
unsigned int _times;

/* 当前重复次数 */
unsigned int _total;

/* 每次所占时间比率 */
float _nextDt;

/* 是否是瞬时动作 */
bool _actionInstant;

/* 待执行动作 */
FiniteTimeAction *_innerAction;

private:
CC_DISALLOW_COPY_AND_ASSIGN(Repeat);
};

初始化函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
bool Repeat::initWithAction(FiniteTimeAction *action, unsigned int times)
{
float d = action->getDuration() * times;

if (ActionInterval::initWithDuration(d))
{
_times = times;
_innerAction = action;
action->retain();

_actionInstant = dynamic_cast<ActionInstant*>(action) ? true : false;
//an instant action needs to be executed one time less in the update method since it uses startWithTarget to execute the action
/* 对于瞬时动作需要减少一次 */
/* 由于step肯定会先执行一次 */
if (_actionInstant) {
_times -=1;
}
_total = 0;

return true;
}

return false;
}

void Repeat::startWithTarget(Node *target)
{
_total = 0;

/* 计算每个重复动作所占比率 */
_nextDt = _innerAction->getDuration()/_duration;

ActionInterval::startWithTarget(target);
_innerAction->startWithTarget(target);
}

update函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
void Repeat::update(float dt)
{
if (dt >= _nextDt) {
/* 之所以是循环,主要是为了瞬时动作 */
while (dt > _nextDt && _total < _times) {
_innerAction->update(1.0f);
_total++;

_innerAction->stop();
_innerAction->startWithTarget(_target);
_nextDt += _innerAction->getDuration()/_duration;
}

/* 对于最后一次退出循环的问题 */
if(dt >= 1.0f && _total < _times) {
_total++;
}

// don't set an instant action back or update it, it has no use because it has no duration
if (!_actionInstant) {
if (_total == _times) {
_innerAction->update(1);
_innerAction->stop();
} else {
// issue #390 prevent jerk, use right update
_innerAction->update(dt - (_nextDt - _innerAction->getDuration()/_duration));
}
}
} else {
/* 对于延时动作的update */
_innerAction->update(fmodf(dt * _times,1.0f));
}
}

RepeatForever

Spawn

总结