-
cocos2d-x调剂器道理
添加时间:2013-7-1 点击量:法度运行后每达到一帧的时候间隔就会履行一次mainLoop
void CCDisplayLinkDirector::mainLoop(void)
{
//断定是否须要开释CCDirector,凡是游戏停止才会履行这个步调
if (m_bPurgeDirecotorInNextLoop)
{
m_bPurgeDirecotorInNextLoop = false;
purgeDirector();
}
else if (! m_bInvalid)
{
//绘制当前场景并履行其他须要的处理惩罚
drawScene();
//弹出主动收受接管池,使这一帧被放入收受接管池的对象全部履行release
oolManager::sharedPoolManager()->pop();
}
}
那么法度的关键步奏就在这里在drawScene里面了
void CCDirector::drawScene(void)
{
// 策画全局帧间时候差
calculateDeltaTime();
//1. 激发按时器事务
if (! m_bPaused)
{
m_pScheduler->(m_fDeltaTime);
}
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//2. 是否切换场景
if (m_pNextScene)
{
setNextScene();
}
kmGLPushMatrix();
// 3. 绘制当前场景
if (m_pRunningScene)
{
m_pRunningScene->visit();
}
// draw the notifications node处理惩罚通知节点
if (m_pNotificationNode)
{
m_pNotificationNode->visit();
}
if (m_bDisplayStats)
{
showStats();
}
kmGLPopMatrix();
m_uTotalFrames++;
// swap buffers
if (m_pobOpenGLView)
{
m_pobOpenGLView->swapBuffers();
}
if (m_bDisplayStats)
{
calculateMPF();
}
}那么可以看出,在游戏的每一帧,都邑调用CCScheduler的来调剂按时器;然后遍历衬着树,对游戏进行绘制。
调剂器CCScheduler
在游戏中要显示的元素都持续于CCNode类,当持续于CCNode的节点调用schedule()添加一个按时器时,CCNode经由过程导演->getScheduler()获得按时器CCScheduler对象,然后将按时器交给该CCScheduler对象经管。
再来看CCScheduler内,按时器首要分为Update按时器 和 通俗interval按时器。如下CCScheduler 中的首要存储变量。(为进步调剂器效力,应用了链表 和 散列表保存按时器信息。)
//Update按时器
struct _listEntry m_pUpdatesNegList; // list of priority < 0
struct _listEntry m_pUpdates0List; // list priority == 0
struct _listEntry m_pUpdatesPosList; // list priority > 0
struct _hashUpdateEntry m_pHashForUpdates; // hash used to fetch quickly the list entries for pause,,etc
// 通俗interval按时器
struct _hashSelectorEntry m_pHashForTimers;
在主轮回的drawScene函数中调用了CCScheduler::,下面来解析这个函数:
void CCScheduler::(float dt)
{
m_bUpdateHashLocked = true; //¥
//1. 时候差缩放系数 一改变游戏全局速度,可经由过程CCScheduler的TimeScale属性设置
if (m_fTimeScale != 1.0f)
{
dt = m_fTimeScale;
}
//2. 分别列举优先级小于0、便是0、大于0的按时器。若是按时器没有暂停也没有“标识表记标帜为删除”,则触发按时器。
// Iterate over all the Updates ors
tListEntry pEntry, pTmp;
// s with priority < 0
DL_FOREACH_SAFE(m_pUpdatesNegList, pEntry, pTmp)
{
if ((! pEntry->paused) && (! pEntry->markedForDeletion))
{
pEntry->target->(dt);
}
}
// s with priority == 0
DL_FOREACH_SAFE(m_pUpdates0List, pEntry, pTmp)
{
if ((! pEntry->paused) && (! pEntry->markedForDeletion))
{
pEntry->target->(dt);
}
}
// s with priority > 0
DL_FOREACH_SAFE(m_pUpdatesPosList, pEntry, pTmp)
{
if ((! pEntry->paused) && (! pEntry->markedForDeletion))
{
pEntry->target->(dt);
}
}
//3. 1列举所有注册过的通俗interval按时器节点;2在列举该节点的按时器,调用按时器的更新办法,从而决意是否触发该按时器
// Iterate over all the custom ors
for (tHashTimerEntry elt = m_pHashForTimers; elt != NULL; )
{
m_pCurrentTarget = elt;
m_bCurrentTargetSalvaged = false;
if (! m_pCurrentTarget->paused)
{
// The timers array may change while inside this loop
for (elt->timerIndex = 0; elt->timerIndex < elt->timers->num; ++(elt->timerIndex))
{
elt->currentTimer = (CCTimer)(elt->timers->arr[elt->timerIndex]);
elt->currentTimerSalvaged = false;
elt->currentTimer->(dt);
if (elt->currentTimerSalvaged)
{
// The currentTimer told the remove itself. To prevent the timer
// accidentally deallocating itself before finishing its step, we retained
// it. Now that step is done, its safe to release it.
elt->currentTimer->release();
}
elt->currentTimer = NULL;
}
}
// elt, at this moment, is still valid
// so it is safe to ask this here (issue #490)
elt = (tHashTimerEntry )elt->hh.next;
// only currentTarget if no actions were scheduled during the cycle (issue #481)
if (m_bCurrentTargetSalvaged && m_pCurrentTarget->timers->num == 0)
{
removeHashElement(m_pCurrentTarget);
}
}
// 4. 处理惩罚脚本引擎相干事务
// Iterate over all the script callbacks
if (m_pScriptHandlerEntries)
{
for (int i = m_pScriptHandlerEntries->count() - 1; i >= 0; i--)
{
CCSchedulerScriptHandlerEntry pEntry = static_cast<CCSchedulerScriptHandlerEntry>(m_pScriptHandlerEntries->objectAtIndex(i));
if (pEntry->isMarkedForDeletion())
{
m_pScriptHandlerEntries->removeObjectAtIndex(i);
}
else if (!pEntry->isPaused())
{
pEntry->getTimer()->(dt);
}
}
}
// 5. 再次列举Update按时器,删除前面被“标识表记标帜为删除”的按时器
// all s that are marked for deletion
// s with priority < 0
DL_FOREACH_SAFE(m_pUpdatesNegList, pEntry, pTmp)
{
if (pEntry->markedForDeletion)
{
this->removeUpdateFromHash(pEntry);
}
}
// s with priority == 0
DL_FOREACH_SAFE(m_pUpdates0List, pEntry, pTmp)
{
if (pEntry->markedForDeletion)
{
this->removeUpdateFromHash(pEntry);
}
}
// s with priority > 0
DL_FOREACH_SAFE(m_pUpdatesPosList, pEntry, pTmp)
{
if (pEntry->markedForDeletion)
{
this->removeUpdateFromHash(pEntry);
}
}
m_bUpdateHashLocked = false; //¥
m_pCurrentTarget = NULL;
}
对于Update按时器,每个节点只能注册一个按时器,是以调剂器中存储按时器数据的布局体首要保存了注册节点和优先级。每一帧经由过程迭代调用链表中节点的Update函数来实现Update按时器。
对于通俗interval按时器,每个节点能注册多个按时器,引擎应用回调函数(选择器)来区分同一个节点的不合按时器。调剂器为每一个按时器创建了一个CCTimer对象,它记录了按时器的目标、回调函数、触发周期、反复触发等属性。
法度起首列举了每个注册了按时器的对象,然后再列举对象中按时器对应的CCTimer对象,调用CCTimer对象的办法来更新按时器的状况,以便触发按时器事务。(在CCTimer的办法中会把每一次调用时接管的时候间隔dt堆集下来,若是经验的时候达到一次按时触发周期,就会触发对应节点的按时器事务(回调函数)。若是是一次的按时器,就会终止,否者会从头计时,从而反复触发按时事务)
//注:¥ 、“标识表记标帜为删除”: Update按时器三个链表正在迭代过程中,开辟者完全可能在一个按时器事务中停用另一个按时器,若是立即停用,如许会导致Update办法的迭代破损。所以当按时器在迭代时(m_bUpdateHashLocked = true),删除一个节点的Update按时器不会立即删除,而是“标识表记标帜为删除”,在迭代完成后第5步再来清理被标识表记标帜了的按时器,如许就包管了迭代的正确性。
对于通俗interval按时器,经由过程办法获知currentTimerSalvaged为true时,就会履行release,所以在迭代过程中CCTimer数组会改变,须要警惕处理惩罚。
至此可知,指定按时器后均由按时调剂器把握,每个按时器互不干扰,串行履行。