cocos2dx中的CCAction

稍微了解cocos2d-x的朋友应该都知道cocos2d-x里面的动作都是通过CCAction的各种派生类来实现的。

比如我要将一个Sprite1秒内从(0,0)移动到(400,400

pSprite->setPosition(ccp(0,0));

CCMoveTo* moveTo = CCMoveTo::create(1.0f,,ccp(400,400));

pSprite->runAction(moveTo);

那么,让我们来看看Actioncocos2d-x到底是如何实现的。

首先必须要了解的几个类

1.CCActionCCActionInterval

CCAction是所有动作的基类,从代码里可以看出它本身并不执行动作

void CCAction::update(float time)

{

    CC_UNUSED_PARAM(time);

    CCLOG("[Action update]. override me");

}

他的作用只是提供一部分接口,如stopupdate等等,其本身的实际作用并不是太大

CCActionInterval反而相对重要些,这个是大部分常用动作的基类,如常用的CCMoveToCCRotateBy等都是继承自CCActionInterval,CCActionInterval还有很重要的一点就是他实现了step方法,这个方法是大部分其派生类更新动作的基础(后文会有讨论)

2.CCActionManager

从名字可以看出,这个类负责管理着所有的动作,

前面代码里的pSprite->runAction(moveTo);其实就是将moveTo这个ActionpSprite这个Node加入到了CCActionManager内部维护的m_pTargets的这个hash表中

看看代码:

  1.   CCAction * CCNode::runAction(CCAction* action)  
  2.   {  
  3.       CCAssert( action != NULL, "Argument must be non-nil");  
  4.       m_pActionManager->addAction(action, this, !m_bIsRunning);  
  5.       return action;  
  6.   }  
  CCAction * CCNode::runAction(CCAction* action)
  {
      CCAssert( action != NULL, "Argument must be non-nil");
      m_pActionManager->addAction(action, this, !m_bIsRunning);
      return action;
  }


 

看看,内部是否是调用CCActionManageraddAction

接着我们再看看addAction的实现

  1.   void CCActionManager::addAction(CCAction *pAction, CCNode *pTarget, bool paused)  
  2.   {  
  3.       CCAssert(pAction != NULL, "");  
  4.       CCAssert(pTarget != NULL, "");  
  5.     
  6.       tHashElement *pElement = NULL;  
  7.       // we should convert it to CCObject*, because we save it as CCObject*   
  8.       CCObject *tmp = pTarget;  
  9.       HASH_FIND_INT(m_pTargets, &tmp, pElement);  
  10.       if (! pElement)  
  11.       {  
  12.           pElement = (tHashElement*)calloc(sizeof(*pElement), 1);  
  13.           pElement->paused = paused;  
  14.           pTarget->retain();  
  15.           pElement->target = pTarget;  
  16.           HASH_ADD_INT(m_pTargets, target, pElement);  
  17.       }  
  18.     
  19.     
  20.        actionAllocWithHashElement(pElement);  
  21.      
  22.        CCAssert(! ccArrayContainsObject(pElement->actions, pAction), "");  
  23.        ccArrayAppendObject(pElement->actions, pAction);  
  24.      
  25.        pAction->startWithTarget(pTarget);  
  26.   }  
  void CCActionManager::addAction(CCAction *pAction, CCNode *pTarget, bool paused)
  {
      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);
  }


 

略去见面的Assert检查和后面的初始化设定不看,上面代码的大概意思就是将pAction(上文的moveTo)pTarget(上文的pSprite)加入到内部维护的m_Targets中,至于m_Targets到底是个什么东西我们可以不必深究,就理解为一个数组或者hash表就够了

贴出这个结构的源码,有兴趣的朋友可以看看

  1.   typedef struct _hashElement  
  2.   {  
  3.       struct _ccArray             *actions;  
  4.       CCObject                  *target;  
  5.       unsigned int                actionIndex;  
  6.       CCAction                  *currentAction;  
  7.       bool                      currentActionSalvaged;  
  8.       bool                      paused;  
  9.       UT_hash_handle            hh;  
  10.   } tHashElement;  
  typedef struct _hashElement
  {
      struct _ccArray             *actions;
      CCObject                  *target;
      unsigned int                actionIndex;
      CCAction                  *currentAction;
      bool                      currentActionSalvaged;
      bool                      paused;
      UT_hash_handle            hh;
  } tHashElement;


 

