好久没用动笔写博客了,今天是1024节,还是要表示下。而且今天检查邮箱发现了5个垃圾评论,觉得很高兴,也要来篇文章庆祝下。哈哈。
时间隔得太久,我都不记得我讲到那里来了。原来是打算将几个特殊点的Action为大家结尾,最后一看源码,发现有点杂乱,待我整理好之后再写。今天来讲一讲CCScheduler。之前讲过,在ActionManager创建出来之后立马就加入到CCScheduler中去了,然后用ActionManager来控制不同Action的Update。也就是说CCScheduler是Action能工作的保障。我再贴这段代码回顾下:
// scheduler
m_pScheduler = new CCScheduler();
// action manager
m_pActionManager = new CCActionManager();
m_pScheduler->scheduleUpdateForTarget(m_pActionManager, kCCPrioritySystem, false);
而在mainLoop中的drawScene中也只有一个除绘制以外的逻辑,那就是update。
// Draw the Scene
void CCDirector::drawScene(void)
{
// calculate "global" dt
calculateDeltaTime();
//tick before glClear: issue #533
if(!m_bPaused)
{
m_pScheduler->update(m_fDeltaTime);
}
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
......
}
所有的根源都是那个m_pScheduler的update。可见CCScheduler的重要性。CCScheduler的构造函数只是简单的初始化了几个指针变量,没做任何的init逻辑处理。而且它不是单间,虽说cocos2d-x只支持一个scheduler update。需要了解CCScheduler还有一个伴生的概念叫做CCTimer,我们可以叫做定时器。CCScheduler内建了对update函数的支持,如果你要Scheduler自定义的函数就要借助CCTimer了,它就相当于一个适配器。
以上面那个scheduleUpdateForTarget为例,我们看起代码:
void CCScheduler::scheduleUpdateForTarget(CCObject *pTarget, int nPriority, bool bPaused)
{
tHashUpdateEntry *pHashElement = NULL;
HASH_FIND_INT(m_pHashForUpdates, &pTarget, pHashElement);
if (pHashElement)
{
#if COCOS2D_DEBUG >= 1
CCAssert(pHashElement->entry->markedForDeletion,"");
#endif
// TODO: check if priority has changed!
pHashElement->entry->markedForDeletion = false;
return;
}
// most of the updates are going to be 0, that's way there
// is an special list for updates with priority 0
if (nPriority == 0)
{
appendIn(&m_pUpdates0List, pTarget, bPaused);
}
else if (nPriority < 0)
{
priorityIn(&m_pUpdatesNegList, pTarget, nPriority, bPaused);
}
else
{
// priority > 0
priorityIn(&m_pUpdatesPosList, pTarget, nPriority, bPaused);
}
}
前面的检测保障其不能重复添加,但是允许在一帧之间先删除再添加。我们可以发现这部分代码和ActionManager是很像的, 这种标记删除的手法在游戏开发中很常见,如果你要统一管理自己的对象,记得也要采用这种方法,可以避免掉很多问题。与ActionManager不同的是,他内部采用了三个update容器来管理。我们看到
#define kCCPrioritySystem INT_MIN
可见,Action的update的优先级是最高的。从注释上我们可以看出,默认的update的优先级是0,而且没有特殊情况下优先级大于0的容器中是没有元素的。这种设计是必要的,因为有些逻辑比较分散,最后又要统一处理。添加到大于0的优先级中就可以了。
上面可以看到真正添加到update容器中的是priorityIn这个函数,添加到容器的步骤要注意几点,新建一个容器元素,排好优先级,而且有个特殊的地方是它用了一个Hash表来快速的update这个列表中的函数。所以还要添加到Hash表中去。上面就是CCScheduler update的大致流程。我们知道CCScheduler除了update之外,还可以指定自定义的函数,也就是下面这个函数做的:
scheduleSelector(SEL_SCHEDULE pfnSelector,
CCObject *pTarget, float fInterval,
unsigned int repeat, float delay, bool bPaused)
这个函数的参数值简直是丧心病狂,好在CCNode再次封装了一次。这种手法与Action类似,我就不多做介绍了。观其代码:
void CCScheduler::scheduleSelector(SEL_SCHEDULE pfnSelector,CCObject*pTarget,float fInterval,unsigned int repeat,float delay,bool bPaused)
{
CCAssert(pfnSelector,"Argument selector must be non-NULL");
CCAssert(pTarget,"Argument target must be non-NULL");
tHashTimerEntry* pElement=NULL;
HASH_FIND_INT(m_pHashForTimers,&pTarget,pElement);
if(!pElement)
{
pElement=(tHashTimerEntry*)calloc(sizeof(*pElement),1);
pElement->target=pTarget;
if(pTarget)
{
pTarget->retain();
}
HASH_ADD_INT(m_pHashForTimers,target,pElement);
// Is this the 1st element ? Then set the pause level to all the selectors of this target
pElement->paused=bPaused;
}
else
{
CCAssert(pElement->paused==bPaused,"");
}
if(pElement->timers==NULL)
{
pElement->timers=ccArrayNew(10);
}
else
{
for(unsignedinti=0;i<pElement->timers->num;++i)
{
CCTimer*timer=(CCTimer*)pElement->timers->arr[i];
if(pfnSelector==timer->getSelector())
{
CCLOG("CCScheduler#scheduleSelector. Selector already scheduled. Updating interval from: %.4f to %.4f",timer->getInterval(),fInterval);
timer->setInterval(fInterval);
return;
}
}
ccArrayEnsureExtraCapacity(pElement->timers,1);
}
CCTimer* pTimer=newCCTimer();
pTimer->initWithTarget(pTarget,pfnSelector,fInterval,repeat,delay);
ccArrayAppendObject(pElement->timers,pTimer);
pTimer->release();
}
这些操作简直与Action如出一辙,他为每个新加的Target分配了10个定时器的空间,而且允许重复进入(其作用是更新参数)。再看其update函数,可以说这个函数是cocos2d-x的逻辑主体,他首先处理3个update容器中的元素,再处理自定义的timer,最后删除标记为删除的管理对象。代码我就不贴了。其他一些管理逻辑和要注意的问题与ActionManager是类似的,就不再多啰嗦了。大家可以对比来加深印象。