將Cocos2dX渲染到MFC窗口上


 本文歡迎轉載,但是必須得保住原文地址 http://www.cocos2dres.com/view.asp?id=63 否則將追究責任。

引言

        現在智能手機已經慢慢進入大眾化,移動類應用開始火爆起來,游戲類應用更是占據了手機用戶的大部分碎片時間。

        現在手機開發游戲也逐漸流行開來,手機的平台目前主打是 Andoird、IOS和WindowPhone。Cocos2DX跨平台開發成為吸引手機開發商和獨立游戲制作人的一大亮點。

        Cocos2dX脫胎於Cocos2D,有優良的血統,成熟的框架,加上不錯的效率,成為跨平台手機游戲開發的首選。

         在游戲開發過程中,各種輔助工具的開發是難免的。下面的文章 http://www.cocos2dres.com/view.asp?id=55 中有 介紹網絡上可以找到一些工具,其中一些需要收費。

         如果您是制作一些小游戲,網上找到的那些工具,也許可以解決制作中的問題,但是如果您在制作大型游戲,您就不得不自己動手制作工具了。

        本文介紹將Cocos2dX渲染到MFC窗口,是制作游戲工具所需的一些知識。

 

COCOS2DX開發環境

        本文的Cocos2DX的版本是 cocos2d-2.0-x-2.0.3,  點擊進入下載頁面  www.cocos2d-x.org/projects/cocos2d-x/wiki/Download

        Cocos2DX下載后以及windows下配置這里就不贅述了 相關教程鏈接 http://www.cocos2dres.com/post/7.html win7下 VS2010教程

        在理解了 helloCPP后,我們就可以進行下一步了。

 

MFC工程

      第一步,創建MFC工程,修改工程屬性  

   打開 cocos2d-win32.vc2010.sln 解決方案,添加一個MFC單文檔程序。不會添加MFC工程的同學,到網上找找教程。

       以下是工程(工程名叫MFCTest)截圖:

                      

 

  其中 Cocos2DApp是自己添加的。

  在工程屬性面板,

        

  圖中紅圈處設置成現HelloCPP一樣就可以了。調試的工作路徑,需要設置成自己的路徑。

  第二步,分析HelloCpp下的Windows窗口

  查看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;
    CCEGLView* eglView = CCEGLView::sharedOpenGLView();
    eglView->setFrameSize( 960, 640 );
    return CCApplication::sharedApplication()->run();
}

  這里首先創建一個app,然后設置窗口大小,然后就是run()了。Windows的窗口在哪里創建的呢?

  eglView應當是與窗口相關的,我們看看setFrameSize的相關實現:

void CCEGLView::setFrameSize(float width, float height)
{
    Create((LPCTSTR)m_szViewName, (int)width, (int)height );
    CCEGLViewProtocol::setFrameSize(width, height);
}

  首先映入眼簾就是Create方法,看來windows窗口的創建就在這個方法里面:

