前言
游戏中,通过定时器实现画面和动作的更新.
用到的就是系统的ccschedule类.
下面就会谈一下计时器的实现机制.
定义
scheduler = new (std::nothrow) Scheduler();
_actionManager = new (std::nothrow) ActionManager();
_scheduler->scheduleUpdate(_actionManager, Scheduler::PRIORITY_SYSTEM, false);
在director类中,定义并初始化了动作管理器的定时器.
优先级的值为INT_MIN.
还可以看到,这个schedule的优先级是最低的,这个放到下一篇文章来讲.
(优先级的值越低,优先级越高)
分析
下面就先来看一下scheduleUpdate这个函数.
template <class T>
void scheduleUpdate(T *target, int priority, bool paused)
{
this->schedulePerFrame([target](float dt){
target->update(dt);
}, target, priority, paused);
}
传入的三个参数含义分别为需要更新的目标,优先级(用来比较),是否需要暂停.
可以看到,每一帧都调用了schedulePerFrame来对优先级进行排名.
然后调用了update函数来更新自己.
void Scheduler::schedulePerFrame(const ccSchedulerFunc& callback, void *target, int priority, bool paused)
{
tHashUpdateEntry *hashElement = nullptr;
HASH_FIND_PTR(_hashForUpdates, &target, hashElement);//根据哈希表找到要排序的元素
if (hashElement)//如果已经存在
{
// change priority: should unschedule it first
if (hashElement->entry->priority != priority)
{
unscheduleUpdate(target);//取消调度
}
else
{
// don't add it again
CCLOG("warning: don't update it again");
return;
}
}
// most of the updates are going to be 0, that's way there
// is an special list for updates with priority 0
if (priority == 0)
{
appendIn(&_updates0List, callback, target, paused);//优先级为0,直接然后插入数据
}
else if (priority < 0)
{
priorityIn(&_updatesNegList, callback, target, priority, paused);//不为0,判断优先级再插入
}
else
{
// priority > 0
priorityIn(&_updatesPosList, callback, target, priority, paused);
}
}
tHashUpdateEntry是一个结构体,它的定义如下:
typedef struct _hashUpdateEntry
{
tListEntry **list; // Which list does it belong to ?
tListEntry *entry; // entry in the list
void *target;
ccSchedulerFunc callback;
UT_hash_handle hh;
} tHashUpdateEntry;
里面有嵌套了一个结构体tListEntry;
typedef struct _listEntry
{
struct _listEntry *prev, *next;
ccSchedulerFunc callback;
void *target;
int priority;
bool paused;
bool markedForDeletion; // selector will no longer be called and entry will be removed at end of the next tick
} tListEntry;
很容易发现这是一个双链表
hashUpdateEntry用来快速检索双链表.
struct _listEntry *_updatesNegList; // list of priority < 0
struct _listEntry *_updates0List; // list priority == 0
struct _listEntry *_updatesPosList; // list priority > 0
通过查看定义,了解到系统用了三个链表存放不同优先级的元素.
比较appendIn和priorityIn这两个函数:
void Scheduler::priorityIn(tListEntry **list, const ccSchedulerFunc& callback, void *target, int priority, bool paused)
{
tListEntry *listElement = new (std::nothrow) tListEntry();
listElement->callback = callback;
listElement->target = target;
listElement->priority = priority;
listElement->paused = paused;
listElement->next = listElement->prev = nullptr;
listElement->markedForDeletion = false;//赋值给链表
// empty list ?
if (! *list)
{
DL_APPEND(*list, listElement);//如果为空,直接向后插入数据
}
else
{
bool added = false;
for (tListEntry *element = *list; element; element = element->next)
{
if (priority < element->priority)//根据优先级比较并插入
{
if (element == *list)
{
DL_PREPEND(*list, listElement);
}
else
{
listElement->next = element;
listElement->prev = element->prev;
element->prev->next = listElement;
element->prev = listElement;
}
added = true;
break;
}
}
if (! added)
{
DL_APPEND(*list, listElement);
}
}
// update hash entry for quick access
tHashUpdateEntry *hashElement = (tHashUpdateEntry *)calloc(sizeof(*hashElement), 1);
hashElement->target = target;
hashElement->list = list;
hashElement->entry = listElement;
memset(&hashElement->hh, 0, sizeof(hashElement->hh));
HASH_ADD_PTR(_hashForUpdates, target, hashElement);
}
void Scheduler::appendIn(_listEntry **list, const ccSchedulerFunc& callback, void *target, bool paused)
{
tListEntry *listElement = new (std::nothrow) tListEntry();//同上
listElement->callback = callback;
listElement->target = target;
listElement->paused = paused;
listElement->priority = 0;
listElement->markedForDeletion = false;
DL_APPEND(*list, listElement);
// update hash entry for quicker access
tHashUpdateEntry *hashElement = (tHashUpdateEntry *)calloc(sizeof(*hashElement), 1);
hashElement->target = target;
hashElement->list = list;
hashElement->entry = listElement;
memset(&hashElement->hh, 0, sizeof(hashElement->hh));
HASH_ADD_PTR(_hashForUpdates, target, hashElement);
}
通过比较可以发现区别:
一个是按优先级插入,另一个是直接加入链表尾.
尾声
调用了target对应的update函数来实现具体的更新逻辑,这样,
定时器的部分就讲完了.
另外,还有系统自定义的schedule,里面用到了Timer类,实现上
也大同小异.
总结
schedule通过每帧调用,来比较并插入链表,
从而实现对优先级序列的排序,以此来管理更新的顺序.
整个流程差不多就这样了,有疏漏再补充吧.
博客首发于github
转载请说明出处