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。- Cocos2dx 3.0 以上版本 集成 MFC
- cocos2dx 3.0以上版本的触摸事件
- cocos2dx 2.2.x版本和3.0以上版本创建项目
- cocos2dx 2.0以上版本修改
- cocos2dx 2.2版本以上生成新项目
- Cocos2dx 截屏功能 3.2以上版本
- 多平台响应键盘事件!(适用于Cocos2dx 3.0 alpha以上版本)
- 多平台响应键盘事件!(适用于Cocos2dx 3.0 alpha以上版本)
- cocos2dx-3.0以上版本之 创建Sprite精灵的6种形式
- 蜗牛—cocos2dx之2.2以上版本新建项目
- cocos2dx 2.2.2版本以上解析json ----rapidjson
- cocos2dx 3.x以上版本 lua中使用protobuf
- cocos2dx在安卓6.0(android-23)以上版本打包
- ClearQuest 2002与Crystal Report 8.5以上版本的集成
- Aspera Connect 3.6与Chrome 42以上版本集成杂记
- vc6以上版本的mfc为对话框添加启动画面
- mfc连接ACCESS2010及以上版本,部分“MicrosoftC++异常”处理
- Cocos2dx(2.0版本以上)+eclipse开发配置(不需要Cygwin)
- web.xml中load-on-startup的作用
- 程序员在周末学习的8个实用技术
- Object类
- OGG 12C Oracle to Mysql
- 统计数据库所有表的字段之和
- Cocos2dx 3.0 以上版本 集成 MFC
- C#调用WPS的两种方式
- 【二十】二叉树及其性质
- JAVA书籍
- JS方法代理
- mac系统如何显示和隐藏文件
- leetcode:Container With Most Water 6行AC
- Qt Creater调试时一直出现:“DEBUGGER: Waiting for debug socket connect” 和“DEBUGGER: go to sleep”
- Hibernate封装通用数据操作基类DAO