文章出自於 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