在了解了CCAction
、CCFiniteTimeAction
和CCActionInterval
的类结构后,下面我们以它们为例分析Cocos2d-x的动作机制。
当我们对CCNode
调用runAction(CCAction*
action)
方法时,动作管理类CCActionManager
(它是一个单例对象)会将新的CCAction
和对应的目标节点添加到其管理的动作表中。
在CCActionManager
的addAction
方法中,我们将动作添加到动作队列之后,就会对该CCAction
调用成员函数startWithTarget(CCNode*
pTarget)
来绑定该动作的执行者。而在CCAction
的子类中(如CCActionInterval
),还初始化了一些参数:
void CCActionInterval::startWithTarget(CCNode *pTarget)
{
CCFiniteTimeAction::startWithTarget(pTarget);
m_elapsed = 0.0f;
m_bFirstTick = true;
}
当这些准备工作都完成后,每一帧刷新屏幕时,系统都会在CCActionManager
中遍历其动作表中的每一个动作,并调用该动作的step(ccTimedt)
方法。step
方法主要负责计算m_elapsed
的值,并调用update(float
time)
方法,相关代码如下:
void CCActionInterval::step(float dt)
{
if (m_bFirstTick)
{
m_bFirstTick = false;
m_elapsed = 0;
}
else
{
m_elapsed += dt;
}
this->update(MAX (0,
MIN(1, m_elapsed / MAX(m_fDuration, FLT_EPSILON))
)
);
}
传入update
方法的time
参数表示逝去的时间与动作完成需要的时间的比值,是介于0和1之间的一个数,即动作完成的百分比。
CCActionInterval
并没有进一步实现update
方法。下面我们继续以继承自CCActionInterval
的CCRotateTo
动作的update
方法为例,分析update
函数是如何实现的,其实现代码如下:
void CCRotateTo::update(float time)
{
if (m_pTarget)
{
m_pTarget->setRotation(m_fStartAngle + m_fDiffAngle * time);
}
}
看到这里,我们已经能看出Cocos2d-x的动作机制的整个工作流程了。在CCRotateTo
中,最终完成的操作是修改目标节点的Rotation
属性值,更新该目标节点的旋转属性值。
最后,在每一帧刷新结束后,在CCActionManager
类的update
方法中都会检查动作队列中每一个动作的isDone
函数是否返回true
。如果返回true
,则动作已完成,将其从队列中删除。isDone
函数的代码如下:
bool CCActionInterval::isDone(void)
{
return m_elapsed >= m_fDuration;
}
对于不同的动作类,虽然整体流程大致都是先调用step
方法,然后按照各个动作的具体定义来更新目标节点的属性,但是不同动作的具体实现会有所不同。例如,CCRepeatForever
动作的isDone
函数始终返回false
,因为它是永远在执行的动作;又如CCActionInstant
及其子类的step
函数中,向update
传递的参数值始终是1,因为瞬时动作会在下一帧刷新后完成,不需要多次执行update
。