Cocos2dx源碼賞析(4)之Action動作

Cocos2dx源碼賞析(4)之Action動作

本篇,依然是通過閱讀源碼的方式來簡單賞析下Cocos2dx中Action動畫的執(zhí)行過程。當(dāng)然,這里也只是通過這種方式來總結(jié)下對Cocos2dx引擎的理解,還遠(yuǎn)沒有達(dá)到舉一反三改造現(xiàn)有引擎或開發(fā)自己的游戲引擎的境界。但“千里之行,始于足下”,這點滴的積累都是更進(jìn)一步的階梯。

傳送門:
Cocos2dx源碼賞析(1)之啟動流程與主循環(huán)
Cocos2dx源碼賞析(2)之渲染
Cocos2dx源碼賞析(3)之事件分發(fā)

1、Action說明

Action是作用于Node節(jié)點的所有動作的基類。通過下面這個類繼承層次關(guān)系圖,來了解下Action的具體作用:


以上圖片來自Cocos官網(wǎng)

(以上圖片來自Cocos官網(wǎng))

下面簡述下Action子類的作用:
FiniteTimeAction:
有限時間動作類。包含即時動作和持續(xù)動作。

Follow:
跟隨節(jié)點的動作。可以使layer層跟隨一個節(jié)點的運(yùn)動,而該節(jié)點絕對位置不變,layer層作為背景以相反方向移動。

Speed:
用來線性的改變一個action動作的運(yùn)行速度。它包裝一個ActionInterval對象,當(dāng)speed大于1時,動作持續(xù)的時間更長;當(dāng)speed小于1時,動作持續(xù)的時間更短。

ActionInstant:
即時動作。繼承自FiniteTimeAction,顧名思義,繼承自該類的動作是在一幀內(nèi)執(zhí)行結(jié)束的動作。

ActionInterval:
持續(xù)動作。繼承自FiniteTimeAction,繼承自該類的動作執(zhí)行過程會有開始時間和完成事件或者說有一定的持續(xù)時間(duration)。當(dāng)然,它們也可以正常運(yùn)行、逆向運(yùn)行,也可以變速運(yùn)行。

CallFunc:
用于執(zhí)行回調(diào)的動作。即執(zhí)行該動作時,會調(diào)用作參數(shù)傳遞過去的函數(shù)。具體的實現(xiàn)有分CallFunc和CallFuncN,CallFunc是執(zhí)行不帶參數(shù)的函數(shù),CallFuncN是執(zhí)行一個帶Node參數(shù)的函數(shù)。

FlipX和FlipY:
將精靈沿X軸和Y軸翻轉(zhuǎn)。與設(shè)置精靈的FlipX和FlipY屬性相同,包裝成動作是為了便于與其他動作進(jìn)行組合。

Hide和Show:
隱藏和顯示節(jié)點。作用與設(shè)置節(jié)點的visible屬性作用一樣。

Place:
將節(jié)點放置到某個指定位置,與設(shè)置節(jié)點的position屬性相同。

RemoveSelf:
移除節(jié)點。與調(diào)用節(jié)點的removeFromParentAndCleanup方法作用相同。

ReuseGrid:
重復(fù)網(wǎng)格動作。和GridAction關(guān)聯(lián)使用。

StopGrid:
停止網(wǎng)格動作。和GridAction關(guān)聯(lián)使用。

ToggleVisibility:
切換節(jié)點的可視屬性。

CCBSetSpriteFrame:
用坐標(biāo)創(chuàng)建一個位置動作,用于設(shè)置Sprite的位置。

CCBSoundEffect:
可用來播放聲音效果。

AccelAmplitude和AccelDeccelAmplitude:
振幅動作。

ActionCamera:
攝像機(jī)動作。ActionCamera,不能直接調(diào)用,因為ActionCamera沒有重寫update方法,只能使用它的子類OrbitCamera。

ActionEase:
用于實現(xiàn)動作的速度由快到慢、速度隨事件改變的勻速運(yùn)動。該動作又包含5類運(yùn)動:
(1)指數(shù)緩沖:EaseExponentialIn、EaseExponentialOut、EaseExponentialInOut
(2)Sine緩沖:EaseSineIn、EaseSineOut、EaseSineInOut
(3)彈性緩沖:EaseElasticIn、EaseElasticOut、EaseElasticInOut
(4)跳躍緩沖:EaseBounceIn、EaseBounceOut、EaseBounceInOut
(5)回震緩沖:EaseBackIn、EaseBackOut、EaseBackInOut
其中,每類動作中都有In、Out和InOut三種運(yùn)動方式:
In表示開始的時候加速
Out表示結(jié)束的時候加速
InOut表示開始和結(jié)束的時候加速

ActionTween:
補(bǔ)間動作。作用的目標(biāo)必須繼承ActionTweenDelegate并實現(xiàn)updateTweenAction方法。例如:漸變、縮放、位移、旋轉(zhuǎn)等改變的。

Animate:
序列幀動畫動作。

BezierBy和BezierTo:
貝塞爾曲線動作。其中By是移動的間隔,To是移動到指定位置。

Blink:
閃爍動作。

CardinalSplineBy和CardinalSplineTo:
曲線路徑動作。參考Cardinal spline wikipedia

DeccelAmplitude:
振幅動作。

DelayTime:
延時動作。只能在復(fù)合動作內(nèi)使用

FadeIn, FadeOut和FateTo:
淡入淡出效果和透明變化效果。即漸變動作。FadeIn的反轉(zhuǎn)動作(reverse)是FadeOut,F(xiàn)adeOut的反轉(zhuǎn)動作(reverse)是FadeIn,F(xiàn)ateTo不支持反轉(zhuǎn)動作(reverse)。

