在MFC 窗口中运行 cocos2d-x 3.2 (二) 让其在MFC picture控件中运行

时间:2023-01-22 00:21:28

上一篇我们配置了运行环境,但是并不完美,MFC窗口 和 cosos2d 窗口是分开运行的。 如果用来做工具 看起来不太好看,这一篇我们将修改cocos2d 代码,让其运行在MFC控件上

参考:http://blog.csdn.net/akof1314/article/details/8133800


要把cocos2d 窗口运行在 MFC 控件上, 我们就要找到这个窗口的句柄,下面我们来一步步找,看看怎样得到这个窗口句柄


1.首先我们来分析cocos2d的运行机制

打开cocos2d::Application::getInstance()->run(); run()函数的源码:

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

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

    QueryPerformanceFrequency(&nFreq);
    QueryPerformanceCounter(&nLast);
    //这里调用了AppDelegate中的<span style="font-family: Arial, Helvetica, sans-serif;">applicationDidFinishLaunching()

    // Initialize instance and cocos2d.
    if (!applicationDidFinishLaunching())
    {
        return 0;
    }
    //那么游戏窗口一定是在这之前创建的</span>
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();

    // Retain glview to avoid glview being released in the while loop
    glview->retain();
   //下面是游戏主循环</span>
    while(!glview->windowShouldClose())
    {
        QueryPerformanceCounter(&nNow);
        if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart)
        {
            nLast.QuadPart = nNow.QuadPart;
            
            director->mainLoop();
            glview->pollEvents();
        }
        else
        {
            Sleep(0);
        }
    }

    // Director should still do a cleanup if the window was closed manually.
    if (glview->isOpenGLReady())
    {
        director->end();
        director->mainLoop();
        director = nullptr;
    }
    glview->release();
    return true;
}
2. 于是我们查看AppDelegate::applicationDidFinishLaunching() 代码:

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();
    if(!glview) {
    //第一次运行肯定会进入这里,这个create函数创建了GLView对象
        glview = GLView::create("My Game");
        director->setOpenGLView(glview);
    }

    // turn on display FPS
    director->setDisplayStats(true);

    // set FPS. the default value is 1.0/60 if you don't call this
    director->setAnimationInterval(1.0 / 60);

    // create a scene. it's an autorelease object
    auto scene = HelloWorld::createScene();

    // run
    director->runWithScene(scene);

    return true;
}
3. 我们看看GLView::create函数代码

GLView* GLView::create(const std::string& viewName)
{
    auto ret = new GLView;
 //调用了initWithRect()</span>
    if(ret && ret->initWithRect(viewName, Rect(0, 0, 960, 640), 1)) {
        ret->autorelease();
        return ret;
    }

    return nullptr;
}


4.initWithRect 代码

bool GLView::initWithRect(const std::string& viewName, Rect rect, float frameZoomFactor)
{
    setViewName(viewName);

    _frameZoomFactor = frameZoomFactor;

    glfwWindowHint(GLFW_RESIZABLE,GL_FALSE);

//找到了,glfwCreateWindow 这个函数就是创建窗口的(我只知道glfw是个open gl 的库,想了解的可以去搜一下),下面我们来取窗口句柄

    _mainWindow = glfwCreateWindow(rect.size.width * _frameZoomFactor,
                                   rect.size.height * _frameZoomFactor,
                                   _viewName.c_str(),
                                   _monitor,
                                   nullptr);
    glfwMakeContextCurrent(_mainWindow);

    glfwSetMouseButtonCallback(_mainWindow, GLFWEventHandler::onGLFWMouseCallBack);
    glfwSetCursorPosCallback(_mainWindow, GLFWEventHandler::onGLFWMouseMoveCallBack);
    glfwSetScrollCallback(_mainWindow, GLFWEventHandler::onGLFWMouseScrollCallback);
    glfwSetCharCallback(_mainWindow, GLFWEventHandler::onGLFWCharCallback);
    glfwSetKeyCallback(_mainWindow, GLFWEventHandler::onGLFWKeyCallback);
    glfwSetWindowPosCallback(_mainWindow, GLFWEventHandler::onGLFWWindowPosCallback);
    glfwSetFramebufferSizeCallback(_mainWindow, GLFWEventHandler::onGLFWframebuffersize);
    glfwSetWindowSizeCallback(_mainWindow, GLFWEventHandler::onGLFWWindowSizeFunCallback);

    setFrameSize(rect.size.width, rect.size.height);

    // check OpenGL version at first
    const GLubyte* glVersion = glGetString(GL_VERSION);

    if ( utils::atof((const char*)glVersion) < 1.5 )
    {
        char strComplain[256] = {0};
        sprintf(strComplain,
                "OpenGL 1.5 or higher is required (your version is %s). Please upgrade the driver of your video card.",
                glVersion);
        MessageBox(strComplain, "OpenGL version too old");
        return false;
    }

    initGlew();

    // Enable point size by default.
    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);

    return true;
}
5. 有个API 函数glfwGetWin32Window() 可以取得窗口句柄。(由于不了解glfw 网上搜了半天才找到这个。。。)

