Cocos2dx-3.2 引擎学习(四)之CCScheduler

先看CCSheduler的头文件:

类的声明:

  1.|Scheduler

  2.|Timer

       |----------2.1 TimerTargetSelector

       |----------2.2 TimerTargetCallback

       |----------2.3 TimerScriptHandler

其中targetSelector,targetCallback,ScriptHandler分别继承自Timer类。

这样继承的好处,在于设计模式,因为可以多态调用父类的方法,具体对象调用自己具体的实现方法~

 

 游戏在CCDirector的drawScene()函数中,设置了Schedule作为一个主循环,调用update()函数

_scheduler->update(_deltaTime);
_eventDispatcher->dispatchEvent(_eventAfterUpdate);

就是这个驱动着引擎的执行。
知道了Scheduler的update函数在每帧都调用后,再仔细看看Scheduler里面的update函数的具体实现。

细节实现使用了双向链表double linked用来存储。

两个重要的数据容器:(存放Scheduler家庭中的两个重要成员:update,selector)

_hashUpdateEntry   存放update每帧刷新相关

_hashSelectorEntry    存放Selector间隔执行相关

 

CCScheduler::update()函数具体实现过程:

首先是执行update的方法:

按优先级来:首先优先级<0的执行,然后是优先级==0的执行,最后是优先级>0的执行

然后调用传统的selector的方法,传统的定时会调用下面贴的代码(Timer::update(float dt)),

判断是否到达间隔时间,决定是否执行的逻辑。当所有scheduler(update,selector两大类的调用结束)调用结束后,

开始回收删除失效的定时器(代码中的注释:// delete all updates that are marked for deletion)

脚本的scheduleScript的函数的执行or回收删除。脚本逻辑代码:(其实不难,也容易看懂意思)

 1 #if CC_ENABLE_SCRIPT_BINDING
 2     //
 3     // Script callbacks
 4     //
 5 
 6     // Iterate over all the script callbacks
 7     if (!_scriptHandlerEntries.empty())
 8     {
 9         for (auto i = _scriptHandlerEntries.size() - 1; i >= 0; i--)
10         {
11             SchedulerScriptHandlerEntry* eachEntry = _scriptHandlerEntries.at(i);
12             if (eachEntry->isMarkedForDeletion())
13             {
14                 _scriptHandlerEntries.erase(i);
15             }
16             else if (!eachEntry->isPaused())
17             {
18                 eachEntry->getTimer()->update(dt);
19             }
20         }
21     }
22 #endif

最后是performFunctionInCocosThread中的_functionsToPerform列表中的函数执行。

由于在cocos线程中,用到了线程资源加锁,解锁的操作,防止,多个线程一次操作引起的错误。(个人理解)

至此,CCScheduler::update这个重大牛逼的函数就执行完了。

 

具体的CCScheduler.cpp的使用:

[经典实现]的地方:

这里就是scheduler具体的逻辑实现的地方。很好懂,但是很经典的代码~

void Timer::update(float dt)
{
    if (_elapsed == -1)
    {
        _elapsed = 0;
        _timesExecuted = 0;
    }
    else
    {
        if (_runForever && !_useDelay)
        {//standard timer usage
            _elapsed += dt;
            if (_elapsed >= _interval)
            {
                trigger();   //达到循环间隔,触发一次回调

                _elapsed = 0;
            }
        }    
        else
        {//advanced usage  //延迟_useDelay秒后执行
            _elapsed += dt;
            if (_useDelay)
            {
                if( _elapsed >= _delay )   
                {
                    //流逝的时间大于延迟时间 开始第一次触发
                    trigger();
                    
                    _elapsed = _elapsed - _delay;
                    _timesExecuted += 1;
                    _useDelay = false;
                }
            }
            else
            {
                if (_elapsed >= _interval)
                {
                    trigger();
                    
                    _elapsed = 0;
                    _timesExecuted += 1;  //触发次数+1

                }
            }
            //当触发次数达到当初约定的循环次数时,unschedule
            if (!_runForever && _timesExecuted > _repeat)
            {    //unschedule timer
                cancel();
            }
        }
    }
}            

 

上面提到了,Scheduler分为2种,(update每帧调用, selector间隔调用)

第一种(update每帧调用):

具体使用:

scheduleUpdate();

void MyClass::update(float dt)

{

    CCLog("call ing..");

}

释放:unscheduleUpdate(target);

 

再看具体的scheduleUpdate源码:

void scheduleUpdate(T *target, int priority, bool paused)
{
  this->schedulePerFrame([target](float dt){
    target->update(dt);
  }, target, priority, paused);
}

所以在一个场景中调用scheduleUpdate的时候,需要override重载update(dt)方法,

这样才可以在上面的函数实现中回调母体target中的update函数。

 

第二种(selector间隔调用):

具体使用:

schedule(schedule_selector(MyClass::checkCollision), target, interval, repeat, delay, paused);

void MyClass::checkCollision(float dt)

{

    CCLog("check collision...");

}

释放:unschedule(_selector, _target);

 

纵观的别人的代码,收益匪浅。数据结构,逻辑,定时器,资源的释放,等等都值得我们好好学习一番。

posted @ 2014-10-14 17:28  Mr轨迹  阅读(910)  评论(0编辑  收藏  举报