zoukankan      html  css  js  c++  java
  • 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);

     

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

  • 相关阅读:
    【Express系列】第3篇——接入mysql
    【Express系列】第2篇——主程序的改造
    【Express系列】第1篇——项目创建
    AngularJS内置指令
    node服务端搭建学习笔记
    生成ssh key
    webstorm的常用操作
    VSCode 常用插件
    php集成包
    composer安装特别慢的解决方案
  • 原文地址:https://www.cnblogs.com/vokie/p/4024878.html
Copyright © 2011-2022 走看看