首先,从Action * Node::runAction(Action* action)看它的实现:
Action * Node::runAction(Action* action) { CCASSERT( action != nullptr,"Argument must be non-nil"); _actionManager->addAction(action,this,!_running); return action; }
然后看到了_actionManager,这个东西是ActionManager,看看他是从哪来的:
//初始化node类的ActionManager,从_director获取过来,得到一个全局的ActionManager _actionManager = _director->getActionManager();
跟到Director中,真相大白:
_actionManager = new (std::nothrow) ActionManager(); //初始化一个ActionManager,初始化成功立即开启scheduler,保证每一帧action可以每一帧更新 _scheduler->scheduleUpdate(_actionManager,Scheduler::PRIORITY_SYSTEM,false);</span></span>
然后接着看_actionManager->addAction(action,!_running);,我们是不是要到ActionManager的addAction中看看呢
void ActionManager::addAction(Action *action,Node *target,bool paused) { CCASSERT(action != nullptr,""); CCASSERT(target != nullptr,""); tHashElement *element = nullptr; // we should convert it to Ref*,because we save it as Ref* Ref *tmp = target; HASH_FIND_PTR(_targets,&tmp,element);//通过target找到对应的哈希表项返回给element if (! element) { //如果找不到。则node对应的哈希表还不存在 则申请内存创建此哈希表项,并将其加入哈希表中。 //创建一个新的哈希表项 element = (tHashElement*)calloc(sizeof(*element),1); element->paused = paused; //设置暂停状态 target->retain(); //引用计数加1代表被管理器使用中 element->target = target; //设置哈希表项中的演员为参数指定的演员 HASH_ADD_PTR(_targets,target,element);//将哈希表项添加到哈希表 } //为哈希表申请内存 用来存放action actionAllocWithHashElement(element); //动作已经在动作列表 中断 //判断pAction是否在pElement的动画集中。确保只放入一次 CCASSERT(! ccArrayContainsObject(element->actions,action),""); ccArrayAppendObject(element->actions,action);//添加到动作列表 action->startWithTarget(target);//动作绑定目标 //设置是哪个CCNode要进行当前动画 }
上面我们说过的,ActionManager在Director的int中会初始化,并且开启一个update调度器。然后在每帧都会调用到ActionManager的update函数。那么现在让我们来看看ActionManager的update函数的实现:
void ActionManager::update(float dt) { //遍历哈希表中所有项 for (tHashElement *elt = _targets; elt != nullptr; ) { //将当前哈希表项保存到变量_currentTarget中。并将此项对应的回收标记m_currentTargetSalvaged 设为false, _currentTarget = elt; _currentTargetSalvaged = false; if (! _currentTarget->paused)//当前动作没有暂停 { //遍历当前动画集中所有的actions for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num; _currentTarget->actionIndex++) { //取得遍历项的动画,即当前action _currentTarget->currentAction = (Action*)_currentTarget->actions->arr[_currentTarget->actionIndex]; if (_currentTarget->currentAction == nullptr) { continue; } //设置回收标志位false _currentTarget->currentActionSalvaged = false; //更新当前播放的动画 _currentTarget->currentAction->step(dt); if (_currentTarget->currentActionSalvaged) { //如果当前的回收标志位true,进行释放处理 _currentTarget->currentAction->release(); } else if (_currentTarget->currentAction->isDone()) { //如果当前动画处于结束状态,则停止动画 _currentTarget->currentAction->stop(); //为了在removeAction中正确释放动画,这里先创建一个临时变量pAction记录一下要释放的动画。 Action *action = _currentTarget->currentAction; _currentTarget->currentAction = nullptr; removeAction(action); } _currentTarget->currentAction = nullptr; } } elt = (tHashElement*)(elt->hh.next);//使for循环能够继续 // 如果当前哈希表项处于回收状态且其动画集为空,删除此哈希表项。 if (_currentTargetSalvaged && _currentTarget->actions->num == 0) { deleteHashElement(_currentTarget); } } _currentTarget = nullptr; }
在ActionManager::update的外层循环中HASH表中所有的Bucket,内层循环遍历当前动画集中所有的actions。执行动作是通过调用action中的step函数(多态调用的),在不同的action中有不同的实现。currentActionSalvaged用来标记动作是否被要求删除。CCAction的stop虚函数是在清理动作之前执行的回调函数(这个不能手动调用,动作执行完成后会自动调用)。如果动作执行完step之后,已经完成了,那么动作就会从HASH表中Bucket的数组上删除。最后,如果某个CCSprite上已经没有附加任何动作,就删除HASH表中这个Bucket。
因为在ActionManager::update中的_currentTarget->currentAction->step(dt)会调用action的step函数,有的action(比如moveBy)没有step方法,会到父类中调用父类的step,看一下他的实现:
void ActionInterval::step(float dt) { if (_firstTick) { _firstTick = false; _elapsed = 0; } else { _elapsed += dt; } this->update(MAX (0,// needed for rewind. elapsed could be negative MIN(1,_elapsed / MAX(_duration,FLT_EPSILON) // division by 0 ) ) ); }
先这样吧,不知道写点什么了。还是没有完全闹明白啊
原文链接:https://www.f2er.com/cocos2dx/341966.html