bool CCEGLView::Create(LPCTSTR pTitle, int w, int h)
{
    bool bRet = false;
    if( hWnd == NULL )
    {
        do 
        {
            CC_BREAK_IF(m_hWnd);

            HINSTANCE hInstance = GetModuleHandle( NULL );
            WNDCLASS  wc;        // Windows Class Structure

            // Redraw On Size, And Own DC For Window.
            wc.style          = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;  
            wc.lpfnWndProc    = _WindowProc;                    // WndProc Handles Messages
            wc.cbClsExtra     = 0;                              // No Extra Window Data
            wc.cbWndExtra     = 0;                                // No Extra Window Data
            wc.hInstance      = hInstance;                        // Set The Instance
            wc.hIcon          = LoadIcon( NULL, IDI_WINLOGO );    // Load The Default Icon
            wc.hCursor        = LoadCursor( NULL, IDC_ARROW );    // Load The Arrow Pointer
            wc.hbrBackground  = NULL;                           // No Background Required For GL
            wc.lpszMenuName   = NULL;                           // We Don't Want A Menu
            wc.lpszClassName  = kWindowClassName;               // Set The Class Name

            CC_BREAK_IF(! RegisterClass(&wc) && 1410 != GetLastError());        

            // center window position
            RECT rcDesktop;
            GetWindowRect(GetDesktopWindow(), &rcDesktop);

            WCHAR wszBuf[50] = {0};
            MultiByteToWideChar(CP_UTF8, 0, m_szViewName, -1, wszBuf, sizeof(wszBuf));

            // create window
            m_hWnd = CreateWindowEx(
                WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,    // Extended Style For The Window
                kWindowClassName,                                    // Class Name
                wszBuf,                                                // Window Title
                WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBOX,        // Defined Window Style
                0, 0,                                                // Window Position
                0,                                                  // Window Width
                0,                                                  // Window Height
                NULL,                                                // No Parent Window
                NULL,                                                // No Menu
                hInstance,                                            // Instance
                NULL );

            CC_BREAK_IF(! m_hWnd);

            resize(w, h);

            bRet = initGL();
            CC_BREAK_IF(!bRet);

            s_pMainWindow = this;
            bRet = true;
        } while (0);
    }return bRet;
}

  果不出我所料,我們看到了熟悉的CreateWindowEx,然后調用initGL()初始化OpenGL相關。

  我們再看看 initGL的實現:

bool CCEGLView::initGL()
{
    m_hDC = GetDC(m_hWnd); // 拿到窗口的DC
    SetupPixelFormat(m_hDC); // 設置像素格式
    //SetupPalette();
    m_hRC = wglCreateContext(m_hDC); /// 創建OpenGLES渲染環境
    wglMakeCurrent(m_hDC, m_hRC);    /// 設置當前渲染環境

    GLenum GlewInitResult = glewInit();
    if (GLEW_OK != GlewInitResult) 
    {
        fprintf(stderr,"ERROR: %s\n",glewGetErrorString(GlewInitResult));
        return false;
    }

    if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader)
    {
        CCLog("Ready for GLSL\n");
    }
    else 
    {
        CCLog("Not totally ready :( \n");
    }

    if (glewIsSupported("GL_VERSION_2_0"))
    {
        CCLog("Ready for OpenGL 2.0\n");
    }
    else
    {
        CCLog("OpenGL 2.0 not supported\n");
    }
    return true;
}

  這樣 Windows窗口就和OpenGLES的渲染環境關聯起來了。

  我們如何將MFC的窗口和OpenGLES的渲染環境關聯起來呢?

  MFC窗口是預先已經創建好的,我們只要在創建的時候,用已經創建好的窗口來做 initGL應當就可以實現。於是我將CCEGLView的Create和SetFrameSize做了調整,可以   傳入窗口句柄。新的函數代碼如下:

  

void CCEGLView::setFrameSize(float width, float height,HWND hWnd)/// 將窗口句柄通過參數傳進來
{
    Create((LPCTSTR)m_szViewName, (int)width, (int)height, hWnd);
    CCEGLViewProtocol::setFrameSize(width, height);
}

