1. 程式人生 > >cocos2d-x中的動作分析

cocos2d-x中的動作分析

在cocos2d-x中動作的執行排程是受cocos2d-x的全域性定時器控制的,當初始完導演後便已經啟動用於管理動作的update定時器。

bool CCDirector::init(void)
{
	...
    m_pActionManager = new CCActionManager();
    m_pScheduler->scheduleUpdateForTarget(m_pActionManager, kCCPrioritySystem, false);
    ...

    return true;
}
也就說主迴圈每幀都會呼叫這個定時器的排程,且優先順序是小於0的,是一個高優先順序的定時器。從上一篇文章可以得到的一個結果是當執行這個定時器排程時,由於是update的定時器,所以直接是每幀的呼叫m_pActionManager的update方法。

所以先看下這個動作管理器的定義:

//動作管理類
class CC_DLL CCActionManager : public CCObject
{
public:
    /**
     *  @js ctor
     */
	//構造
    CCActionManager(void);
    /**
     *  @js NA
     *  @lua NA
     */
    //析構
    ~CCActionManager(void);

    // actions
    
    /** Adds an action with a target. 
     If the target is already present, then the action will be added to the existing target.
     If the target is not present, a new instance of this target will be created either paused or not, and the action will be added to the newly created target.
     When the target is paused, the queued actions won't be 'ticked'.
     */
    //新增一個動作到管理器中
    void addAction(CCAction *pAction, CCNode *pTarget, bool paused);

    /** Removes all actions from all the targets.
    */
    //從管理器中移除所有動作
    void removeAllActions(void);

    /** Removes all actions from a certain target.
     All the actions that belongs to the target will be removed.
     */
    //從管理器中移除節點的所有動作
    void removeAllActionsFromTarget(CCObject *pTarget);

    /** Removes an action given an action reference.
    */
    //從管理器中移除一個動作
    void removeAction(CCAction *pAction);

    /** Removes an action given its tag and the target */
    //從管理器中根據動作標記移除一個動作
    void removeActionByTag(unsigned int tag, CCObject *pTarget);

    /** Gets an action given its tag an a target
     @return the Action the with the given tag
     */
    //從管理器中獲取一個帶有指定標記的動作
    CCAction* getActionByTag(unsigned int tag, CCObject *pTarget);

    /** Returns the numbers of actions that are running in a certain target. 
     * Composable actions are counted as 1 action. Example:
     * - If you are running 1 Sequence of 7 actions, it will return 1.
     * - If you are running 7 Sequences of 2 actions, it will return 7.
     */
    //返回指定節點中正在執行的動作的數量
    unsigned int numberOfRunningActionsInTarget(CCObject *pTarget);

    /** Pauses the target: all running actions and newly added actions will be paused.
    */
    //暫停節點的動作
    void pauseTarget(CCObject *pTarget);

    /** Resumes the target. All queued actions will be resumed.
    */
    //恢復節點的動作
    void resumeTarget(CCObject *pTarget);
    
    /** Pauses all running actions, returning a list of targets whose actions were paused.
     */
    //暫停所有正在執行的動作,並返回這些動作的集合
    CCSet* pauseAllRunningActions();
    
    /** Resume a set of targets (convenience function to reverse a pauseAllRunningActions call)
     */
    //恢復集合中指定的動作
    void resumeTargets(CCSet *targetsToResume);

protected:
    // declared in CCActionManager.m
    //根據索引移除
    void removeActionAtIndex(unsigned int uIndex, struct _hashElement *pElement);
    //刪除雜湊表中指定的項
    void deleteHashElement(struct _hashElement *pElement);
    //為動作申請記憶體空間
    void actionAllocWithHashElement(struct _hashElement *pElement);
    //更新,由主迴圈的update定時器呼叫更新
    void update(float dt);

protected:
    struct _hashElement    *m_pTargets;//儲存目標節點的雜湊表
    struct _hashElement    *m_pCurrentTarget;//當前的節點的雜湊元素
    bool            m_bCurrentTargetSalvaged;//可回收標記
};

從上面的註釋可以看出它的功能就是將動作加到自己的管理器中進行管理,當動作執行完後便會把這個動作從管理器中移除掉,另外還定義了兩個雜湊表來管理多個節點和執行節點和一個輔助的回收標記變數,跟定時器是設計十分相像:
typedef struct _hashElement
{
    struct _ccArray             *actions;//動作陣列,即一個節點可以允許有多個動作
    CCObject                    *target;//執行動作的目標節點
    unsigned int                actionIndex;//索引
    CCAction                    *currentAction;//當前正在執行的動作
    bool                        currentActionSalvaged;//當前正在執行的動作是否被標記為可回收
    bool                        paused;//是否暫停
    UT_hash_handle                hh;//操作雜湊表的控制代碼
} tHashElement;
整個實現細節如下(最終要的便是update是每幀都會被主迴圈所呼叫):‘
CCActionManager::CCActionManager(void)
: m_pTargets(NULL), 
  m_pCurrentTarget(NULL),
  m_bCurrentTargetSalvaged(false)
{

}

CCActionManager::~CCActionManager(void)
{
    CCLOGINFO("cocos2d: deallocing %p", this);

    removeAllActions();//移除多有的動作
}

// private

void CCActionManager::deleteHashElement(tHashElement *pElement)
{
    ccArrayFree(pElement->actions);//釋放資源
    HASH_DEL(m_pTargets, pElement);//從雜湊表中刪除元素
    pElement->target->release();//動作目標的引用計數減1
    free(pElement);//釋放雜湊元素
}

void CCActionManager::actionAllocWithHashElement(tHashElement *pElement)
{
    // 4 actions per Node by default
    if (pElement->actions == NULL)//每個節點初始化可存放4個節點
    {
        pElement->actions = ccArrayNew(4);
    }else 
    if (pElement->actions->num == pElement->actions->max)//如果節點的動作數量大於當前的已擁有的動作數量
    {
        ccArrayDoubleCapacity(pElement->actions);//將容量擴大2倍
    }

}

void CCActionManager::removeActionAtIndex(unsigned int uIndex, tHashElement *pElement)
{
    CCAction *pAction = (CCAction*)pElement->actions->arr[uIndex];

    if (pAction == pElement->currentAction && (! pElement->currentActionSalvaged))//如果待移除的動作與當前正在執行的動作相等且沒有被標記可回收,那麼將其標記可回收
    {
        pElement->currentAction->retain();//引用計數加1,保證當前的動作能完整的被執行完
        pElement->currentActionSalvaged = true;//標記為可回收該動作
    }

    ccArrayRemoveObjectAtIndex(pElement->actions, uIndex, true);//將該動作從動作管理器中移除掉

    // update actionIndex in case we are in tick. looping over the actions
    if (pElement->actionIndex >= uIndex)//保證動作被移除時迴圈能被正確執行
    {
        pElement->actionIndex--;
    }

    if (pElement->actions->num == 0)//如果節點的沒有動作列表
    {
        if (m_pCurrentTarget == pElement)//如果待移除的動作與當前正在執行的動作相等
        {
            m_bCurrentTargetSalvaged = true;//將該正在執行的動作標記為可回收
        }
        else
        {//不相等
            deleteHashElement(pElement);//直接從雜湊表中移除掉
        }
    }
}

