cocos2d-X 2.0嵌入MFC的子窗体的方法

 Cocos2d-X 2.0嵌入MFC的子窗体的方法(1.0姐妹篇)

 

           本节所用Cocos2d-x版本:cocos2d-2.0-x-2.0.2

          之前讲解了如何将Cocos2d-x 1.0版本嵌入MFC的窗体中的具体方法,应朋友们的要求,将Cocos2d-x 2.0版本嵌入MFC窗体的方法也整理发布。

 

      首先,我们用VC++在Cocos2d-x的目录里建立了个Unicode字符集MFC对话框程序。这里命名为Cocos2dXEditor。按照HelloWorld工程设置把包含头文件目录,库文件目录,包含库都设置好。并且画好对话框界面

如图:

 

 

    我把界面设计为三部分,左边和右边用来创建对话框面板,至于要具体显示什么根据我们的工具开发需求而定。暂时先不管。而中间放一个Picture控件,做为Cocos2d-x的显示窗口。为了生成相应的窗口控件变量,我们需要将此Picture控件的ID改成自定义的一个ID,这里我们改成IDC_COCOS2DXWIN保存。

    我们画好界面后,选中Picture控件后右键单击,在弹出菜单中找到添加变量。为Picture控件生成一个控件变量。这里命名为m_Cocos2dXWin,并点击完成。


 好,现在主对话框类里有了这么一句:

 

[cpp]  view plain copy
  1. public:  
  2.          CStatic      m_Cocos2dXWin;  

     这个变量是CStatic类型的,它只是一个最简单的CWnd派生类。并不能显示Cocos2d-x。我们需要做点工作来创建一个新的类来替代它完成显示Cocos2d-x的工作。

     在工程右键弹出菜单里找到“添加”一项,在其子菜单中点击“类”。


在弹出的“添加类”对话框中“MFC”项中的“MFC类”。



        点击“添加”。这时会弹出“MFC类向导”对话框。我们在类名里输入“CCocos2dXWin”,并选择基类CWnd。然后点击“完成”。


        向导会为我们的工程自动加入两个文件“Cocos2dXWin.h”和“Cocos2dXWin.cpp”。这才是我们要进行Cocos2d-x显示的窗体类,它的基类是CWnd,与Picture控件有相同的基类。

        打开Cocos2dXWin.h,在CCocos2dXWin类中增加一个public成员函数声明:

[cpp]  view plain copy
  1. //创建Cocos2dX窗口  
  2. BOOL        CreateCocos2dXWindow();  

       我们另外增加一个private变量用来标记窗口是否已经进行了Cocos2d-x的OpenGL窗口创建。

[cpp]  view plain copy
  1. private:  
  2.     //是否已经初始化  
  3.     BOOL                m_bInitCocos2dX;  

       在CPP文件中加入需要用到的头文件:

[cpp]  view plain copy
  1. #include "../Classes/AppDelegate.h"  
  2. #include "cocos2d.h"  

       下面来手动增加函数定义:

[cpp]  view plain copy
  1. //创建Cocos2dX窗口  
  2. BOOL    CCocos2DXWin::CreateCocos2dXWindow()  
  3. {  
  4.     //新建一个CRect变量获取窗口的客户区大小  
  5.     CRect   tClientRect;  
  6.     GetClientRect(&tClientRect);  
  7.     //取得使用的OpenGL视窗  
  8.     CCEGLView* eglView = CCEGLView::sharedOpenGLView();  
  9.     //以指定的客户区大小创建视窗,这里我们对setFrameSize增加了参数3以传入当前控件的窗口句柄。  
  10.     eglView->setFrameSize(tClientRect.Width(),tClientRect.Height(),GetSafeHwnd());  
  11.     //调用程序的运行函数,增加参数bool型变量控制是否进行消息循环。因为MFC控件本身有自已的消息响应处理。如果不改动的话,这里就会进入死循环。  
  12.     cocos2d::CCApplication::sharedApplication()->run(false);  
  13.     //这里将变量设置为TRUE  
  14.     m_bInitCocos2dX = TRUE;  
  15.     return TRUE;  
  16. }  

       我们要修改两处地方。我们先对CCApplication动个小手术,使相应参数能够发挥作用。

       找到CCApplication.cpp中的run函数做以下修改:     