bool CCEGLView::Create(LPCTSTR pTitle, int w, int h, HWND hWnd)
{
    bool bRet = false;
    if( hWnd == NULL ) /// 如果等於 NULL 則按以前的方式走
    {
        do 
        {
            CC_BREAK_IF(m_hWnd);

            HINSTANCE hInstance = GetModuleHandle( NULL );
            WNDCLASS  wc;        // Windows Class Structure

            // Redraw On Size, And Own DC For Window.
            wc.style          = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;  
            wc.lpfnWndProc    = _WindowProc;                    // WndProc Handles Messages
            wc.cbClsExtra     = 0;                              // No Extra Window Data
            wc.cbWndExtra     = 0;                                // No Extra Window Data
            wc.hInstance      = hInstance;                        // Set The Instance
            wc.hIcon          = LoadIcon( NULL, IDI_WINLOGO );    // Load The Default Icon
            wc.hCursor        = LoadCursor( NULL, IDC_ARROW );    // Load The Arrow Pointer
            wc.hbrBackground  = NULL;                           // No Background Required For GL
            wc.lpszMenuName   = NULL;                           // We Don't Want A Menu
            wc.lpszClassName  = kWindowClassName;               // Set The Class Name

            CC_BREAK_IF(! RegisterClass(&wc) && 1410 != GetLastError());        

            // center window position
            RECT rcDesktop;
            GetWindowRect(GetDesktopWindow(), &rcDesktop);

            WCHAR wszBuf[50] = {0};
            MultiByteToWideChar(CP_UTF8, 0, m_szViewName, -1, wszBuf, sizeof(wszBuf));

            // create window
            m_hWnd = CreateWindowEx(
                WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,    // Extended Style For The Window
                kWindowClassName,                                    // Class Name
                wszBuf,                                                // Window Title
                WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBOX,        // Defined Window Style
                0, 0,                                                // Window Position
                0,                                                  // Window Width
                0,                                                  // Window Height
                NULL,                                                // No Parent Window
                NULL,                                                // No Menu
                hInstance,                                            // Instance
                NULL );

            CC_BREAK_IF(! m_hWnd);

            resize(w, h);

            bRet = initGL();
            CC_BREAK_IF(!bRet);

            s_pMainWindow = this;
            bRet = true;
        } while (0);
    }
    else
    {
        m_hWnd = hWnd; /// 直接將外部窗口當做當前窗口
        bRet = initGL(); /// 初始化OpenGLES相關
    }

    return bRet;
}

  底層已經支持將外部窗口傳入作為渲染窗口了,那我們就只要做相應的調整就可以了。

  第三步,添加Cocos2DApp

  在工程中添加 類 CCocos2DApp繼承自 cocos2d::CCApplication, 這就相當於HelloCPP中的 AppDelegate.之所以在這里自己實現,是想將MFC的窗口句柄交給

  Cocos2D的渲染層。

  CCocos2DApp需要在AppDelegate的基礎上,添加兩個方法,代碼如下:

 1 /**
 2 @brief 系統初始化
 3 @param uWnd 窗口句柄
 4 @param nWidth 窗口寬
 5 @param nHeight 窗口高
 6 */
 7 void CCocos2DApp::init( uint32 uWnd, int32 nWidth, int32 nHeight )/// uint32 int32 是自定義數據類型 就是int 和 unsinged int
 8 {
 9     m_uWnd = uWnd;
10 
11     cocos2d::CCEGLView* eglView = cocos2d::CCEGLView::sharedOpenGLView();
12     if( eglView == NULL )
13     {
14         return;
15     }
16 
17     eglView->setFrameSize(nWidth, nHeight, (HWND)m_uWnd); /// 這里調用已經修改的cocos2d::CCEGLView方法
18 
19     // Initialize instance and cocos2d.
20     if (!applicationDidFinishLaunching())
21     {
22         return;
23     }
24 
25     n_bCocos2DInit = true;
26 }
27 //------------------------------------------------------------------------------
28 /**
29 @brief 系統運行 重載基類的run方法
30 @param
31 */
32 int CCocos2DApp::run()
33 {
34     if( m_uWnd == 0 )
35     {
36         return cocos2d::CCApplication::run();  /// 如果是引擎底層創建窗口,在消息循環在這里執行,在消息循環中執行mainLoop
37     }
38     else
39     {
40         if( n_bCocos2DInit)
41         {
42             cocos2d::CCDirector::sharedDirector()->mainLoop(); 
43         }
44 
45         return 1;
46     }
47 }

   再把HelloWorldScene添加到工程。運行HelloCPP需要的圖片資源,也需要復制到工作目錄下。

  第四步 將CCocos2DApp添加到CMFCTestView中

    在CMFCTestView.h中添加

    CCocos2DApp       m_Cocos2DApp;

  我們希望在窗口初始后,調用CCocos2DApp的init方法,實現渲染環境初始化。

  切換到類視圖,選擇屬性,選擇重寫(Overide)標簽,找到OnInitialUpdate,添加重寫CMFCTestView::InitUpdate方法

                                                      

  在其中添加代碼,調用CCocos2DApp::init,代碼如下:

 1 void CMFCTestView::OnInitialUpdate()
 2 {
 3     CView::OnInitialUpdate();
 4 
 5     // TODO: 在此添加專用代碼和/或調用基類
 6 
 7     RECT rc;
 8     ::GetClientRect( m_hWnd, &rc );
 9 
10     m_Cocos2DApp.init( (uint32)m_hWnd, rc.right - rc.left, rc.bottom - rc.top ); /// 傳入窗口句柄和寬高
11     SetTimer( 1, 30, NULL ); /// 見下面的分析
12 }

  在之前分析中已經提到,HelloCpp的Render是在消息循環中執行的,MFC窗口接管消息循環,我們Render放在哪里比較好呢?因為我們是用來制作工具,對渲染效率要求不是很高,所以我想用定時器來驅動我們的Render。所以在初始化的時候啟動了一個定時器。

  在CMFCTestView中添加定時器處理方法:

