Cocos2dx 3.0 以上版本 集成 MFC

Cocos2dx 3.0 以上版本 集成 MFC,第1张

概述之前写过一篇将cocos2dx-2.x版本集成到MFC的文章,现在到了cocos2dx-3.x版本,之前的方法已经行不通了,最近有点时间,研究了一下,算是做出来点样子,下面写一下步骤,算是做个笔记。 我采用的方案步骤很多,改动量也比较大, 对于那些期望只修改几行就能达到目的的人来说,让你们失望了-_-,但本着学习的目的,改的越多不就意味着你学的越多么:) 首先要改的不是cocos2dx,而是GLF

之前写过一篇将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所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1061703.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-25
下一篇 2022-05-25

发表评论

登录后才能评论

评论列表(0条)

保存