Cocos2dx集成于windows桌面窗口程序的步骤
2D游戏需要做编辑器,而编辑器总是希望可以复用游戏中的逻辑来运行场景试看效果。
对于cocos2dx开发的程序,这个需求可以描述为:
实现一种方法,在桌面窗口程序中的某个控件上显示cocos2dx的场景,而其他部分保持该操作系统原生ui的功能。
初级版v1.0
这里以windows桌面程序为例,描述如何实现这一点,cocos2dx版本为2.2.5
代码用qt实现,但是没有用到太多qt的东西,windows api通用。
1 创建工程。这里我们依然使用cocos2dx提供的工程生成器创建工程,以得到完美的cocos2dx+box2d的环境。
2 将qt环境引入工程。将qt的include目录和lib目录加到项目设置中的c++目录中,然后加上qt的对应lib。
3 在某个widget上显示qt场景。
这一段比较关键,首先我们的代码应该以qt为主框架,因此删除main函数中CCEGLView初始化部分
//eglView->setViewName("test");
//eglView->setFrameSize(480, 320);
注意必须保留appdelegate,因为cocos2dx运行需要有这个。
接着创建qapp:
QApplication qapp(count, NULL);
在最后,我们在main函数调用
CCApplication::sharedApplication()->run();
而不是调用qt的主循环,因为这个run里还要执行一些cocos2dx的初始化方法。而qt主循环不调用则不影响。
然后我们得修改cocos2dx源代码了,首先修改CCEGLView,增加一个方法CreateByHWND,里面基本复制Create,唯独创建窗口一块删除,直接用参数提供的hWND赋值。
然后增加一个静态方法sharedOpenGLViewCreateByHWND,里面基本复制sharedOpenGLView,只是不调用create,调用我们刚才写的CreateByHWND。
接着就可以开始我们的外部调用了。
首先获得目标widget的winId,这个过程将widget转化成了native。然后将获得的winId(其实就是hWnd)传入 sharedOpenGLViewCreateByHWND,创建出Cocosdx的绘图表面EGLView。
为了得到连续的刷新,定义一个定时器,每次时间触发代码:
CCDirector::sharedDirector()->drawScene();
遇到的问题:
1 显示一个大黑框,这是因为没有设置frameSize,在定位的时候,getVisibleSize全部返回了0,0。这里我们需要在创建完EGLView之后调用一句:
eglView->setFrameSize(w->size().width(), w->size().height());
把widget的size设上去。
2 游戏画面不占全部widget,有偏移。这是因为setFrameSize调用了centerWindow,centerWindow会根据屏幕来定位到中间,对此我们需要把centerwindow注释不执行。
4 实现事件传向cocos2Dx
由于我们自己创建了窗体,因此没有使用cocos2dx的WndProc,解决方案是在CCApplication的主循环中处理消息的部分加上:
TranslateMessage(&msg);
DispatchMessage(&msg);
CCEGLView::sharedOpenGLView()->WindowProc(msg.message, msg.wParam, msg.lParam); //加上此句
进化版version 2.0
之前的版本 对cocos2dx改动太大,不好集成。hack代码的行为,还是越少越好吧,又想出了一个新改法,尽量减少对cocos2dx代码的修改:
1 写一个新的继承自CCEGLView的类,名为CCEGLViewForHWND。
头文件如下:
#pragma once #include "CCEGLView.h" namespace cocos2d { class CCEGLViewForHWND : public CCEGLView { public: CCEGLViewForHWND(HWND); ~CCEGLViewForHWND(); void centerWindow(); void setCurrent(); static LRESULT HWNDProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); protected: bool CreateWithHWND(HWND hWnd); WNDPROC wndProc; static CCEGLViewForHWND* s_current; }; }
源文件如下:
#include "CEGLViewForHWND.h" #include "CCDirector.h" namespace cocos2d { CCEGLViewForHWND* CCEGLViewForHWND::s_current = NULL; CCEGLViewForHWND::CCEGLViewForHWND(HWND hWnd) { CreateWithHWND(hWnd); } LRESULT CCEGLViewForHWND::HWNDProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { CCEGLViewForHWND* This = (CCEGLViewForHWND*)GetWindowLong(hWnd, GWL_USERDATA); if (This != NULL && This == s_current) { LRESULT res = CCEGLView::sharedOpenGLView()->WindowProc(msg, wParam, lParam); if (msg == WM_DESTROY) { CCDirector::sharedDirector()->end(); } if (This->wndProc) { return This->wndProc(hWnd, msg, wParam, lParam); } else { return DefWindowProc(hWnd, msg, wParam, lParam); } } else { return DefWindowProc(hWnd, msg, wParam, lParam); } } CCEGLViewForHWND::~CCEGLViewForHWND() { } void CCEGLViewForHWND::setCurrent() { RegisterView(this); RECT rect; GetClientRect(m_hWnd, &rect); setFrameSize(rect.right - rect.left, rect.bottom - rect.top); s_current = this; wglMakeCurrent(m_hDC, m_hRC); } bool CCEGLViewForHWND::CreateWithHWND(HWND hWnd) { m_hWnd = hWnd; bool bRet = false; do { CC_BREAK_IF(!m_hWnd); bRet = initGL(); if (s_current != NULL) { wglMakeCurrent(s_current->m_hDC, s_current->m_hRC); } bRet = true; wndProc = (WNDPROC)GetWindowLong(hWnd, GWL_WNDPROC); SetWindowLong(hWnd, GWL_WNDPROC, (long)&CCEGLViewForHWND::HWNDProc); SetWindowLong(hWnd, GWL_USERDATA, (long)this); } while (0); SetupTouch(); return bRet; } void CCEGLViewForHWND::centerWindow() { return; } }
这个类完成的作用是,封装将已有HWND作为渲染目标的功能。
2 写一个新的继承自qwidget的类QtCocosWidget
头文件如下:
#pragma once #include "cocos2d.h" #include <QtWidgets\qwidget.h> namespace cocos2d { class CCEGLViewForHWND; } class QtCocosWidget : public QWidget { public: QtCocosWidget(QWidget* parent = 0); ~QtCocosWidget(); void MakeActive(); protected: virtual void resizeEvent(QResizeEvent *); cocos2d::CCEGLViewForHWND* view; };
源文件如下:
#include "QtCocosWidget.h" #include "cocos2d.h" #include "CEGLViewForHWND.h" #include <QtGui\qevent.h> using namespace cocos2d; QtCocosWidget::QtCocosWidget(QWidget* parent) : QWidget(parent) { HWND id = (HWND)winId(); view = new CCEGLViewForHWND(id); } void QtCocosWidget::MakeActive() { view->setCurrent(); } void QtCocosWidget::resizeEvent(QResizeEvent* ev) { //CCDirector::sharedDirector()->setViewport(); QWidget::resizeEvent(ev); } QtCocosWidget::~QtCocosWidget() { }
这个类的作用是,将第一步实现的类和qt结合起来。
3 还是要小改cocos2d的代码(没办法,谁让CCEGLView使用了单例呢)
加入函数
static void RegisterView(CCEGLView* view);
函数实现如下:
void CCEGLView::RegisterView(CCEGLView* view) { s_pMainWindow = view; s_pEglView = view; }
完工!
使用方法:
#include "QtCocosWidget.h" ... ... ... ... AppDelegate ccapp; int argc = 0; QApplication app(argc, NULL); ... QtCocosWidget* target = new QtCocosWidget(parent); target->MakeActive(); return CCApplication::sharedApplication()->run();
可以看到出来效果了!
浙公网安备 33010602011771号