scheduler 这个类, 负责了引擎的自定义更新, 及定时更新相关的操作, 看看下面的代码,很熟悉吧。
 
schedule(schedule_selector(HelloWorld::update), );
 
它是如何工作的呢, 咱还记得前面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), );
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();
}
else
{
for (int i = ; 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, );
} 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() = ;
virtual void cancel() = ; /** 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 = ; 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 == )
{
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 = ;
}
}
}
参考:
       uthash 哈希Table . 
 
 
  另外,uthash的英文使用文档介绍可从下面网址获得:
 

4 cocos2dx 3.0 源码分析- scheduler的更多相关文章

  1. 3 cocos2dx 3.0 源码分析-mainLoop详细

    简述:   我靠上面图是不是太大了, 有点看不清了.  总结一下过程: 之前说过的appController 之后经过了若干初始化, 最后调用了displayLinker 的定时调用, 这里调用了函数 ...

  2. 5 cocos2dx 3.0源码分析 渲染 render

    渲染,感觉这个挺重要了,这里代入一个简单的例子 Sprite 建立及到最后的画在屏幕上, 我们描述一下这个渲染的流程:   1 sprite 初始化(纹理, 坐标,及当前元素的坐标大小信息) 2 主循 ...

  3. 2 cocos2dx 3.0 源码分析-Director

    Director 导演类, 这个类在整个引擎中担当着最重要的角色, 先看看它是如何初始化的,它共管理了哪些内容呢?    1初始化- 更新处理Scheduler   Scheduler 这个类负责用户 ...

  4. AFNetWorking3.0源码分析

    分析: AFNetWorking(3.0)源码分析(一)——基本框架 AFNetworking源码解析 AFNetworking2.0源码解析<一> end

  5. Solr5.0源码分析-SolrDispatchFilter

    年初,公司开发法律行业的搜索引擎.当时,我作为整个系统的核心成员,选择solr,并在solr根据我们的要求做了相应的二次开发.但是,对solr的还没有进行认真仔细的研究.最近,事情比较清闲,翻翻sol ...

  6. Solr4.8.0源码分析(25)之SolrCloud的Split流程

    Solr4.8.0源码分析(25)之SolrCloud的Split流程(一) 题记:昨天有位网友问我SolrCloud的split的机制是如何的,这个还真不知道,所以今天抽空去看了Split的原理,大 ...

  7. Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五)

    Solr4.8.0源码分析(24)之SolrCloud的Recovery策略(五) 题记:关于SolrCloud的Recovery策略已经写了四篇了,这篇应该是系统介绍Recovery策略的最后一篇了 ...

  8. Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四)

    Solr4.8.0源码分析(23)之SolrCloud的Recovery策略(四) 题记:本来计划的SolrCloud的Recovery策略的文章是3篇的,但是没想到Recovery的内容蛮多的,前面 ...

  9. Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三)

    Solr4.8.0源码分析(22)之SolrCloud的Recovery策略(三) 本文是SolrCloud的Recovery策略系列的第三篇文章,前面两篇主要介绍了Recovery的总体流程,以及P ...

随机推荐

  1. IIS 部署nodejs

    1.Node.js 2.IIS的URL Rewrite模块 3.iisnode

  2. Nodejs 接收RabbitMQ消息

    参考官方地址:https://www.rabbitmq.com/tutorials/tutorial-one-javascript.html 关于C#消息发送端,请参考<c# RabbitMQ ...

  3. Host aggregate分区

    问题描述:生产区一期环境增加4台计算结点,希望被大数据租户独占1. 该大数据用户创建的虚拟机必须被调度到这4个计算结点2. 其他租户创建虚拟机禁止调度到这4台机器 标黄的需要自行根据情况修改解决方案: ...

  4. openvpn部署centos7

    [root@openvpn ~]# cat /etc/redhat-release CentOS Linux release 7.3.1611 (Core) 安装包 yum upgrade yum i ...

  5. Number 和 parseInt 区别

    把字符串 转换成 数字的时候, Number 有点不靠谱, 因为会对 '' 和 null 转换成0, parseInt 相对靠谱些; 判断是否是数值时, isNaN 对于字符串'2'的判断是数字, 对 ...

  6. CSS 笔记——列表表格

    6. 列表表格 -> 列表 (1)list-style 基本语法 list-style : list-style-image || list-style-position || list-sty ...

  7. POJ1716 贪心

    题目大意:在[0,10000]上给出n个区间,要求在区间选整数点,每个区间至少包含两个点,问至少要几个点.题目保证有解决方案. 题目分析: 我们做过在区间上至少包含一个点的题目.类似的方法,我们先排序 ...

  8. [HihoCoder1169]猜单词

    题目大意: 给你一个数列,问区间[l,r]内与k最接近的数与k的差是多少. 思路: 将数列中的数和询问的数先从小到大排序, 从小到大枚举每个数,如果是数列上的,就加到线段树中, 如果是询问中的,就在线 ...

  9. 斐波那契数列(python实现)

    描述 一个斐波那契序列,F(0) = 0, F(1) = 1, F(n) = F(n-1) + F(n-2) (n>=2),根据n的值,计算斐波那契数F(n),其中0≤n≤1000. 输入 输入 ...

  10. bzoj 1833

    f[pos][top] 统计数字个数 g[pos][top][digit] 统计数位(对于f[pos][top]中的所有数) /************************************ ...