// pause / resume

void CCActionManager::pauseTarget(CCObject *pTarget)
{
    tHashElement *pElement = NULL;
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);//在雜湊表中查詢該節點的雜湊項
    if (pElement)//找到,設定暫停
    {
        pElement->paused = true;
    }
}

void CCActionManager::resumeTarget(CCObject *pTarget)
{
    tHashElement *pElement = NULL;
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);//在雜湊表中查詢該節點的雜湊項
    if (pElement)//找到,恢復執行
    {
        pElement->paused = false;
    }
}

CCSet* CCActionManager::pauseAllRunningActions()
{
    CCSet *idsWithActions = new CCSet();//建立一個收集即將被設定為暫停的集合
    idsWithActions->autorelease();//放到記憶體回收池
    
    for (tHashElement *element=m_pTargets; element != NULL; element = (tHashElement *)element->hh.next) //遍歷動作管理器
    {
        if (! element->paused) //如果不是暫停狀態
        {
            element->paused = true;//設定暫停
            idsWithActions->addObject(element->target);//加到集合中
        }
    }    
    //返回集合
    return idsWithActions;
}

void CCActionManager::resumeTargets(cocos2d::CCSet *targetsToResume)
{    
    CCSetIterator iter;
    for (iter = targetsToResume->begin(); iter != targetsToResume->end(); ++iter)//遍歷集合中的動作
    {
        resumeTarget(*iter);//恢復動作
    }
}

// run

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();//引用計數加1
        pElement->target = pTarget;//設定目標節點
        HASH_ADD_INT(m_pTargets, target, pElement);//將新建立的雜湊項放到雜湊表中
    }
    //為雜湊項申請存放記憶體空間
     actionAllocWithHashElement(pElement);
 
     CCAssert(! ccArrayContainsObject(pElement->actions, pAction), "");
     ccArrayAppendObject(pElement->actions, pAction);//加到動作管理器中
     //初始化執行目標節點
     pAction->startWithTarget(pTarget);
}

// remove

void CCActionManager::removeAllActions(void)
{
    for (tHashElement *pElement = m_pTargets; pElement != NULL; )//遍歷動作管理器中的所有動作
    {
        CCObject *pTarget = pElement->target;//執行動作的目標節點
        pElement = (tHashElement*)pElement->hh.next;//下一個元素
        removeAllActionsFromTarget(pTarget);//將該目標節點從動作管理器中移除掉
    }
}

void CCActionManager::removeAllActionsFromTarget(CCObject *pTarget)
{
    // explicit null handling
    if (pTarget == NULL)//引數檢查
    {
        return;
    }

    tHashElement *pElement = NULL;
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);//在雜湊表中查詢該節點的雜湊項
    if (pElement)//找到
    {
        if (ccArrayContainsObject(pElement->actions, pElement->currentAction) && (! pElement->currentActionSalvaged))//如果待移除的動作與當前正在執行的動作相等且沒有被標記可回收,那麼將其標記可回收
        {
            pElement->currentAction->retain();//引用計數加1,保證當前的動作能完整的被執行完
            pElement->currentActionSalvaged = true;//標記為可回收
        }

        ccArrayRemoveAllObjects(pElement->actions);//將該動作從動作管理器中移除掉
        if (m_pCurrentTarget == pElement)//如果待移除的動作與當前正在執行的動作相等
        {
            m_bCurrentTargetSalvaged = true;//將該正在執行的動作標記為可回收
        }
        else
        {//不相等
            deleteHashElement(pElement);//直接從雜湊表中移除掉
        }
    }
    else
    {
//        CCLOG("cocos2d: removeAllActionsFromTarget: Target not found");
    }
}

void CCActionManager::removeAction(CCAction *pAction)
{
    // explicit null handling
    if (pAction == NULL)
    {
        return;
    }

    tHashElement *pElement = NULL;
    CCObject *pTarget = pAction->getOriginalTarget();//獲取原始的動作執行節點
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);//在雜湊表中查詢該節點的雜湊項
    if (pElement)//找到
    {
        unsigned int i = ccArrayGetIndexOfObject(pElement->actions, pAction);//獲取該動作在動作管理中的索引值
        if (UINT_MAX != i)//如果是有效的索引值
        {
            removeActionAtIndex(i, pElement);//將該動作從動作管理器中移除掉
        }
    }
    else
    {//沒找到,輸出沒找到日誌
        CCLOG("cocos2d: removeAction: Target not found");
    }
}

void CCActionManager::removeActionByTag(unsigned int tag, CCObject *pTarget)
{
	//引數檢查
    CCAssert((int)tag != kCCActionTagInvalid, "");
    CCAssert(pTarget != NULL, "");

    tHashElement *pElement = NULL;
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);//在雜湊表中查詢該節點的雜湊項

    if (pElement)//找到
    {
        unsigned int limit = pElement->actions->num;
        for (unsigned int i = 0; i < limit; ++i)
        {
            CCAction *pAction = (CCAction*)pElement->actions->arr[i];

            if (pAction->getTag() == (int)tag && pAction->getOriginalTarget() == pTarget)//標記和目標都相等
            {
                removeActionAtIndex(i, pElement);//從動作管理器中移除該動作
                break;
            }
        }
    }
}

// get

CCAction* CCActionManager::getActionByTag(unsigned int tag, CCObject *pTarget)
{
    CCAssert((int)tag != kCCActionTagInvalid, "");

    tHashElement *pElement = NULL;
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);//在雜湊表中查詢該節點的雜湊項

    if (pElement)
    {//找到
        if (pElement->actions != NULL)
        {
            unsigned int limit = pElement->actions->num;
            for (unsigned int i = 0; i < limit; ++i)
            {
                CCAction *pAction = (CCAction*)pElement->actions->arr[i];

                if (pAction->getTag() == (int)tag)//找到標記位相等的動作
                {
                    return pAction;//找到,則返回
                }
            }
        }
        CCLOG("cocos2d : getActionByTag(tag = %d): Action not found", tag);
    }
    else
    {
        // CCLOG("cocos2d : getActionByTag: Target not found");
    }

    return NULL;
}

unsigned int CCActionManager::numberOfRunningActionsInTarget(CCObject *pTarget)
{
    tHashElement *pElement = NULL;
    HASH_FIND_INT(m_pTargets, &pTarget, pElement);//在雜湊表中查詢該節點的雜湊項
    if (pElement)//找到,返回數量
    {
        return pElement->actions ? pElement->actions->num : 0;
    }

    return 0;
}

