cocos2d-x 源码剖析(9)

上一节讲了ActionManager,这节就顺势讲讲Action吧。下面是Action的继承图,这个结构看起来有点不对称,我也想不透作者是基于这种理由这样设计的,或许阅读晚源码之后会有所了解吧:

classcocos2d_1_1_action

 

我们先从最简单的CCMove开始讲起。可以知道,CCMove继承至ActionInterval。ActionInterval有个特点,需要在一段时间内不断的调用step函数。与之对应的是ActionInstant,它不需要反复调用,一次就能完成。这两种action又都继承至FiniteTimeAction,也就是有限时间的Action。而之对应,还有Follow和Speed两个特殊的Action。好吧,来看看CCMove的创建过程:

CCMoveBy* CCMoveBy::create(float duration, 
  const CCPoint& deltaPosition)
{
    CCMoveBy *pRet = new CCMoveBy();
    pRet->initWithDuration(duration, deltaPosition);
    pRet->autorelease();
    return pRet;
}
 
bool CCMoveBy::initWithDuration(float duration, 
  const CCPoint& deltaPosition)
{
    if (CCActionInterval::initWithDuration(duration))
    {
        m_positionDelta = deltaPosition;
        return true;
    }
    return false;
}
 
bool CCActionInterval::initWithDuration(float d)
{
    m_fDuration = d;
 
    // prevent division by 0
    // This comparison could be in step:, 
    // but it might decrease the performance
    // by 3% in heavy based action games.
    if (m_fDuration == 0)
    {
        m_fDuration = FLT_EPSILON;
    }
 
    m_elapsed = 0;
    m_bFirstTick = true;
 
    return true;
}


对于所有的CCActionInterval,他都需要一个持续时间来表述何时结束。对于CCMoveBy来讲init函数中m_positionDelta记录下了需要移动的距离。前面我们讲过如果Action创建出来,被CCNode调用runAction之后,它就被添加到ActonManager中去了。

voidCCActionManager::addAction(CCAction*pAction,
  CCNode*pTarget,boolpaused)
{
  CCAssert(pAction!=NULL,"");
  CCAssert(pTarget!=NULL,"");
 
  tHashElement*pElement=NULL;
  // we should convert it to CCObject*, because 
  // we save it as CCObject*
  CCObject*tmp=pTarget;
  HASH_FIND_INT(m_pTargets,&tmp,pElement);
  if(!pElement)
  {
    pElement=(tHashElement*)calloc(sizeof(*pElement),1);
    pElement->paused=paused;
    pTarget->retain();
    pElement->target=pTarget;
    HASH_ADD_INT(m_pTargets,target,pElement);
  }
 
  actionAllocWithHashElement(pElement);
 
  CCAssert(!ccArrayContainsObject(pElement->actions,
    pAction),"");
  ccArrayAppendObject(pElement->actions,pAction);
 
  pAction->startWithTarget(pTarget);
}

(cocos2d-x的缩进已经到了丧心病狂的地步,在一句之中同时用tab和space,又涨见识啦),注意到action本身是没有将target的引用计数加1的,将引用计数加1的只是ActionManager而已,而且只在第一次添加时加1。发现这点我突然觉得不应该再爱了。之后调用startWithTarget。

void CCMoveBy::startWithTarget(CCNode *pTarget)
{
    CCActionInterval::startWithTarget(pTarget);
    m_previousPosition = m_startPosition = pTarget->getPosition();
}

这个函数只是在做开始action的准备工作,如果在Node本身是活动的,这个action在添加到Manager的时候就在调用step了。叫Perpare或许更合适呢,因为它完全没有开始运行的逻辑。

有点意外的是CCMoveBy没有重载step函数,但却实现了update函数。

voidCCMoveBy::update(floatt)
{
  if(m_pTarget)
  {
#if CC_ENABLE_STACKABLE_ACTIONS
      CCPointcurrentPos=m_pTarget->getPosition();
      CCPointdiff=ccpSub(currentPos,m_previousPosition);
      m_startPosition=ccpAdd(m_startPosition,diff);
      CCPointnewPos=  ccpAdd(m_startPosition,
        ccpMult(m_positionDelta,t));
      m_pTarget->setPosition(newPos);
      m_previousPosition=newPos;
#else
      m_pTarget->setPosition(ccpAdd(m_startPosition,
        ccpMult(m_positionDelta,t)));
#endif // CC_ENABLE_STACKABLE_ACTIONS
  }
}

我们知道,这里确实是做实际逻辑的地方,而且根据update的注释,那个float t是0~1.0范围的。cocos2d-x给出了2种实现,第一种是默认的,它的好处是能同时调用多个位置相关的Action,效果累加。而第二种就是直接覆盖了,位置很可能是最后一个Action的效果。这个update在step中被调用:

void CCActionInterval::step(float dt)
{
    if (m_bFirstTick)
    {
        m_bFirstTick = false;
        m_elapsed = 0;
    }
    else
    {
        m_elapsed += dt;
    }
    // needed for rewind. elapsed could be negative
    this->update(MAX (0,                                  
                      MIN(1, m_elapsed /
                          MAX(m_fDuration, FLT_EPSILON)
                          )
                      )
                 );
}

那个update中的0~1.0的范围便是如此而来。而且这个代码造型的确略显犀利了些。至于判断Action是否完成则十分简单:

boolCCActionInterval::isDone(void)
{
    returnm_elapsed>=m_fDuration;
}

根据这个例子,大家应该知道CCActionInterval的运作方式了。如果你要实现一个自己的CCActionInterval(哪怕一个最简单的),需要重载update,startWithTarget,create,initWithDuration,copyWithZone。起码我认为后3个函数是值得改进的。实作update的时候千万别忘了范围是0~1.0之间。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值