这里要注意的一点,一个target是可以附带多个Action的,从上面actions的类型是一个数组我们可以加以确认。

OK,一切都设置妥当,我们开始讲解最核心的update方法

首先要说明一点,这个CCActionManagerupdate是在CCDisplayLinkDirector的mainLoop->drawScene里调用m_pScheduler->update(m_fDeltaTime);中被调用的

m_pScheduler是CCScheduler类型,他负责系统全部子系统的更新,比如TouchAction,脚本,定时器等等都在这里updateAction是在优先级 < 0的那个list里面的

好了,继续讨论update,还是先看看源码

  1.   void CCActionManager::update(float dt)  
  2.   {  
  3.       for (tHashElement *elt = m_pTargets; elt != NULL; )  
  4.       {  
  5.           m_pCurrentTarget = elt;  
  6.           m_bCurrentTargetSalvaged = false;  
  7.     
  8.           if (! m_pCurrentTarget->paused)  
  9.           {  
  10.               // The 'actions' CCMutableArray may change while inside this loop.   
  11.               for (m_pCurrentTarget->actionIndex = 0; m_pCurrentTarget->actionIndex < m_pCurrentTarget->actions->num;  
  12.                   m_pCurrentTarget->actionIndex++)  
  13.               {  
  14.                   m_pCurrentTarget->currentAction = (CCAction*)m_pCurrentTarget->actions->arr[m_pCurrentTarget->actionIndex];  
  15.                   if (m_pCurrentTarget->currentAction == NULL)  
  16.                   {  
  17.                       continue;  
  18.                   }  
  19.     
  20.                   m_pCurrentTarget->currentActionSalvaged = false;  
  21.     
  22.                   m_pCurrentTarget->currentAction->step(dt);  
  23.     
  24.                   if (m_pCurrentTarget->currentActionSalvaged)  
  25.                   {  
  26.                       // The currentAction told the node to remove it. To prevent the action from   
  27.                       // accidentally deallocating itself before finishing its step, we retained   
  28.                       // it. Now that step is done, it's safe to release it.   
  29.                       m_pCurrentTarget->currentAction->release();  
  30.                   } else  
  31.                   if (m_pCurrentTarget->currentAction->isDone())  
  32.                   {  
  33.                       m_pCurrentTarget->currentAction->stop();  
  34.     
  35.                       CCAction *pAction = m_pCurrentTarget->currentAction;  
  36.                       // Make currentAction nil to prevent removeAction from salvaging it.   
  37.                       m_pCurrentTarget->currentAction = NULL;  
  38.                       removeAction(pAction);  
  39.                   }  
  40.     
  41.                   m_pCurrentTarget->currentAction = NULL;  
  42.               }  
  43.           }  
  44.     
  45.           // elt, at this moment, is still valid   
  46.           // so it is safe to ask this here (issue #490)   
  47.           elt = (tHashElement*)(elt->hh.next);  
  48.     
  49.           // only delete currentTarget if no actions were scheduled during the cycle (issue #481)   
  50.           if (m_bCurrentTargetSalvaged && m_pCurrentTarget->actions->num == 0)  
  51.           {  
  52.               deleteHashElement(m_pCurrentTarget);  
  53.           }  
  54.       }  
  55.     
  56.       // issue #635   
  57.       m_pCurrentTarget = NULL;  
  58.   }  
  void CCActionManager::update(float dt)
  {
      for (tHashElement *elt = m_pTargets; elt != NULL; )
      {
          m_pCurrentTarget = elt;
          m_bCurrentTargetSalvaged = false;
  
          if (! m_pCurrentTarget->paused)
          {
              // The 'actions' CCMutableArray may change while inside this loop.
              for (m_pCurrentTarget->actionIndex = 0; m_pCurrentTarget->actionIndex < m_pCurrentTarget->actions->num;
                  m_pCurrentTarget->actionIndex++)
              {
                  m_pCurrentTarget->currentAction = (CCAction*)m_pCurrentTarget->actions->arr[m_pCurrentTarget->actionIndex];
                  if (m_pCurrentTarget->currentAction == NULL)
                  {
                      continue;
                  }
  
                  m_pCurrentTarget->currentActionSalvaged = false;
  
                  m_pCurrentTarget->currentAction->step(dt);
  
                  if (m_pCurrentTarget->currentActionSalvaged)
                  {
                      // The currentAction told the node to remove it. To prevent the action from
                      // accidentally deallocating itself before finishing its step, we retained
                      // it. Now that step is done, it's safe to release it.
                      m_pCurrentTarget->currentAction->release();
                  } else
                  if (m_pCurrentTarget->currentAction->isDone())
                  {
                      m_pCurrentTarget->currentAction->stop();
  
                      CCAction *pAction = m_pCurrentTarget->currentAction;
                      // Make currentAction nil to prevent removeAction from salvaging it.
                      m_pCurrentTarget->currentAction = NULL;
                      removeAction(pAction);
                  }
  
                  m_pCurrentTarget->currentAction = NULL;
              }
          }
  
          // elt, at this moment, is still valid
          // so it is safe to ask this here (issue #490)
          elt = (tHashElement*)(elt->hh.next);
  
          // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
          if (m_bCurrentTargetSalvaged && m_pCurrentTarget->actions->num == 0)
          {
              deleteHashElement(m_pCurrentTarget);
          }
      }
  
      // issue #635
      m_pCurrentTarget = NULL;
  }


 