// main loop
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;
}
既然動作類是被這個管理器所管理的那麼它的實現肯定是相當重要。


(CCActionInterval的子類沒截完)

從上圖可以看出動作類CCAction有三個子類分別是:

1)有限時間的執行的動作(CCFiniteTimeAction)

2)跟隨動作(CCFollow)

3)可變速度動作(CCSpeed)

而其中有限時間的執行的動作又有兩個重要的子類,分別是:

1)瞬時動作(CCActionInstant):持續時間為0,

2)持續動作(CCActionInterval):擁有自己的持續時間

這兩個類衍生很多通用的動作供開發者使用。

接下來是動作類的標頭檔案:

enum {
    //! Default tag
	//動作的預設標記
    kCCActionTagInvalid = -1,
};

/**
 * @addtogroup actions
 * @{
 */

/** 
@brief Base class for CCAction objects.
 */
//所有動作的基類
class CC_DLL CCAction : public CCObject 
{
public:
    /**
     * @js ctor
     */
	//構造
    CCAction(void);
    /**
     * @js NA
     * @lua NA
     */
    //析構
    virtual ~CCAction(void);
    /**
     * @js NA
     * @lua NA
     */
    //描述該動作
    const char* description();
    /**
     * @js NA
     * @lua NA
     */
    //拷貝一個動作
    virtual CCObject* copyWithZone(CCZone *pZone);

    //! return true if the action has finished
    //返回動作已經執行完畢
    virtual bool isDone(void);

    //! called before the action start. It will also set the target.
    //在動作開始前會設定一個執行目標
    virtual void startWithTarget(CCNode *pTarget);

    /** 
    called after the action has finished. It will set the 'target' to nil.
    IMPORTANT: You should never call "[action stop]" manually. Instead, use: "target->stopAction(action);"
    */
    //動手執行完畢後會呼叫它,並將執行目標重置為空
    virtual void stop(void);

    //! called every frame with it's delta time. DON'T override unless you know what you are doing.
    //計算動作執行的進度
    virtual void step(float dt);

    /** 
    called once per frame. time a value between 0 and 1

    For example: 
    - 0 means that the action just started
    - 0.5 means that the action is in the middle
    - 1 means that the action is over
    */
    //動作的實現方法
    virtual void update(float time);
    //獲取執行目標
    inline CCNode* getTarget(void) { return m_pTarget; }
    /** The action will modify the target properties. */
    //設定執行目標
    inline void setTarget(CCNode *pTarget) { m_pTarget = pTarget; }
    //獲取原始的執行目標
    inline CCNode* getOriginalTarget(void) { return m_pOriginalTarget; } 
    /** Set the original target, since target can be nil.
    Is the target that were used to run the action. Unless you are doing something complex, like CCActionManager, you should NOT call this method.
    The target is 'assigned', it is not 'retained'.
    @since v0.8.2
    */
    //設定原始的執行目標
    inline void setOriginalTarget(CCNode *pOriginalTarget) { m_pOriginalTarget = pOriginalTarget; }
    //獲取動作的標記
    inline int getTag(void) { return m_nTag; }
    //設定動作的標記
    inline void setTag(int nTag) { m_nTag = nTag; }

public:
    /** Create an action */
    //建立一個動作
    static CCAction* create();
protected:
    //原始執行目標
    CCNode    *m_pOriginalTarget;
    /** The "target".
    The target will be set with the 'startWithTarget' method.
    When the 'stop' method is called, target will be set to nil.
    The target is 'assigned', it is not 'retained'.
    */
    //執行目標
    CCNode    *m_pTarget;
    /** The action tag. An identifier of the action */
    //動作標記,預設為kCCActionTagInvalid(-1)
    int     m_nTag;
};

/** 
@brief 
 Base class actions that do have a finite time duration.
 Possible actions:
   - An action with a duration of 0 seconds
   - An action with a duration of 35.5 seconds

 Infinite time actions are valid
 */
//有限時間型動作(包括瞬時和持續型的)
class CC_DLL CCFiniteTimeAction : public CCAction
{
public:
    /**
     *  @js ctor
     */
	//構造,內聯實現,初始化執行時間為0,即瞬時執行完畢
    CCFiniteTimeAction()
        : m_fDuration(0)
    {}
    /**
     *  @js NA
     *  @lua NA
     */
    //析構
    virtual ~CCFiniteTimeAction(){}
    //! get duration in seconds of the action
    //獲取執行動作的所需時間(單位:秒)
    inline float getDuration(void) { return m_fDuration; }
    //! set duration in seconds of the action
    //設定執行動作的所需時間(單位:秒)
    inline void setDuration(float duration) { m_fDuration = duration; }

    /** returns a reversed action */
    //返回一個反向動作
    virtual CCFiniteTimeAction* reverse(void);
protected:
    //! duration in seconds
    //動作時長
    float m_fDuration;
};

class CCActionInterval;
class CCRepeatForever;

/** 
 @brief Changes the speed of an action, making it take longer (speed>1)
 or less (speed<1) time.
 Useful to simulate 'slow motion' or 'fast forward' effect.
 @warning This action can't be Sequenceable because it is not an CCIntervalAction
 */
//可變速度的動作
class CC_DLL CCSpeed : public CCAction
{
public:
    /**
     *  @js ctor
     */
	//夠著,內聯實現
    CCSpeed()
        : m_fSpeed(0.0)
        , m_pInnerAction(NULL)
    {}
    /**
     *  @js NA
     *  @lua NA
     */
    //析構
    virtual ~CCSpeed(void);
    //返回當前動作速度
    inline float getSpeed(void) { return m_fSpeed; }
    /** alter the speed of the inner function in runtime */
    //設定當前動作速度
    inline void setSpeed(float fSpeed) { m_fSpeed = fSpeed; }

    /** initializes the action */
    //初始動作
    bool initWithAction(CCActionInterval *pAction, float fSpeed);
    /**
     *  @js NA
     *  @lua NA
     */
    //過載基類CCAtion的方法
    virtual CCObject* copyWithZone(CCZone *pZone);
    virtual void startWithTarget(CCNode* pTarget);
    virtual void stop();
    virtual void step(float dt);
    virtual bool isDone(void);
    //返回一個反向動作
    virtual CCActionInterval* reverse(void);
    //設定一個要控制的動畫(內部動畫)
    void setInnerAction(CCActionInterval *pAction);
    //返回一個要控制的動畫(內部動畫)
    inline CCActionInterval* getInnerAction()
    {
        return m_pInnerAction;
    }

public:
    /** create the action */
    //建立可變速度的動作
    static CCSpeed* create(CCActionInterval* pAction, float fSpeed);
protected:
    //執行速度
    float m_fSpeed;
    //內部動作
    CCActionInterval *m_pInnerAction;
};

