先看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);
纵观的别人的代码,收益匪浅。数据结构,逻辑,定时器,资源的释放,等等都值得我们好好学习一番。