本篇,依然是經過閱讀源碼的方式來簡單賞析下Cocos2dx中Action動畫的執行過程。固然,這裏也只是經過這種方式來總結下對Cocos2dx引擎的理解,還遠沒有達到觸類旁通改造現有引擎或開發本身的遊戲引擎的境界。但「千里之行,始於足下」,這點滴的積累都是更進一步的階梯。html
傳送門:
Cocos2dx源碼賞析(1)之啓動流程與主循環
Cocos2dx源碼賞析(2)之渲染
Cocos2dx源碼賞析(3)之事件分發ide
Action是做用於Node節點的全部動做的基類。經過下面這個類繼承層次關係圖,來了解下Action的具體做用:
(以上圖片來自Cocos官網)函數
下面簡述下Action子類的做用:
FiniteTimeAction:
有限時間動做類。包含即時動做和持續動做。動畫
Follow:
跟隨節點的動做。能夠使layer層跟隨一個節點的運動,而該節點絕對位置不變,layer層做爲背景以相反方向移動。this
Speed:
用來線性的改變一個action動做的運行速度。它包裝一個ActionInterval對象,當speed大於1時,動做持續的時間更長;當speed小於1時,動做持續的時間更短。code
ActionInstant:
即時動做。繼承自FiniteTimeAction,顧名思義,繼承自該類的動做是在一幀內執行結束的動做。htm
ActionInterval:
持續動做。繼承自FiniteTimeAction,繼承自該類的動做執行過程會有開始時間和完成事件或者說有必定的持續時間(duration)。固然,它們也能夠正常運行、逆向運行,也能夠變速運行。對象
CallFunc:
用於執行回調的動做。即執行該動做時,會調用做參數傳遞過去的函數。具體的實現有分CallFunc和CallFuncN,CallFunc是執行不帶參數的函數,CallFuncN是執行一個帶Node參數的函數。blog
FlipX和FlipY:
將精靈沿X軸和Y軸翻轉。與設置精靈的FlipX和FlipY屬性相同,包裝成動做是爲了便於與其餘動做進行組合。繼承
Hide和Show:
隱藏和顯示節點。做用與設置節點的visible屬性做用同樣。
Place:
將節點放置到某個指定位置,與設置節點的position屬性相同。
RemoveSelf:
移除節點。與調用節點的removeFromParentAndCleanup方法做用相同。
ReuseGrid:
重複網格動做。和GridAction關聯使用。
StopGrid:
中止網格動做。和GridAction關聯使用。
ToggleVisibility:
切換節點的可視屬性。
CCBSetSpriteFrame:
用座標建立一個位置動做,用於設置Sprite的位置。
CCBSoundEffect:
可用來播放聲音效果。
AccelAmplitude和AccelDeccelAmplitude:
振幅動做。
ActionCamera:
攝像機動做。ActionCamera,不能直接調用,由於ActionCamera沒有重寫update方法,只能使用它的子類OrbitCamera。
ActionEase:
用於實現動做的速度由快到慢、速度隨事件改變的勻速運動。該動做又包含5類運動:
(1)指數緩衝:EaseExponentialIn、EaseExponentialOut、EaseExponentialInOut
(2)Sine緩衝:EaseSineIn、EaseSineOut、EaseSineInOut
(3)彈性緩衝:EaseElasticIn、EaseElasticOut、EaseElasticInOut
(4)跳躍緩衝:EaseBounceIn、EaseBounceOut、EaseBounceInOut
(5)回震緩衝:EaseBackIn、EaseBackOut、EaseBackInOut
其中,每類動做中都有In、Out和InOut三種運動方式:
In表示開始的時候加速
Out表示結束的時候加速
InOut表示開始和結束的時候加速
ActionTween:
補間動做。做用的目標必須繼承ActionTweenDelegate並實現updateTweenAction方法。例如:漸變、縮放、位移、旋轉等改變的。
Animate:
序列幀動畫動做。
BezierBy和BezierTo:
貝塞爾曲線動做。其中By是移動的間隔,To是移動到指定位置。
Blink:
閃爍動做。
CardinalSplineBy和CardinalSplineTo:
曲線路徑動做。參考Cardinal spline wikipedia
DeccelAmplitude:
振幅動做。
DelayTime:
延時動做。只能在複合動做內使用
FadeIn, FadeOut和FateTo:
淡入淡出效果和透明變化效果。即漸變更做。FadeIn的反轉動做(reverse)是FadeOut,FadeOut的反轉動做(reverse)是FadeIn,FateTo不支持反轉動做(reverse)。
GridAction:
網格(grid)動做的基類。
JumpBy和JumpTo:
使節點以必定的軌跡跳躍到指定位置。其中By是移動的間隔,To是移動到指定位置。
MoveBy和MoveTo:
移動動做。使節點作直線運動,設置了動做時間和終點位置,在規定時間內會移動到終點。
ProgressFromTo:
從一個百分比到另外一個百分比的動做。
ProgressTo:
百分比進度。
Repeat和RepeatForever:
重複執行動做。RepeatForever是不停的重複。
ReverseTime:
反轉動做。
RotateBy和RotateTo:
旋轉動做。保證周長相等。
ScaleBy和ScaleTo:
縮放動做。
Sequence:
順序執行一系列動做。
SkewBy和SkewTo:
傾斜動做。保證面積相等。
Spawn:
同時執行多個動畫。
TargetedAction:
給動做指定一個運行的目標上。
TintBy和TintTo:
顏色漸變更做。
CCBRotateXTo、CCBRotateYTo、CCBRotateTo:
旋轉動做。
在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,知足條件的會執行Action的step方法,因爲,幾乎全部的Action動做實例都是繼承自ActionInstant和ActionInterval,因此,這裏直接看這兩個類的step方法的實現。
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); }
能夠發現,在step方法中,會調用Action的update方法來執行具體的更新邏輯。這個update方法會交給Action的實例實現。
當要執行一個Action動做時,通常會調用Node的runAction方法:
Action * Node::runAction(Action* action) { CCASSERT( action != nullptr, "Argument must be non-nil"); _actionManager->addAction(action, this, !_running); return action; }
即這裏會將實際要執行的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動做的執行過程。從源碼的角度來了解了下這種方式的動畫是如何實現的,真正深刻到源碼的每一個細節仍是能學到很多東西的。