GridAction:
網(wǎng)格(grid)動作的基類。

JumpBy和JumpTo:
使節(jié)點以一定的軌跡跳躍到指定位置。其中By是移動的間隔,To是移動到指定位置。

MoveBy和MoveTo:
移動動作。使節(jié)點做直線運(yùn)動,設(shè)置了動作時間和終點位置,在規(guī)定時間內(nèi)會移動到終點。

ProgressFromTo:
從一個百分比到另一個百分比的動作。

ProgressTo:
百分比進(jìn)度。

Repeat和RepeatForever:
重復(fù)執(zhí)行動作。RepeatForever是不停的重復(fù)。

ReverseTime:
反轉(zhuǎn)動作。

RotateBy和RotateTo:
旋轉(zhuǎn)動作。保證周長相等。

ScaleBy和ScaleTo:
縮放動作。

Sequence:
順序執(zhí)行一系列動作。

SkewBy和SkewTo:
傾斜動作。保證面積相等。

Spawn:
同時執(zhí)行多個動畫。

TargetedAction:
給動作指定一個運(yùn)行的目標(biāo)上。

TintBy和TintTo:
顏色漸變動作。

CCBRotateXTo、CCBRotateYTo、CCBRotateTo:
旋轉(zhuǎn)動作。

2、Action調(diào)用過程

在Cocos2dx中所有的Action動作的管理都是由ActionManager類來管理的。那么,ActionManager的初始化實在CCDirector類中:

bool Director::init(void)
{
    _actionManager = new (std::nothrow) ActionManager();
    _scheduler->scheduleUpdate(_actionManager, Scheduler::PRIORITY_SYSTEM, false);
}

這里,開啟了個定時器,來不停的更新ActionManager的邏輯:

void ActionManager::update(float dt)
{
    for (tHashElement *elt = _targets; elt != nullptr; )
    {
        _currentTarget = elt;
        _currentTargetSalvaged = false;

        if (! _currentTarget->paused)
        {
            for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num;
                _currentTarget->actionIndex++)
            {
                _currentTarget->currentAction = static_cast<Action*>(_currentTarget->actions->arr[_currentTarget->actionIndex]);
                if (_currentTarget->currentAction == nullptr)
                {
                    continue;
                }

                _currentTarget->currentActionSalvaged = false;
                _currentTarget->currentAction->step(dt);

                if (_currentTarget->currentActionSalvaged)
                {
                    _currentTarget->currentAction->release();
                } else
                if (_currentTarget->currentAction->isDone())
                {
                    _currentTarget->currentAction->stop();
                    Action *action = _currentTarget->currentAction;
                    _currentTarget->currentAction = nullptr;
                    removeAction(action);
                }

                _currentTarget->currentAction = nullptr;
            }
        }

        elt = (tHashElement*)(elt->hh.next);

        if (_currentTargetSalvaged && _currentTarget->actions->num == 0)
        {
            deleteHashElement(_currentTarget);
        }

        else if (_currentTarget->target->getReferenceCount() == 1)
        {
            deleteHashElement(_currentTarget);
        }
    }

    _currentTarget = nullptr;
}

這里會遍歷所有的Action,滿足條件的會執(zhí)行Action的step方法,由于,幾乎所有的Action動作實例都是繼承自ActionInstant和ActionInterval,所以,這里直接看這兩個類的step方法的實現(xiàn)。

void ActionInstant::step(float /*dt*/)
{
    float updateDt = 1;
#if CC_ENABLE_SCRIPT_BINDING
    if (_scriptType == kScriptTypeJavascript)
    {
        if (ScriptEngineManager::sendActionEventToJS(this, kActionUpdate, (void *)&updateDt))
            return;
    }
#endif
    update(updateDt);
}

void ActionInterval::step(float dt)
{
    if (_firstTick)
    {
        _firstTick = false;
        _elapsed = 0;
    }
    else
    {
        _elapsed += dt;
    }
    
    
    float updateDt = MAX (0,MIN(1, _elapsed / _duration));

    if (sendUpdateEventToScript(updateDt, this)) return;
    
    this->update(updateDt);
}

可以發(fā)現(xiàn),在step方法中,會調(diào)用Action的update方法來執(zhí)行具體的更新邏輯。這個update方法會交給Action的實例實現(xiàn)。

當(dāng)要執(zhí)行一個Action動作時,一般會調(diào)用Node的runAction方法:

Action * Node::runAction(Action* action)
{
    CCASSERT( action != nullptr, "Argument must be non-nil");
    _actionManager->addAction(action, this, !_running);
    return action;
}

即這里會將實際要執(zhí)行的Action實例添加到ActionManager中:

void ActionManager::addAction(Action *action, Node *target, bool paused)
{
    CCASSERT(action != nullptr, "action can't be nullptr!");
    CCASSERT(target != nullptr, "target can't be nullptr!");
    if(action == nullptr || target == nullptr)
        return;

    tHashElement *element = nullptr;
    Ref *tmp = target;
    HASH_FIND_PTR(_targets, &tmp, element);
    if (! element)
    {
        element = (tHashElement*)calloc(sizeof(*element), 1);
        element->paused = paused;
        target->retain();
        element->target = target;
        HASH_ADD_PTR(_targets, target, element);
    }

     actionAllocWithHashElement(element);
 
     CCASSERT(! ccArrayContainsObject(element->actions, action), "action already be added!");
     ccArrayAppendObject(element->actions, action);
 
     action->startWithTarget(target);
}

以上,簡單梳理了下Cocos2dx的Action動作的執(zhí)行過程。從源碼的角度來了解了下這種方式的動畫是如何實現(xiàn)的,真正深入到源碼的每個細(xì)節(jié)還是能學(xué)到不少東西的。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容