/** 
@brief CCFollow is an action that "follows" a node.

Eg:
layer->runAction(CCFollow::actionWithTarget(hero));

Instead of using CCCamera as a "follower", use this action instead.
@since v0.99.2
*/
//跟隨動作
class CC_DLL CCFollow : public CCAction
{
public:
    /**
     *  @js ctor
     */
	//構造,內聯實現
    CCFollow()
        : m_pobFollowedNode(NULL)
        , m_bBoundarySet(false)
        , m_bBoundaryFullyCovered(false)        
        , m_fLeftBoundary(0.0)
        , m_fRightBoundary(0.0)
        , m_fTopBoundary(0.0)
        , m_fBottomBoundary(0.0)
    {}
    /**
     *  @js NA
     *  @lua NA
     */
    //析構
    virtual ~CCFollow(void);
    //獲取是否有設定邊界
    inline bool isBoundarySet(void) { return m_bBoundarySet; }
    /** alter behavior - turn on/off boundary */
    //設定是否有邊界
    inline void setBoudarySet(bool bValue) { m_bBoundarySet = bValue; }

    /** initializes the action with a set boundary */
    //初始化執行目標
    bool initWithTarget(CCNode *pFollowedNode, const CCRect& rect = CCRectZero);
    /**
     *  @js NA
     *  @lua NA
     */
    //過載基類CCAction的方法
    virtual CCObject* copyWithZone(CCZone *pZone);
    virtual void step(float dt);
    virtual bool isDone(void);
    virtual void stop(void);

public:
    /** creates the action with a set boundary,
    It will work with no boundary if @param rect is equal to CCRectZero.
    */
    //建立一個跟隨動作
    static CCFollow* create(CCNode *pFollowedNode, const CCRect& rect = CCRectZero);
protected:
    // node to follow
    //被跟隨的節點
    CCNode *m_pobFollowedNode;

    // whether camera should be limited to certain area
    //是否現在攝像頭的可見區域內
    bool m_bBoundarySet;

    // if screen size is bigger than the boundary - update not needed
    //邊界是否只是一個點
    bool m_bBoundaryFullyCovered;

    // fast access to the screen dimensions
    //用於快速獲取螢幕此存資訊
    //儲存螢幕一半的大小
    CCPoint m_obHalfScreenSize;
    //儲存螢幕全部的大小
    CCPoint m_obFullScreenSize;

    // world boundaries
    //邊界,左、右、上、下
    float m_fLeftBoundary;
    float m_fRightBoundary;
    float m_fTopBoundary;
    float m_fBottomBoundary;
};

實現:
CCAction::CCAction()
:m_pOriginalTarget(NULL)
,m_pTarget(NULL)
,m_nTag(kCCActionTagInvalid)
{
}

CCAction::~CCAction()
{
    CCLOGINFO("cocos2d: deallocing");
}

CCAction* CCAction::create()
{
    CCAction * pRet = new CCAction();//建立一個新動作例項
    pRet->autorelease();//放入記憶體回收池
    return pRet;
}

const char* CCAction::description()
{
    return CCString::createWithFormat("<CCAction | Tag = %d>", m_nTag)->getCString();//返回繫結tag的描述
}

CCObject* CCAction::copyWithZone(CCZone *pZone)
{
    CCZone *pNewZone = NULL;
    CCAction *pRet = NULL;
    if (pZone && pZone->m_pCopyObject)//如果傳進來的引數的物件不為空
    {
        pRet = (CCAction*)(pZone->m_pCopyObject);//直接強轉
    }
    else
    {//傳進來的引數的物件為空
        pRet = new CCAction();//建立一個新的動作
        pNewZone = new CCZone(pRet);//將該動作的指標讓CCZone儲存(這個指標所指向的記憶體空間在這個方法返回前被釋放了,也就是這句話是沒用的)
    }
    //copy member data
    pRet->m_nTag = m_nTag;//設定動作的標記
    CC_SAFE_DELETE(pNewZone);//釋放pNewZone的資源
    return pRet;
}

void CCAction::startWithTarget(CCNode *aTarget)
{
    m_pOriginalTarget = m_pTarget = aTarget;//初始化原始執行目標和執行目標都為aTarget
}

void CCAction::stop()
{
    m_pTarget = NULL;//將執行目標置空,動作結束後會呼叫它
}

bool CCAction::isDone()
{
    return true;//動作執行完畢,直接返回true
}

void CCAction::step(float dt)
{
	//空實現,全部交由子類實現
    CC_UNUSED_PARAM(dt);
    CCLOG("[Action step]. override me");
}

void CCAction::update(float time)
{
	//空實現,全部交由子類實現
    CC_UNUSED_PARAM(time);
    CCLOG("[Action update]. override me");
}

//
// FiniteTimeAction
//

CCFiniteTimeAction *CCFiniteTimeAction::reverse()
{
	//空實現,由持續性動作子類過載
    CCLOG("cocos2d: FiniteTimeAction#reverse: Implement me");
    return NULL;
}

//
// Speed
//
CCSpeed::~CCSpeed()
{
    CC_SAFE_RELEASE(m_pInnerAction);//釋放內部動作的資源
}

CCSpeed* CCSpeed::create(CCActionInterval* pAction, float fSpeed)
{
    CCSpeed *pRet = new CCSpeed();//建立一個例項
    if (pRet && pRet->initWithAction(pAction, fSpeed))//初始化
    {
        pRet->autorelease();//放入記憶體回收池
        return pRet;
    }
    CC_SAFE_DELETE(pRet);//初始化失敗,釋放資源
    return NULL;
}

bool CCSpeed::initWithAction(CCActionInterval *pAction, float fSpeed)
{
    CCAssert(pAction != NULL, "");
    pAction->retain();//引用計數加1,不然在下一幀動作執行時會出錯
    m_pInnerAction = pAction;//設定內部動作
    m_fSpeed = fSpeed;//設定速度
    return true;
}

CCObject *CCSpeed::copyWithZone(CCZone *pZone)
{
	//拷貝,過程跟CCAction類似
    CCZone* pNewZone = NULL;
    CCSpeed* pRet = NULL;
    if(pZone && pZone->m_pCopyObject) //in case of being called at sub class
    {
        pRet = (CCSpeed*)(pZone->m_pCopyObject);
    }
    else
    {
        pRet = new CCSpeed();
        pZone = pNewZone = new CCZone(pRet);
    }
    CCAction::copyWithZone(pZone);

    pRet->initWithAction( (CCActionInterval*)(m_pInnerAction->copy()->autorelease()) , m_fSpeed );
    
    CC_SAFE_DELETE(pNewZone);
    return pRet;
}

void CCSpeed::startWithTarget(CCNode* pTarget)
{
    CCAction::startWithTarget(pTarget);//初始化執行目標
    m_pInnerAction->startWithTarget(pTarget);//內部動作初始化執行目標
}