其实update的代码很简单,就是遍历m_pTargets数组(或者链表,hash,随你高兴,不过他实际是hash),然后逐个更新每个tHashElement 里面的动作,当然他会有一些判断,比如动作是否暂停,是否结束之类的,并做相应处理,不如终止了自然要remove掉。如果一切正常,那就调用动作的step方法(这里必须知道多态的概念哦,因为step和后面的update方法都是virtual的)。

前面讲过CCActionInterval有实现step方法,好的,现在我们再来看看他做了什么事情

  1.   void CCActionInterval::step(float dt)  
  2.   {  
  3.       if (m_bFirstTick)  
  4.       {  
  5.           m_bFirstTick = false;  
  6.           m_elapsed = 0;  
  7.       }  
  8.       else  
  9.       {  
  10.           m_elapsed += dt;  
  11.       }  
  12.         
  13.       this->update(MAX (0,                                  // needed for rewind. elapsed could be negative   
  14.                         MIN(1, m_elapsed /  
  15.                             MAX(m_fDuration, FLT_EPSILON)   // division by 0   
  16.                             )  
  17.                         )  
  18.                    );  
  19.   }  
  void CCActionInterval::step(float dt)
  {
      if (m_bFirstTick)
      {
          m_bFirstTick = false;
          m_elapsed = 0;
      }
      else
      {
          m_elapsed += dt;
      }
      
      this->update(MAX (0,                                  // needed for rewind. elapsed could be negative
                        MIN(1, m_elapsed /
                            MAX(m_fDuration, FLT_EPSILON)   // division by 0
                            )
                        )
                   );
  }


 

简而言之,就是根据传入的时间间隔(dt),增加m_elapsed(目前动作的执行到的位置),并将其处理在一个合法范围内(通过MAXMIN宏),然后传给update方法,最后在update方法里面会进行实际的动作更新,接下来,我们以CCMoveTo为例,看看他的update是如何实现的

  1.   void CCMoveTo::update(float time)  
  2.   {  
  3.       if (m_pTarget)  
  4.       {  
  5.           m_pTarget->setPosition(ccp(m_startPosition.x + m_delta.x * time,  
  6.               m_startPosition.y + m_delta.y * time));  
  7.       }  
  8.   }  
  void CCMoveTo::update(float time)
  {
      if (m_pTarget)
      {
          m_pTarget->setPosition(ccp(m_startPosition.x + m_delta.x * time,
              m_startPosition.y + m_delta.y * time));
      }
  }


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值