liangzhimy

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::
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的英文使用文档介绍可从下面网址获得:
 
posted on 2015-04-15 15:57  liangzhimy  阅读(2195)  评论(0编辑  收藏  举报