void CCSpeed::stop()
{
    m_pInnerAction->stop();//停止內部動作
    CCAction::stop();//呼叫父類的停止方法
}

void CCSpeed::step(float dt)
{
    m_pInnerAction->step(dt * m_fSpeed);//內部動作進行計算執行進度
}

bool CCSpeed::isDone()
{
    return m_pInnerAction->isDone();//返回內部動作是否執行完畢
}

CCActionInterval *CCSpeed::reverse()
{
     return (CCActionInterval*)(CCSpeed::create(m_pInnerAction->reverse(), m_fSpeed));//執行內部動作的反向動作,並以相同的速度執行
}

void CCSpeed::setInnerAction(CCActionInterval *pAction)
{
    if (m_pInnerAction != pAction)//如果傳進來的內部動作與當前的內部動作不相等
    {
        CC_SAFE_RELEASE(m_pInnerAction);//釋放掉上一個內部動作
        m_pInnerAction = pAction;//繫結新的內部動作
        CC_SAFE_RETAIN(m_pInnerAction);//新的內部動作的引用計數加1
    }
}

//
// Follow
//
CCFollow::~CCFollow()
{
    CC_SAFE_RELEASE(m_pobFollowedNode);
}

CCFollow* CCFollow::create(CCNode *pFollowedNode, const CCRect& rect/* = CCRectZero*/)
{
    CCFollow *pRet = new CCFollow();//建立一個新的跟隨動作
    if (pRet && pRet->initWithTarget(pFollowedNode, rect))//初始化
    {
        pRet->autorelease();//放入記憶體回收池
        return pRet;
    }
    CC_SAFE_DELETE(pRet);//初始化失敗,釋放資源
    return NULL;
}

bool CCFollow::initWithTarget(CCNode *pFollowedNode, const CCRect& rect/* = CCRectZero*/)
{
    CCAssert(pFollowedNode != NULL, "");
 
    pFollowedNode->retain();//被跟隨的節點引用計數加1
    m_pobFollowedNode = pFollowedNode;//設定跟隨動作
    if (rect.equals(CCRectZero))//如果引數2無效,無邊界
    {
        m_bBoundarySet = false;
    }
    else//引數2有效,有邊界
    {
        m_bBoundarySet = true;
    }
    
    m_bBoundaryFullyCovered = false;

    CCSize winSize = CCDirector::sharedDirector()->getWinSize();//視窗大小
    m_obFullScreenSize = CCPointMake(winSize.width, winSize.height);//繫結全屏的大小
    m_obHalfScreenSize = ccpMult(m_obFullScreenSize, 0.5f);//繫結全屏大小的一半

    if (m_bBoundarySet)//如果有設定邊界
    {
        m_fLeftBoundary = -((rect.origin.x+rect.size.width) - m_obFullScreenSize.x);//計算左邊界
        m_fRightBoundary = -rect.origin.x ;//計算右邊界
        m_fTopBoundary = -rect.origin.y;//計算頂部邊界
        m_fBottomBoundary = -((rect.origin.y+rect.size.height) - m_obFullScreenSize.y);//計算底部邊界

        //假設螢幕的大小是480*320,設定的邊界矩形是(0,0,720,480),那麼計算後的結果是:
        //最左邊是(0 + 720) - 480 = 240,也就是可以向右移動最多240個單位,因為是向右移,所以取負數,即最終的結果是-240,代表最多向右移動240個單位,其餘三個邊界類似

        if(m_fRightBoundary < m_fLeftBoundary)//如果右邊界大於左邊界,即移動過頭了,則將左右邊界都設定為中間位置
        {
            // screen width is larger than world's boundary width
            //set both in the middle of the world
            m_fRightBoundary = m_fLeftBoundary = (m_fLeftBoundary + m_fRightBoundary) / 2;
        }
        if(m_fTopBoundary < m_fBottomBoundary)//如果頂部邊界大於底部邊界,即移動過頭了,則將頂部底部邊界都設定為中間位置
        {
            // screen width is larger than world's boundary width
            //set both in the middle of the world
            m_fTopBoundary = m_fBottomBoundary = (m_fTopBoundary + m_fBottomBoundary) / 2;
        }
        //如果左右邊界位置相等且頂部底部邊界位置相等,則認為邊界矩形就只是一個點。
        if( (m_fTopBoundary == m_fBottomBoundary) && (m_fLeftBoundary == m_fRightBoundary) )
        {
            m_bBoundaryFullyCovered = true;
        }
    }
    
    return true;
}

CCObject *CCFollow::copyWithZone(CCZone *pZone)
{
	//拷貝,過程跟CCAction類似
    CCZone *pNewZone = NULL;
    CCFollow *pRet = NULL;
    if(pZone && pZone->m_pCopyObject) //in case of being called at sub class
    {
        pRet = (CCFollow*)(pZone->m_pCopyObject);
    }
    else
    {
        pRet = new CCFollow();
        pZone = pNewZone = new CCZone(pRet);
    }
    CCAction::copyWithZone(pZone);
    // copy member data
    pRet->m_nTag = m_nTag;
    CC_SAFE_DELETE(pNewZone);
    return pRet;
}

void CCFollow::step(float dt)
{
    CC_UNUSED_PARAM(dt);

    if(m_bBoundarySet)//如果設定類邊界限制
    {
        // whole map fits inside a single screen, no need to modify the position - unless map boundaries are increased
        if(m_bBoundaryFullyCovered)//如果邊界是一個點,則不需改變
            return;
        //計算相對於螢幕中間的點
        CCPoint tempPos = ccpSub( m_obHalfScreenSize, m_pobFollowedNode->getPosition());
        //更新目標的座標
        m_pTarget->setPosition(ccp(clampf(tempPos.x, m_fLeftBoundary, m_fRightBoundary), 
                                   clampf(tempPos.y, m_fBottomBoundary, m_fTopBoundary)));
    }
    else
    {
    	//更新目標的座標
        m_pTarget->setPosition(ccpSub(m_obHalfScreenSize, m_pobFollowedNode->getPosition()));
    }
}

bool CCFollow::isDone()
{
    return ( !m_pobFollowedNode->isRunning() );//如果跟隨節點不在執行中,返回true
}

void CCFollow::stop()
{
    m_pTarget = NULL;//執行目標重置為空
    CCAction::stop();//呼叫父類的停止動作方法
}
實現都較為簡單。

那麼接下來便分析幾個常見的動作的具體實現。

每個cocos2d-x的開發者都很清楚,如果某個節點要執行某個動作都是呼叫節點的runAction方法來執行動作的。所以節點肯定是封裝了常用的動作的操作,隱藏了動作類的真正細節,其思路跟定時器的思路一模一樣:

首先在構造中每個節點都會從導演中獲取動作管理器供自己管理動作。

