Cocos2d-x   发布时间:2022-05-03  发布网站:大佬教程  code.js-code.com
大佬教程收集整理的这篇文章主要介绍了Cocos2dx 3.0 以上版本 集成 MFC大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。

之前写过一篇将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"


// CCocos2dxWin

class 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"


// CCocos2dxWin

IMPLEMENT_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所遇到的程序开发问题。

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

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。