[cpp]  view plain copy
  1. [Cocos2d-x相关教程来源于红孩儿的游戏编程之路 CSDN博客地址:http://blog.csdn.net/honghaier]    
  2.   
  3. int CCApplication::run(bool bMsgLoop)  
  4. {  
  5.     PVRFrameEnableControlWindow(false);  
  6.   
  7.     // Main message loop:  
  8.     MSG msg;  
  9.     LARGE_INTEGER nFreq;  
  10.     LARGE_INTEGER nNow;  
  11.     //帧定时器取得CPU时钟频率和计数  
  12.     QueryPerformanceFrequency(&nFreq);  
  13.     QueryPerformanceCounter(&m_nLast);  
  14.   
  15.     // 调用派生类的程序启动处理函数  
  16.     if (!applicationDidFinishLaunching())  
  17.     {  
  18.         return 0;  
  19.     }  
  20.       CCEGLView* pMainWnd = CCEGLView::sharedOpenGLView();  
  21.     //手动修改  
  22.      if(true == bMsgLoop)  
  23.     {  
  24.         //窗口居中显示  
  25.         mainWnd->centerWindow();  
  26.         ShowWindow(mainWnd->getHWnd(), SW_SHOW);  
  27.   
  28.         while (1)  
  29.         {  
  30.             if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))  
  31.             {  
  32.                 // Get current time tick.  
  33.                 QueryPerformanceCounter(&nNow);  
  34.   
  35.                 //由帧间隔来控制刷新  
  36.                 if (nNow.QuadPart - m_nLast.QuadPart > m_nAnimationInterval.QuadPart)  
  37.                 {  
  38.                     m_nLast.QuadPart = nNow.QuadPart;  
  39.                     CCDirector::sharedDirector()->mainLoop();  
  40.                 }  
  41.                 else  
  42.                 {  
  43.                     Sleep(0);  
  44.                 }  
  45.                 continue;  
  46.             }  
  47.             //如果收到退出消息,中断消息循环。  
  48.             if (WM_QUIT == msg.message)  
  49.             {  
  50.                 // Quit message loop.  
  51.                 break;  
  52.             }  
  53.   
  54.             // 按键消息处理  
  55.             if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))  
  56.             {  
  57.                 TranslateMessage(&msg);  
  58.                 DispatchMessage(&msg);  
  59.             }  
  60.         }  
  61.   
  62.         return (int) msg.wParam;  
  63.     }  
  64.     return 0;  
  65. }  

          修改.h中函数声明与cpp保持一致,再打开CCEGLView.cpp,找到setFrameSize函数,继续手术:

[cpp]  view plain copy
  1. void CCEGLView::setFrameSize(float width, float height,HWND hWnd)  
  2. {  
  3.         //由指定的大小和句柄创建窗体  
  4.     Create((LPCTSTR)m_szViewName, (int)width, (int)height,hWnd);  
  5.         //调用基类的setFrameSize函初始化整屏幕和分辨率  
  6.         CCEGLViewProtocol::setFrameSize(width, height);  
  7. }  

        再找到Create函数:

[cpp]  view plain copy
  1. //创建窗口  
  2. bool CCEGLView::Create(LPCTSTR pTitle, int w, int h, HWND hWnd)  
  3. {  
  4.     bool bRet = false;  
  5.     do   
  6.     {  
  7.         //如果已经创建了窗体,直接返回,不允许再重复创建。  
  8.         CC_BREAK_IF(m_hWnd);  
  9.         //在这里做个判断,如果参数中的窗口句柄不为空,则使用参数句柄做为成员变量m_hWnd的值。  
  10.         if(hWnd)  
  11.         {  
  12.             m_hWnd = hWnd ;  
  13.             //新增bool变量m_bIsPopupWin,用于标记是否使用已经创建好的WINDOWS控件窗口句柄做为当前OpenGL视窗的WINDOWS窗口句柄。  
  14.             m_bIsPopupWin = false;  
  15.         }  
  16.         else  
  17.         {  
  18.   
  19.             HINSTANCE hInstance = GetModuleHandle( NULL );  
  20.             WNDCLASS  wc;       // Windows Class Structure  
  21.   
  22.             // Redraw On Size, And Own DC For Window.  
  23.             wc.style          = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;    
  24.             wc.lpfnWndProc    = _WindowProc;                    // WndProc Handles Messages  
  25.             wc.cbClsExtra     = 0;                              // No Extra Window Data  
  26.             wc.cbWndExtra     = 0;                              // No Extra Window Data  
  27.             wc.hInstance      = hInstance;                      // Set The Instance  
  28.             wc.hIcon          = LoadIcon( NULL, IDI_WINLOGO );  // Load The Default Icon  
  29.             wc.hCursor        = LoadCursor( NULL, IDC_ARROW );  // Load The Arrow Pointer  
  30.             wc.hbrBackground  = NULL;                           // No Background Required For GL  
  31.             wc.lpszMenuName   = NULL;                           // We Don't Want A Menu  
  32.             wc.lpszClassName  = kWindowClassName;               // Set The Class Name  
  33.   
  34.             CC_BREAK_IF(! RegisterClass(&wc) && 1410 != GetLastError());          
  35.   
  36.             // center window position  
  37.             RECT rcDesktop;  
  38.             GetWindowRect(GetDesktopWindow(), &rcDesktop);  
  39.   
  40.                WCHAR wszBuf[50] = {0};  
  41.             MultiByteToWideChar(CP_UTF8, 0, m_szViewName, -1, wszBuf, sizeof(wszBuf));  
  42.   
  43.             // create window  
  44.             m_hWnd = CreateWindowEx(  
  45.                 WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, // Extended Style For The Window  
  46.                 kWindowClassName,                                   // Class Name  
  47.                 wszBuf,                                             // Window Title  
  48.                 WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBOX,       // Defined Window Style  
  49.                 0, 0,                                               // Window Position  
  50.                 0,                                                  // Window Width  
  51.                 0,                                                  // Window Height  
  52.                 NULL,                                               // No Parent Window  
  53.                 NULL,                                               // No Menu  
  54.                 hInstance,                                          // Instance  
  55.                 NULL );  
  56.         }  
  57.         //判断窗口句柄有效  
  58. CC_BREAK_IF(! m_hWnd);  
  59.        //调整窗口大小  
  60.         resize(w, h);  
  61.          //初始化OpenGL  
  62.                  bRet = initGL();  
  63.              CC_BREAK_IF(!bRet);  
  64.                  s_pMainWindow = this;  
  65. bRet = true;  
  66.     } while (0);  
  67.   
  68.     return bRet;  
  69. }  

        修改.h中函数声明与cpp保持一致。这样我们就完成了对于创建窗口函数的修改。但是因为屏蔽了原窗口的创建,所以也同时屏蔽了Cocos2d-x对于窗口消息的处理。我们在类视图中找到CCocos2dXWin,在右键弹出菜单中点击“属性”。打开类属性编辑框。



      在WindowProc一栏中增加函数WindorProc,完成后进入CCocos2dXWin的WindorProc函数,因为我们在CCEGLView的Create函数中可以找到在注册窗口类时,其设定的窗口消息处理回调函数是WindowProc.而其实例对象是单件。故可以这样修改:

[cpp]  view plain copy
  1. LRESULT CCocos2DXWin::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)  
  2. {  
  3.     // 这里我们先判断一下是否初始化完成Cocos2dX再进行消息处理  
  4.     if(m_bInitCocos2dX)  
  5.     {  
  6.         CCEGLView::sharedOpenGLView()->WindowProc(message, wParam, lParam);  
  7.     }  
  8.     return CWnd::WindowProc(message, wParam, lParam);  
  9. }  

       这样Cocos2dXWin所实例化的窗口就可以接收到鼠标等对于窗口的消息了。但是!我们屏幕了Cocos2d-x的消息循环,而Cocos2d-x的渲染处理函数是在消息循环中调用的。我们就算现在运行程序,也看不到帅气的小怪物。所以我们必须想办法实时的调用Cocos2d-x的渲染处理函数。对于工具来说,我们并不需要考虑太高的FPS刷新帧数,所以我们只需要使用一个定时器,并在定时触发函数中进行Cocos2d-x的渲染处理函数的调用就可以了。

       在CCocos2DXWin::CreateCocos2dXWindow()函数尾部加入一句:

      

        SetTimer(1,1,NULL);


       加入一个ID为1的定时器,设置它每1毫秒响应一次,其处理函数为默认,则使用CCocos2DXWin的WINDOWS消息处理函数对于WM_TIMER进行响应就可以了。

       按之前增加WindorProc函数的方式找到CCocos2DXWin的消息WM_TIMER一项,增加函数OnTimer.如图:




         在其生成的函数OnTimer中我们新写一个函数调用,则每次定时响应调用:

[cpp]  view plain copy
  1. void CCocos2DXWin::OnTimer(UINT_PTR nIDEvent)  
  2. {  
  3.     //我们写一个renderWorld函数代表Cocos2d-x的世界渲染  
  4.     cocos2d::CCApplication::sharedApplication()->renderWorld();  
  5.     CWnd::OnTimer(nIDEvent);  
  6. }  

          我们打开CCApplication.h,为CCApplication类增加一个public的renderWorld函数。

[cpp]  view plain copy
  1. //帧循环调用渲染  
  2.     bool    renderWorld();  

          在cpp中我们将消息循环中的相关处理移入进来

