Cocos2d-x 動作之創建自定義動作

爲了追蹤魚遊動的方向,我們可以編寫一個定時器,通過幀的轉換來更新魚的方向,不過這也是一個既煩瑣又難以維護的辦法。參考引擎的做法,我們不妨進一步抽象出獨立的旋轉跟蹤動作,根據精靈的移動路徑設置合適的旋轉角度。

CCAction包含兩個重要的方法:stepupdatestep方法會在每一幀動作更新時觸發,該方法接受一個表示調用時間間隔的參數dtdt的積累即爲動作運行的總時間。引擎利用積累時間來計算動作運行的進度(一個從0到1的實數),並調用update方法更新動作。update方法是CCAction的核心,它由step方法調用,接受一個表示動作進度的參數,每一個動作都需要利用進度值改變目標節點的屬性或執行其他指令。自定義動作只需要從這兩個方法入手即可,我們通常只需要修改update方法就可以實現簡單的動作。

下面我們編寫一個繼承於CCActionCCRotateAction動作。如同複合動作與變速動作一樣,它會把另一個動作包裝起來,在執行被包裝動作的同時,設置精靈的方向。爲此,我們需要在每一幀記錄上一幀精靈的位置,然後再根據精靈兩幀的位移確定精靈的方向。由於我們必須在CCRotateAction執行的同時運行被包含的目標動作,所以我們需要在step方法中調用目標動作的step方法。下面我們來看CCRotateAction的實現。

“RotateWithAction.h”中的定義如下:

class RotateWithAction : public CCActionInterval
{
public:
    CCObject* copyWithZone(CCZone* pZone);
    ~RotateWithAction();
    static RotateWithAction* create(CCActionInterval * action);
    virtual void startWithTarget(CCNode* pTarget);
    bool initWithAction(CCActionInterval* pAction);
    bool isDone();
    void step(ccTime dt);

protected:
    void RotateWithAction::setInnerAction(CCActionInterval* pAction);

    CCNode* pInnerTarget;
    CCActionInterval* pInnerAction;

};

“RotateWithAction.cpp”中的實現如下:

RotateWithAction::~RotateWithAction()
{
    CC_SAFE_RELEASE(pInnerAction);
}

RotateWithAction* RotateWithAction::create(CCActionInterval* pAction)
{
    RotateWithAction* action = new RotateWithAction();
    if (action && action->initWithAction(pAction))
    {
        action->autorelease();
        return action;
    }
    CC_SAFE_DELETE(action);
    return NULL;
}

bool RotateWithAction::initWithAction(CCActionInterval* pAction)
{
    pAction->retain();
    pInnerAction = pAction;
    return true;
}

void RotateWithAction::startWithTarget(CCNode* pTarget)
{
    pInnerTarget = pTarget;
    CCAction::startWithTarget(pTarget);
    pInnerAction->startWithTarget(pTarget);
}

bool RotateWithAction::isDone()
{
    return pInnerAction->isDone();
}

void RotateWithAction::step(ccTime dt)
{
    CCPoint prePos = pInnerTarget->getPosition();
    pInnerAction->step(dt);
    CCPoint curPos = pInnerTarget->getPosition();

    float tan = -(curPos.y - prePos.y) / (curPos.x - prePos.x);
    float degree = atan(tan);
    degree = degree / 3.14159f * 180;

    pInnerTarget->setRotation(degree);
}

void RotateWithAction::setInnerAction(CCActionInterval* pAction)
{
    if (pInnerAction != pAction)
    {
        CC_SAFE_RELEASE(pInnerAction);
        pInnerAction = pAction;
        CC_SAFE_RETAIN(pInnerAction);
    }
}

CCObject* RotateWithAction::copyWithZone(CCZone* pZone)
{
    CCZone* pNewZone = NULL;
    RotateWithAction* pCopy = NULL;
    if(pZone && pZone->m_pCopyObject) 
    {
        pCopy = (RotateWithAction*)(pZone->m_pCopyObject);
    }
    else
    {
        pCopy = new RotateWithAction();
        pZone = pNewZone = new CCZone(pCopy);
    }

    CCActionInterval::copyWithZone(pZone);

    pCopy->initWithAction(dynamic_cast<CCActionInterval*>
        (pInnerAction->copy()->autorelease()));

    CC_SAFE_DELETE(pNewZone);
    return pCopy;
}

也許有的讀者已經有了疑問,step方法與update方法都可以做到每一幀判斷一次方向,爲什麼選擇重載step方法而不是update方法呢?這是因爲引擎在step方法中對動作對象的內部成員進行了更新,更新後纔會由此方法調用update方法來更新目標節點。在方向追蹤的動作中,我們除了在每一幀判斷方向,還必須同步執行被包裝的動作。這就需要我們調用被包裝動作的step方法,以保證對象能夠被完整地更新。

現在,我們已經不需要使用4.6節介紹的CCSpawn來實現蹩腳的方向追蹤效果了,只要把需要追蹤方向的動作傳遞給CCRotateAction,即可得到一個自動改變魚方向的智能動作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章