• 将Cocos2dX渲染到MFC窗口上


    引用:http://www.cnblogs.com/windeer/archive/2012/11/18/2767750.html

    引言

            现在智能手机已经慢慢进入大众化,移动类应用开始火爆起来,游戏类应用更是占据了手机用户的大部分碎片时间。

            现在手机开发游戏也逐渐流行开来,手机的平台目前主打是 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
    ",glewGetErrorString(GlewInitResult));
            return false;
        }
    
        if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader)
        {
            CCLog("Ready for GLSL
    ");
        }
        else 
        {
            CCLog("Not totally ready :( 
    ");
        }
    
        if (glewIsSupported("GL_VERSION_2_0"))
        {
            CCLog("Ready for OpenGL 2.0
    ");
        }
        else
        {
            CCLog("OpenGL 2.0 not supported
    ");
        }
        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!!!

  • 相关阅读:
    ssh协议运用
    linux系统安装oracle遇到的问题
    Qt中mysql编译出错问题
    winform控件添加鼠标事件
    Linux网络编程
    多线程
    守护进程
    openssl 生成自签CA和pkcs12证书
    Linux CPU使用率获取 c
    temp
  • 原文地址:https://www.cnblogs.com/sode/p/3156928.html
Copyright © 2020-2023  润新知