1 // CMFCTestView 消息處理程序
2 void CMFCTestView::OnTimer(UINT_PTR nIDEvent)
3 {
4     // TODO: 在此添加消息處理程序代碼和/或調用默認值
5     m_Cocos2DApp.run(); /// 這里執行mainLoop,在mainLoop中執行Render
6     CView::OnTimer(nIDEvent);
7 }

  編譯運行應當就可以看到可愛的Cocoser了,如下圖

                                       

   畫面是渲染出來了,可是卻收不到鼠標消息。原來引擎實現了CCEGLView::WindowProc用來處理Windows消息,系統在這里模擬了觸屏的一些操作。那我們只要將MFC的窗口消息傳遞給CCEGLView::WindowProc就可以實現了。

  在CMFCTestView中添加重寫(Ovreide)函數WindProc,代碼如下:

/**
@brief 窗口回調函數
@param
*/
LRESULT CCocos2DApp::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    cocos2d::CCEGLView* eglView = cocos2d::CCEGLView::sharedOpenGLView();
    if( eglView )
    {
        return eglView->WindowProc( message, wParam, lParam );
    }

    return 0;
}

   這樣就可以正常收到windows窗口消息了。

 還有一個需要注意的問題,當窗口大小發生變化,我們也需要通知到引擎底層,所以我們需要再添加一個針對WM_SIZE的處理函數,函數代碼如下:

1 void CMFCTestView::OnSize(UINT nType, int cx, int cy)
2 {
3     CView::OnSize(nType, cx, cy);
4 
5     // TODO: 在此處添加消息處理程序代碼
6     m_Cocos2DApp.OnSize( cx, cy );
7 }

  在CCocos2DApp::OnSize中再對底層進行相關的設置,我們看下代碼:

/**
@brief 重置大小
@param
*/
void CCocos2DApp::OnSize( int nWidth, int nHeight )
{
    // TODO: 在此處添加消息處理程序代碼  
    cocos2d::CCEGLView* eglView = cocos2d::CCEGLView::sharedOpenGLView();
    if( eglView )
    {if( n_bCocos2DInit )  
        {  
            //重新設置窗口大小及投影矩陣  
            cocos2d::CCEGLView::sharedOpenGLView()->resize(nWidth,nHeight);  
            cocos2d::CCDirector::sharedDirector()->reshapeProjection(cocos2d::CCSizeMake(nWidth,nHeight));
        }
    }    
}

  至此,你已經看到怎樣將Cocos2DX渲染到MFC窗口的全過程,相信聰明的你也一定可以制作你自己想要的游戲工具,最后希望大家Coding happy!!!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM