首页 > 代码库 > cocos2d-x学习日志(18) --程序是怎样開始执行与结束?

cocos2d-x学习日志(18) --程序是怎样開始执行与结束?

问题的由来


      怎么样使用 Cocos2d-x 高速开发游戏。方法非常easy,你能够看看其自带的例程,或者从网上搜索教程,执行起第一个HelloWorld,然后在 HelloWorld 里面写相关逻辑代码。加入我们的层、精灵等 ~ 我们并不一定须要知道 Cocos2d-x 是怎样执行或者在各种平台之上执行,也不用知道 Cocos2d-x 的游戏是怎样执行起来的。它又是怎样渲染界面的 ~~~

两个入口


      程序入口的概念是相对的,AppDelegate 作为跨平台程序入口,在这之上做了还有一层的封装,封装了不同平台的不同实现。比方我们通常觉得一个程序是由 main 函数開始执行,那我们就去找寻。我们看到了在proj.win32文件夹下存在 main.cpp 文件,这就是我们要看的内容,例如以下:

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

#ifdef USE_WIN32_CONSOLE
    AllocConsole();
    freopen("CONIN$", "r", stdin);
    freopen("CONOUT$", "w", stdout);
    freopen("CONOUT$", "w", stderr);
#endif

    // create the application instance
    AppDelegate app;
    CCEGLView* eglView = CCEGLView::sharedOpenGLView();
    eglView->setViewName("HelloLua");
    eglView->setFrameSize(480, 320);
    int ret = CCApplication::sharedApplication()->run();

#ifdef USE_WIN32_CONSOLE
    FreeConsole();
#endif

    return ret;
}


在这里我们看见了程序的真正入口,包括一个 main 函数。从此进入。执行 cocos2d-x 程序。



Android:

我们找到 Android 平台与上面 等价 的入口点,proj.android\jni\hellolua\main.cpp

void Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit(JNIEnv*  env, jobject thiz, jint w, jint h)
{
    if (!CCDirector::sharedDirector()->getOpenGLView())
    {
        CCEGLView *view = CCEGLView::sharedOpenGLView();
        view->setFrameSize(w, h);

        AppDelegate *pAppDelegate = new AppDelegate();
        CCApplication::sharedApplication()->run();
    }
    else
    {
        ccGLInvalidateStateCache();
        CCShaderCache::sharedShaderCache()->reloadDefaultShaders();
        ccDrawInit();
        CCTextureCache::reloadAllTextures();
        CCNotificationCenter::sharedNotificationCenter()->postNotification(EVENT_COME_TO_FOREGROUND, NULL);
        CCDirector::sharedDirector()->setGLDefaultValues(); 
    }
}

      我们并没有看到所谓的 main 函数,这是因为不同的平台封装所以有着不同的实现,在 Android 平台,默认是使用 Java 开发,能够使用 Java 通过 Jni 调用 C++ 程序。而这里也正式如此。

我们暂且仅仅需知道。由 Android 启动一个应用,通过各种峰回路转,终于执行到了Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeInit 函数。由此,变開始了我们 cocos2d-x Android 平台的程序入口处。对于跨平台的 cocos2d-x 来说,除非必要,否则可不必深究其理,比方想要使用 Android 平台固有的特性等,那就须要很多其它的了解 Jni 用法,以及 Android 操作系统的很多其它细节。


程序的流程 (这里以 Win32的实现为主。其它平台触类旁通就可以)


技术分享

凝视:

      我们从 main.cpp 中 CCApplication::sharedApplication()–>run(); 这一句看起。这一句标志着, cocos2d-x 程序正式開始执行。一点点開始分析,我们定位到 sharedApplication() 方法的实现。

CCApplication.cpp:

