引用:http://www.cnblogs.com/windeer/archive/2012/11/18/2767750.html
引言
现在智能手机已经慢慢进入大众化,移动类应用开始火爆起来,游戏类应用更是占据了手机用户的大部分碎片时间。
现在手机开发游戏也逐渐流行开来,手机的平台目前主打是 Andoird、IOS和WindowPhone。Cocos2DX跨平台开发成为吸引手机开发商和独立游戏制作人的一大亮点。
Cocos2dX脱胎于Cocos2D,有优良的血统,成熟的框架,加上不错的效率,成为跨平台手机游戏开发的首选。
在游戏开发过程中,各种辅助工具的开发是难免的。下面的文章 http://www.cocos2dres.com/view.asp?id=55 中有 介绍网络上可以找到一些工具,其中一些需要收费。
如果您是制作一些小游戏,网上找到的那些工具,也许可以解决制作中的问题,但是如果您在制作大型游戏,您就不得不自己动手制作工具了。
本文介绍将Cocos2dX渲染到MFC窗口,是制作游戏工具所需的一些知识。
COCOS2DX开发环境
本文的Cocos2DX的版本是 cocos2d-2.0-x-2.0.3, 点击进入下载页面 www.cocos2d-x.org/projects/cocos2d-x/wiki/Download
Cocos2DX下载后以及windows下配置这里就不赘述了 相关教程链接 http://www.cocos2dres.com/post/7.html win7下 VS2010教程
在理解了 helloCPP后,我们就可以进行下一步了。
MFC工程
第一步,创建MFC工程,修改工程属性
打开 cocos2d-win32.vc2010.sln 解决方案,添加一个MFC单文档程序。不会添加MFC工程的同学,到网上找找教程。
以下是工程(工程名叫MFCTest)截图:
其中 Cocos2DApp是自己添加的。
在工程属性面板,
图中红圈处设置成现HelloCPP一样就可以了。调试的工作路径,需要设置成自己的路径。
第二步,分析HelloCpp下的Windows窗口
查看main.cpp的代码:
int APIENTRY _tWinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine); // create the application instance
AppDelegate app;
CCEGLView* eglView = CCEGLView::sharedOpenGLView();
eglView->setFrameSize( 960, 640 );
return CCApplication::sharedApplication()->run();
}
这里首先创建一个app,然后设置窗口大小,然后就是run()了。Windows的窗口在哪里创建的呢?
eglView应当是与窗口相关的,我们看看setFrameSize的相关实现:
void CCEGLView::setFrameSize(float width, float height)
{
Create((LPCTSTR)m_szViewName, (int)width, (int)height );
CCEGLViewProtocol::setFrameSize(width, height);
}
首先映入眼帘就是Create方法,看来windows窗口的创建就在这个方法里面:
bool CCEGLView::Create(LPCTSTR pTitle, int w, int h)
{
bool bRet = false;
if( hWnd == NULL )
{
do
{
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); WCHAR wszBuf[50] = {0};
MultiByteToWideChar(CP_UTF8, 0, m_szViewName, -1, wszBuf, sizeof(wszBuf)); // create window
m_hWnd = CreateWindowEx(
WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, // Extended Style For The Window
kWindowClassName, // Class Name
wszBuf, // 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); resize(w, h); bRet = initGL();
CC_BREAK_IF(!bRet); s_pMainWindow = this;
bRet = true;
} while (0);
}return bRet;
}
果不出我所料,我们看到了熟悉的CreateWindowEx,然后调用initGL()初始化OpenGL相关。
我们再看看 initGL的实现:
bool CCEGLView::initGL()
{
m_hDC = GetDC(m_hWnd); // 拿到窗口的DC
SetupPixelFormat(m_hDC); // 设置像素格式
//SetupPalette();
m_hRC = wglCreateContext(m_hDC); /// 创建OpenGLES渲染环境
wglMakeCurrent(m_hDC, m_hRC); /// 设置当前渲染环境 GLenum GlewInitResult = glewInit();
if (GLEW_OK != GlewInitResult)
{
fprintf(stderr,"ERROR: %s\n",glewGetErrorString(GlewInitResult));
return false;
} if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader)
{
CCLog("Ready for GLSL\n");
}
else
{
CCLog("Not totally ready :( \n");
} if (glewIsSupported("GL_VERSION_2_0"))
{
CCLog("Ready for OpenGL 2.0\n");
}
else
{
CCLog("OpenGL 2.0 not supported\n");
}
return true;
}
这样 Windows窗口就和OpenGLES的渲染环境关联起来了。
我们如何将MFC的窗口和OpenGLES的渲染环境关联起来呢?
MFC窗口是预先已经创建好的,我们只要在创建的时候,用已经创建好的窗口来做 initGL应当就可以实现。于是我将CCEGLView的Create和SetFrameSize做了调整,可以 传入窗口句柄。新的函数代码如下:
void CCEGLView::setFrameSize(float width, float height,HWND hWnd)/// 将窗口句柄通过参数传进来
{
Create((LPCTSTR)m_szViewName, (int)width, (int)height, hWnd);
CCEGLViewProtocol::setFrameSize(width, height);
} bool CCEGLView::Create(LPCTSTR pTitle, int w, int h, HWND hWnd)
{
bool bRet = false;
if( hWnd == NULL ) /// 如果等于 NULL 则按以前的方式走
{
do
{
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); WCHAR wszBuf[50] = {0};
MultiByteToWideChar(CP_UTF8, 0, m_szViewName, -1, wszBuf, sizeof(wszBuf)); // create window
m_hWnd = CreateWindowEx(
WS_EX_APPWINDOW | WS_EX_WINDOWEDGE, // Extended Style For The Window
kWindowClassName, // Class Name
wszBuf, // 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); resize(w, h); bRet = initGL();
CC_BREAK_IF(!bRet); s_pMainWindow = this;
bRet = true;
} while (0);
}
else
{
m_hWnd = hWnd; /// 直接将外部窗口当做当前窗口
bRet = initGL(); /// 初始化OpenGLES相关
} return bRet;
}
底层已经支持将外部窗口传入作为渲染窗口了,那我们就只要做相应的调整就可以了。
第三步,添加Cocos2DApp
在工程中添加 类 CCocos2DApp继承自 cocos2d::CCApplication, 这就相当于HelloCPP中的 AppDelegate.之所以在这里自己实现,是想将MFC的窗口句柄交给
Cocos2D的渲染层。
CCocos2DApp需要在AppDelegate的基础上,添加两个方法,代码如下:
1 /**
2 @brief 系统初始化
3 @param uWnd 窗口句柄
4 @param nWidth 窗口宽
5 @param nHeight 窗口高
6 */
7 void CCocos2DApp::init( uint32 uWnd, int32 nWidth, int32 nHeight )/// uint32 int32 是自定义数据类型 就是int 和 unsinged int
8 {
9 m_uWnd = uWnd;
10
11 cocos2d::CCEGLView* eglView = cocos2d::CCEGLView::sharedOpenGLView();
12 if( eglView == NULL )
13 {
14 return;
15 }
16
17 eglView->setFrameSize(nWidth, nHeight, (HWND)m_uWnd); /// 这里调用已经修改的cocos2d::CCEGLView方法
18
19 // Initialize instance and cocos2d.
20 if (!applicationDidFinishLaunching())
21 {
22 return;
23 }
24
25 n_bCocos2DInit = true;
26 }
27 //------------------------------------------------------------------------------
28 /**
29 @brief 系统运行 重载基类的run方法
30 @param
31 */
32 int CCocos2DApp::run()
33 {
34 if( m_uWnd == 0 )
35 {
36 return cocos2d::CCApplication::run(); /// 如果是引擎底层创建窗口,在消息循环在这里执行,在消息循环中执行mainLoop
37 }
38 else
39 {
40 if( n_bCocos2DInit)
41 {
42 cocos2d::CCDirector::sharedDirector()->mainLoop();
43 }
44
45 return 1;
46 }
47 }
再把HelloWorldScene添加到工程。运行HelloCPP需要的图片资源,也需要复制到工作目录下。
第四步 将CCocos2DApp添加到CMFCTestView中
在CMFCTestView.h中添加
CCocos2DApp m_Cocos2DApp;
我们希望在窗口初始后,调用CCocos2DApp的init方法,实现渲染环境初始化。
切换到类视图,选择属性,选择重写(Overide)标签,找到OnInitialUpdate,添加重写CMFCTestView::InitUpdate方法
在其中添加代码,调用CCocos2DApp::init,代码如下:
1 void CMFCTestView::OnInitialUpdate()
2 {
3 CView::OnInitialUpdate();
4
5 // TODO: 在此添加专用代码和/或调用基类
6
7 RECT rc;
8 ::GetClientRect( m_hWnd, &rc );
9
10 m_Cocos2DApp.init( (uint32)m_hWnd, rc.right - rc.left, rc.bottom - rc.top ); /// 传入窗口句柄和宽高
11 SetTimer( 1, 30, NULL ); /// 见下面的分析
12 }
在之前分析中已经提到,HelloCpp的Render是在消息循环中执行的,MFC窗口接管消息循环,我们Render放在哪里比较好呢?因为我们是用来制作工具,对渲染效率要求不是很高,所以我想用定时器来驱动我们的Render。所以在初始化的时候启动了一个定时器。
在CMFCTestView中添加定时器处理方法:
1 // CMFCTestView 消息处理程序
2 void CMFCTestView::OnTimer(UINT_PTR nIDEvent)
3 {
4 // TODO: 在此添加消息处理程序代码和/或调用默认值
5 m_Cocos2DApp.run(); /// 这里执行mainLoop,在mainLoop中执行Render
6 CView::OnTimer(nIDEvent);
7 }
编译运行应当就可以看到可爱的Cocoser了,如下图
画面是渲染出来了,可是却收不到鼠标消息。原来引擎实现了CCEGLView::WindowProc用来处理Windows消息,系统在这里模拟了触屏的一些操作。那我们只要将MFC的窗口消息传递给CCEGLView::WindowProc就可以实现了。
在CMFCTestView中添加重写(Ovreide)函数WindProc,代码如下:
/**
@brief 窗口回调函数
@param
*/
LRESULT CCocos2DApp::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
cocos2d::CCEGLView* eglView = cocos2d::CCEGLView::sharedOpenGLView();
if( eglView )
{
return eglView->WindowProc( message, wParam, lParam );
} return 0;
}
这样就可以正常收到windows窗口消息了。
还有一个需要注意的问题,当窗口大小发生变化,我们也需要通知到引擎底层,所以我们需要再添加一个针对WM_SIZE的处理函数,函数代码如下:
1 void CMFCTestView::OnSize(UINT nType, int cx, int cy)
2 {
3 CView::OnSize(nType, cx, cy);
4
5 // TODO: 在此处添加消息处理程序代码
6 m_Cocos2DApp.OnSize( cx, cy );
7 }
在CCocos2DApp::OnSize中再对底层进行相关的设置,我们看下代码:
/**
@brief 重置大小
@param
*/
void CCocos2DApp::OnSize( int nWidth, int nHeight )
{
// TODO: 在此处添加消息处理程序代码
cocos2d::CCEGLView* eglView = cocos2d::CCEGLView::sharedOpenGLView();
if( eglView )
{if( n_bCocos2DInit )
{
//重新设置窗口大小及投影矩阵
cocos2d::CCEGLView::sharedOpenGLView()->resize(nWidth,nHeight);
cocos2d::CCDirector::sharedDirector()->reshapeProjection(cocos2d::CCSizeMake(nWidth,nHeight));
}
}
}
至此,你已经看到怎样将Cocos2DX渲染到MFC窗口的全过程,相信聪明的你也一定可以制作你自己想要的游戏工具,最后希望大家Coding happy!!!
将Cocos2dX渲染到MFC窗口上的更多相关文章
-
重写MFC窗口上的关闭按钮事件(SDI, MDI, Dialog)
This piece of code demonstrate how to override WM_CLOSE event. 点击窗口关闭按钮,触发相关事件! 有时候,在MFC程序退出之前,我们通常会 ...
-
原来MFC窗口样式随字符集而改变
以前好像发现,MFC窗口上按钮的自动样式有时是有亮色边框3D效果的,有时没有,不知道原因,也没有追究,今天正好有机会发现了原因,原来是随字符集而改变的. 1.Unicode版本下的窗口 2.未设置的窗 ...
-
MFC窗口重绘
Invalidate()与 UpdateAllViews()有什么分别 Invalidate()是让程序重画窗口. UpdateAllViews()是在DOC/VIEW结构中, 当一个视图的数据改变后 ...
-
MFC窗口分割以及各窗口间的通讯
一个偶然的机会又重新接触了MFC窗口的分割,自己结合资料重新写了一个窗口分割的程序,现将具体流程跟大家分享一下: 1.我们先创建一个MFC单文档类的程序,具体分割方式先将单文档整个客户区分成两行一列, ...
-
同时支持控制台和MFC窗口程序的APP
BOOL CMyApp::InitInstance() { if ( m_bShowGui==FALSE ) { FILE *stream = NULL; AllocConsole(); // 开辟控 ...
-
如何让窗口控件半透明(控件在Paint自己时,首先向主窗口询问,获取主窗口上控件所在区域的背景图)
在网上关于窗口视觉效果,有2个问题被问得最多:第一个是如何让窗口边框有阴影效果?第二个是如何让窗口控件有半透明效果? 对于第一个问题,我们的答案是用双层窗口模拟或是用Layered Window.在X ...
-
mfc窗口,父窗口parentwindow,所有者窗口ownerwindow 区别
一. parent:创建者,owner:所有者 小玉的父母生下小玉,养到8岁,卖给贾府当丫头小玉的父母是parent,贾府是owner 二. 1.Pop-up窗口: 一个弹出窗口是必须具有WS_POP ...
-
[WPF疑难]如何禁用窗口上的关闭按钮
原文 [WPF疑难]如何禁用窗口上的关闭按钮 [WPF疑难]如何禁用窗口上的关闭按钮 周银辉 哈哈,主要是调用Rem ...
-
qt 在窗口上画框
在窗口w上面画个黄色的框:在窗口上添加一个label,然后在label上画框 QLabel label(&w); label.setScaledContents(true); QPixmap ...
随机推荐
-
n个元素的入栈顺序有多少种出栈顺序?
问题:w1.w2.w3.w4.w5,5个元素将会按顺序入栈,求出栈顺序有多少种情况. 先写一下结论方便记忆: 1个元素:1种 2个元素:2种 3个元素:5种 4个元素:14种 5个元素:42种 简单的 ...
-
Java并发工具类Semaphore应用实例
package com.thread.test.thread; import java.util.Random; import java.util.concurrent.*; /** * Semaph ...
-
C# 数字带逗号(千分位符、金钱千分位字符)
首先要明确带了逗号之后 数字就变成字符串了 ,不再是数字了. 昨天做项目的时候需要格式化数字变成带逗号的,本来打算自己写个方法的,后来时间太紧了,就打算从网上查个,查来查去都是要对字符串的位进行操作 ...
-
Java:String和Date、Timestamp之间的转
Java:String和Date.Timestamp之间的转 一.String与Date(java.util.Date)互转 1.1 String -> Date String dateStr ...
-
MySQL —— 程序连接时的驱动名称和URL
CONNECTION_DRIVER : com.mysql.jdbc.Driver CONNECTION_URL : jdbc:mysql://localhost/DB_NAME
-
sqlserver中的 数据转换 与 子查询
数据类型转换 --cast转换 select CAST(1.23 as int) select CAST(1.2345 as decimal(18,2)) select CAST(123 as var ...
-
为MVC 添加下载权限
今天碰到一个错误,极其郁闷,本地开发和本地部署测试没有问题,但是放到阿里云上,出现了权限问题. 报错:ASP.NET 无权访问所请求的资源.请考虑对 ASP.NET 请求标识授予. 参考网上很多资料, ...
-
spark Intellij IDEA开发环境搭建
(1)创建Scala项目 File->new->Project,如下图 选择Scala 然后next 其中Project SDK指定安装的JDK,Scala SDK指定安装的Scala(这 ...
-
word break II(单词切分)
Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, add space ...
-
python进阶之正则表达式
概念: 正则表达式是对字符串操作的一种逻辑公式,就是用事先定义好的一些特定字符.及这些特定字符的组合,组成一个“规则字符串”,这个“规则字符串”用来表达对字符串的一种过滤逻辑. 目的? 给定一个正则表 ...