上篇文章分析到了定时器的定义。这篇的重点就是定时器是怎样执行起来的。

1.从main中寻找定时器的回调

讲定时器的执行,就不得不触及到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();
}

直接调用了run函数,直接进入到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函数中到底运行了什么东西呢?

2.定时器的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;
}

有三个部分值得注意的:

  1. update定时器优先调用
  2. update定时器中优先级越低的越优先调用
  3. 自己定义定时器的回调在elt->currentTimer->update(dt);中运行
关于第三点,我们继续分析这个update函数

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)的更多相关文章

  1. Linux时间子系统之六:高精度定时器(HRTIMER)的原理和实现

    转自:http://blog.csdn.net/droidphone/article/details/8074892 上一篇文章,我介绍了传统的低分辨率定时器的实现原理.而随着内核的不断演进,大牛们已 ...

  2. cocos2d调度器(定时执行某函数)

    调度器(scheduler) 继承关系 原理介绍 Cocos2d-x调度器为游戏提供定时事件和定时调用服务.所有Node对象都知道如何调度和取消调度事件,使用调度器有几个好处: 每当Node不再可见或 ...

  3. 【转】 实现 Cocos2d-x 全局定时器

    转自:http://www.tairan.com/archives/3998 cocos2d-x 中有自己的定时器实现,一般用法是在场景,层等内部实现,定时器的生命周期随着它们的消亡而消亡,就运行周期 ...

  4. mysql的 深度使用 - 游标 , 定时器, 触发器 的使用 ?

    游标 叶叫做 光标; 只能使用在 mysql的 存储过程 或函数中! 游标的概念? 为什么要使用 游标? 什么叫 定时器, 就是事件 event! 是在 mysql 5.0以上的版本中, 才能使用支持 ...

  5. (5)调度器(scheduler)

    继承关系 原理介绍 Cocos2d-x调度器为游戏提供定时事件和定时调用服务.所有Node对象都知道如何调度和取消调度事件,使用调度器有几个好处: 每当Node不再可见或已从场景中移除时,调度器会停止 ...

  6. STM32之通用定时器

    广大的互联网的大家早上中午晚上..又好..没错了..我又来了..写博客不是定时的..为什么我要提写博客不是定时的呢??聪明的人又猜到我要说什么了吧.有前途.其实我还是第一次听到定时器有通用和高级之分的 ...

  7. JavaScript定时器的工作原理(翻译)

    JavaScript定时器的工作原理(翻译) 标签(空格分隔): JavaScript定时器 最近在看ajax原理的时候,看到了一篇国外的文章,讲解了JavaScript定时器的工作原理,帮助我很好的 ...

  8. javascript中的定时器

    本文地址:[http://www.xiabingbao.com/javascript/2015/04/20/javascript-timer/] 在以前的文章[javascript中的定时器]中,简单 ...

  9. Go定时器--Timer

    目录 前言 Timer 定时器 简介 使用场景 1. 设定超时时间 2. 延迟执行某个方法 Timer对外接口 1. 创建定时器 2. 停止定时器 3. 重置定时器 其他接口 1. After() 2 ...

随机推荐

  1. asp.net 中的那些编译错误(1):控件包含代码块(即<% ... %>),因此无法修改控件集合

    在编译页面的时候出现:控件包含代码块(即 <% ... %>),因此无法修改控件集合错误 一般原因是: 在<head runat="server">< ...

  2. ASP.NET页面错误处理

    ASP.NET 提供三种用于在出现错误时捕获和响应错误的主要方法:Page_Error 事件.Application_Error 事件以及应用程序配置文件 (Web.config). 这里主要介绍Ap ...

  3. c#字符串驻留机制

    http://www.cnblogs.com/instance/archive/2011/05/24/2056091.html

  4. App Store审核指南(中文版)2010版

    前言 感谢您付出宝贵的才华与时间来开发iOS应用程程序.从职业与报酬的角度而言,这对于成千上万的开发员来说一直都是一项值得投入的事业.我们希望帮助您加入这个成功的组织.这是我们首次发布<应用程序 ...

  5. ASP.NET菜鸟之路之实现新闻列表增删改

    背景 我是一个ASP.NET菜鸟,暂时开始学习ASP.NET,在此记录下我个人敲的代码,没有多少参考价值,请看到的盆友们为我点个赞支持我一下,多谢了. 网站介绍 根据视频的例子修改的方法,其中数据不经 ...

  6. UVA 10254 - The Priest Mathematician (dp | 汉诺塔 | 找规律 | 大数)

    本文出自   http://blog.csdn.net/shuangde800 题目点击打开链接 题意: 汉诺塔游戏请看 百度百科 正常的汉诺塔游戏是只有3个柱子,并且如果有n个圆盘,至少需要2^n- ...

  7. TCP回射服务器程序:str_echo函数

    str_echo函数执行处理每个客户的服务: 从客户读入数据,并把它们回射给客户 读入缓冲区并回射其中内容: read函数从套接字读入数据,writen函数把其中内容回射给客户 如果客户关闭连接,那么 ...

  8. C# 代码 设置 前台 页面 JS提示

    方法1: Page.ClientScript.RegisterStartupScript(this.GetType(), "", "<script>alert ...

  9. 关于-webkit-tap-highlight-color的一些事儿

    这个属性只用于iOS (iPhone和iPad).当你点击一个链接或者通过Javascript定义的可点击元素的时候,它就会出现一个半透明的灰色背景.要重设这个表现,你可以设置-webkit-tap- ...

  10. Python的XMLRPC机制:实现跨进程间、client/server端通信

    SimpleXMLRPCServer模块式python语言的一个基于 xml 格式的进程间通信的基础框架. SimpleXMLRPCServer是一个单线程的服务器,这意味着,如果几个客户端同时发出多 ...