[cpp]  view plain copy
  1. bool CCApplication::renderWorld()  
  2. {     
  3.     LARGE_INTEGER nNow;  
  4.     // Get current time tick.  
  5.     QueryPerformanceCounter(&nNow);  
  6.   
  7.     // If it's the time to draw next frame, draw it, else sleep a while.  
  8.     if (nNow.QuadPart - m_nLast.QuadPart > m_nAnimationInterval.QuadPart)  
  9.     {  
  10.         m_nLast.QuadPart = nNow.QuadPart;  
  11.         CCDirector::sharedDirector()->mainLoop();  
  12.         return true;  
  13.     }  
  14.     return false;  
  15. }  

        OK,这样我们就可以在外部来调用Cocos2d-x的显示设备进行渲染处理了。当然,我们也不能忘了在CCocos2dXWin窗口销毁时把定时器资源进行一下释放。找到类消息WM_DESTROY新增函数OnDestroy。

 



[cpp]  view plain copy
  1. void CCocos2DXWin::OnDestroy()  
  2. {  
  3.     //在Cocos2d-x的引擎文件CCEGLView_win32.cpp中CCEGLView::release()会再次发送调用DestroyWindow,所以这里用变量m_bInitCocos2dX做下判断,避免二次销毁  
  4.     if(TRUE == m_bInitCocos2dX)  
  5.     {  
  6.         //退出将m_bInitCocos2dX设为FALSE  
  7.         m_bInitCocos2dX = FALSE;  
  8.         //释放定时器资源  
  9.         KillTimer(1);  
  10.         CWnd::OnDestroy();  
  11.     }  
  12. }  

          我们知道在Cocos2d-x程序退出时先后响应WM_CLOSE和WM_DESTROY。在CCEGLView::WindowProc可以看到


[cpp]  view plain copy
  1. case WM_CLOSE:  
  2.     CCDirector::sharedDirector()->end();  
  3.     break;  
  4.   
  5. case WM_DESTROY:  
  6.                destroyGL();  
  7.     PostQuitMessage(0);  
  8.     break;  

         而CCDirector的end函数并不是立即执行停止,他设置成员变量m_bPurgeDirecotorInNextLoop为true,并在下一帧mainLoop循环时调用purgeDirector()函数进行显示设备和场景的最终释放。所以我们要想在窗口退出时释放Cocos2d-x显示设备和场景,必须做一下相关处理。理解了这一点。其实就很简单了。

          只需要在CCocos2DXWin::OnDestroy()中增加两句代码即可:

[cpp]  view plain copy
  1. void CCocos2DXWin::OnDestroy()  
  2. {  
  3.     …  
  4.     KillTimer(1);  
  5.     //调用显示设备单件实例对象的end函数  
  6.     CCDirector::sharedDirector()->end();  
  7.     //处理一次下一帧,必须调用.  
  8.     CCDirector::sharedDirector()->mainLoop();  
  9.     CWnd::OnDestroy();  
  10.     }  
  11. }  

OK,CCocos2DXWin类基本完成。现在在Cocos2dXEditorDlg.h中将

CStatic            m_Cocos2dXWin;

改为

CCocos2DXWin       m_Cocos2dXWin;

 

       并将CCocos2DXWin的头文件包含进来就可以顺利编译成功。在控件初始化(比如OnInitDialog函数)中调用一下m_Cocos2dXWin.CreateMainCoco2dWindow()。此时我们就算完成了将Cocos2d-x嵌入MFC的CWnd控件的过程。不过,您需要在窗口大小变化时对这个控件重设位置及大小以及投影矩阵。

[cpp]  view plain copy
  1. void CCocos2DXWin::OnSize(UINT nType, int cx, int cy)  
  2. {  
  3.     CWnd::OnSize(nType, cx, cy);  
  4.     // TODO: 在此处添加消息处理程序代码  
  5.     if(TRUE == m_bInitCocos2dX)  
  6.     {  
  7.         CRect   tClientRect;  
  8.         GetClientRect(&tClientRect);  
  9.         //重新设置窗口大小及投影矩阵  
  10.         CCEGLView::sharedOpenGLView()->resize(tClientRect.Width(),tClientRect.Height());  
  11.         CCDirector::sharedDirector()->reshapeProjection(CCSizeMake(tClientRect.Width(),tClientRect.Height()));  
  12.         }  
  13. }  


做下总结:

     与1.0相比,作者删除了对于过多的目标平台的区分。(1)只保留了WINDOWS,ANDROID,IOS三个目标平台,(2)各类更清晰紧凑了,CCEGLView与CCApplication之间的耦合性大大降低。(3)将引用改成了指针,这点需要多留心。


最后运行一下看下成果吧。

       下面是我我现在做的编辑器的截图:


       

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值