标签:
上篇文章分析到了定时器的定义,这篇的重点就是定时器是如何运行起来的。
讲定时器的运行,就不得不触及到cocos2dx的main函数了,因为定时器是主线程上运行的,并不是单独线程的,所以它的调用必然会在main函数中,每帧调用。
以下代码就是win32平台下的main函数
int APIENTRY _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); // create the application instance AppDelegate app; return Application::getInstance()->run(); }
int Application::run() { while(!glview->windowShouldClose()) { QueryPerformanceCounter(&nNow); if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart) { nLast.QuadPart = nNow.QuadPart - (nNow.QuadPart % _animationInterval.QuadPart); director->mainLoop(); //看这里 glview->pollEvents(); } else { Sleep(1); } } return 0; }
run函数中其他不相关的调用我已经去掉了,可以看到mainLoop函数才是真正的主循环
void DisplayLinkDirector::mainLoop() { //做其他不相关的事情 if (! _invalid) { drawScene(); } }看到这里实际上也是调用drawScene函数
void Director::drawScene() { if (! _paused) { _scheduler->update(_deltaTime); _eventDispatcher->dispatchEvent(_eventAfterUpdate); } //之后才进行绘制 }drawScene要做的事情很多,我将绘制部分都去掉了,值得注意的是 绘制场景会在定时器之后才执行。
这里可以看到,实际上执行的就是定时器的update函数,那么这个update函数中究竟执行了什么东西呢?
首先来看看Update的代码
// main loop void Scheduler::update(float dt) { _updateHashLocked = true; if (_timeScale != 1.0f) { dt *= _timeScale; } // // 定时器回调 // tListEntry *entry, *tmp; // Update定时器中优先级小于0的队列先执行 DL_FOREACH_SAFE(_updatesNegList, entry, tmp) { if ((! entry->paused) && (! entry->markedForDeletion)) { entry->callback(dt); } } // 接下来是优先级等于0的 DL_FOREACH_SAFE(_updates0List, entry, tmp) { if ((! entry->paused) && (! entry->markedForDeletion)) { entry->callback(dt); } } // 最后是大于0的 DL_FOREACH_SAFE(_updatesPosList, entry, tmp) { if ((! entry->paused) && (! entry->markedForDeletion)) { entry->callback(dt); } } // 这里循环的是自定义定时器 for (tHashTimerEntry *elt = _hashForTimers; elt != nullptr; ) { _currentTarget = elt; _currentTargetSalvaged = false; if (! _currentTarget->paused) { // 遍历当前对象附属的所有定时器 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) { // 当定时器结束任务了,就应该释放掉 elt->currentTimer->release(); } elt->currentTimer = nullptr; } } // 指向链表的下一对象 elt = (tHashTimerEntry *)elt->hh.next; // 当对象的所有定时器已经执行完成,并且对象附属的定时器为空,则将对象从哈希链表中移除 if (_currentTargetSalvaged && _currentTarget->timers->num == 0) { removeHashElement(_currentTarget); } } // 移除所有标记为删除的优先级小于0的update定时器元素 DL_FOREACH_SAFE(_updatesNegList, entry, tmp) { if (entry->markedForDeletion) { this->removeUpdateFromHash(entry); } } // 移除所有标记为删除的优先级等于0的update定时器元素 DL_FOREACH_SAFE(_updates0List, entry, tmp) { if (entry->markedForDeletion) { this->removeUpdateFromHash(entry); } } // 移除所有标记为删除的优先级大于0的update定时器元素 DL_FOREACH_SAFE(_updatesPosList, entry, tmp) { if (entry->markedForDeletion) { this->removeUpdateFromHash(entry); } } _updateHashLocked = false; _currentTarget = nullptr; }
void Timer::update(float dt) { // 初次执行 会进入到这个if中初始化 if (_elapsed == -1) { _elapsed = 0; //已执行时间 _timesExecuted = 0; //初始化重复次数 } else { if (_runForever && !_useDelay) {//循环延时函数 _elapsed += dt; if (_elapsed >= _interval) { trigger(); //真正的回调 _elapsed = 0; } } else {//advanced usage _elapsed += dt; if (_useDelay) //延时 { if( _elapsed >= _delay ) { trigger(); //真正的回调 _elapsed = _elapsed - _delay; _timesExecuted += 1; _useDelay = false; } } else //每帧调用 { if (_elapsed >= _interval) { trigger(); //真正的回调 _elapsed = 0; _timesExecuted += 1; } } //回调完成,执行取消函数 if (!_runForever && _timesExecuted > _repeat) { //unschedule timer cancel(); } } } }
实际上核心的函数在
trigger(); cancel();第一个函数是真正的回调执行的函数,第二个函数是去掉执行的函数
void TimerTargetSelector::trigger() { if (_target && _selector) { (_target->*_selector)(_elapsed); } } void TimerTargetSelector::cancel() { _scheduler->unschedule(_selector, _target); }
以上就是定时器的实现原理分析的全过程,定时器的实现在文章中我感觉还是说的不是很清楚。真正去代码中自己走一遍应该会更加明了,对以后的应用也会更得心应手。
【深入了解cocos2d-x 3.x】定时器(scheduler)的使用和原理探究(3)
标签:
原文地址:http://blog.csdn.net/nxshow/article/details/46381249