标签:
上篇文章分析到了定时器的定义,这篇的重点就是定时器是如何运行起来的。
讲定时器的运行,就不得不触及到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