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,即可得到一个自动改变鱼方向的智能动作。

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