在游戏中,经常会周期执行一些检测、操作或更新一些数据等,我们称之为调度。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. restful

    根据名称对应 相当于换了别名(很无聊)

  2. mongo 导入json数据

    删除库以及导入库

  3. sqlserverdriver配置方法 jdbc连接sqlserver

    一.下载驱动程序. 下载地址:http://download.microsoft.com/download/8/B/D/8BDABAE2-B6EA-41D4-B903-7916EF3690EF/sql ...

  4. Vector和ArrayList的比较

    今天研究了一下Vector和ArrayList的源码,又加深了对这两个类的理解. List接口下一共实现了三个类:ArrayList,Vector,LinkedList.LinkedList就不多说了 ...

  5. Ubuntu 64位下搭建ADT的种种问题

    我使用的adt版本为 adt-bundle-linux-x86_64-20140702.zip 1. Eclipse启动时提示 adb 无法加载动态链接库 libstdc++.so.6 以及  lib ...

  6. django文件批量上传-简写版

    模板中创建表单 <form method='post' enctype='multipart/form-data' action='/upload/'> <input type='f ...

  7. redis 数据类型详解 以及 redis适用场景场合

    1.  MySql+Memcached架构的问题 实际MySQL是适合进行海量数据存储的,通过Memcached将热点数据加载到cache,加速访问,很多公司都曾经使用过这样的架构,但随着业务数据量的 ...

  8. Drools环境搭建(转)

    Eclipse3.5安装Drools5.2.0.Final插件 到Drools下载页面(现在是http://www.jboss.org/drools/downloads.html) -下载并解压Dro ...

  9. 用Python3实现表达式求值

    一.题目描述 请用 python3 编写一个计算器的控制台程序,支持加减乘除.乘方.括号.小数点,运算符优先级为括号>乘方>乘除>加减,同级别运算按照从左向右的顺序计算. 二.输入描 ...

  10. Java随笔二

    1.常量:final可以设置变量,也可以表示这个变量只能被赋值一次(即可以声明一个空变量,只能赋值一次):可以使用关键字static final设置一个类常量,以供一个类中的多个方法使用. 2.字符串 ...