在游戏中,经常会周期执行一些检测、操作或更新一些数据等,我们称之为调度。Cocos2D-x中将调度封装为类CCScheduler,方便在游戏开发中使用。我们一起来学习一下,CCScheduler具有哪些调度功能。

   从上图可知,CCScheduler直接从基类CCObject继承而来。CCScheduler的调度模式分为两种:一种是update定时器调度,它是按照优先级来进行调度,在Cocos2D-x中优先级分为三类(大于零、小于零和等于零),值越小优先级越高。另一种是按照自定义定时器调度。这种调度方式需要设定一个或多个回调函数,当定时器计时到达时,调用回调函数。
 
   CCScheduler的内部结构和功能如下:
   ·CCScheduler属性:
     //用于update定时器调度
   float m_fTimeScale:时间刻度(默认1.0f),作用是调整计时器的快慢
   struct _listEntry *m_pUpdatesNegList:优先级小于0的链表,保存调度对象
   struct _listEntry *m_pUpdates0List:优先级等于0的链表,保存调度对象
   struct _listEntry *m_pUpdatesPosList: 优先级大于0的链表,保存调度对象
   struct _hashUpdateEntry *m_pHashForUpdates:该哈希表不用于调度,只用于快速查找调度对象所在的链表
 
     //用于自定义定时器调度
   struct _hashSelectorEntry *m_pHashForSelectors:该链表用于自定义定时器调度对象容器
   struct _hashSelectorEntry *m_pCurrentTarget:该链表用于处理当前调度对象
   bool m_bCurrentTargetSalvaged:当前调度对象状态标志
   bool m_bUpdateHashLocked:调度对象更新标志
   CCArray* m_pScriptHandlerEntries:脚本调度
   ·CCScheduler方法:
     void update(float dt):调度函数
     void scheduleSelector(SEL_SCHEDULE pfnSelector, CCObject *pTarget, float fInterval, bool bPaused, unsigned int repeat, float delay):自定义调度选择器(回调函数)
     void scheduleSelector(SEL_SCHEDULE pfnSelector, CCObject *pTarget, float fInterval, bool bPaused):自定义调度选择器(自定义调度选择器(回调函数))
     void pauseTarget(CCObject *pTarget):暂停调度
     void resumeTarget(CCObject *pTarget):恢复调度
    
   以上就是CCScheduler的主要属性和方法。CCScheduler内部处理机制涉及一些编程技巧(如使用哈希表快速查找调度对象),有兴趣专研的同学也许可以从中提升编程技能。由于我们重点关注CCScheduler的功能和使用,所以不详细讲解它的处理机制。
   下面就具体讲解一下两种调试模式的使用和流程:
   ·update定时器调度:
   如果对前面介绍的概念类比较熟悉的同学,一定发现在CCDirector和CCNode的属性中都有一个CCScheduler指针。
