cocos2d-x: 死磕"HelloWorld"(3)——游戏运行主函数run()

在该篇中,我们将分析游戏运行主函数run(),看看它都干了些啥。并且会重点分析其中调用的两个函数:即应用初始化函数applicationDidFinishLaunching()和渲染主循环函数mainLoop()。但是这两个函数所调用的更为细节的函数将放在后续篇章中分析。一次分析两层函数应该是可以接受的,多了就晕了。现在我们就来一顿run()函数的庐山真面:

CCApplication.cpp

int CCApplication::run()
{
    PVRFrameEnableControlWindow(false);//设置和获取注册表信息.

    // Main message loop:
    MSG msg;//创建消息对象
    LARGE_INTEGER nFreq;
    LARGE_INTEGER nLast;
    LARGE_INTEGER nNow;
    //创建三个大整数对象(大整数定义详解请移步博主另一博文[1])。这三大整数用于记录程序运行过程中两个事件的时间间隔。下面两个函数便是它们的赋值函数。

    QueryPerformanceFrequency(&nFreq);//把windows高精度定时器的时钟频率传递给nFreq对象。
    QueryPerformanceCounter(&nLast);//把定时器当前计数传递给 nLast。后面还会再次调用该函数,把定时器计数传递给nNow。这样一来,我们便分别获得了两个事件发生时定时器的计数,这两个计数之差nNow.QuadPart-nLast.QuadPart便是这两个事件的间隔计数(即事件间隔内时钟的振荡次数),把间隔计数除以时钟频率nFreq.QuadPart便是间隔时间。这种计时方法可以达到很高的精度。

    // Initialize instance and cocos2d.
    if (!applicationDidFinishLaunching())//applicationDidFinishLaunching()用于对实例进行初始化。我们马上会详解该函数。
    {
        return 0;
    }

    CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView();//创建一个视窗类对象。由于sharedOpenGLView()采用单例模式,所以该对象即为之前在applicationDidFinishLaunching()里创建的那个视窗对象(见下面对applicationDidFinishLaunching()的分析)。
    pMainWnd->centerWindow();//将该窗口居中
    ShowWindow(pMainWnd->getHWnd(), SW_SHOW);//显示窗口

    while (1)//渲染死循环
    {
        if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))//先调用一个API函数PeekMessage。如果无法获得当前窗口(第二个参数为被检查的窗口句柄,如果为NULL,则为当前窗口)的消息,则PeekMessage返回值为零,条件判断为真,允许执行语块内代码。
        {
            // Get current time tick.
            QueryPerformanceCounter(&nNow);//读取定时器当前计数传递给nNow。

            // If it's the time to draw next frame, draw it, else sleep a while.
            if (nNow.QuadPart - nLast.QuadPart > m_nAnimationInterval.QuadPart)//如果当前计数和上一个计数之差大于某一设定值 m_nAnimationInterval则进入mainLoop()开始渲染,并且更新定时器计数, 否则将当前线程挂起,挂起时间在该例中为0秒,相当于没挂。然后continue进入下一个渲染循环,跳过后面的两个条件语句。
            {
                nLast.QuadPart = nNow.QuadPart;
                CCDirector::sharedDirector()->mainLoop();
            }
            else
            {
                Sleep(0);
            }
            continue;
        }

        if (WM_QUIT == msg.message)//如果消息结构体中的message成员等于退出编码,则退出循环。在这里我们可以大胆猜测按下HelloWorld窗口中的关闭按钮,会导致消息结构体中的message成员等于退出编码,从而退出循环。这一猜测可以通过在break处设置断点进行验证。至于细节我们会在本节末再次讨论。
        {
            // Quit message loop.
            break;
        }

        // Deal with windows message.
        if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))// 假如加速键表句柄为零,或者调用翻译加速键表函数失败,则先将虚拟键消息转换为字符消息,寄送到调用线程的消息队列里,然后将消息发给窗口程序。
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
    }

    return (int) msg.wParam;
}


下面就来分析一下其中的实例初始化函数applicationDidFinishLaunching()

CCDelegate.cpp

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    CCDirector* pDirector = CCDirector::sharedDirector();//创建一个导演对象,并初始化。所谓初始化即对其成员数据赋值。
    CCEGLView* pEGLView = CCEGLView::sharedOpenGLView();//创建一个视窗类对象,并初始化。

    pDirector->setOpenGLView(pEGLView);//把视窗对象指针传递给导演。注意setOpenGLView(CCEGLView *pobOpenGLView)里的m_pobOpenGLView = pobOpenGLView。
	CCSize frameSize = pEGLView->getFrameSize();//获取窗口大小。

    // Set the design resolution //设置分辨率。
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WINRT) || (CC_TARGET_PLATFORM == CC_PLATFORM_WP8)
    pEGLView->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, kResolutionShowAll);
