Cocos2dx 3.0 以上版本 集成 MFC

来源:互联网 发布:sql union 去重复 编辑:程序博客网 时间:2024/06/08 15:12

之前写过一篇将cocos2dx-2.x版本集成到MFC的文章,现在到了cocos2dx-3.x版本,之前的方法已经行不通了,最近有点时间,研究了一下,算是做出来点样子,下面写一下步骤,算是做个笔记。

我采用的方案步骤很多,改动量也比较大, 对于那些期望只修改几行就能达到目的的人来说,让你们失望了-_-,但本着学习的目的,改的越多不就意味着你学的越多么:)

首先要改的不是cocos2dx,而是GLFW。原因是因为cocos2dx-3.x底层渲染是基于GLFW的,但GLFW是不支持渲染到控件的,所以要改一下它。

下载GLFW源码,地址:http://www.glfw.org/download.html

解压后用CMake生成win32工程,然后用vs打开,整个工程是这样的:

我们要修改的就是glfw这个项目。

在glfw3.h中增加函数声明

GLFWAPI GLFWwindow* glfwCreateWindowEx(void* winHwnd, int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share);

然后在window.c中实现此函数

GLFWAPI GLFWwindow* glfwCreateWindowEx(void* winHwnd, int width, int height, const char* title, GLFWmonitor* monitor, GLFWwindow* share){_GLFWfbconfig fbconfig;_GLFWctxconfig ctxconfig;_GLFWwndconfig wndconfig;_GLFWwindow* window;_GLFWwindow* previous;_GLFW_REQUIRE_INIT_OR_RETURN(NULL);if (width <= 0 || height <= 0){_glfwInputError(GLFW_INVALID_VALUE, "Invalid window size");return NULL;}fbconfig = _glfw.hints.framebuffer;ctxconfig = _glfw.hints.context;wndconfig = _glfw.hints.window;wndconfig.width = width;wndconfig.height = height;wndconfig.title = title;wndconfig.monitor = (_GLFWmonitor*)monitor;ctxconfig.share = (_GLFWwindow*)share;if (wndconfig.monitor){wndconfig.resizable = GL_FALSE;wndconfig.visible = GL_TRUE;wndconfig.focused = GL_TRUE;}// Check the OpenGL bits of the window configif (!_glfwIsValidContextConfig(&ctxconfig))return NULL;window = calloc(1, sizeof(_GLFWwindow));window->next = _glfw.windowListHead;_glfw.windowListHead = window;window->videoMode.width = width;window->videoMode.height = height;window->videoMode.redBits = fbconfig.redBits;window->videoMode.greenBits = fbconfig.greenBits;window->videoMode.blueBits = fbconfig.blueBits;window->videoMode.refreshRate = _glfw.hints.refreshRate;window->monitor = wndconfig.monitor;window->resizable = wndconfig.resizable;window->decorated = wndconfig.decorated;window->autoIconify = wndconfig.autoIconify;window->floating = wndconfig.floating;window->cursorMode = GLFW_CURSOR_NORMAL;// Save the currently current context so it can be restored laterprevious = _glfwPlatformGetCurrentContext();// Open the actual window and create its contextif (!_glfwPlatformCreateWindowEx(winHwnd, window, &wndconfig, &ctxconfig, &fbconfig)){glfwDestroyWindow((GLFWwindow*)window);_glfwPlatformMakeContextCurrent(previous);return NULL;}_glfwPlatformMakeContextCurrent(window);// Retrieve the actual (as opposed to requested) context attributesif (!_glfwRefreshContextAttribs(&ctxconfig)){glfwDestroyWindow((GLFWwindow*)window);_glfwPlatformMakeContextCurrent(previous);return NULL;}// Verify the context against the requested parametersif (!_glfwIsValidContext(&ctxconfig)){glfwDestroyWindow((GLFWwindow*)window);_glfwPlatformMakeContextCurrent(previous);return NULL;}// Clearing the front buffer to black to avoid garbage pixels left over// from previous uses of our bit of VRAMglClear(GL_COLOR_BUFFER_BIT);_glfwPlatformSwapBuffers(window);// Restore the previously current context (or NULL)_glfwPlatformMakeContextCurrent(previous);if (wndconfig.monitor){int width, height;_glfwPlatformGetWindowSize(window, &width, &height);window->cursorPosX = width / 2;window->cursorPosY = height / 2;_glfwPlatformSetCursorPos(window, window->cursorPosX, window->cursorPosY);}else{if (wndconfig.visible){if (wndconfig.focused)_glfwPlatformShowWindow(window);else_glfwPlatformUnhideWindow(window);}}return (GLFWwindow*)window;return NULL;}

在internal.h中增加函数声明

int _glfwPlatformCreateWindowEx(void* handle, _GLFWwindow* window,const _GLFWwndconfig* wndconfig,const _GLFWctxconfig* ctxconfig,const _GLFWfbconfig* fbconfig);

在win32_window.c文件中实现此函数

int _glfwPlatformCreateWindowEx(void* handle,_GLFWwindow* window,const _GLFWwndconfig* wndconfig,const _GLFWctxconfig* ctxconfig,const _GLFWfbconfig* fbconfig){int status;if (!createWindowEx(handle, window, wndconfig, ctxconfig, fbconfig))return GL_FALSE;status = _glfwAnalyzeContext(window, ctxconfig, fbconfig);if (status == _GLFW_RECREATION_IMPOSSIBLE)return GL_FALSE;if (window->monitor){_glfwPlatformShowWindow(window);//if (!enterFullscreenMode(window))//return GL_FALSE;}return GL_TRUE;}

在win32_window.c文件中添加函数

static int createWindowEx(void* phandle,_GLFWwindow* window,const _GLFWwndconfig* wndconfig,const _GLFWctxconfig* ctxconfig,const _GLFWfbconfig* fbconfig){window->win32.handle = (HWND)(phandle);if (!window->win32.handle){_glfwInputError(GLFW_PLATFORM_ERROR, "Win32: Failed to create window");return GL_FALSE;}if (_glfw_ChangeWindowMessageFilterEx){_glfw_ChangeWindowMessageFilterEx(window->win32.handle,WM_DROPFILES, MSGFLT_ALLOW, NULL);_glfw_ChangeWindowMessageFilterEx(window->win32.handle,WM_COPYDATA, MSGFLT_ALLOW, NULL);_glfw_ChangeWindowMessageFilterEx(window->win32.handle,WM_COPYGLOBALDATA, MSGFLT_ALLOW, NULL);}if (wndconfig->floating && !wndconfig->monitor){SetWindowPos(window->win32.handle,HWND_TOPMOST,0, 0, 0, 0,SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);}DragAcceptFiles(window->win32.handle, TRUE);if (!_glfwCreateContext(window, ctxconfig, fbconfig))return GL_FALSE;return GL_TRUE;}

编译,如果没有错误的话会生成新的库文件。这样glfw就修改完毕了,我们做的就是让glfw支持传入窗口句柄,并在句柄对应的窗体上渲染。

 

其实在这步做完后,就可以创建个MFC工程,在工程上添加个Picture控件,然后使用我们刚添加的函数渲染到控件中,但我们的目的是使用cocos2dx,这个就不做了,免得跑题:)

 

创建一个cocos2dx工程,名字随意,我起的名字是MFCTest。

将glfw的include\GLFW目录下的.h文件复制到MFCTest\cocos2d\external\glfw3\include\win32下。

将glfw的src\Debug中的glfw3.lib文件复制到MFCTest\cocos2d\external\glfw3\prebuilt\win32下。


接着在MFCTest下创建一个MFC工程,我起名叫MFCDialog

在这步选择对话框,简单方便:)

添加一个Picture控件

并将其ID改为IDC_PIC_COCOS2DX


给Picture控件添加变量,我起的名字是m_nPicCocos2dx

点击完成后,在MFCDialogDlg.h文件中会自动添加m_nPicCocos2dx的声明。

新生成的变量类型是CStatic,这个类无法完成我们的需求,所以我们要创建自己的类型。

右键工程=》添加=》类,选择MFC类,如图:

点击添加,类名随意,我写的是CCocos2dxWin。点击完成。

将m_nPicCocos2dx这个变量的类型改为我们新添加的类。


接下来我们要实现CCocos2DXWin类,那么首先,我们要将cocos2dx的文件包含到工程中,
首先添加头文件,选择项目属性=》配置属性=》C/C++ =》常规 =》 附加包含目录,配置如图:

加了一大堆,也没管有用没用-_-!。

接下来设置链接库文件与路径,首先设置库搜索路径,选择项目属性=》配置属性=》链接器 =》常规 =》 附加库目录,配置如图:

然后设置需要链接的库,选择项目属性=》配置属性=》链接器 =》输入 =》 附加依赖项,如图:

最后要设置一下dll搜索路径,配置项目属性=》配置属性=》调试=》环境,如图:

将cocos2dx工程下的AppDelegate.cpp,HelloWorldScene.cpp,AppDelegate.h,HelloWorldScene.h复制并关联到工程下,将图片资源也都复制到工程下。

下面开始修改CCocos2dxWin类,打开Cocos2dxWin.h文件,修改成如下所示:

#pragma once#include "AppDelegate.h"#include "glfw3.h"// CCocos2dxWinclass CCocos2dxWin : public CWnd{DECLARE_DYNAMIC(CCocos2dxWin)public:CCocos2dxWin();virtual ~CCocos2dxWin();BOOL createGLWin();protected:AppDelegate app;GLFWmousebuttonfun _fnMouseFunc;GLFWcursorposfun _fnCursorFunc;protected:DECLARE_MESSAGE_MAP()public:afx_msg void OnTimer(UINT_PTR nIDEvent);afx_msg void OnLButtonDown(UINT nFlags, CPoint point);afx_msg void OnLButtonUp(UINT nFlags, CPoint point);afx_msg void OnMouseMove(UINT nFlags, CPoint point);};

其中createGLWin函数功能为初始化cocos2dx。

AppDelegate app为cocos2dx使用

GLFWmousebuttonfun _fnMouseFunc是GLFW鼠标点击事件的回调函数

GLFWcursorposfun_fnCursorFunc是GLFW鼠标移动事件回调函数

下面四个函数是Dialog的消息处理函数,分别对应WM_TIMER,WM_LBUTTONDOWN,

WM_LBUTTONUP,WM_MOUSEMOVE消息。


首先实现createGLWin函数,别忘了添加cocos2dx相关的头文件

// Cocos2dxWin.cpp : 实现文件//#include "stdafx.h"#include "MFCDialog.h"#include "Cocos2dxWin.h"#include "cocos2d.h"USING_NS_CC;#include "platform/desktop/CCGLViewImpl-desktop.h"// CCocos2dxWinIMPLEMENT_DYNAMIC(CCocos2dxWin, CWnd)CCocos2dxWin::CCocos2dxWin(){AllocConsole();freopen("CONOUT$", "w", stdout);}CCocos2dxWin::~CCocos2dxWin(){}BOOL CCocos2dxWin::createGLWin(){CRect rc;GetClientRect(&rc);cocos2d::Application::getInstance()->initInstance(GetSafeHwnd(), "", rc.right-rc.left, rc.bottom-rc.top);cocos2d::Application::getInstance()->run(GetSafeHwnd());auto director = Director::getInstance();auto glview = director->getOpenGLView();_fnMouseFunc = static_cast<GLViewImpl*>(glview)->getMouseBtnFunc();_fnCursorFunc = static_cast<GLViewImpl*>(glview)->getCursorFunc();SetTimer(1, 1, NULL);this->MoveWindow(rc.left, rc.top, rc.right, rc.bottom);return TRUE;}

此时可以看到有几个函数cocos2dx没有提供,这就需要我们修改cocos2dx,添加上这几个函数。


在cocos2dx工程中找到文件CCApplication-win32.h,在其中的Application类中添加如下几个函数:

int run(HWND hWnd);bool initInstance(HWND hWnd, LPCSTR szTitle, UINT width, UINT height);void renderWorld();

然后在类中添加变量:

LARGE_INTEGERm_nLast;


接下来在CCApplication-win32.cpp中实现这些函数,首先在文件最上端引入头文件

#include"../desktop//CCGLViewImpl-desktop.h"

实现run函数

int Application::run(HWND hWnd){PVRFrameEnableControlWindow(false);QueryPerformanceCounter(&m_nLast);initGLContextAttrs();// Initialize instance and cocos2d.if (!applicationDidFinishLaunching()){return 1;}auto director = Director::getInstance();auto glview = director->getOpenGLView();// Retain glview to avoid glview being released in the while loopglview->retain();}

然后是initInstance函数

bool Application::initInstance(HWND hWnd, LPCSTR szTitle, UINT width, UINT height){auto director = Director::getInstance();auto glview = director->getOpenGLView();if (!glview) {cocos2d::Rect rect;rect.origin = cocos2d::Vec2::ZERO;rect.size = cocos2d::Size(width, height);glview = GLViewImpl::create(hWnd, rect);director->setOpenGLView(glview);}return true;}

最后是renderWorld函数

void Application::renderWorld(){LARGE_INTEGER nNow;QueryPerformanceCounter(&nNow);if (nNow.QuadPart - m_nLast.QuadPart > _animationInterval.QuadPart){m_nLast.QuadPart = nNow.QuadPart;auto director = Director::getInstance();auto glview = director->getOpenGLView();director->mainLoop();glview->pollEvents();}}

Application类修改完毕,现在可以看到initInstance函数中GLViewImpl类没有我们需要的create函数,这个需要我们修改GLViewImpl类。

定位到CCGLViewImpl-desktop.h,在GLViewImpl类中增加如下几个函数

static GLViewImpl* create(HWND hWnd, Rect rect);bool initWithHWND(HWND hWnd, Rect rect, float frameZoomFactor);static GLFWmousebuttonfun getMouseBtnFunc();static GLFWcursorposfun getCursorFunc();

在CCGLViewImpl-desktop.cpp中实现它们

首先实现create函数

GLViewImpl* GLViewImpl::create(HWND hWnd, Rect rect){auto ret = new (std::nothrow) GLViewImpl;if (ret && ret->initWithHWND(hWnd, rect, 1.0)) {ret->autorelease();return ret;}return nullptr;}

initWithHWND函数

bool GLViewImpl::initWithHWND(HWND hWnd, Rect rect, float frameZoomFactor){setViewName("Custom HWND View");_frameZoomFactor = frameZoomFactor;if (!glfwInit()) {return FALSE;}glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);glfwWindowHint(GLFW_RED_BITS, _glContextAttrs.redBits);glfwWindowHint(GLFW_GREEN_BITS, _glContextAttrs.greenBits);glfwWindowHint(GLFW_BLUE_BITS, _glContextAttrs.blueBits);glfwWindowHint(GLFW_ALPHA_BITS, _glContextAttrs.alphaBits);glfwWindowHint(GLFW_DEPTH_BITS, _glContextAttrs.depthBits);glfwWindowHint(GLFW_STENCIL_BITS, _glContextAttrs.stencilBits);_mainWindow = glfwCreateWindowEx((void*)hWnd, rect.size.width*_frameZoomFactor, rect.size.height*_frameZoomFactor, _viewName.c_str(), nullptr, nullptr);glfwMakeContextCurrent(_mainWindow);setFrameSize(rect.size.width, rect.size.height);// check OpenGL version at firstconst 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;}
getMouseBtnFunc和getCursorFunc函数

GLFWmousebuttonfun GLViewImpl::getMouseBtnFunc(){return GLFWEventHandler::onGLFWMouseCallBack;}GLFWcursorposfun GLViewImpl::getCursorFunc(){return GLFWEventHandler::onGLFWMouseMoveCallBack;}

接着定位到CCFileUtils-win32.cpp文件

修改_checkPath函数

static void _checkPath(){    if (0 == s_resourcePath.length())    {WCHAR pUtf16ExePath[512];GetModuleFileName(NULL, pUtf16ExePath, 512);        // We need only directory part without exe        WCHAR *pUtf16DirEnd = wcsrchr(pUtf16ExePath, L'\\');        char utf8ExeDir[CC_MAX_PATH] = { 0 };        int nNum = WideCharToMultiByte(CP_UTF8, 0, pUtf16ExePath, pUtf16DirEnd-pUtf16ExePath+1, utf8ExeDir, sizeof(utf8ExeDir), nullptr, nullptr);        s_resourcePath = convertPathFormatToUnixStyle(utf8ExeDir);    }}
现在可以编译一下libcocos2d工程,如果没提示错误,恭喜你!

接着编译一下我们的MFC工程,很不幸,会得到几百个错误,分析原因,根源是指向CCApplicationProtocol.h文件中

ApplicationProtocol:: Platform:: OS_WINDOWS这个枚举,原因是OS_WINDOWS这个名字与windows某个头文件中的定义重复了,我们把cocos2dx中的OS_WINDOWS改为CC_OS_WINDOWS,并将其他使用此枚举的地方一并修改。

再次编译libcocos2d。


然后编译MFC工程,如果没有错误,那么离成功不远了。

还记得在CCocos2dxWin类中我们增加了四个消息处理函数么,实现它们的时候到了。

void CCocos2dxWin::OnTimer(UINT_PTR nIDEvent){// TODO:  在此添加消息处理程序代码和/或调用默认值cocos2d::Application::getInstance()->renderWorld();CWnd::OnTimer(nIDEvent);}void CCocos2dxWin::OnLButtonDown(UINT nFlags, CPoint point){// TODO:  在此添加消息处理程序代码和/或调用默认值auto director = Director::getInstance();auto glview = director->getOpenGLView();_fnMouseFunc(static_cast<GLViewImpl*>(glview)->getWindow(), GLFW_MOUSE_BUTTON_LEFT, GLFW_PRESS, 0);CWnd::OnLButtonDown(nFlags, point);}void CCocos2dxWin::OnLButtonUp(UINT nFlags, CPoint point){// TODO:  在此添加消息处理程序代码和/或调用默认值auto director = Director::getInstance();auto glview = director->getOpenGLView();_fnMouseFunc(static_cast<GLViewImpl*>(glview)->getWindow(), GLFW_MOUSE_BUTTON_LEFT, GLFW_RELEASE, 0);CWnd::OnLButtonUp(nFlags, point);}void CCocos2dxWin::OnMouseMove(UINT nFlags, CPoint point){// TODO:  在此添加消息处理程序代码和/或调用默认值auto director = Director::getInstance();auto glview = director->getOpenGLView();_fnCursorFunc(static_cast<GLViewImpl*>(glview)->getWindow(), point.x, point.y);CWnd::OnMouseMove(nFlags, point);}

接下来要在MFCDialogDlg.h文件中增加几个函数

首先是两个用于判断坐标位置的函数

bool isPointInPictureWin(CPoint& pt);CPoint getPicturePoint(CPoint& pt);
然后是几个消息处理函数

afx_msg void OnLButtonDown(UINT nFlags, CPoint point);afx_msg void OnLButtonUp(UINT nFlags, CPoint point);afx_msg void OnMouseMove(UINT nFlags, CPoint point);
这几个函数分别对应WM_LBUTTONDOWN, WM_LBUTTONUP, WM_MOUSEMOVE消息。


在MFCDialogDlg.cpp文件中实现这几个函数

bool CMFCDialogDlg::isPointInPictureWin(CPoint& pt){CRect rc;m_nPicCocos2dx.GetWindowRect(&rc);ScreenToClient(&rc);if (pt.x >= rc.left && pt.x <= rc.right && pt.y >= rc.top && pt.y <= rc.bottom) return true;return false;}CPoint CMFCDialogDlg::getPicturePoint(CPoint& pt){CRect rc;m_nPicCocos2dx.GetWindowRect(&rc);ScreenToClient(&rc);CPoint picPt;picPt.x = pt.x - rc.left;picPt.y = pt.y - rc.top;return picPt;}void CMFCDialogDlg::OnLButtonDown(UINT nFlags, CPoint point){// TODO:  在此添加消息处理程序代码和/或调用默认值if (isPointInPictureWin(point)) {CPoint pt = getPicturePoint(point);SendMessageA(m_nPicCocos2dx.GetSafeHwnd(), WM_LBUTTONDOWN, MK_LBUTTON, pt.y << 16 | pt.x);}CDialogEx::OnLButtonDown(nFlags, point);}void CMFCDialogDlg::OnLButtonUp(UINT nFlags, CPoint point){// TODO:  在此添加消息处理程序代码和/或调用默认值if (isPointInPictureWin(point)) {CPoint pt = getPicturePoint(point);SendMessageA(m_nPicCocos2dx.GetSafeHwnd(), WM_LBUTTONUP, MK_LBUTTON, pt.y << 16 | pt.x);}CDialogEx::OnLButtonUp(nFlags, point);}void CMFCDialogDlg::OnMouseMove(UINT nFlags, CPoint point){// TODO:  在此添加消息处理程序代码和/或调用默认值if (isPointInPictureWin(point)) {CPoint pt = getPicturePoint(point);SendMessageA(m_nPicCocos2dx.GetSafeHwnd(), WM_MOUSEMOVE, MK_LBUTTON, pt.y << 16 | pt.x);}CDialogEx::OnMouseMove(nFlags, point);}

这些函数的主要功能是判断点击区是否在控件范围内,是的话就将消息发送给控件。


现在,我们的MFC Dialog已经可以运行起来,并可以接收鼠标点击事件,如图

功能基本完成了,希望能对有此需求的人有所帮助。

本文使用开发环境为cocos2dx 3.6,VS2013,win8.1。

0 0
原创粉丝点击