override了以下方法
virtual void mainLoop() override; virtual void setAnimationInterval(double value) override; virtual void startAnimation() override; virtual void stopAnimation() override;
mainLoop()是游戏主循环,通过setAnimationInterval设置主循环每秒的调用次数。 mainLoop()的代码:
void DisplayLinkDirector::mainLoop()
{
    if (_purgeDirectorInNextLoop)
    {
        _purgeDirectorInNextLoop = false;
        purgeDirector();
    }
    else if (! _invalid)
    {
        drawScene();
        // release the objects
        PoolManager::getInstance()->getCurrentPool()->clear();
    }
} 
在drawScene()方法中,会动用scene的visit方法。
// draw the scene
    if (_runningScene)
    {
        _runningScene->visit(_renderer, Mat4::IDENTITY, false);
        _eventDispatcher->dispatchEvent(_eventAfterVisit);
    } 
在visit方法中,如果node不可见,直接返回,然后判断Node是否需要变形。
bool dirty = _transformUpdated || parentTransformUpdated;
    if(dirty)
        _modelViewTransform = this->transform(parentTransform);
    _transformUpdated = false; 
当调用node的setScale()、setPosition()等方法,_transformUpdated为true。 
这就是为什么cocos2d-x中设置了node的属性,node会自动变化。
接着,Node会调用children的visit方法。
if(!_children.empty())
    {
        sortAllChildren();
        // draw children zOrder < 0
        for( ; i < _children.size(); i++ )
        {
            auto node = _children.at(i);
            if ( node && node->_localZOrder < 0 )
                node->visit(renderer, _modelViewTransform, dirty);
            else
                break;
        }
        // self draw
        this->draw(renderer, _modelViewTransform, dirty);
        for(auto it=_children.cbegin()+i; it != _children.cend(); ++it)
            (*it)->visit(renderer, _modelViewTransform, dirty);
    }
    else
    {
        this->draw(renderer, _modelViewTransform, dirty);
    } 
Node里面包含一个ActionManager的成员变量_actionManager用于管理动作。
ActionManger提供addAction等方法来管理动作,调用Node的runAction()方法,实际上是调用ActionManager的addAction把动作放到ActionManger里,并以node为key放到哈希表里。
ActionManager是单例,在Director的init函数里,调用_scheduler->scheduleUpdate():
_actionManager = new ActionManager();
    _scheduler->scheduleUpdate(_actionManager, Scheduler::PRIORITY_SYSTEM, false); 
Scheduler每一帧都会动用ActionManager的update函数。
在ActionManager的update函数里,遍历所有动作,并调用action->step(dt)来设置动作往前一步。
用action->isDone()来判断动作时候执行完,如果执行完,就把动作从ActionManager中移除。
Scheduler用来定时触发回调函数。
Node里面也有一个Scheduler的指针_scheduler,调用Node的schedule等方法其实是调用Scheduler对应的方法,把Node和回调函数添加到Scheduler的链表里。
Scheduler定了了两个优先级,系统优先级和非系统最低优先级。
// Priority level reserved for system services. const int Scheduler::PRIORITY_SYSTEM = INT_MIN; // Minimum priority level for user scheduling. const int Scheduler::PRIORITY_NON_SYSTEM_MIN = PRIORITY_SYSTEM + 1;
在Scheduler的构造函数看到了作者的注释:
// I don‘t expect to have more than 30 functions to all per frame
所有,每一帧不要超过30个定时回调函数。
在Director的drawScene函数中会调用scheduler的update方法。
if (! _paused)
    {
        _scheduler->update(_deltaTime);
        _eventDispatcher->dispatchEvent(_eventAfterUpdate);
    } 