#else
    pEGLView->setDesignResolutionSize(designResolutionSize.width, designResolutionSize.height, kResolutionNoBorder);
#endif

    
    vector<string> searchPath; //声明路径字符串

    // In this demo, we select resource according to the frame's height.
    // If the resource size is different from design resolution size, you need to set contentScaleFactor.
    // We use the ratio of resource's height to the height of design resolution,
    // this can make sure that the resource's height could fit for the height of design resolution.

    //根据窗口大小选择图片路径和缩放比例。
    // if the frame's height is larger than the height of medium resource size, select large resource.   
	if (frameSize.height > mediumResource.size.height)
	{
        searchPath.push_back(largeResource.directory);

        pDirector->setContentScaleFactor(MIN(largeResource.size.height/designResolutionSize.height, largeResource.size.width/designResolutionSize.width));
	}
    // if the frame's height is larger than the height of small resource size, select medium resource.
    else if (frameSize.height > smallResource.size.height)
    {
        searchPath.push_back(mediumResource.directory);
        
        pDirector->setContentScaleFactor(MIN(mediumResource.size.height/designResolutionSize.height, mediumResource.size.width/designResolutionSize.width));
    }
    // if the frame's height is smaller than the height of medium resource size, select small resource.
	else
    {
        searchPath.push_back(smallResource.directory);

        pDirector->setContentScaleFactor(MIN(smallResource.size.height/designResolutionSize.height, smallResource.size.width/designResolutionSize.width));
    }


    // set searching path 
    CCFileUtils::sharedFileUtils()->setSearchPaths(searchPath); //设置搜索路径。
	
    // turn on display FPS 
    pDirector->setDisplayStats(true);//开启帧频显示。

    // set FPS. the default value is 1.0/60 if you don't call this 
    pDirector->setAnimationInterval(1.0 / 60);  //设置渲染间隔。该间隔先传给m_dAnimationInterval,后传给m_nAnimationInterval.

    // create a scene. it's an autorelease object
    CCScene *pScene = HelloWorld::scene(); //创建一个HelloWorld场景。对于单纯的游戏制作者而言,这是重头戏。我们将在第四篇中详解该函数。
    // run
    pDirector->runWithScene(pScene); //渲染准备函数,我们将在第五篇中详解该函数。

    return true;
}
除了窗口和路径设置之外,导演指挥一切。初始化若成功则返回值为真,否则为假。当返回值为真时,则继续run(),否则退出整个程序。

下面就来看重点之二,渲染主循环函数mainLoop()。该函数是导演类的成员函数。

CCDirector.cpp

void CCDisplayLinkDirector::mainLoop(void)
{
    if (m_bPurgeDirecotorInNextLoop)
    {
        m_bPurgeDirecotorInNextLoop = false;
        purgeDirector();
    }
    else if (! m_bInvalid)
     {
         drawScene();
     
         // release the objects
         CCPoolManager::sharedPoolManager()->pop();        
     }
}

假如导演类数据成员m_bPurgeDirecotorInNextLoop为真,则将其设为假,并且调用purgeDirector(),停止并关闭渲染窗口。如果m_bPurgeDirecotorInNextLoop为假,并且m_bInvalid也为假(从CCDisplayLinkDirector构造中看出其初始值为假)时,调用渲染场景函数drawScene()进行渲染,渲染完调用sharedPoolManager()->pop()释放内存。

至此,我们对run()的内容也有了个了解。它主要就是利用applicationDidFinishLaunching()创建了一个导演,一个窗口和一个HelloWorld场景,然后利用一个死循环和mainLoop对该场景进行渲染。

还有个遗留问题是关闭键如何关闭程序。它调用了一个HelloWold类里的回调函数menuCloseCallback(CCObject* pSender)。该回调函数又调用了导演类的end()函数。该end()函数的功能很简单就是把导演类的成员数据m_bPurgeDirecotorInNextLoop设为真,因此当渲染循环再次进入到mainLoop()里时,便会调用purgeDirector(),停止并关闭渲染窗口。当程序进入下一渲染循环时,PeekMessage的返回值为真,条件判断为假,因此直接进入第二个条件判断语句。此时消息符合退出编码,因此执行break,退出渲染循环。


引文:

[1] http://blog.csdn.net/u014078216/article/details/24401421

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值