scheduleUpdate的源码分析

前言

游戏中,通过定时器实现画面和动作的更新.
用到的就是系统的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
转载请说明出处

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值