CCNode::CCNode(void)...
{
    // set default scheduler and actionManager
    CCDirector *director = CCDirector::sharedDirector();
    m_pActionManager = director->getActionManager();
    m_pActionManager->retain();
    ...
}
然後再通過這個管理器間接的呼叫動作的各個方法:
void CCNode::setActionManager(CCActionManager* actionManager)//設定一個新的動作管理器
{
    if( actionManager != m_pActionManager ) {//如果傳進來新的動作管理器與當前的動作管理器不相等
        this->stopAllActions();//先停止所有的動作呼叫
        CC_SAFE_RETAIN(actionManager);//將新的管理器的引用計數加1,防止被回收池回收
        CC_SAFE_RELEASE(m_pActionManager);//刪掉上一個的動作管理器
        m_pActionManager = actionManager;//設定新的動作管理器
    }
}

CCActionManager* CCNode::getActionManager()
{
    return m_pActionManager;//返回當前的動作管理器
}

CCAction * CCNode::runAction(CCAction* action)//執行動作
{
    CCAssert( action != NULL, "Argument must be non-nil");
    m_pActionManager->addAction(action, this, !m_bRunning);//將要執行的動作加到動作管理器中,動作管理器會在合適的時間呼叫它
    return action;//返回執行的動作
}

void CCNode::stopAllActions()
{
    m_pActionManager->removeAllActionsFromTarget(this);//移除掉該節點的所有動作
}

void CCNode::stopAction(CCAction* action)
{
    m_pActionManager->removeAction(action);//從動作管理器中移除該動作
}

void CCNode::stopActionByTag(int tag)
{
    CCAssert( tag != kCCActionTagInvalid, "Invalid tag");
    m_pActionManager->removeActionByTag(tag, this);//根據標記位移除動作
}

CCAction * CCNode::getActionByTag(int tag)
{
    CCAssert( tag != kCCActionTagInvalid, "Invalid tag");
    return m_pActionManager->getActionByTag(tag, this);//根據標記位獲取動作
}

unsigned int CCNode::numberOfRunningActions()
{
    return m_pActionManager->numberOfRunningActionsInTarget(this);//獲取當前節點的正在執行的動作的數量
}
可以看到呼叫runAction其實就是將其加入到動作管理器中,然後動作管理器會自動的在合適的時間去呼叫它。

下面再看兩個擁有大量子類的動作的實現:

1)瞬時動作:

class CC_DLL CCActionInstant : public CCFiniteTimeAction //<NSCopying>
{
public:
    /**
     *  @js ctor
     */
	//構造
    CCActionInstant();
    /**
     *  @js NA
     *  @lua NA
     */
    //析構
    virtual ~CCActionInstant(){}
    // CCAction methods
    /**
     *  @js NA
     *  @lua NA
     */
    //過載父類方法
    virtual CCObject* copyWithZone(CCZone *pZone);
    virtual bool isDone(void);
    virtual void step(float dt);
    virtual void update(float time);
    //CCFiniteTimeAction method
    virtual CCFiniteTimeAction * reverse(void);
};
CCActionInstant::CCActionInstant() {
}

CCObject * CCActionInstant::copyWithZone(CCZone *pZone) {
	//拷貝操作
    CCZone *pNewZone = NULL;
    CCActionInstant *pRet = NULL;

    if (pZone && pZone->m_pCopyObject) {
        pRet = (CCActionInstant*) (pZone->m_pCopyObject);
    } else {
        pRet = new CCActionInstant();
        pZone = pNewZone = new CCZone(pRet);
    }

    CCFiniteTimeAction::copyWithZone(pZone);
    CC_SAFE_DELETE(pNewZone);
    return pRet;
}

bool CCActionInstant::isDone() {
	//是否執行完
    return true;
}

void CCActionInstant::step(float dt) {
    CC_UNUSED_PARAM(dt);
    update(1);//執行排程
}

void CCActionInstant::update(float time) {
	//空實現,子類實現
    CC_UNUSED_PARAM(time);
    // nothing
}

CCFiniteTimeAction * CCActionInstant::reverse() {
	//返回一個複製動作
    return (CCFiniteTimeAction*) (copy()->autorelease());
}
以上是瞬時動作的基本實現,那麼下面便用它的一個子類來分析如何去使用它。
class CC_DLL CCHide : public CCActionInstant//繼承瞬時動作類CCActionInstant
{
public:
    /**
     *  @js ctor
     *  @lua NA
     */
	//構造
    CCHide(){}
    /**
     *  @js NA
     *  @lua NA
     */
    //析構
    virtual ~CCHide(){}
    //super methods
    /**
     *  @lua NA
     */
    //重寫父類方法
    virtual void update(float time);
    virtual CCFiniteTimeAction * reverse(void);
    /**
     *  @js NA
     *  @lua NA
     */
    virtual CCObject* copyWithZone(CCZone *pZone);
public:

    /** Allocates and initializes the action */
    static CCHide * create();//建立一個具體的瞬時動作類
};
CCHide * CCHide::create() 
{
    CCHide *pRet = new CCHide();//建立一個即時隱藏節點的動作

    if (pRet) {
        pRet->autorelease();
    }

    return pRet;
}

void CCHide::update(float time) {
    CC_UNUSED_PARAM(time);
    m_pTarget->setVisible(false);//設定節點為不可見
}

CCFiniteTimeAction *CCHide::reverse() {
	//它的反動作是顯示動作,也就是CCShow,所以之建立了一個CCShow類後直接返回
    return (CCFiniteTimeAction*) (CCShow::create());
}

CCObject* CCHide::copyWithZone(CCZone *pZone) {
	//拷貝
    CCZone *pNewZone = NULL;
    CCHide *pRet = NULL;

    if (pZone && pZone->m_pCopyObject) {
        pRet = (CCHide*) (pZone->m_pCopyObject);
    } else {
        pRet = new CCHide();
        pZone = pNewZone = new CCZone(pRet);
    }

    CCActionInstant::copyWithZone(pZone);
    CC_SAFE_DELETE(pNewZone);
    return pRet;
}
可以看到如果要自定義一個瞬時動作,便可以按照以上的方式來實現,首先要繼承瞬時動作類,然後從寫拷貝方法,使拷貝出來的類確定是自定義的類,然後再update中實現自定義動作真正要執行的邏輯,如果有必要,也可以重寫它的反向動作的實現。

2)持續動作:

class CC_DLL CCActionInterval : public CCFiniteTimeAction
{
public:
    /** how many seconds had elapsed since the actions started to run. */
	//返回記錄了已執行的時間
    inline float getElapsed(void) { return m_elapsed; }

    /** initializes the action */
    //初始化動作
    bool initWithDuration(float d);

    /** returns true if the action has finished */
    //返回是否已執行完畢
    virtual bool isDone(void);
    /**
     *  @js NA
     *  @lua NA
     */
    //重寫父類方法
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void step(float dt);
    virtual void startWithTarget(CCNode *pTarget);
    /** returns a reversed action */
    virtual CCActionInterval* reverse(void);

public:

    /** creates the action */
    static CCActionInterval* create(float d);//建立持續動作

public:
    //extension in CCGridAction 
    //與表格特效相關
    void setAmplitudeRate(float amp);
    float getAmplitudeRate(void);

protected:
    float m_elapsed;//記錄已累計的執行時間
    bool   m_bFirstTick;//是否是第一次運算
};
CCActionInterval* CCActionInterval::create(float d)
{
    CCActionInterval *pAction = new CCActionInterval();//建立新的持續動作類
    pAction->initWithDuration(d);//初始化
    pAction->autorelease();//放入記憶體回收池

    return pAction;
}

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)//如果時長為0,那麼為了防止計算時除數為0,將時長設定一個值
    {
        m_fDuration = FLT_EPSILON;
    }

    m_elapsed = 0;
    m_bFirstTick = true;

    return true;
}

CCObject* CCActionInterval::copyWithZone(CCZone *pZone)
{
	//拷貝操作
    CCZone* pNewZone = NULL;
    CCActionInterval* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCActionInterval*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCActionInterval();
        pZone = pNewZone = new CCZone(pCopy);
    }

    
    CCFiniteTimeAction::copyWithZone(pZone);

    CC_SAFE_DELETE(pNewZone);

    pCopy->initWithDuration(m_fDuration);

    return pCopy;
}

bool CCActionInterval::isDone(void)
{
    return m_elapsed >= m_fDuration;//如果以累積的時間長度大於執行時長,則返回true
}

void CCActionInterval::step(float dt)
{
    if (m_bFirstTick)//如果是第一次執行運算
    {
        m_bFirstTick = false;//非第一次運算
        m_elapsed = 0;//將累計時間置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
                          )
                      )
                 );
}

void CCActionInterval::setAmplitudeRate(float amp)
{
    CC_UNUSED_PARAM(amp);
    // Abstract class needs implementation
    CCAssert(0, "");
}

float CCActionInterval::getAmplitudeRate(void)
{
    // Abstract class needs implementation
    CCAssert(0, "");

    return 0;
}

void CCActionInterval::startWithTarget(CCNode *pTarget)
{
    CCFiniteTimeAction::startWithTarget(pTarget);//設定執行動作的目標節點
    m_elapsed = 0.0f;
    m_bFirstTick = true;
}

CCActionInterval* CCActionInterval::reverse(void)
{
	//空實現,子類實現(有的子類有實現,有的沒有,一般是相對型的動作有實現)
    CCAssert(false, "CCIntervalAction: reverse not implemented.");
    return NULL;
}
以上是持續動作的基本實現,那麼下面便用它的兩個子類來分析如何去使用它。
class CC_DLL CCRotateBy : public CCActionInterval
{
public:
    /** creates the action */
    static CCRotateBy* create(float fDuration, float fDeltaAngle);//建立旋轉動作
    /** initializes the action */
    bool initWithDuration(float fDuration, float fDeltaAngle);//初始化
    
    static CCRotateBy* create(float fDuration, float fDeltaAngleX, float fDeltaAngleY);//建立旋轉動作
    bool initWithDuration(float fDuration, float fDeltaAngleX, float fDeltaAngleY);//初始化
    /**
     *  @js NA
     *  @lua NA
     */
    //重寫父類構造方法
    virtual CCObject* copyWithZone(CCZone* pZone);
    virtual void startWithTarget(CCNode *pTarget);
    virtual void update(float time);
    virtual CCActionInterval* reverse(void);
    
protected:
    float m_fAngleX;//x軸的旋轉角度
    float m_fStartAngleX;//初始的x軸角度
    float m_fAngleY;//y軸的旋轉角度
    float m_fStartAngleY;//初始的y軸角度
};
CCRotateBy* CCRotateBy::create(float fDuration, float fDeltaAngle)
{
    CCRotateBy *pRotateBy = new CCRotateBy();//建立一個新的動作
    pRotateBy->initWithDuration(fDuration, fDeltaAngle);//初始化
    pRotateBy->autorelease();//放入記憶體回收池

    return pRotateBy;
}

bool CCRotateBy::initWithDuration(float fDuration, float fDeltaAngle)
{
    if (CCActionInterval::initWithDuration(fDuration))//呼叫父類的初始化時長
    {
        m_fAngleX = m_fAngleY = fDeltaAngle;//初始化要旋轉的角度
        return true;
    }

    return false;
}

CCRotateBy* CCRotateBy::create(float fDuration, float fDeltaAngleX, float fDeltaAngleY)
{
    CCRotateBy *pRotateBy = new CCRotateBy();//建立一個新的動作
    pRotateBy->initWithDuration(fDuration, fDeltaAngleX, fDeltaAngleY);//初始化
    pRotateBy->autorelease();//放入記憶體回收池
    
    return pRotateBy;
}

bool CCRotateBy::initWithDuration(float fDuration, float fDeltaAngleX, float fDeltaAngleY)
{
    if (CCActionInterval::initWithDuration(fDuration))//呼叫父類的初始化時長
    {
        m_fAngleX = fDeltaAngleX;//初始化要旋轉的x軸角度
        m_fAngleY = fDeltaAngleY;//初始化要旋轉的y軸角度
        return true;//返回執行成功
    }
    //返回執行失敗
    return false;
}