Scheduler的update方法相当于Scheduler的main loop。
Scheduler要处理两种selector函数,一种是默认的update(float dt),另一种是用户自定义的,有时间间隔或者调用次数的selector函数。
Scheduler用链表来存放第一种selector,用哈希表存放第二种selector。
默认的update selector函数每一帧都调用一次,这里主要分析一下用户自定义的selector函数。
下面的结构体用来保存一个自定义的selector
// Hash Element used for "selectors with interval"
typedef struct _hashSelectorEntry
{
    ccArray             *timers;
    void                *target;
    int                 timerIndex;
    Timer               *currentTimer;
    bool                currentTimerSalvaged;
    bool                paused;
    UT_hash_handle      hh;
} tHashTimerEntry; 
void Node::schedule(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay)
{
    CCASSERT( selector, "Argument must be non-nil");
    CCASSERT( interval >=0, "Argument must be positive");
    _scheduler->schedule(selector, this, interval , repeat, delay, !_running);
} 
void Scheduler::schedule(SEL_SCHEDULE selector, Ref *target, float interval, unsigned int repeat, float delay, bool paused)
{
    CCASSERT(target, "Argument target must be non-nullptr");
    tHashTimerEntry *element = nullptr;
    HASH_FIND_PTR(_hashForTimers, &target, element);
    if (! element)
    {
        element = (tHashTimerEntry *)calloc(sizeof(*element), 1);
        element->target = target;
        HASH_ADD_PTR(_hashForTimers, target, element);
        // Is this the 1st element ? Then set the pause level to all the selectors of this target
        element->paused = paused;
    }
    else
    {
        CCASSERT(element->paused == paused, "");
    }
    if (element->timers == nullptr)
    {
        element->timers = ccArrayNew(10);
    }
    else
    {
        for (int i = 0; i < element->timers->num; ++i)
        {
            TimerTargetSelector *timer = static_cast<TimerTargetSelector*>(element->timers->arr[i]);
            if (selector == timer->getSelector())
            {
                CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f", timer->getInterval(), interval);
                timer->setInterval(interval);
                return;
            }
        }
        ccArrayEnsureExtraCapacity(element->timers, 1);
    }
    TimerTargetSelector *timer = new TimerTargetSelector();
    timer->initWithSelector(this, selector, target, interval, repeat, delay);
    ccArrayAppendObject(element->timers, timer);
    timer->release();
} 
1、从哈希表中以target为key查找,如果没有占到对应的value,就创建一个tHashTimerEntry对象,然后添加到哈希表中。
2、如果element的timers数组为空,就分配10个空间给timers。跳到4。
3、如果element的timers数组不为空,遍历timer数组,判断是否已经存在,如果selector已存在,跳到5。
4、新建一个TimerTargetSelector对象,添加到timers数组里面。
5、结束。
PS:这里暂时不分析这个TimerTargetSelector类。
这里只关注自定义selector的部分代码。
// main loop
void Scheduler::update(float dt)
{
    // Iterate over all the custom selectors
    for (tHashTimerEntry *elt = _hashForTimers; elt != nullptr; )
    {
        _currentTarget = elt;
        _currentTargetSalvaged = false;
        if (! _currentTarget->paused)
        {
            // The ‘timers‘ array may change while inside this loop
            for (elt->timerIndex = 0; elt->timerIndex < elt->timers->num; ++(elt->timerIndex))
            {
                elt->currentTimer = (Timer*)(elt->timers->arr[elt->timerIndex]);
                elt->currentTimerSalvaged = false;
                elt->currentTimer->update(dt);
                if (elt->currentTimerSalvaged)
                {
                    // The currentTimer told the remove itself. To prevent the timer from
                    // accidentally deallocating itself before finishing its step, we retained
                    // it. Now that step is done, it‘s safe to release it.
                    elt->currentTimer->release();
                }
                elt->currentTimer = nullptr;
            }
        }
        // elt, at this moment, is still valid
        // so it is safe to ask this here (issue #490)
        elt = (tHashTimerEntry *)elt->hh.next;
        // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
        if (_currentTargetSalvaged && _currentTarget->timers->num == 0)
        {
            removeHashElement(_currentTarget);
        }
    }
} 
遍历_hashForTimers哈希表。然后依次调用timer的update方法。在timer的update方法中,会累加dt,如果累加的时间大于之前设置的interval,那么就触发设置的selector方法。
void Timer::update(float dt)
{
// 省略
        if (_runForever && !_useDelay)
        {//standard timer usage
            _elapsed += dt;
            if (_elapsed >= _interval)
            {
                trigger();
                _elapsed = 0;
            }
        }    
// 省略
} 
void TimerTargetSelector::trigger()
{
    if (_target && _selector)
    {
        (_target->*_selector)(_elapsed);
    }
} 
一个自定义schedule的流程就是这样。
Cocos2d-x引擎是单线程的,Director类的mainLoop是游戏的主循环函数,每次循环,都会调用scene的visit函数去显示或者更新界面上的元素。
Scheduler类是Cocos2d-x的调度类,用来定时触发回调函数,回调函数有默认的updateselector函数和用户自定义的selector函数,mainLoop中会调用Scheduler的update函数,然后Scheduler再去调用其他的selector函数。
ActionManager类是管理游戏中全部的动作。ActionManger是依赖Scheduler来进行动作的更新。
Cocos2d-x 3.1 Director ActionManger Scheduler初步分析,布布扣,bubuko.com
Cocos2d-x 3.1 Director ActionManger Scheduler初步分析
原文:http://my.oschina.net/skyhacker2/blog/297919