一,游戏帧循环的理解
我们的游戏从main.cpp中的return CCApplication::sharedApplication()->run();
进入到applicationDidFinishLaunching() ;其中在一个死循环中调用了mainLoop(),mainLoop是CCDirector的一个纯虚函数,在CCDirector的子类CCDisplayLinkDirector中进行实现。mainLoop中
1, drawScene() //完成绘图功能
1)m_pRuningScene->visit(); //遍历当前运行的Scene中每个节点
2)m_pScheduler->update(m_fDeletatime); //完成定时功能
3)CCPoolmanager::sharedPoolManager()->pop(); //游戏中的autoRelease就是将内存放置到这个内存池中,而在内存池中每一次帧循环都会做检测,对计数为零的内存释放
通过1), 2), 我们可以说,动画的本质就是定时器加属性的改变
2,处理触摸消息
TranslateMessage(&msg); //完成对触摸消息的派发
DispatchMessage(&msg);
如此,我们的Cocos游戏的脉络已经很清晰了,通过mainLoop中每一帧的循环,每一帧都做以下几件事,也就是while中的
遍历渲染树中每一个节点,完成绘图;通过定时器完成定时功能;检查内存池中内存使用情况; 对触摸消息进行派发。
二,调度器的使用
2.1 什么是调度器?
顾名思义,定时器,就是一个“某个时刻运行”的东西,游戏中,按照时间比如每隔一段时间(这个时间单位一般都很小)对某个量进行检测(血量,位置等等)
调度器就是在指定时间间隔调用指定的函数,去完成特定的功能。
2.2 调度器的特点
2.2.1 每当Node(调度器是Node类的方法)不在可见或者已从场景中移除时,调度器会停止。也可以理解为该对象被回收掉了,所以对应的调度器也没有了作用。
2.2.2 Cocos2d-x暂停时,调度器也会停止。当Cocos2d-x重新开始时,调度器也会自动继续启动。
2.3 调度器的使用
在开发中,我们经常使用的有三种调度器
默认调度器 | scheduleUpdate() |
自定义调度器 | schedule(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay) |
单词调度器 | scheduleOnce(SEL_SCHEDULE selector, float delay); |
/**
* 一般情况下,我们都是使用的默认调度器,每一个Node对象,都有一个Scheduler *_scheduler;
* 在初始化该Node时,会调用director->getScheduler();获得这个默认的调度器。
*
* 调用setScheduler设置新的Scheduler以后,之前设置的默认调度器将会被移除
*/
virtual void setScheduler(Scheduler* scheduler);
/**
* 获得调度器对象
*/
virtual Scheduler* getScheduler() { return _scheduler; }
virtual const Scheduler* getScheduler() const { return _scheduler; }
/**
* 判断调度函数是否被调度中
*/
bool isScheduled(SEL_SCHEDULE selector);
/**
* 这个是默认调度器
* 调用update方法,执行调度函数
* 在其内部调用的是scheduleUpdateWithPriority(0)函数,设置了优先级为0,这个函数会每一帧都会被调用
* 拥有比较小的调度值的调度函数会比拥有比较大的调度值的调度函数优先被调用
* 对于每个节点来说,只能有一个个update函数被调度
*/
void scheduleUpdate(void);
/**
* 指定一个自定义的调度优先级
* 拥有比较小的调度值的调度函数会比拥有比较大的调度值的调度函数优先被调用
*/
void scheduleUpdateWithPriority(int priority);
/**
* 停止调度update函数
* 该函数是与scheduleUpdate函数相对应的
*/
void unscheduleUpdate(void);
/**
* 自定义调度器
* @param selector 调度函数
* @param interval 调度时间. 0表示每帧都会被调度. 如果该值为0,那么该函数就与scheduleUpdate一样
* @param repeat 调度函数会被调度repeat + 1次, 可以传入kRepeatForever,表示永远执行调度
* @param delay 进行第一次调度之前,需要延迟的时间
*/
void schedule(SEL_SCHEDULE selector, float interval, unsigned int repeat, float delay);
void schedule(SEL_SCHEDULE selector, float interval);
/**
* 单次调度器
*/
void scheduleOnce(SEL_SCHEDULE selector, float delay);
/**
* 自定义调度器,每帧都会被调用
*/
void schedule(SEL_SCHEDULE selector);
/**
* 停止自定义调度器
*/
void unschedule(SEL_SCHEDULE selector);
/**
* 停止所有调度器
* 这个方法不会影响动作的执行
*/
void unscheduleAllSelectors(void);
/**
* Resumes all scheduled selectors, actions and event listeners.
* This method is called internally by onEnter
* 重新启动所有调度器,动作和事件监听器
* 该函数在内部被onEnter函数调用
*/
void resume(void);
/**
* 停止所有调度器,动作和事件监听器
* 该函数在内部被onExit函数调用
*/
void pause(void);bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
// ...
// 开启默认调度器
scheduleUpdate();
return true;
}
// 重新定义update这个virtual函数
void HelloWorld::update(float dt)
{
log("%f", dt++);
if(dt > 100) unschedulerUpdate();
}
该方法实在每一帧绘制之前都会被调用
该调度器在内部会调用update方法,所以在使用的时候只需要override void update(float dt); 在update中填入我们自己的逻辑代码,当我们不在需要这个调度器时,使用unschedulerUpdate()方法,
代码示例
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
// ...
// 开启默认调度器
scheduleUpdate();
return true;
}
// 重新定义update这个virtual函数
void HelloWorld::update(float dt)
{
log("%f", dt++);
if(dt > 100) unschedulerUpdate();
}
2.3.2 自定义调度器(schedule)
有时候我们并不需要每一帧都调度update方法,只需要在指定的时间间隔的情况下调用个函数。
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
// ...
// 开启自定义调度器
schedule(schedule_selector(HelloWorld::updateCustom), 1.0f, kRepeatForever, 0);
return true;
}
// 定义自定义调度器函数
void HelloWorld::updateCustom(float dt)
{
log("Cocos2d-x Custom schedule");
}
解除该调度器用
unschedule(schedule_selector(HelloWorld::updateCustom));
2.3.3 单次调度器(scheduleOnce)
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
return false;
}
scheduleOnce(schedule_selector(HelloWorld::updateOnce), 0);
return true;
}
解除该调度器
unschedule(schedule_selector(HelloWorld::updateOnce));