爲了追蹤魚遊動的方向,我們可以編寫一個定時器,通過幀的轉換來更新魚的方向,不過這也是一個既煩瑣又難以維護的辦法。參考引擎的做法,我們不妨進一步抽象出獨立的旋轉跟蹤動作,根據精靈的移動路徑設置合適的旋轉角度。
CCAction
包含兩個重要的方法:step
與update
。step
方法會在每一幀動作更新時觸發,該方法接受一個表示調用時間間隔的參數dt
,dt
的積累即爲動作運行的總時間。引擎利用積累時間來計算動作運行的進度(一個從0到1的實數),並調用update
方法更新動作。update
方法是CCAction
的核心,它由step
方法調用,接受一個表示動作進度的參數,每一個動作都需要利用進度值改變目標節點的屬性或執行其他指令。自定義動作只需要從這兩個方法入手即可,我們通常只需要修改update
方法就可以實現簡單的動作。
下面我們編寫一個繼承於CCAction
的CCRotateAction
動作。如同複合動作與變速動作一樣,它會把另一個動作包裝起來,在執行被包裝動作的同時,設置精靈的方向。爲此,我們需要在每一幀記錄上一幀精靈的位置,然後再根據精靈兩幀的位移確定精靈的方向。由於我們必須在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
,即可得到一個自動改變魚方向的智能動作。