`
yidongkaifa
  • 浏览: 4052574 次
文章分类
社区版块
存档分类
最新评论

Cocos2d-X游戏工具开发之一:将Cocos2d-X嵌入MFC的子窗体方法讲解

 
阅读更多

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路 CSDN博客地址:http://blog.csdn.net/honghaier]

红孩儿Cocos2d-X学习园地:249941957 加群写:Cocos2d-x

另本章为我的Cocos2d-x教程一书初稿。望各位看官多提建议!

本节所用Cocos2d-x版本:cocos2d-1.0.1-x-0.12.0

我在之前的“如何利用Cocos2d-x开发一个游戏?”。在实际的项目开发过程中,工具是支撑开发流程规范的必要条件。一套好的工具可以使工作流程顺畅。从而极大的提高效率。我们知道Unity3d场景编辑器就是一个很好的例子。它将游戏开发中的各个流程节点的工作都集成到编辑器中,这是一个很COOL的想法。对于游戏开发团队来说,只需要掌握好这个流程,使用编辑器做好每一步的编辑工作就可以快速开发出高品质的游戏。

但对于Cocos2d-x来说。虽然网络上有一些开放的工具,但零零散散的不成系统。并不方便使用。这一章来简要介绍一下如何使用MFC来进行Cocos2d-x相关工具的开发。请对MFC不熟悉的朋友先学习一下基本的对话框程序再进行本章内容的学习。


第一节:将Cocos2d-x嵌入MFC的子窗体中

[注:学习这一章之前请先学习Cocos2d-x 的“HelloWorld” 深入分析一文]

首先,我们用VC++在Cocos2d-x的目录里建立了个Unicode字符集MFC对话框程序。这里命名为Cocos2dXEditor。按照HelloWorld工程设置把包含头文件目录,库文件目录,包含库都设置好。并且画好对话框界面

如图:

我把界面设计为三部分,左边和右边用来创建对话框面板,至于要具体显示什么根据我们的工具开发需求而定。暂时先不管。而中间放一个Picture 控件,做为Cocos2d-x的显示窗口。为了生成相应的窗口控件变量,我们需要将此Picture控件的ID改成自定义的一个ID,这里我们改成IDC_COCOS2DXWIN保存。

我们画好界面后,选中Picture控件后右键单击,在弹出菜单中找到“添加变量”。为Picture控件生成一个控件变量。这里命名为m_Cocos2dXWin,并点击完成。

好,现在主对话框类里有了这么一句:

public:
         CStatic      m_Cocos2dXWin;

这个变量是CStatic类型的,它只是一个最简单的CWnd派生类。并不能显示Cocos2d-x。我们需要做点工作来创建一个新的类来替代它完成显示Cocos2d-x的工作。

在工程右键弹出菜单里找到“添加”一项,在其子菜单中点击“类”。

在弹出的“添加类”对话框中“MFC”项中的“MFC类”。

点击“添加”。这时会弹出“MFC类向导”对话框。我们在类名里输入“CCocos2dXWin”,并选择基类CWnd。然后点击“完成”。



向导会为我们的工程自动加入两个文件“Cocos2dXWin.h”和“Cocos2dXWin.cpp”。这才是我们要进行Cocos2d-x显示的窗体类,它的基类是CWnd,与Picture控件有相同的基类。

打开Cocos2dXWin.h,在CCocos2dXWin类中增加一个public成员函数声明:

//创建Cocos2dX窗口
BOOL        CreateCocos2dXWindow();

我们另外增加一个private变量用来标记窗口是否已经进行了Cocos2d-x的OpenGL窗口创建。

private:
	//是否已经初始化
	BOOL				m_bInitCocos2dX;

在CPP文件中加入需要用到的头文件

#include "CCEGLView_win32.h"
#include "../Classes/AppDelegate.h"
#include "cocos2d.h"

下面来手动增加函数定义

/创建Cocos2dX窗口
BOOL	CCocos2DXWin::CreateCocos2dXWindow()
{
	//新建一个CRect变量获取窗口的客户区大小
	CRect	tClientRect;
	GetClientRect(&tClientRect);
	//调用cocos2d中的CCApplication::sharedApplication()获取Cocos2d-x程序类单件实例对象。调用其run函数。在这里我们传入四个参数,与第一章“HelloWorld”不同的是这里增加了新参数“当前窗口的句柄”。这是使用MFC来嵌入Cocos2d-x窗口的关健所在。我们暂时埋个伏笔,后面再详解。
	cocos2d::CCApplication::sharedApplication().run(GetSafeHwnd(),TEXT("第一个Cocos2d-x程序"),tClientRect.Width(),tClientRect.Height());
	//这里将变量设置为TRUE
	m_bInitCocos2dX = TRUE;
	return TRUE;
}

现在还不能运行。因为CCApplication的run函数跟本就没有HWND参数,我们必须对CCApplication动个小手术。使它能够接收到窗体的句柄并使用这个句柄来创建OpenGL。

找到CCApplication_win32.cpp中的run函数做以下修改:

[Cocos2d-x相关教程来源于红孩儿的游戏编程之路 CSDN博客地址:http://blog.csdn.net/honghaier]  

int CCApplication::run(HWND hWnd,LPCTSTR szTitle,UINT wWidth,UINT wHeight)
{
    PVRFrameEnableControlWindow(false);

    // Main message loop:
    MSG msg;
    LARGE_INTEGER nFreq;
    LARGE_INTEGER nNow;

    QueryPerformanceFrequency(&nFreq);
   //将原临时变量nLast改为成员变量m_nLast。因为后面要把每帧的渲染做到外部调用的public函数里,所以需要存一下上帧的时间。
    QueryPerformanceCounter(&m_nLast);

    // 关键点1:必须将参数hWnd也做为参数传给initInstance函数。
    if (! initInstance(hWnd,szTitle,wWidth,wHeight) || ! applicationDidFinishLaunching())
    {
        return 0;
    }

    CCEGLView& mainWnd = CCEGLView::sharedOpenGLView();
	//关键点2:我们如果要使用指定的CWnd窗口来创建OpenGL,那么必须放弃使用Cocos2d-x封装好的消息循环。因为这些工作都会交由MFC来做。我们为了不破坏原来的Cocos2d-x创建窗口方式。就在这里做个判断处理,如果要使用原来由Cocos2d-x创建窗口的方式,只需要第一个参数传NULL就好了。
    if(NULL == hWnd)
	{
		mainWnd.centerWindow();
		ShowWindow(mainWnd.getHWnd(), SW_SHOW);

		while (1)
		{
			if (! PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
			{
				// Get current time tick.
				QueryPerformanceCounter(&nNow);

				// If it's the time to draw next frame, draw it, else sleep a while.
				if (nNow.QuadPart - m_nLast.QuadPart > m_nAnimationInterval.QuadPart)
				{
					m_nLast.QuadPart = nNow.QuadPart;
					CCDirector::sharedDirector()->mainLoop();
				}
				else
				{
					Sleep(0);
				}
				continue;
			}

			if (WM_QUIT == msg.message)
			{
				// Quit message loop.
				break;
			}

			// Deal with windows message.
			if (! m_hAccelTable || ! TranslateAccelerator(msg.hwnd, m_hAccelTable, &msg))
			{
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
		}

		return (int) msg.wParam;
	}
	return 0;
}

修改.h中的函数声明与cpp一致并保存,然后打开CCApplication的派生类AppDelegateinitInstance函数。做同样的修改工作。

#if (CC_TARGET_PLATFORM == CC_PLATFORM_WIN32)
		CCEGLView * pMainWnd = new CCEGLView();
		CC_BREAK_IF(! pMainWnd
			|| ! pMainWnd->Create(hWnd,szTitle, wWidth, wHeight));
			//|| ! pMainWnd->Create(TEXT("cocos2d: Hello World"), 480, 320));
#endif  // CC_PLATFORM_WIN32
修改.h中函数声明与cpp保持一致,再打开CCEGL_View_win32.cpp,找到Create函数,继续手术:

bool CCEGLView::Create(HWND hWnd,LPCTSTR pTitle, int w, int h)
{
	bool bRet = false;
	do 
	{
		//在这里做个判断,如果参数中的窗口句柄不为空,则使用参数句柄做为成员变量m_hWnd的值。而在
		if(hWnd)
		{
			m_hWnd = hWnd ;
		}
		else
		{
			CC_BREAK_IF(m_hWnd);

			HINSTANCE hInstance = GetModuleHandle( NULL );
			WNDCLASS  wc;		// Windows Class Structure

			// Redraw On Size, And Own DC For Window.
			wc.style          = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;  
			wc.lpfnWndProc    = _WindowProc;					// WndProc Handles Messages
			wc.cbClsExtra     = 0;                              // No Extra Window Data
			wc.cbWndExtra     = 0;								// No Extra Window Data
			wc.hInstance      = hInstance;						// Set The Instance
			wc.hIcon          = LoadIcon( NULL, IDI_WINLOGO );	// Load The Default Icon
			wc.hCursor        = LoadCursor( NULL, IDC_ARROW );	// Load The Arrow Pointer
			wc.hbrBackground  = NULL;                           // No Background Required For GL
			wc.lpszMenuName   = NULL;                           // We Don't Want A Menu
			wc.lpszClassName  = kWindowClassName;               // Set The Class Name

			CC_BREAK_IF(! RegisterClass(&wc) && 1410 != GetLastError());		

			// center window position
			RECT rcDesktop;
			GetWindowRect(GetDesktopWindow(), &rcDesktop);

			// create window
			m_hWnd = CreateWindowEx(
				WS_EX_APPWINDOW | WS_EX_WINDOWEDGE,	// Extended Style For The Window
				kWindowClassName,									// Class Name
				pTitle,												// Window Title
				WS_CAPTION | WS_POPUPWINDOW | WS_MINIMIZEBOX,		// Defined Window Style
				0, 0,								                // Window Position
				0,                                                  // Window Width
				0,                                                  // Window Height
				NULL,												// No Parent Window
				NULL,												// No Menu
				hInstance,											// Instance
				NULL );

			CC_BREAK_IF(! m_hWnd);
		}
        m_eInitOrientation = CCDirector::sharedDirector()->getDeviceOrientation();
        m_bOrientationInitVertical = (CCDeviceOrientationPortrait == m_eInitOrientation
            || kCCDeviceOrientationPortraitUpsideDown == m_eInitOrientation) ? true : false;
        m_tSizeInPoints.cx = w;
        m_tSizeInPoints.cy = h;
        resize(w, h);

		// 当调用CCEGL的静态函数create来创建OpenGL窗口时,传入的参数是CCEGLView实例对象,在内部会取得实例对象的m_hWnd变量做为OpenGL的窗口句柄。这样我们就实现了使用参数传递的窗口句柄来创建OpenGL窗口。
		m_pEGL = CCEGL::create(this);

		if (! m_pEGL)
		{
			DestroyWindow(m_hWnd);
			m_hWnd = NULL;
			break;
		}

		s_pMainWindow = this;
		bRet = true;
	} while (0);

	return bRet;
}

这样我们就完成了对于创建窗口函数的修改。但是因为屏蔽了原窗口的创建,所以也同时屏蔽了Cocos2d-x对于窗口消息的处理。我们在类视图中找到CCocos2dXWin,在右键弹出菜单中点击“属性”。打开类属性编辑框。

在WindowProc一栏中增加函数WindorProc,完成后进入CCocos2dXWin的WindorProc函数,因为我们在CCEGLView的Create函数中可以找到在注册窗口类时,其设定的窗口消息处理回调函数是WindowProc.而其实例对象是单件。故可以这样修改:

LRESULT CCocos2DXWin::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	// 这里我们先判断一下是否初始化完成Cocos2dX再进行消息处理
	if(m_bInitCocos2dX)
	{
		CCEGLView::sharedOpenGLView().WindowProc(message, wParam, lParam);
	}
	return CWnd::WindowProc(message, wParam, lParam);
}

这样Cocos2dXWin所实例化的窗口就可以接收到鼠标等对于窗口的消息了。但是!我们屏幕了Cocos2d-x的消息循环,而Cocos2d-x的渲染处理函数是在消息循环中调用的。我们就算现在运行程序,也看不到帅气的小怪物。所以我们必须想办法实时的调用Cocos2d-x的渲染处理函数。对于工具来说,我们并不需要考虑太高的FPS刷新帧数,所以我们只需要使用一个定时器,并在定时触发函数中进行Cocos2d-x的渲染处理函数的调用就可以了。

在CCocos2DXWin::CreateCocos2dXWindow()函数尾部加入一句:

SetTimer(1,1,NULL);

加入一个ID为1的定时器,设置它每1毫秒响应一次,其处理函数为默认,则使用CCocos2DXWin的WINDOWS消息处理函数对于WM_TIMER进行响应就可以了。

按之前增加WindorProc函数的方式找到CCocos2DXWin的消息WM_TIMER一项,增加函数OnTimer.如图:


在其生成的函数OnTimer中我们新写一个函数调用,则每次定时响应调用

void CCocos2DXWin::OnTimer(UINT_PTR nIDEvent)
{
	//我们写一个renderWorld函数代表Cocos2d-x的世界渲染
	cocos2d::CCApplication::sharedApplication().renderWorld();
	CWnd::OnTimer(nIDEvent);
}

我们打开CCApplication_win32.h,为CCApplication类增加一个public的renderWorld函数。

//帧循环调用渲染
	bool	renderWorld();

在cpp中我们将消息循环中的相关处理移入进来
bool CCApplication::renderWorld()
{   
	LARGE_INTEGER nNow;
	// Get current time tick.
	QueryPerformanceCounter(&nNow);

	// If it's the time to draw next frame, draw it, else sleep a while.
	if (nNow.QuadPart - m_nLast.QuadPart > m_nAnimationInterval.QuadPart)
	{
		m_nLast.QuadPart = nNow.QuadPart;
		CCDirector::sharedDirector()->mainLoop();
		return true;
	}
	return false;
}

OK,这样我们就可以在外部来调用Cocos2d-x的显示设备进行渲染处理了。当然,我们也不能忘了在CCocos2dXWin窗口销毁时把定时器资源进行一下释放。找到类消息WM_DESTROY新增函数OnDestroy。



void CCocos2DXWin::OnDestroy()
{
	//在Cocos2d-x的引擎文件CCEGLView_win32.cpp中CCEGLView::release()会再次发送调用DestroyWindow,所以这里用变量m_bInitCocos2dX做下判断,避免二次销毁
	if(TRUE == m_bInitCocos2dX)
	{
		//退出将m_bInitCocos2dX设为FALSE
		m_bInitCocos2dX = FALSE;
		//释放定时器资源
		KillTimer(1);
		CWnd::OnDestroy();
	}
}

我们在“HelloWorld”的代码分析中可以知道在Cocos2d-x程序退出时先后响应WM_CLOSE和WM_DESTROY。在CCEGLView::WindowProc 可以看到

	case WM_CLOSE:
		CCDirector::sharedDirector()->end();
		break;

	case WM_DESTROY:
		PostQuitMessage(0);
		break;

而CCDirector的end函数并不是立即执行停止,他设置成员变量m_bPurgeDirecotorInNextLoop为true,并在下一帧mainLoop循环时调用purgeDirector()函数进行显示设备和场景的最终释放。所以我们要想在窗口退出时释放Cocos2d-x显示设备和场景,必须做一下相关处理。理解了这一点。其实就很简单了。

只需要在CCocos2DXWin::OnDestroy()中增加两句代码即可:

void CCocos2DXWin::OnDestroy()
{
	…
	KillTimer(1);
	//调用显示设备单件实例对象的end函数
	CCDirector::sharedDirector()->end();
	//处理一次下一帧,必须调用.
	CCDirector::sharedDirector()->mainLoop();
	CWnd::OnDestroy();
	}
}

OK,CCocos2DXWin类基本完成。现在在Cocos2dXEditorDlg.h中将

CStatic            m_Cocos2dXWin;

改为

CCocos2DXWin       m_Cocos2dXWin;

将CCocos2DXWin的头文件包含进来,然后在控件初始化(比如OnInitDialog函数)中调用一下m_Cocos2dXWin.CreateMainCoco2dWindow()。编译成功后就算完成了将Cocos2d-x嵌入MFC的CWnd控件的过程。不过,您需要在窗口大小变化时对这个控件重设位置及大小以使窗口始终上下充满空间。这些属于MFC的基础知识,在此不再赘述。


最后运行一下看下成果吧。下面是我运行的结果(:左边面板稍加制做~):




分享到:
评论

相关推荐

    Cocos2d-x实战:JS卷——Cocos2d-JS开发

    资源名称:Cocos2d-x实战:JS卷——Cocos2d-JS开发内容简介:本书是介绍Cocos2d-x游戏编程和开发技术书籍,介绍了使用Cocos2d-JS中核心类、瓦片地图、物理引擎、音乐音效、数据持久化、网络通信、性能优化、多平台...

    Cocos2D-X游戏开发技术精解

    资源名称:Cocos2D-X游戏开发技术精解内容简介:Cocos2D-X是一款支持多平台的 2D手机游戏引擎,支持iOS、Android、BlackBerry等众多平台。当前,很多移动平台流行的游戏,都是基于Cocos2D-X开发的。 《Cocos2D-X...

    精通COCOS2D-X游戏开发 基础卷_2016.4-P399-13961841.pdf

    精通COCOS2D-X游戏开发 精通COCOS2D-X游戏开发 精通COCOS2D-X游戏开发 精通COCOS2D-X游戏开发 精通COCOS2D-X游戏开发

    大富翁手机游戏开发实战基于Cocos2d-x3.2引擎

    本书根据大富翁项目一一展开讲解游戏开发过程中涉及的各方面内容,读者可以通过这个游戏的开发,全面掌握Cocos2d-x游戏开发的方法和技巧。 本书理论和实践相结合, 资源太大,传百度网盘了,链接在附件中,有需要的...

    Cocos2d-x-3.x游戏开发之旅

    Cocos2d-x-3.x游戏开发之旅-钟迪龙著 全新pdf版和附书代码(代码为工程文件,可复制) 附带目录标签

    Cocos2D-X游戏开发技术精解.pdf

    全书共15章,主要内容包括:Cocos2D-X引擎简介;如何建立跨平台的开发环境;引擎的核心模块——渲染框架;如何实现动态画面和用户交互;二维游戏中背景的实现方法和技术;Box2D物理引擎;如何掌握声音引擎的用法;...

    Cocos2d-x高级开发教程

    Cocos2d-x是移动跨平台开发最流行的游戏引擎,而本书是一本很全面的、比较‘接地气’的游戏开发教程。书中汇聚了热门手机游戏《捕鱼达人》开发的实战经验,作者从最基础的内容开始,逐步深入地介绍了Cocos2d-x的相关...

    精通Cocos2d-x游戏开发(进阶卷)源代码

    精通Cocos2d-x游戏开发(进阶卷)源代码 精通Cocos2d-x游戏开发(进阶卷)源代码 精通Cocos2d-x游戏开发(进阶卷)源代码

    cocos2d-x游戏开发实战精解

    本光盘是《Cocos2d-x游戏开发实战精解》一书的配书光盘,内容介绍如下。 (1)本书教学视频:该文件夹收录了本书的配套多媒体教学视频,可用暴风影音等视频播放器播放。 (2)本书源文件:该文件夹收录了本书涉及...

    实例妙解Cocos2D-X游戏开发

    一线资深游戏开发工程师根据Cocos2D-X 最新版本撰写,Cocos2D...完全通过真实游戏案例驱动,不仅将Cocos2D-X的各种功能、原理、技巧融入其中,而且还详细讲解了空战类、塔防类、物理类游戏的开发过程和方法,实战性极强

    Cocos2d-X案例开发大全

    资源名称:Cocos2d-X案例开发大全内容简介:《Cocos2d-X案例开发大全》包括大量基于Cocos2d-X开发的游戏案例,详细讲解了基于Cocos2d-X游戏引擎在Android及iOS平台下进行游戏开发的思路,能够快速帮助读者提升利用...

    Cocos2d-x 3.x游戏开发实战pdf含目录

    Cocos2d-x 3.x游戏开发实战pdf含目录,内容详细,强烈推荐给大家。

    Cocos2d-x高级开发教程制作自己的《捕鱼达人》

    Cocos2d-x高级开发教程:制作自己的《捕鱼达人》 图书简介: 《Cocos2d-x高级开发教程:制作自己的《捕鱼达人》》是国内第一本全面深入讲解Cocos2d-x进阶内容的图书,Cocos2d-x创始人王哲作序推荐,《捕鱼达人》开发...

    Cocos2d-x 3.x游戏开发之旅

    本书是《Cocos2d-x 游戏开发之旅》的升级版,修改了2.0版进阶到3.0版后的一些内容,新增了对CocoStudio、UI编辑器、Cocos2d-x 3.0新特性以及网络方面的知识点。主要介绍常用的API使用方式;介绍如何通过官方Demo获取...

    Cocos2d-x游戏编程——C++篇 .iso

    Cocos2d-x游戏编程——C++篇(电子工业出版社,徐飞 著)书本配套的光盘代码,

    将Cocos2d-X嵌入MFC的子窗体方法讲解

    Cocos2d-X嵌入MFC的子窗体,用MFC做工具的人有福了

    cocos2d-x-2.1.5

    cocos2d-x-2.1.5

    cocos2d-x事件类

    在使用cocos2d-x开发游戏的过程中,为了实现逻辑和显示相分离。 在下通宵了一个晚上,写出了该事件类。 谨记,该事件只能用于cocos2d-x中。 事件发送者需要继承EventDispatcher类 事件接收者需要继承EventHandle类...

    大富翁手机游戏开发实战 基于Cocos2d-x3.2引擎

    本书根据大富翁项目一一展开讲解游戏开发过程中涉及的各方面内容,读者可以通过这个游戏的开发,全面掌握Cocos2d-x游戏开发的方法和技巧。  本书理论和实践相结合,避免空泛的原理讲解,在理解了原理之上紧接着根据...

    cocos2d-x 动画工具 Flash2Cocos2d-x 1.3

    cocos2d-x 动画工具 Flash2Cocos2d-x 1.3

Global site tag (gtag.js) - Google Analytics