int CCApplication::run()
{
    PVRFrameEnableControlWindow(false);

    // Main message loop:
    MSG msg;
    LARGE_INTEGER nFreq;
    LARGE_INTEGER nLast;
    LARGE_INTEGER nNow;

    QueryPerformanceFrequency(&nFreq);
    QueryPerformanceCounter(&nLast);

    // Initialize instance and cocos2d.
    if (!applicationDidFinishLaunching())
    {
        return 0;
    }

    CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView();
    pMainWnd->centerWindow();
    ShowWindow(pMainWnd->getHWnd(), SW_SHOW);

    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)
            {
                nLast.QuadPart = nNow.QuadPart;
                CCDirector::sharedDirector()->mainLoop();
            }
            else
            {
                Sleep(0);
            }
            continue;
        }

        if (WM_QUIT == msg.message)
        {
            // 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;
}

void CCApplication::setAnimationInterval(double interval)
{
    LARGE_INTEGER nFreq;
    QueryPerformanceFrequency(&nFreq);
    m_nAnimationInterval.QuadPart = (LONGLONG)(interval * nFreq.QuadPart);
}

//////////////////////////////////////////////////////////////////////////
// static member function
//////////////////////////////////////////////////////////////////////////
CCApplication* CCApplication::sharedApplication()
{
    CC_ASSERT(sm_pSharedApplication);
    return sm_pSharedApplication;
}

      从 sharedApplication() 方法,到 run() 方法。在这之前,我们须要调用到它的构造函数,否则不能执行。这就是为什么在 CCApplication::sharedApplication()–>run(); 

之前,我们首先使用了 AppDelegate app; 创建 AppDelegate 变量的原因!

 

      AppDelegate 和 CCAppliation 是什么关系! 

      由 AppDelegate 的定义我们能够知道,它是 CCApplication 子类,在创建子类对象的时候,调用其构造函数的同一时候。父类构造函数也会执行,然后就将 AppDelegate 的对象赋给了 CCApplication 的静态变量,而在 AppDelegate 之中我们实现了applicationDidFinishLaunching 方法,所以在 CCApplication 中 run 方法的開始处调用的就是 AppDelegate 之中的实现。而我们在此方法中我们初始化了一些变量,创建了第一个 CCScene 场景等。之后的控制权,便全权交给了 CCDirector::sharedDirector()–>mainLoop(); 方法了。


从 CCApplication 到 CCDirector

      cocos2d-x 程序已经执行起来了。我们继续下一步,mainLoop 函数:
CCDirector* CCDirector::sharedDirector(void)
{
    if (!s_SharedDirector)
    {
        s_SharedDirector = new CCDisplayLinkDirector();
        s_SharedDirector->init();
    }

    return s_SharedDirector;
}

void CCDisplayLinkDirector::mainLoop(void)
{
    if (m_bPurgeDirecotorInNextLoop)
    {
        m_bPurgeDirecotorInNextLoop = false;
        purgeDirector();
    }
    else if (! m_bInvalid)
     {
         drawScene();
     
         // release the objects
         CCPoolManager::sharedPoolManager()->pop();        
     }
}
      游戏的执行以场景为基础。每时每刻都有一个场景正在执行,其内部有一个场景栈,遵循后进后出的原则,当我们显示的调用 end() 方法,或者弹出当前场景之时,其自己主动推断,假设没有场景存在,也会触发 end() 方法,以说明场景执行的结束,而游戏假设没有场景,就像演出没有了舞台,程序进入最后收尾的工作,通过改动变量 m_bPurgeDirecotorInNextLoop 促使在程序 mainLoop 方法之内调用 purgeDirector方法。



      走到最后一步,看 CCEGLView 是假设负责收尾工作的:
void CCEGLView::end()
{
    if (m_hWnd)
    {
#if(_MSC_VER >= 1600)
        if(m_bSupportTouch)
		{
		    s_pfUnregisterTouchWindowFunction(m_hWnd);
		}
#endif /* #if(_MSC_VER >= 1600) */
        DestroyWindow(m_hWnd);
        m_hWnd = NULL;
    }
    s_pMainWindow = NULL;
    UnregisterClass(kWindowClassName, GetModuleHandle(NULL));
    delete this;
}

      end() 方法非常easy,仅仅须要看到最后一句 delete this; 就明确了。

參考博文:http://blog.leafsoar.com/archives/2013/05-05.html

cocos2d-x学习日志(18) --程序是怎样開始执行与结束?