cocos2d-x 框架深入分析(一)? MainLoop

    学习游戏开发也有3个月了,其中也做一个安卓的原创游戏(2D游戏,目前没有接触到3D游戏),之前没有接触过游戏引擎,游戏的一些简单的物理效果都是自己写,cocos2d-x是我第一次接触游戏引擎,下载源码后就开始深入源码去学习,学习别人更好的思想,也在寻找在设计上和自己做的有什么异同点。记录下我现在所看到的,了解到的。废话不多说,我现在看的版本是cocos2d-2.1rc0-x-2.1.2。
    做个游戏的应该都知道,我们的游戏一般是在一个死循环中渲染的,比如下面:
 float dt;
	while (1)
	{
		...
		update(dt); //通过时间差更新数据
		present(dt);//通过时间差呈现、绘制游戏画面
		...
	}

所以我首先在底层源代码中寻找这个样子的东东。(注:我是用VS在WIN32平台下查看)

首先打开 …\cocos2d-2.1rc0-x-2.1.2\samples\Cpp\TestCpp\proj.win32\main.cpp

int APIENTRY _tWinMain(HINSTANCE hInstance,
                       HINSTANCE hPrevInstance,
                       LPTSTR    lpCmdLine,
                       int       nCmdShow)
{
    UNREFERENCED_PARAMETER(hPrevInstance);
    UNREFERENCED_PARAMETER(lpCmdLine);

    // create the application instance
    AppDelegate app;    //实例化应用代理对象,这个对象是CCApplication的子类,
						//实现了applicationDidFinishLaunching()等方法,目的
						//目的是为了在底层框架中回调,做一些初始化或者结尾的工作

    CCEGLView* eglView = CCEGLView::sharedOpenGLView();//继承和封装了一些对Opengl接口的操作。不同平台实现不一样
    eglView->setViewName("TestCpp");
    eglView->setFrameSize(480, 320);
    return CCApplication::sharedApplication()->run();//一个单实例类,通过run方法进入引擎主循环
}

这里就是程序的主入口,没有什么好说的,唯一注意的就是AppDelegate类在这里被实例化了。接着进入 CCApplication::run();
…\cocos2d-2.1rc0-x-2.1.2\cocos2dx\platform\win32\CCApplication.cpp

int CCApplication::run()
{
    ......

    // Initialize instance and cocos2d.
    if (!applicationDidFinishLaunching())//AppDelegate的回调方法,主要就是初始化CCDirector等数据,方便显示第一个场景
    {
        return 0;
    }

    ......

    while (1)
    {
        if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            // Get current time tick.
            QueryPerformanceCounter(&nNow);

            // If it's the time to draw next frame, draw it, else sleep a while.
            if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)//判断时间差是不是达到了到下一帧的条件
            {																   <span style="white-space:pre">									</span>//主要是看锁定多少的FPS
                nLast.QuadPart = nNow.QuadPart;
                CCDirector::sharedDirector()->mainLoop();//进入引擎的主循环
            }
            else
            {
                Sleep(0);
            }
            continue;
        }

        ......
    }

    return (int) msg.wParam;
}

在这里省去了一些平台有关的代码,主要是WIN32平台的窗口消息的获取等,还有一些计算时间的代码。在这个循环中,只要达到渲染要求,就进入CCDirector::sharedDirector()->mainLoop();代码中,把处理交给引擎自己去完成。接着进入。。。
…\cocos2d-2.1rc0-x-2.1.2\cocos2dx\CCDirector.cpp

void CCDisplayLinkDirector::mainLoop(void)
{
    if (m_bPurgeDirecotorInNextLoop)//进入下一个主循环,也就是结束这次的主循环,就净化,也就是一些后期处理
    {
        m_bPurgeDirecotorInNextLoop = false;
        purgeDirector();
    }
    else if (! m_bInvalid)
     {
         drawScene();//绘制屏幕
     
         // release the objects
         CCPoolManager::sharedPoolManager()->pop();//释放一些没有用的对象,主要保件内存的合理管理
     }
}

CCDisplayLinkDirector是CCDisplay的子类,从命名就应该可以很清晰的知道它的用处。通过CCDirector::sharedDirector()代码,得到的都是CCDisplayLinkDirector对象。通过drawScene()代码就可以实现场景的绘制了。而开篇我要寻找的那个东东,也就在里面了。。。
…\cocos2d-2.1rc0-x-2.1.2\cocos2dx\CCDirector.cpp

void CCDirector::drawScene(void)
{
    // calculate "global" dt
    calculateDeltaTime();//计算时间差

    //tick before glClear: issue #533
    if (! m_bPaused)    //如果不暂停,就更新数据
    {
        m_pScheduler->update(m_fDeltaTime);//调度者对象,是整个框架中,非常重要的东东,他负责者引擎中精灵、动作等的调度
					 //而里面所用的数据结构的组织,一定程度决定者引擎的效率。
    }

    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 (m_pNextScene)
    {
        setNextScene();//如果有m_pNextScene对象不为空,就说明需要调用到新的场景中,
					   //在其中onEnter()、onEnterTransitionDidFinish()等函数被回调。
    }

    kmGLPushMatrix();//opengl:把当前矩阵放到栈中

    // draw the scene
    if (m_pRunningScene)
    {
        m_pRunningScene->visit();//通过访问方法,去绘制场景中包含的每个层和每个层中的每个节点的draw,
								 //这里面是一个递归的过程,其中transform()方法实现,opengl矩阵的变化<span style="white-space:pre">								</span>//,移动,旋转等。
    }

    // draw the notifications node
    if (m_pNotificationNode)
    {
        m_pNotificationNode->visit();//绘制通知节点,目前我也不知道这个是什么!
    }
    
    if (m_bDisplayStats)
    {
        showStats();
    }

    kmGLPopMatrix();//opengl:把当前矩阵从栈中移除,回复之前的矩阵

    m_uTotalFrames++;//记录总帧数

    // swap buffers
    if (m_pobOpenGLView)
    {
        m_pobOpenGLView->swapBuffers();//opengl:交换帧缓冲区,把绘制的东东显示在屏幕。
    }
    
    if (m_bDisplayStats)
    {
        calculateMPF();
    }
}

好了,到目前为止,我们看到了,我们想看到的东东,开心了,第一次写博文,记录下自己的成长路,今天放假,玩去。。。 未来记下引擎核心之一的CCScheduler的工作方式和里面所用到的数据结构。