之前写过一篇将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,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 config if (!_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 later prevIoUs = _glfwPlatformGetCurrentContext(); // Open the actual window and create its context if (!_glfwPlatformCreateWindowEx(winHwnd,window,&wndconfig,&ctxconfig,&fbconfig)) { glfwDestroyWindow((GLFWwindow*)window); _glfwPlatformMakeContextCurrent(prevIoUs); return NulL; } _glfwPlatformMakeContextCurrent(window); // RetrIEve the actual (as opposed to requested) context attributes if (!_glfwRefreshContextAttribs(&ctxconfig)) { glfwDestroyWindow((GLFWwindow*)window); _glfwPlatformMakeContextCurrent(prevIoUs); return NulL; } // Verify the context against the requested parameters if (!_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 VRAM glClear(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,const _GLFWfbconfig* fbconfig){ int status; if (!createWindowEx(handle,wndconfig,ctxconfig,fbconfig)) return GL_FALSE; status = _glfwAnalyzeContext(window,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,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,WM_copYglobalData,NulL); } if (wndconfig->floating && !wndconfig->monitor) { SetwindowPos(window->win32.handle,HWND_topMOST,SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE); } DragAcceptfiles(window->win32.handle,TRUE); if (!_glfwCreateContext(window,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 loop glvIEw->retain();}
然后是initInstance函数
bool Application::initInstance(HWND hWnd,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,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 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;}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,pUtf16DirEnd-pUtf16ExePath+1,utf8ExeDir,sizeof(utf8ExeDir),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,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(),MK_Lbutton,pt.y << 16 | pt.x); } CDialogEx::OnLbuttonDown(nFlags,point);}voID CMFCDialogDlg::OnLbuttonUp(UINT nFlags,pt.y << 16 | pt.x); } CDialogEx::OnLbuttonUp(nFlags,point);}voID CMFCDialogDlg::OnMouseMove(UINT nFlags,WM_MOUSEMOVE,pt.y << 16 | pt.x); } CDialogEx::OnMouseMove(nFlags,point);}
这些函数的主要功能是判断点击区是否在控件范围内,是的话就将消息发送给控件。
现在,我们的MFC Dialog已经可以运行起来,并可以接收鼠标点击事件,如图
功能基本完成了,希望能对有此需求的人有所帮助。
本文使用开发环境为cocos2dx 3.6,VS2013,win8.1。 总结以上是内存溢出为你收集整理的Cocos2dx 3.0 以上版本 集成 MFC全部内容,希望文章能够帮你解决Cocos2dx 3.0 以上版本 集成 MFC所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)