我们看一下CCScheduler对象的创建过程:
   bool CCDirector::init(void) //CCDirector初始化函数
   {
       ...
       m_pScheduler = new CCScheduler();//这里创建CCScheduler实例
       ...
       return true;
   }
 
   CCNode::CCNode(void) //CCNode构造函数
   {
       // set default scheduler and actionManager
       CCDirector *director = CCDirector::sharedDirector();
       m_pActionManager = director->getActionManager();
       m_pActionManager->retain();
       m_pScheduler = director->getScheduler(); //引用在CCDirector中创建的CCScheduler实例
       m_pScheduler->retain(); //CCScheduler引用数加1
   }
   CCScheduler对象原来是在CCDirector的初始化过程中创建的,CCNode引用了该对象。这样,只要是CCNode继承类都能引用到CCScheduler对象。
   update定时器调度其实是调用CCScheduler的update方法实现的。这个方法又是在哪里调用的呢?看代码,如下:
   void CCDirector::drawScene(void)
   {
       // calculate "global" dt
       calculateDeltaTime();
       //tick before glClear: issue #533
       if (! m_bPaused)
       {
         m_pScheduler->update(m_fDeltaTime);
       }
       ...
   }
   它是在CCDirector绘制场景时调用的。有人可能继续追问,drawScene方法又是在哪里调用的。当然是在游戏的主循环里了。
   int CCApplication::run()
   {
    ...
    //主消息循环
    while (1)
    {
        if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            QueryPerformanceCounter(&nNow);
            if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)
            {
                nLast.QuadPart = nNow.QuadPart;
                CCDirector::sharedDirector()->mainLoop();//游戏主循环
            }
            else
            {
                Sleep(0);
            }
            continue;
        }
 
        ...
    }
    return (int) msg.wParam;
    }
   如果有人还问我CCApplication的run又是在哪里调用,我只能无语了,建议回过头再看一看介绍Cocos2D-x基本框架这一节。
   言归正传,现在CCScheduler对象有了,update也可以周期调用了,万事俱备就等着添加想要调度对象。
   那如何添加呢?不用担心,在CCNode里已经给大家准备好了。如下:
   void scheduleUpdate(void)://调度优先级默认为0
   void scheduleUpdateWithPriority(int priority):设置优先级可设置
   void CCNode::unscheduleUpdate():从调度对象中取消调度自己
   所以,被调度对象想要使用CCScheduler调度就很简单了。首先,它需要继承自CCNode,然后就是调用scheduleUpdate或scheduleUpdateWithPriority添加自己到CCScheduler调度中。
   这样就完了吗?当然不是,还有一个最重要的问题。被调度对象如何调用自己的调度方法呢?因为说白了,调度最终的目的就是周期执行本对象的调度方法,才能达到调度的目的。
   这个问题在CCScheduler的update方法里已经给出了答案,我们还是一起去看一下吧。
   void CCScheduler::update(float dt)
   {
    ...
    // updates with priority < 0
    DL_FOREACH_SAFE(m_pUpdatesNegList, pEntry, pTmp)
    {
        if ((! pEntry->paused) && (! pEntry->markedForDeletion))
        {
            pEntry->target->update(dt); //调用待调度对象的调度方法
        }
    }
    // updates with priority == 0
    DL_FOREACH_SAFE(m_pUpdates0List, pEntry, pTmp)
    {
        if ((! pEntry->paused) && (! pEntry->markedForDeletion))
        {
            pEntry->target->update(dt); //调用待调度对象的调度方法      
        }
    }
    // updates with priority > 0
    DL_FOREACH_SAFE(m_pUpdatesPosList, pEntry, pTmp)
    {
        if ((! pEntry->paused) && (! pEntry->markedForDeletion))
        {
            pEntry->target->update(dt); //调用待调度对象的调度方法          
        }
    }
    ...
   }
   这段代码比较长,读起来有点累,为大家考虑我将次要代码省略了,直奔主题。原来待调度对象的调度方法也是update。追溯一下这个update方法的来源,发现原来在基类CCObject里就已经有定义了。这就怪不得CCScheduler对象和待调度对象都有update方法。想一想,忽然豁然开朗,整个CCScheduler的调度机制其实很简单,它就是利用所有被调度对象和它都有一个共同的基类CCObject以及update方法。所以,我推测这个update方法放在CCObject里定义,可能主要目的就是为了调度。 
 
   ·自定义定时器调度:
   有了update定时器调度,为什么还需要自定义定时器调度呢?我想原因是update定时器调度只支持单一化调度。举个例子,如果在游戏中有一个节点对象,需要每隔2秒检测一下操作,每隔3秒更新一下后台数据,每隔5秒添加一些道具,这个时候使用update定时器调度就只能完成其中一项调度,而自定义定时器调度支持多定时器调度,可以很轻松和优雅的搞定这一切。
   下面看一看它是如何实现的。在介绍CCScheduler的属性和方法时,有如下两个方法:
  void scheduleSelector(SEL_SCHEDULE pfnSelector, CCObject *pTarget, float fInterval, bool bPaused, unsigned int repeat, float delay):自定义调度选择器(回调函数)

  void scheduleSelector(SEL_SCHEDULE pfnSelector, CCObject *pTarget, float fInterval, bool bPaused):自定义调度选择器(自定义调度选择器(回调函数))
  这两个方法专门用于自定义定时器调度注册函数,它的作用就是添加任意个待调度对象和方法。
  可能上面函数中的参数SEL_SCHEDULE pfnSelector看起来有点怪异,这只是一个函数指针类型重定义,如下:
  typedef void (CCObject::*SEL_SCHEDULE)(float); 
  定义这个函数指针用于指向回调函数,而回调函数在调度定时器间隔时间到达时会被调用。
  值得注意的是,使用调度函数每添加一个自定义定时器调度,将会对应产生一个定时器。
 
  自定义定时器调度同样是调用CCScheduler的update方法。现在纵使update方法的另一部分代码给出,如下:
  void CCScheduler::update(float dt)
  {
    ...
    // Interate all over the custom selectors
    for (tHashSelectorEntry *elt = m_pHashForSelectors; elt != NULL; )
    {
        m_pCurrentTarget = elt;
        m_bCurrentTargetSalvaged = false;
        if (! m_pCurrentTarget->paused)
        {
            //遍历所有调度的定时器
            for (elt->timerIndex = 0; elt->timerIndex < elt->timers->num; ++(elt->timerIndex))
            {
                elt->currentTimer = (CCTimer*)(elt->timers->arr[elt->timerIndex]);
                elt->currentTimerSalvaged = false;
                elt->currentTimer->update(dt); //定时器更新
                if (elt->currentTimerSalvaged)
                {
                    elt->currentTimer->release();
                }
                elt->currentTimer = NULL;
            }
        }
    }
    ...
  }
  //定时器更新函数
  void CCTimer::update(float dt)
  {
    if (m_fElapsed == -1)
    {
        m_fElapsed = 0;
        m_nTimesExecuted = 0;
    }
    else
    {
        if (m_bRunForever && !m_bUseDelay)
        {
            m_fElapsed += dt;  //定时器计时
            if (m_fElapsed >= m_fInterval) //判断定时器间隔时间
            {
                if (m_pTarget && m_pfnSelector)
                {
                    (m_pTarget->*m_pfnSelector)(m_fElapsed);//调用回调函数
                }
                m_fElapsed = 0;//定时器计时清零
                ...
            }
        } 
    ... 
 } 
这段代码的关键点已经注释了,应该很容易明白,就不累述。CCScheduler的自定义调度器使用非常方便,如下:
用户自定义UserClass类,继承于CCNode节点类,然后在其初始化函数onEnter中添加:
schedule(schedule_selector(UserClass::callbackSchedulerFunc1), 10.5f);
schedule(schedule_selector(UserClass::callbackSchedulerFunc2), 10.5f);
UserClass类中分别定义callbackSchedulerFunc1和callbackSchedulerFunc2回调函数,然后在函数实现代码中添加调度代码即可。
     Cocos2D-x封装的CCScheduler调度就介绍到这里,还有更为高级的调度方式(如脚本调度)和使用方法各位有兴趣可以自己研究一下。在TestCpp工程中SchedulerTest测试项有调度的各种使用实例代码,可以参考学习一下。
-------------------------------------------------------------------------------------------------------------------

注:本人在本博客的原创文章采用创作共用版权协议http://creativecommons.org/licenses/by-nc-sa/2.5/cn/), 要求署名、非商业用途和保持一致。要求署名包含注明我的网名及文章来源(我的博客地址:http://www.cnblogs.com/binbingg)。

[原创]cocos2d-x研习录-第三阶 特性之调度器的更多相关文章

  1. [原创]cocos2d-x研习录-第三阶 特性之物理引擎

    游戏物理引擎是指在游戏中涉及物理现象的逻辑处理,它用于模拟现实世界的各种物理规律(如赛车碰撞.子弹飞行.物体掉落等),让玩家能够在游戏中有真实的体验. Cocos2D-x中支持Box2D和Chipmu ...

  2. [原创]cocos2d-x研习录-第三阶 特性之粒子系统

    我想接触过游戏引擎的同学,对粒子系统应该不会陌生.它用于解决由大量按一定规则运动(变化)的微小物质在计算机上的生成和显示问题.粒子系统在游戏中有着非常广泛的应用,可以模拟很多现象,如火花.爆炸.烟雾. ...

  3. [原创]cocos2d-x研习录-第三阶 特性之瓦片地图集

    由于一张大的世界地图或背景图片往往可以由屈指可数的几种地形来表示,每种地形对应于一张小的图片,我们称这些小的地形图片为瓦片.把这些瓦片拼接在一起,组合成一个完整的地图,这就是瓦片地图集的基本原理. C ...

  4. [原创]cocos2d-x研习录-第三阶 特性之动作

    在前面的Cocos2D-x的概念类中,我们了解到节点类CCNode.导演类CCDirector.场景类CCScene.布景层类CCLayer和精灵类CCSprite等,这些类都是构成游戏画面的基本元素 ...

  5. [原创]cocos2d-x研习录-第三阶 特性之按键与虚拟键盘

    Cocos2D-x引擎支持按键事件,它能检测设备的键盘输入并处理相应的事件.而基于不同操作系统的移动设备,可供用户操作的按键数量和功能都存在差异.   Cocos2D-x使用CCKeypadDeleg ...

  6. [原创]cocos2d-x研习录-第三阶 特性之加速度传感器

    智能手机的游戏与应用中,也经常会用到加速传感器事件来丰富用户的体验,比如飞翔的企鹅(英文AirPenguin)游戏就是通过加速度传感器来控制角色的移动和跳跃方向.下面学习Cocos2D-x中如何使用加 ...

  7. [原创]cocos2d-x研习录-第三阶 特性之触屏

    游戏跟视频最大的区别就是互动,而手游(基于智能手机)主要靠触摸屏幕.重力传感和虚拟键盘等方式实现互动.这里主要记录Cocos2D-x对玩家触屏操作的处理. 在Cocos2D-x中触屏分为单点触屏和多点 ...

  8. [原创]cocos2d-x研习录-第三阶 多分辨率适配器

    在移动终端(智能手机)平台下开发游戏一般都会涉及到屏幕多分辨率适配问题,原因是手机款式多种多样,不同的款式存在有不同的尺寸,即使尺寸相同又可能存在不同的分辨率. 手机屏幕尺寸:指手机屏幕对角线长度. ...

  9. [原创]cocos2d-x研习录-第三阶 背景音乐和音效

    在游戏中,音效是一个不可或缺的部分,它可以为我们的游戏增加效果.音效在游戏中一般分为长时间的背景音乐和短促的特效音乐.Cocos2D-x支持多种常见音乐格式(mp3.wav等). Cocos2D-x提 ...

随机推荐

  1. Win Form程序线程点点

    消息循环 Win32窗体程序基于消息驱动的,程序的模型就是一个用户触发事件消息->系统分发事件消息->程序处理事件的循环过程. .NET Win Form程序对消息循环进行了封装,可以看到 ...

  2. B2C电子商务基础系统架构解析(转载)

    系统的开发与演化,前台严格follow消费者的购买流程,后台则盯牢订单流转,牢牢抓住这两条主线,才能高屋建瓴的看清B2C的逻辑链和数据流,更深刻的规划功能模块,从而更有效支撑实际业务的流转. 前台 前 ...

  3. 对客户推荐产品模型+python代码

    首先观看数据: l  数据的基本特征用  describe  描述每个基本特征 l  画图画出每个特征的基本统计图 应用import matplotlib.pylab as pl  画图显示 l  关 ...

  4. C++ Windows进程管理

    功能: 1.各个进程启动.挂起.恢复.停止等 2.监听进程的运行状态,进程退出(正常.非正常)时,通知用户 3.异步队列 4.线程安全 进程管理器类: #ifndef __ProcessManager ...

  5. unix下输出重定向

    > 为重定向符号 >> 重定向不覆盖原文件内容 example: 1. 标准输出重定向 echo "123" > /home/123.txt ---- 标准 ...

  6. pimpl idiom vs. bridge design pattern

    http://stackoverflow.com/questions/2346163/pimpl-idiom-vs-bridge-design-pattern

  7. grep查询文本:问一个简单shell问题,将grep的输出赋值给一个变量

    问一个简单shell问题,将grep的输出赋值给一个变量 用grep命令得到的输出赋值给一个变量不成功. grep命令如下: 代码: $ grep -c '^abc' file.txt 输出为22,表 ...

  8. JAVA 入门编程

    1.输入以及输出 当通过new Scanner(System.in)创建一个Scanner,控制台会一直等待输入,直到敲回车键结束,把所输入的内容传给Scanner,作为扫描对象.如果要获取输入的内容 ...

  9. 【转载】ACM总结

    转自亲学长的总结 声明:本文是写给弱校ACM新手的一点总结,受自身水平和眼界所限,难免会有一些个人主观色彩,希望大牛指正 感谢@Wackysoft .@哇晴天 . @ 一切皆有可能1 的指教,现根据这 ...

  10. cell 内部 设置width 总不对

    今天 在Cell 里设置屏幕宽 如果 在layoutSubviews 使用 self.width(自己写的分类) 或者 self.view.size.width  都可以,这里 4 4s 5 5s 都 ...