CCObject* CCRotateBy::copyWithZone(CCZone *pZone)
{
	//拷貝操作
    CCZone* pNewZone = NULL;
    CCRotateBy* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        //in case of being called at sub class
        pCopy = (CCRotateBy*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new CCRotateBy();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithDuration(m_fDuration, m_fAngleX, m_fAngleY);

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}

void CCRotateBy::startWithTarget(CCNode *pTarget)
{
    CCActionInterval::startWithTarget(pTarget);//設定目標節點
    m_fStartAngleX = pTarget->getRotationX();//初始化節點的初始x軸角度
    m_fStartAngleY = pTarget->getRotationY();//初始化節點的初始y軸角度
}

void CCRotateBy::update(float time)
{
    // XXX: shall I add % 360
    if (m_pTarget)//如果存在執行節點
    {
        m_pTarget->setRotationX(m_fStartAngleX + m_fAngleX * time);//設定新的x軸角度
        m_pTarget->setRotationY(m_fStartAngleY + m_fAngleY * time);//設定新的y軸角度
    }
}

CCActionInterval* CCRotateBy::reverse(void)
{
    return CCRotateBy::create(m_fDuration, -m_fAngleX, -m_fAngleY);//建立反向動作
}
實現一個持續動作的過程跟瞬時動作的過程類似,只不過這裡在初始化的時候要呼叫父類的initWithDuration方法來設定呼叫時長,這裡也呼叫父類的startWithTarget來繫結待執行的目標節點。

總結:由於動作管理器是受控於定時器,所以當控制定時器和動過的暫停和恢復工作時是統一的一個操作,沒有任何的同步問題。

相關推薦

cocos2d-x動作分析

在cocos2d-x中動作的執行排程是受cocos2d-x的全域性定時器控制的,當初始完導演後便已經啟動用於管理動作的update定時器。 bool CCDirector::init(void) { ... m_pActionManager = new CCAct

cocos2d-x,多個精靈執行同樣的動作

簡而言之:雖然是“同樣”(從效果來說)的動作,但是每個精靈必須run不同的action物件 例如:需要三個精靈同時跑到一個點去,使用動作來完成。 //建立3個精靈 auto s1 = Spr

Cocos2d-xVector&lt;T&gt;容器以及實例介紹

top 宋體 hello 操作符 模板類 log ins bsp main Vector<T> 是Cocos2d-x 3.x推出的列表容器,因此它所能容納的是Ref及子類所創建的對象指針,其中的T是模板,表示能夠放入到容器中的類型,在Cocos2d-x 3.x

cocos2d-x XML解析與數據存儲

lba false 網上 unsigned failed popu new ccm cfile 一不小心就玩了一周的遊戲了。哎。玩的時候時間過得總是這麽快。。。 於是今天決定看一下之前不怎麽非常熟悉的XML;(之前做遊戲時數據的儲存用到過XML,但這塊是還有一個同事在做

linux下開發,解決cocos2d-x編譯出現的一個小問題, undefined reference to symbol &#39;pthread_create@@GLIBC_2.2.5&#39;

water span x86 code bject data- ace 技術分享 inux 解決cocos2d-x中編譯出現的一個小問題 對於cocos2d-x 2.×中編譯中,若頭文件裏引入了#include "cocos-ext.h",在進行C++編譯的時候會遇到例

實例介紹Cocos2d-xBox2D物理引擎:碰撞檢測

函數實現 pda creates pty blank oid rtu and 重構 在Box2D中碰撞事件通過實現b2ContactListener類函數實現,b2ContactListener是Box2D提供的抽象類,它的抽象函數:virtual void BeginC

Cocos2d-XMenu的綜合運用

cond edi ros log 程序 cal coo 項目 綜合 今天將曾經寫的代碼和項目集成到了一個菜單中,能夠通過菜單切換到曾經做的項目 程序的project文件夾 主要代碼分析: LessonMenu.h中實現創建菜單,遍歷菜單通過菜單切換

實例介紹Cocos2d-xBox2D物理引擎:HelloBox2D

pre all align 討論 響應 算法 站點 virtual origin 我們通過一個實例介紹一下。在Cocos2d-x 3.x中使用Box2D物理引擎的開發過程,熟悉這些API的使用。這個實例執行後的場景如圖所看到的,當場景啟動後,玩家能夠觸摸點擊屏幕,每次觸

Cocos2d-X的聲音和音效

循環 volume tid ng- cocos2d pop sin 返回值 source 在玩遊戲時都會有各種遊戲音,如啟動遊戲時會有背景音,勝利或者失敗會有一些音效。在Cocos2d-X中能夠使用CocosDenshion實現各種聲音 在使用CocosDensh

cocos2d-x 選單類

選單相關類包含:選單類和選單項類,選單類圖,從類圖可見Menu類繼承於Layer。   選單項類圖,從圖中可見所有的選單項都是從BaseMenuItem繼承而來的,BaseMenuItem是抽象類,具體使用時是使用它的6個子類。 說明如下:   MenuItem       最基本的選單

Cocos2d-x的4種佈局

Cocos2d-x中一共有4種佈局:ABSOLUTE(絕對佈局)、HORIZONTAL(水平佈局)、VERTICAL(垂直佈局)和RELATIVE(相對佈局)。 注意,只有在絕對佈局內部,元件使用setPosition()方法來指定其位置。另外三種佈局中的元件,setP

卡通渲染Cocos2d-x的實現(描邊與對物體表面顏色的色階化)

卡通渲染Cocos2d-x中的實現 在一些型別的遊戲中,使用卡通渲染能夠將原有模型的一些細節剝離,使原本比較寫實的模型變得卡通化。在這裡,我向大家介紹簡單介紹一下如何在Cocos2d-x中實現卡通渲染。 事實上,卡通渲染具體來說,可以分為兩個部分:描邊與對物體表面顏色的

cocos2d-x解決點選事件上層響應,下層不響應

解決方案是重寫一個Layer,加在上下層中間,即可只響應上層事件,阻斷了下層的事件,設定優先順序,程式碼如下 #ifndef _TouchBlockLayer_H_ #define _TouchBlockLayer_H_ class TouchBlockLayer:

Cocos2d-x 的Sleep 和USleep

                Cocos2d-x跨平臺,但是這個Sleep延時需要區分平臺 (不知道是不是我自己沒有找到),還是

Cocos2d-xVector容器以及例項介紹

Vector<T> 是Cocos2d-x 3.x推出的列表容器,因此它所能容納的是Ref及子類所建立的物件指標,其中的T是模板,表示能夠放入到容器中的型別,在Cocos2d-x 3.x中T表

cocos2d-xbox2d的關節

// // HelloWorldScene.cpp // Box2dDemo // // Created by gaocong on 13-6-24. // Copyright __MyCompanyName__ 2013年. All rights reserved

Cocos2d-x替換動畫(Armature)的節點與粒子

     Cocos2d-x遊戲開發中常用到骨骼動畫Armature,Armature不僅佔用資源不大(相對於幀動畫來說),而且還能新增幀事件,它還自帶有動畫的播放、停止、迴圈事件等。      廢話少說,直入主題。拿到一個骨骼動畫的時候,我們往往會有這樣的需求:在情況A

Cocos2d-x 載入骨骼動畫資源

Cocos Studio是一套基於Cocos2d-x的免費遊戲開發工具集,它能幫助開發者快速建立遊戲資源,將大部分繁瑣的遊戲開發工作使用編輯器來快速製作,進一步幫助遊戲開發者減短開發週期、提高開發效率。 Cocos Studio本身不光只是針對骨骼動畫的編輯而設計的,它還提供了UI、場景和資料等資訊的編

談一談Cocos2d-x的某些“大小”

這裡說的“大小”,包括了以下一些內容: (1).視窗的大小 (2).解析度的大小 (3).影幕的大小 (4).視口的大小 (5).裁剪區域的大小 我們先來看(1),視窗的大小 視窗的大小,即是Windows窗體大小。我們以HelloCpp為例,開啟main.cpp,。找到

cocos2d-x場景間的引數保持和傳遞(方法整理)

有時為了在場景間傳遞引數,象一些狀態資訊比如音樂的AudioID等,整理一下場景間引數傳遞的方式 方式一、使用全域性變數 利用全域性變數的方式對變數進行場景間的傳遞,簡單且粗暴。可以定義一個類專門來存放需要傳遞的引數,在各場景類中只需要include一下就可以直接對全域性變