您的位置:首页 > 移动开发 > Cocos引擎

cocos2d-x 3.1.1学习笔记[23]寻找主循环 mainloop

2014-08-08 09:58 459 查看
文章出自于 http://blog.csdn.net/zhouyunxuan

cocos2d究竟是如何把场景展示给我们的,我一直很好奇。

凭个人猜想,引擎内部的结构类似于这样

while(true)
{
if(update_span < min_update_span)
{
update_game();
if(done)
{
break;
}
}
else
{
cal_update_span();
}
}


在app开始运行时会调用里面的方法。

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions

来看看这个函数最后return YES之前的一行代码

cocos2d::Application::getInstance()->run();

没错,就是这个,然后我们进入到run函数里面来看个究竟

int Application::run()
{
if (applicationDidFinishLaunching())
{
//这个函数在这里调用了startMainLoop
[[CCDirectorCaller sharedDirectorCaller] startMainLoop];
}
return 0;
}


然后我们继续跟进看看startMainLoop

-(void) startMainLoop
{
// Director::setAnimationInterval() is called, we should invalidate it first
[displayLink invalidate];
displayLink = nil;

//给CADisplayLink传了一个doCaller函数,让CADisplayLink不断的调用
displayLink = [NSClassFromString(@"CADisplayLink") displayLinkWithTarget:self selector:@selector(doCaller:)];
//设置调用频率
[displayLink setFrameInterval: self.interval];
//开始循环吧!
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}


CADisplayLink,需要加入QuartzCore.framework

这个函数类似于update函数,默认每秒被调用60次,现在我们再进入doCaller函数吧

-(void) doCaller: (id) sender
{
cocos2d::Director* director = cocos2d::Director::getInstance();
[EAGLContext setCurrentContext: [(CCEAGLView*)director->getOpenGLView()->getEAGLView() context]];
//终于进入到这场表演的主角了,我期待了好久!!!
director->mainLoop();
}


其实Director::getInstance();返回的不是Director,被骗了好久-= -

Director::getInstance() 返回的并不是Director,而是Director的子类DisplayLinkDirector();

Director* Director::getInstance()
{
if (!s_SharedDirector)
{
s_SharedDirector = new DisplayLinkDirector();
s_SharedDirector->init();
}

return s_SharedDirector;
}


程序的主要逻辑都通过调用mainloop来完成,这个方法负责调用计时器,绘图,发送全局通知,并处理内存回收池,这个方法按帧调用,每帧调用一次,而帧之间取决于两个因素,一个是预设的帧频(默认为每秒六十次),另一个是每帧的计算量大小,当逻辑处理与绘图计算量过大时,设备无法完成每秒六十次的绘制,此时帧率就会降低。

void DisplayLinkDirector::mainLoop()
{
//是否在下一循环中清除
//bool _purgeDirectorInNextLoop; // this flag will be set to true in end()
if (_purgeDirectorInNextLoop)
{
log("clear director");
_purgeDirectorInNextLoop = false;
//会做一些清理
purgeDirector();
}
//如果不清除的话(且为合法的)ps:一般都是会进入到这里,然后进行绘制等等。
else if (! _invalid)
{
//画场景
drawScene();

// release the objects
PoolManager::getInstance()->getCurrentPool()->clear();
}
}


然后我们接着看看这伟大的 drawScene里面做了什么吧!

void Director::drawScene()
{
//计算时间增量
// calculate "global" dt
calculateDeltaTime();

// 如果两帧间隔时间太短(_deltaTime等于0)就直接忽略这次的绘制
// FLT_EPSILON 是 __FLT_EPSILON__ 的宏,__FLT_EPSILON__ 是c99的特征,它是满足 x+1.0不等于1.0的最小的正数,直接输出为0。
if(_deltaTime < FLT_EPSILON)
{
return;
}

//
if (_openGLView)
{
_openGLView->pollInputEvents();
}

//tick before glClear: issue #533
//只要游戏没有暂停,调度器神马的就会在这里被执行。
if (! _paused)
{
_scheduler->update(_deltaTime);
_eventDispatcher->dispatchEvent(_eventAfterUpdate);
}

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

/* to avoid flickr, nextScene MUST be here: after tick and before draw.
XXX: Which bug is this one. It seems that it can't be reproduced with v0.9 */
if (_nextScene)
{
setNextScene();
}

pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

//画场景
if (_runningScene)
{
_runningScene->visit(_renderer, Mat4::IDENTITY, false);
_eventDispatcher->dispatchEvent(_eventAfterVisit);
}

// 画通知节点
if (_notificationNode)
{
_notificationNode->visit(_renderer, Mat4::IDENTITY, false);
}

//如果设置了显示debug信息,就会在这里进行每帧的更新。
if (_displayStats)
{
showStats();
}

_renderer->render();
_eventDispatcher->dispatchEvent(_eventAfterDraw);

popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);

_totalFrames++;

// 交换缓冲区
if (_openGLView)
{
_openGLView->swapBuffers();
}

//计算fps上的信息
if (_displayStats)
{
calculateMPF();
}
}


最初提出来的结构和发现的结构还是有点相似的。只不过引擎更友好的抽象封装出来了,且做了很多防止异常的处理,程序员还都是很小心的嘛。。。

调用的CADisplayLink是ios平台的,如果换成其他平台就不一样啦。毕竟win是木有CADisplayLink的。

不相信?

好吧,我们来看看win是如何调用的吧,首先找到Application::run()函数。

//如果窗口不关闭
while(!glview->windowShouldClose())
{
//计算时间
QueryPerformanceCounter(&nNow);
//两帧间距时间要大一点才给你画哦
if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart)
{
//上一帧的时间就等于现在这一帧,用于下次计算两帧的间隔时间。
nLast.QuadPart = nNow.QuadPart;

//进入到我们伟大的mainloop了,是不是有点小激动 - -
director->mainLoop();
glview->pollEvents();
}
else
{
//神马!睡0秒。好吧,光是看表面还是很骗人的。
Sleep(0);
}
}


Sleep(0)是指CPU交出当前线程的执行权,让CPU去执行其他线程。也就是放弃当前线程的时间片,转而执行其他线程。

一般来说,如果当前线程比较耗时比较占CPU资源,可以在结尾处加上Sleep(0), 这样效率会得到大大的提高。

看了win上面的调用,发现和我一开始的猜想更像有木有!!!

有时候,做笔记记录下学习过程也挺不错的。

肯定没人会转载的- -

但是为了防止蜘蛛爬走了我的文章,我还是注释下- -

文章出自于 http://blog.csdn.net/zhouyunxuan
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: