在cocos2d-x游戏的主循环中,只用了三行语句便完成了整个引擎的调度系统,十分优雅。
void CCDirector::drawScene(void)
{
...
//处理定时器的调用
if (! m_bPaused)
{
m_pScheduler->update(m_fDeltaTime);
}
...
}
在导演类中定义了一个全局的定时器,有导演单独控制,也正是这个定时器,维护着整个引擎的调度。
bool CCDirector::init(void)
{
...
// scheduler
m_pScheduler = new CCScheduler();
...
}
然后再通过主循环每帧的去调度它,从而完成调度。
在此之前得先知道外界是如何注册定时器供引擎调度的。在之前的CCNode文章中就已经提及到CCNode包括调用和停止定时器的功能,也就是说真正的入口还是在节点类,由节点去注册,然后由导演去完成每帧的调度。
其中,CCNode握有导演类的全局定时器的指针:
CCNode::CCNode(void)...
{
...
m_pScheduler = director->getScheduler();
m_pScheduler->retain();
...
}
然后再通过这个全局定时器完成相应的注册和停止等工作,也就是说,CCNode只是对真正的定时器类进行了包装,这样便能隐藏定时器的细节。
//设置一个新的定时器
void CCNode::setScheduler(CCScheduler* scheduler)
{
if( scheduler != m_pScheduler ) {//如果是新的定时器
this->unscheduleAllSelectors();//停止旧定时器的所有调度
CC_SAFE_RETAIN(scheduler);//新定时器的引用计数加1
CC_SAFE_RELEASE(m_pScheduler);//释放掉旧定时器的资源
m_pScheduler = scheduler;//设置新定时器
}
}
//获取定时器
CCScheduler* CCNode::getScheduler()
{
//放回当前的定时器
return m_pScheduler;
}
//注册update类型的定时器
void CCNode::scheduleUpdate()
{
scheduleUpdateWithPriority(0);//调用优先级为0的定时器
}
//注册带有优先级的update类型的定时器
void CCNode::scheduleUpdateWithPriority(int priority)
{
m_pScheduler->scheduleUpdateForTarget(this, priority, !m_bRunning);//调用指定优先级的定时器
}
//lua脚本相关,略过
void CCNode::scheduleUpdateWithPriorityLua(int nHandler, int priority)
{
unscheduleUpdate();
m_nUpdateScriptHandler = nHandler;
m_pScheduler->scheduleUpdateForTarget(this, priority, !m_bRunning);
}
//停止当前节点的update定时器调度
void CCNode::unscheduleUpdate()
{
m_pScheduler->unscheduleUpdateForTarget(this);//停止当前节点的update定时器
if (m_nUpdateScriptHandler)//脚本相关,略过
{
CCScriptEngineManager::sharedManager()->getScriptEngine()->removeScriptHandler(m_nUpdateScriptHandler);
m_nUpdateScriptHandler = 0;
}
}
//注册自定义定时器,每帧调用一次
void CCNode::schedule(SEL_SCHEDULE selector)
{
this->schedule(selector, 0.0f, kCCRepeatForever, 0.0f);//无限次调用指定定时器
}
//注册自定义定时器,每隔interval秒调用一次
void CCNode::schedule(SEL_SCHEDULE selector, float interval)
{
this->schedule(selector, interval, kCCRepeatForever, 0.0f);//无限次的每隔interval时间调用指定定时器
}
//注册自定义定时器,每隔interval秒调用一次,重复调度repeat次,并设置延迟时间delay
void CCNode::schedule(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay)
{
//检查参数合法化
CCAssert( selector, "Argument must be non-nil");
CCAssert( interval >=0, "Argument must be positive");
m_pScheduler->scheduleSelector(selector, this, interval , repeat, delay, !m_bRunning);//执行调度
}
//延迟delay调用一次
void CCNode::scheduleOnce(SEL_SCHEDULE selector, float delay)
{
this->schedule(selector, 0.0f, 0, delay);//delay时间后调用一次指定定时器
}
//根据选择器停止定时器
void CCNode::unschedule(SEL_SCHEDULE selector)
{
// explicit nil handling
if (selector == 0)//过滤空引用
return;
m_pScheduler->unscheduleSelector(selector, this);//停止指定的定时器
}
//停止该节点的所有定时器
void CCNode::unscheduleAllSelectors()
{
m_pScheduler->unscheduleAllForTarget(this);//停止该节点的所有定时器
}
//恢复该节点的所有定时器和动作
void CCNode::resumeSchedulerAndActions()
{
m_pScheduler->resumeTarget(this);//该节点的定时器重新工作
m_pActionManager->resumeTarget(this);//该节点的动作重新被执行
}
//暂停该节点的所有定时器和动作
void CCNode::pauseSchedulerAndActions()
{
m_pScheduler->pauseTarget(this);//暂停该节点的定时器
m_pActionManager->pauseTarget(this);//暂停该节点的动作
}
//更新函数,一般重写它
void CCNode::update(float fDelta)//一般重写它,在主循环中会自动去调用它
{
if (m_nUpdateScriptHandler)
{
CCScriptEngineManager::sharedManager()->getScriptEngine()->executeSchedule(m_nUpdateScriptHandler, fDelta, this);
}
if (m_pComponentContainer && !m_pComponentContainer->isEmpty())
{
m_pComponentContainer->visit(fDelta);
}
}
可见经过封装后的定时器在节点中使用十分简洁清晰明了。
接下分析真正的定时器类:CCSchedule。
在cocos2d-x中有两种定时器:
1)update定时器:每帧都会被触发,使用scheduleUpdate方法来启动。
2)schedule定时器:根据选择器来自定义的定时器,可以设置触发时间间隔,延迟,重复次数等,使用schedule的相关方法来启动。
其相关的属性和方法如下:
class CC_DLL CCScheduler : public CCObject
{
public:
//构造
CCScheduler();
/**
* @js NA
* @lua NA
*/
//析构
~CCScheduler(void);
//返回时间刻度
inline float getTimeScale(void) { return m_fTimeScale; }
/** Modifies the time of all scheduled callbacks.
You can use this property to create a 'slow motion' or 'fast forward' effect.
Default is 1.0. To create a 'slow motion' effect, use values below 1.0.
To create a 'fast forward' effect, use values higher than 1.0.
@since v0.8
@warning It will affect EVERY scheduled selector / action.
*/
//设置时间刻度,默认为1.0,设置它,可以改变定时器的调度速度
inline void setTimeScale(float fTimeScale) { m_fTimeScale = fTimeScale; }
/** 'update' the scheduler.
* You should NEVER call this method, unless you know what you are doing.
* @js NA
* @lua NA
*/
//update定时器的调度函数
void update(float dt);
/** The scheduled method will be called every 'interval' seconds.
If paused is YES, then it won't be called until it is resumed.
If 'interval' is 0, it will be called every frame, but if so, it's recommended to use 'scheduleUpdateForTarget:' instead.
If the selector is already scheduled, then only the interval parameter will be updated without re-scheduling it again.
repeat let the action be repeated repeat + 1 times, use kCCRepeatForever to let the action run continuously
delay is the amount of time the action will wait before it'll start
@since v0.99.3, repeat and delay added in v1.1
@js NA
@lua NA
*/
//自定义定时器的调度函数
void scheduleSelector(SEL_SCHEDULE pfnSelector, CCObject *pTarget, float fInterval, unsigned int repeat, float delay, bool bPaused);
/** calls scheduleSelector with kCCRepeatForever and a 0 delay
* @js NA
* @lua NA
*/
//自定义定时器的调度函数(repeat = kCCRepeatForever,delay = 0)
void scheduleSelector(SEL_SCHEDULE pfnSelector, CCObject *pTarget, float fInterval, bool bPaused);
/** Schedules the 'update' selector for a given target with a given priority.
The 'update' selector will be called every frame.
The lower the priority, the earlier it is called.
@since v0.99.3
@lua NA
*/
//带有优先级定时器的调度函数
void scheduleUpdateForTarget(CCObject *pTarget, int nPriority, bool bPaused);
/** Unschedule a selector for a given target.
If you want to unschedule the "update", use unscheudleUpdateForTarget.
@since v0.99.3
@lua NA
*/
//根据定时选择器移除定时器的回调
void unscheduleSelector(SEL_SCHEDULE pfnSelector, CCObject *pTarget);
/** Unschedules the update selector for a given target
@since v0.99.3
@lua NA
*/
//根据目标移除定时器的回调
void unscheduleUpdateForTarget(const CCObject *pTarget);
/** Unschedules all selectors for a given target.
This also includes the "update" selector.
@since v0.99.3
@js unscheduleCallbackForTarget
@lua NA
*/
//移除所有带有目标的定时器的回调
void unscheduleAllForTarget(CCObject *pTarget);
/** Unschedules all selectors from all targets.
You should NEVER call this method, unless you know what you are doing.
@since v0.99.3
@js unscheduleAllCallbacks
@lua NA
*/
//移除所有定时器的回调
void unscheduleAll(void);
/** Unschedules all selectors from all targets with a minimum priority.
You should only call this with kCCPriorityNonSystemMin or higher.
@since v2.0.0
@js unscheduleAllCallbacksWithMinPriority
@lua NA
*/
//移除所有小于指定优先级的定时器的回调
void unscheduleAllWithMinPriority(int nMinPriority);
/** The scheduled script callback will be called every 'interval' seconds.
If paused is YES, then it won't be called until it is resumed.
If 'interval' is 0, it will be called every frame.
return schedule script entry ID, used for unscheduleScriptFunc().
@js NA
*/
//脚本相关,略过
unsigned int scheduleScriptFunc(unsigned int nHandler, float fInterval, bool bPaused);
/** Unschedule a script entry.
* @js NA
*/
//脚本相关,略过
void unscheduleScriptEntry(unsigned int uScheduleScriptEntryID);
/** Pauses the target.
All scheduled selectors/update for a given target won't be 'ticked' until the targ