参考: http://www.glfw.org/docs/3.0/group__native.html


6.下面开始修改代码

在CCGLView.cpp 前面添加(头文件一定要在宏定义后面

#define GLFW_EXPOSE_NATIVE_WIN32
#define GLFW_EXPOSE_NATIVE_WGL
#include "glfw3native.h"

给GLView类添加成员:

HWND getHwnd();
void closeWindow();
HWND   m_hwnd;

HWND GLView::getHwnd()
{
	return m_hwnd;
}

void GLView::closeWindow()
{
	glfwSetWindowShouldClose(_mainWindow, 1);
}


修改函数 bool GLView::initWithRect(const std::string& viewName, Rect rect, float frameZoomFactor)

在函数最后添加

m_hwnd = glfwGetWin32Window(_mainWindow);


7.  打开Application 类添加一个 int cocosrun()  public成员函数

int Application::cocosrun()
{
	PVRFrameEnableControlWindow(false);

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

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

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

    auto director = Director::getInstance();
    auto glview = director->getOpenGLView();

    // Retain glview to avoid glview being released in the while loop
    glview->retain();

ShowWindow(glview->getHwnd(), SW_SHOW);

    while(!glview->windowShouldClose())
    {
        QueryPerformanceCounter(&nNow);
        if (nNow.QuadPart - nLast.QuadPart > _animationInterval.QuadPart)
        {
            nLast.QuadPart = nNow.QuadPart;
            
            director->mainLoop();
            glview->pollEvents();
        }
        else
        {
            Sleep(0);
        }
    }

    // Director should still do a cleanup if the window was closed manually.
    if (glview->isOpenGLReady())
    {
        director->end();
        director->mainLoop();
        director = nullptr;
    }
    glview->release();
    return true;
}

8.  打开Application 类添加一个 void  closeWindow() public成员函数

void  Application::closeWindow()
{
	auto director = cocos2d::Director::getInstance();
    auto glview = director->getOpenGLView();
	glview->closeWindow();
}

9. 给类 AppDelegate添加 成员 

HWND m_hwnd;
RECT m_parentRect;
void AppDelegate::setParent(HWND hwnd,RECT rect)
{
	m_hwnd = hwnd;
	m_parentRect.left = rect.left;
	m_parentRect.top  = rect.top;
	m_parentRect.right = rect.right;
	m_parentRect.bottom = rect.bottom;
}

10 修改AppDelegate::applicationDidFinishLaunching()  函数

bool AppDelegate::applicationDidFinishLaunching() {
    // initialize director
    auto director = Director::getInstance();
	auto glview = director->getOpenGLView();
    if(!glview) {
        glview = GLView::create("My Game");
        director->setOpenGLView(glview);
    }

::SetParent(glview->getHwnd(), m_hwnd);
	SetWindowLong(glview->getHwnd(), GWL_STYLE, GetWindowLong(glview->getHwnd(), GWL_STYLE) & ~WS_CAPTION );
	::SetWindowPos(glview->getHwnd(), HWND_TOP, m_parentRect.left, m_parentRect.top, m_parentRect.right - m_parentRect.left, m_parentRect.bottom - m_parentRect.top, SWP_NOCOPYBITS | SWP_HIDEWINDOW);
	
    // turn on display FPS
    director->setDisplayStats(true);

    // set FPS. the default value is 1.0/60 if you don't call this
    director->setAnimationInterval(1.0 / 60);

    // create a scene. it's an autorelease object
	auto scene = HelloWorld::createScene();

    // run
    director->runWithScene(scene);

    return true;
}


11. 给对话框添加一个 Picture 控件(注意更改默认 ID),并为其添加Control 类型成员变量 m_cocosWin , 修改上篇中button的消息响应函数

void CCocosEditorDlg::OnBnClickedButton1()
{
	AppDelegate app;
	RECT rc;
	m_cocos2dWin.GetClientRect(&rc);
	app.setParent(m_cocos2dWin.m_hWnd, rc);
	cocos2d::Application::getInstance()->cocosrun();
}

12. 关闭cocos2d窗口, 在类向导中 给MFC对话框窗口添加 WM_CLOSE消息响应函数:

void CCocosEditorDlg::OnClose()
{
	cocos2d::Application::getInstance()->closeWindow();
	CDialogEx::OnClose();
}

13.编译运行程序:

在MFC 窗口中运行 cocos2d-x 3.2 (二) 让其在MFC picture控件中运行



14 .运行时会发现 cocos2d窗口闪了下,这个原因是cocos2d先创建,然后移到了Picture控件上,那我们让Cocos2d的窗口创建时 先不可见:

bool GLView::initWithRect(const std::string& viewName, Rect rect, float frameZoomFactor)
{
    setViewName(viewName);

    _frameZoomFactor = frameZoomFactor;

    glfwWindowHint(GLFW_RESIZABLE,GL_FALSE);
    glfwWindowHint(GLFW_VISIBLE , GL_FALSE);
    _mainWindow = glfwCreateWindow(rect.size.width * _frameZoomFactor,
                                   rect.size.height * _frameZoomFactor,
                                   _viewName.c_str(),
                                   _monitor,
                                   nullptr);
    glfwMakeContextCurrent(_mainWindow);

    glfwSetMouseButtonCallback(_mainWindow, GLFWEventHandler::onGLFWMouseCallBack);
    glfwSetCursorPosCallback(_mainWindow, GLFWEventHandler::onGLFWMouseMoveCallBack);
    glfwSetScrollCallback(_mainWindow, GLFWEventHandler::onGLFWMouseScrollCallback);
    glfwSetCharCallback(_mainWindow, GLFWEventHandler::onGLFWCharCallback);
    glfwSetKeyCallback(_mainWindow, GLFWEventHandler::onGLFWKeyCallback);
    glfwSetWindowPosCallback(_mainWindow, GLFWEventHandler::onGLFWWindowPosCallback);
    glfwSetFramebufferSizeCallback(_mainWindow, GLFWEventHandler::onGLFWframebuffersize);
    glfwSetWindowSizeCallback(_mainWindow, GLFWEventHandler::onGLFWWindowSizeFunCallback);

    setFrameSize(rect.size.width, rect.size.height);

    // check OpenGL version at first
    const GLubyte* glVersion = glGetString(GL_VERSION);

    if ( utils::atof((const char*)glVersion) < 1.5 )
    {
        char strComplain[256] = {0};
        sprintf(strComplain,
                "OpenGL 1.5 or higher is required (your version is %s). Please upgrade the driver of your video card.",
                glVersion);
        MessageBox(strComplain, "OpenGL version too old");
        return false;
    }

    initGlew();

    // Enable point size by default.
    glEnable(GL_VERTEX_PROGRAM_POINT_SIZE);

m_hwnd = glfwGetWin32Window(_mainWindow);</span>

    return true;
}

这样就解决闪一下的问题


转载请注明出处。


Test下载地址:点击打开链接