zoukankan      html  css  js  c++  java
  • 4 cocos2dx 3.0 源码分析- scheduler

    scheduler 这个类, 负责了引擎的自定义更新, 及定时更新相关的操作, 看看下面的代码,很熟悉吧。
     
    schedule(schedule_selector(HelloWorld::update), 10);
     
    它是如何工作的呢, 咱还记得前面mainLoop->drawScene() 的调用 吗?里面就有调用Scheduler::update(float dt)
     
    我们用一个例子代入一下, 就拿上面的代码来说, 分2个步:
    1 注册更新调用 
    2 引擎调用用户的更新调用函数。 
     
    1 注册更新
     
    schedule(schedule_selector(HelloWorld::update), 10);
     
    流程: 
     
    1 申明一个tHashTimerEntry *element
    2 查询当前是否有注册过这个target的更新
    3 没有则添加一个element更新元素, 有则不添加了,直接使用这个为当前的更新元素。 
    4 判断当前的更新元素中是否有指定的Timer, 没有则直接添加, 有则不加了, 直接返回。 
    5 把selector, target 封装为timer
    6 把timer 加入到当前element的timers中. 
    7 完成整个的注册流程。 
     
    代码:
    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 (std::nothrow) TimerTargetSelector();
        timer->initWithSelector(this, selector, target, interval, repeat, delay);
        ccArrayAppendObject(element->timers, timer);
        timer->release();
    }
     
    总结一下:
     
    1》Scheudler 内部有一个hashTable, _hashForTimers( UT_hash是一个开源的hashTable数据结构,具体的使用网上可以查到。 ), 存放在着所有的注册。 
    2》这个hashTable的Key = target, Value = 是一个tHashTimerEntry类型的数据结构,tHashTimerEntry这个结构存储着一个timers 数组,
    3》这个timers数组封装并保存了用户传入的target, 及select方法。 
     
    见如下两个关键的数据结构, 还有一个UT_hash_handle 这里不展开了,这是个开源的hashtable 详细看一下下面的参考:  
     
    typedef struct _hashSelectorEntry
    {
        ccArray            *timers;
        void                *target;
        int                timerIndex;
        Timer              *currentTimer;
        bool                currentTimerSalvaged;
        bool                paused;
        UT_hash_handle      hh;
    } tHashTimerEntry;
     
    timers 存放的是Timer 类型的对象,TimerTargetSelector 是Timer的子类,主要存放了selector, 及target 
     
    class CC_DLL Timer : public Ref
    {
    protected:
        Timer();
    public:
        /** get interval in seconds */
        inline float getInterval() const { return _interval; };
        /** set interval in seconds */
        inline void setInterval(float interval) { _interval = interval; };
        
        void setupTimerWithInterval(float seconds, unsigned int repeat, float delay);
        
        virtual void trigger() = 0;
        virtual void cancel() = 0;
        
        /** triggers the timer */
        void update(float dt);
        
    protected:
        
        Scheduler* _scheduler; // weak ref
        float _elapsed;
        bool _runForever;
        bool _useDelay;
        unsigned int _timesExecuted;
        unsigned int _repeat; //0 = once, 1 is 2 x executed
        float _delay;
        float _interval;
    };
     
    
    class CC_DLL TimerTargetSelector : public Timer
    {
    public:
        TimerTargetSelector();
     
        bool initWithSelector(Scheduler* scheduler, SEL_SCHEDULE selector, Ref* target, float seconds, unsigned int repeat, float delay);
        
        inline SEL_SCHEDULE getSelector() const { return _selector; };
        
        virtual void trigger() override;
        virtual void cancel() override;
        
    protected:
        Ref* _target;
        SEL_SCHEDULE _selector;
    };
     
     
    2 引擎调用用户的更新调用函数。 
     
    其实调用还是在mainLoop里调用的, 具体位置在Director::drawScene() 里,之前的章节也介绍过这个位置 。 
    主要是调用了。 
     
    Scheduler::update(dt)
     
    // main loop
    void Scheduler::update(float dt)
    {
        _updateHashLocked = true;
    
        if (_timeScale != 1.0f)
        {
            dt *= _timeScale;
        }
    .. 
    // 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);
            }
        }
    具体操作: 
     
    1>  遍历当前自身内部hashTable, _hashForTimers. 
    2>  得到内部存储的每个tHashTimerEntity, 这个类型内部存储着array 类型的timers, 就是这个timers,存储着我们注册更新的调用。 
    3> 遍历这个每个timer的update(),
    Timer这个类封装了我们在做自定义调用时传来的参数.
    Timer的update() 会判断当前时间差, 是否应该进行调用我们注册的函数。 如果需要则触发调用
     
     
    void TimerTargetSelector::trigger()
    {
        if (_target && _selector)
        {
            (_target->*_selector)(_elapsed);
        }
    }
     
    void Timer::update(float dt)
    {
    .. 
    .. 
      if (_runForever && !_useDelay)
            {//standard timer usage
                _elapsed += dt;
                if (_elapsed >= _interval)
                {
                    trigger();
    
                    _elapsed = 0;
                }
            }   
    }
    参考:
           uthash 哈希Table . 
     
     
      另外,uthash的英文使用文档介绍可从下面网址获得:
     
  • 相关阅读:
    操作系统读书笔记01
    k-mean鸢尾花分类
    利用numpy完成波士顿房价预测任务
    软件过程管理读书笔记01
    软件测试读书笔记01
    数据分析与数据挖掘
    oracle 导出导入操作
    oracle降低高水位操作
    dubbo工程刚初始化报错明明找得到jar包还是报错
    get请求参数中带有url
  • 原文地址:https://www.cnblogs.com/liangzhimy/p/4428829.html
Copyright © 2011-2022 走看看