Force rendering Cocos2d-x in MFC forms 3.6

Source: Internet
Author: User

Objective

The method of rendering COCOS2DX to another application framework, in 2.x, there are a lot of great gods have been implemented, and 3.x practice on the Internet can hardly find. These two days take the time to forcibly toss a bit, dare not exclusive, put out for everyone reference.

"A known problem"

  A very serious memory leak occurs when the program exits , the blogger checks for a long time, but the technology is not enough temporarily to solve. If there is a great God can handle, please inform the practice, thank you!

A memory leak can be selectively ignored when a program has and only one COCOS2DX form exists from the start to the shutdown period. If this is a concern, it is recommended to use Cocos2d-x 2.2.6, which is a small memory leak that is placed in MFC.

* Using VLD to check for leaks will be an error

"Why do you do this?"

In the game development, more or less will use some auxiliary tools, such as Cocosstudio. But more often than not, cocosstudio cannot status quo (for example, the function of the Spectrum editor, as mentioned in the blog "I use Cocos2d-x to make 〈love live! College Idol Festival"). In this case, the developer needs a tool that is designed for the current project.

If the accessibility tools need to provide a rich interface and controls, pure use of cocos2d-x to make it will be very chicken. For example, this open file control:

  

Of course, must do the words with COCOS2DX also can do, but quite troublesome. If you are interested you can try writing it yourself and improve your posture level.

So at this point the COCOS2DX layer should be placed in an application framework that provides a variety of controls, COCOS2DX only for display, and the rest of the data operations to be done by the framework.

At present, bloggers are more familiar with the framework of MFC and C # Winform. To be honest, C # WinForm makes forms easier and quicker than MFC. But if you use C # WinForm you have to do C # call C + +, and for some specific parameters (such as String to const char* conversion) must do special processing, it is troublesome, otherwise the DLL stack error. and MFC does not exist this problem.

"Core ideas"

COCOS2DX running on Windows is a window, you must call the CreateWindowEx API inside of it. So as soon as we find this API, set the parameters as sub-windows and pass the handle of the parent window in order to reach the request. The created form is the subform in the parent form.

Also note that the COCOS2DX native program has a message loop of its own, if calling Application::Run directly causes the MFC layer to die, we need to give the message loop to the main thread of the framework to operate.

The flowchart is as follows:

  

"Tools Needed"

1. Visual Studio 2013 with MFC components installed

2, Cocos2d-x 3.6

3, GLFW (: Point me)

4, CMake (: Point me)

"Action Steps"

  1. Create a project

Create an MFC project (the dialog Type I used). Note In the wizard, "Use of MFC" to select "Use MFC in Shared DLLs":

    

  2. Copy the necessary documents

Copy the classes and resources folders in the COCOS2DX source and template project to the project directory (the project template is located under the engine catalog \templates\cpp-template-default) and be sure to use this structure:

    

  3. Modify Project Properties

Open the MFC Project solution and add a cocos2dx two property sheet to the project in the property manager (View-property manager). The property sheet is located in the solution directory \cocos2d\cocos\2d:

    

Then add libcocos2d,libbox2d,libspine to the solution and set libcocos2d as the dependency of the MFC project:

    

Then add in the additional include directory of the MFC project:

$ (engineroot) cocos\audio\include
$ (Engineroot) external
$ (engineroot) External\chipmunk\include\chipmunk
$ (engineroot) extensions
.. \classes
..
% (additionalincludedirectories)
$ (_cocos_header_win32_begin)
$ (_cocos_header_win32_end)

  

In the preprocessor definition, add:

Cocos2d_debug=1

Add to the additional library directory:

$ (_cocos_lib_path_win32_begin)

$ (_cocos_lib_path_win32_end)

  

Additional dependency joins:

$ (_cocos_lib_win32_begin)

$ (_cocos_lib_win32_end)

Libcocos2d.lib

Then modify the project properties--working directory, and build directory:

    

    

Then add all the files under classes to the MFC project:

    

The final setting does not use a precompiled header, or every class added with # include "StdAfx.h", Trouble:

    

  4, modify the GLFW

The Create window in COCOS2DX 2.x is completed in the Cceglview class, and you can modify it directly. By using the GLFW Management window after 3.x, CreateWindowEx was encapsulated in. And COCOS2DX does not come with GLFW source, only the header files and Lib files. So we need to download GLFW source code for modification.

Use Cmakegui to open Glfw,source code where you selected the GLFW extracted folder, build the binaries choose the folder to build the solution, and then generate a corresponding VS version of the solution (GLFW unzipped folder do not delete):

    

Then open the generated sln, look for CreateWindowEx, modify the function it is in (win32_window.c,633 line):

static int CreateWindow (_glfwwindow* window, const _glfwwndconfig* wndconfig, Const _glfwctxconfig* Ctxconfig, const _glfwfbconfig* fbconfig, HWND paren    T)//parent form handle {int xpos, ypos, Fullwidth, fullheight;    wchar* Widetitle; Window->win32.dwstyle = Ws_childwindow | ws_visible; Subform style Window->win32.dwexstyle = Ws_ex_appwindow |        Ws_ex_windowedge;    xpos = 0;    ypos = 0;    Fullwidth = wndconfig->width;       Fullheight = wndconfig->height;    Widetitle = _glfwcreatewidestringfromutf8 (Wndconfig->title); if (!widetitle) {_glfwinputerror (Glfw_platform_error, "win32:failed to convert window t        Itle to UTF-16 ");    return gl_false; } Window->win32.handle = Createwindowexw (Window->win32.dwexstyle, _GLFW           _wndclassname, Widetitle,                                Window->win32.dwstyle, xpos, Ypos,                                           Fullwidth, Fullheight, parent,//pass in parent form handle NULL,//No Window menu Getmodulehandlew (NUL L), window); Pass object to Wm_create////...}

Then, from inside out, modify the place where it was called:

win32_window.c,769 Line

int _glfwplatformcreatewindow (_glfwwindow* window,                              const _glfwwndconfig* wndconfig,                              const _glfwctxconfig* Ctxconfig,                              const _glfwfbconfig* Fbconfig,                              HWND parent) {    //...    if (!createwindow (window, Wndconfig, Ctxconfig, Fbconfig, parent))        return gl_false;    // ...        if (!createwindow (window, Wndconfig, Ctxconfig, Fbconfig, parent))            return gl_false;    //    // ...}

internal.h,524 Line

int _glfwplatformcreatewindow (_glfwwindow* window,                              const _glfwwndconfig* wndconfig,                              const _glfwctxconfig* Ctxconfig,                              const _glfwfbconfig* Fbconfig,                              HWND parent);

window.c,116 Line

Glfwapi glfwwindow* glfwcreatewindow (int width, int height,                                     const char* title,                                     glfwmonitor* Monitor,                                     glfwwindow* share,                                     int parent) {    //...     if (!_glfwplatformcreatewindow (window, &wndconfig, &ctxconfig, &fbconfig, (HWND) parent))     / /    // ...}

glfw3.h,1645 Line:

Glfwapi glfwwindow* glfwcreatewindow (int width, int height, const char* title, glfwmonitor* Monitor, glfwwindow* share, in t parent);

After the change, use the Minsizerel option to compile, compile well after the GLFW solution directory \src\minsizerel found glfw3.lib files, together with Glfw3.h (in GLFW extract directory \INCLUDE\GLFW) together, Overwrite the original file with the MFC Project Solution Catalog \COCOS2D\EXTERNAL\GLFW3\PREBUILT\WIN32 and the MFC Project solution directory \COCOS2D\EXTERNAL\GLFW3\INCLUDE\WIN32, respectively.

5, modify the Cocos layer

Add a method and a member to the header file of the Glviewimpl class (which is the Glview class in 3.2):

Public:    static void SetParent (HWND parent) {m_sparent = parent;} Private:    static HWND m_sparent;

Don't forget to add it in CPP

HWND glviewimpl::m_sparent = NULL;

Then modify the Glviewimpl::initwithrect method to modify the place where the Glfwcreatewindow is called:

BOOL Glviewimpl::initwithrect (const std::string& viewName, rect rect, float framezoomfactor) {    //...    _mainwindow = Glfwcreatewindow (Rect.size.width * _framezoomfactor,                                   rect.size.height * _framezoomfactor,                                   _viewname.c_str (),                                   _monitor,                                   nullptr,                                   (int) m_sparent);//pass in parent window handle    ////    ...}

Modify the Run method of the application class to remove the message loop inside:

int Application::Run () {    Pvrframeenablecontrolwindow (false);    Initglcontextattrs ();    Initialize instance and cocos2d.    if (!applicationdidfinishlaunching ())    {        return 1;    }    Retain Glview to avoid Glview being released in the while Loop Director::getinstance ()->getopenglview (    )->re Tain ();    return 0;}

  

6. Editing MFC forms

Next, add a picture control control in the MFC form, set the control ID to Idc_renderwnd, and then select the control (the very egg is only clicked at the border of the control to select) Right--"Add variable":

    

7. Add Render Class

On the MFC project in Solution Explorer, right-click Add--Class ... to add an MFC class:

    

    

Then modify the class:

#pragma once//crenderwndclass crenderwnd:public cwnd{declare_dynamic (Crenderwnd) Public:crenderwnd (); virtual ~ Crenderwnd ();p Rotected:declare_message_map () public:    afx_msg void OnTimer (Uint_ptr nidevent);    afx_msg void OnDestroy ();p ublic:    void Initialize ();p rivate:    BOOL m_binited;};

Realize:

RenderWnd.cpp: Implement file//#include "stdafx.h" #include "Cocos2dxMFC.h" #include "RenderWnd.h" #include "cocos2d.h" # Include "AppDelegate.h"//Crenderwndimplement_dynamic (Crenderwnd, CWnd) Crenderwnd::crenderwnd (): m_binited (FALSE) { }crenderwnd::~crenderwnd () {}begin_message_map (Crenderwnd, CWnd) On_wm_timer () On_wm_destroy () END_MESSAGE_MAP ()// Crenderwnd Message Handler appdelegate app;void crenderwnd::initialize () {cocos2d::glviewimpl::setparent (this->    GetSafeHwnd ());    Cocos2d::application::getinstance ()->run ();    this->m_binited = TRUE; SetTimer (1, 1, NULL);} void Crenderwnd::ontimer (Uint_ptr nidevent) {if (this->m_binited) {Auto Director = Cocos2d::D irector::get        Instance ();        Director->mainloop ();        Director->getopenglview ()->pollevents ();    Cwnd::ontimer (nidevent);    }}void Crenderwnd::ondestroy () {Cwnd::ondestroy ();        if (this->m_binited) {Auto Director = Cocos2d::D irector::getinstance (); Director->getopeNglview ()->release ();        Director->end ();        Director->mainloop ();    this->m_binited = FALSE; }}

Then change the type of the control m_renderwnd that you just bound from CStatic to Crenderwnd, and

8, run up

In theory, the operation is done, and now it needs to be compiled to run. But is the touch so kind as to do a good job without leaving a hole?

Of course, the legend of the Cocos series of pits can be connected around the earth how many laps, here to pop a bit into the pit, do not believe you F5:

    

What the hell is this? In fact, a value in the platform enumeration in Applicationprotocol has the same name as one of MFC's macros. The solution is to add such a sentence to the stdafx.h:

#undef os_windows

Then continue compiling. Of course, the pit is not a single line, and error.

    

But this simple, according to the error content, in the project preprocessor definition to add _crt_secure_no_warnings.

    

Is it supposed to end up with a boss-level pit? Boss come: At this time compile can pass, but a run must error. Look at the Output window:

    

Aw, the original is not found file. But we've already set up a working directory, and there are files under resources (this pit is not in the 2.2.6).

Track down from Label::createwithttf and finally find the directory where the COCOS2DX search file is set here (ccfileutils-win32.cpp 59 lines):

static void _checkpath () {    if (0 = = S_resourcepath.length ())    {        WCHAR *putf16exepath = nullptr;        _get_wpgmptr (&putf16exepath);        We need only directory part without exe        WCHAR *putf16dirend = WCSRCHR (putf16exepath, L ' \ \ ');        Char Utf8exedir[cc_max_path] = {0};        int nnum = WideCharToMultiByte (Cp_utf8, 0, Putf16exepath, putf16dirend-putf16exepath+1, Utf8exedir, sizeof (UTF8EXEDIR) , nullptr, nullptr);        S_resourcepath = Convertpathformattounixstyle (Utf8exedir);    }}

_get_wpgmptr is a thing? Check it out. This function is used to get the directory where the exe of the process is located.

Let's look at the corresponding section in Cocos2dx 2.2.6 (CCFileUtilsWin32.cpp 34 lines):

static void _checkpath () {    if (! s_pszresourcepath[0])    {        WCHAR  Wszpath[max_path] = {0};        int nnum = WideCharToMultiByte (CP_ACP, 0, Wszpath,            getcurrentdirectoryw (sizeof (Wszpath), Wszpath),            S_ Pszresourcepath, MAX_PATH, NULL, NULL);        S_pszresourcepath[nnum] = ' \ \ ';    }}

Obviously, using Getcurrentdirectoryw in 2.2.6 to get the current directory, you can use this function to get the correct working directory. Why is the 3.6 project with Cocos new not the problem? Because in the pre-link event of the new project, there is a last sentence:

  

This command will copy all the files under the resources to the output directory (that is, the directory where the process EXE is located), naturally there is no problem finding the file.

Not sure what the meaning and purpose of this is? But at this point I want to say:

    

I would also like to say:

    

The method of modification is very simple, refer to 2.2.6 to change the _get_wpgmptr function in _checkpath to GETCURRENTDIRECTORYW:

static void _checkpath () {    if (0 = = S_resourcepath.length ())    {        char Pathbuffer[max_path] = {0};        WCHAR  Wszpath[max_path] = {0};        int nnum = WideCharToMultiByte (CP_ACP, 0, Wszpath,            getcurrentdirectory (sizeof (Wszpath), Wszpath),            Pathbuffer , MAX_PATH, NULL, NULL);        Pathbuffer[nnum] = ' \ \ ';        S_resourcepath = Pathbuffer;    }}

  

⑨, the last minor modification

If you are using an MFC form that is a dialog type, you will find that after you press ENTER or ESC, the form is closed immediately after you run it. Therefore, you also need to block the return and ESC key response. In the MFC dialog class, add a method to override PreTranslateMessage:

Private:    virtual BOOL pretranslatemessage (msg* pMsg);

Realize:

BOOL Ccocos2dxmfcdlg::P retranslatemessage (msg* pMsg) {    if (pmsg->message = wm_keydown)    {        if (pmsg- >wparam = = Vk_escape | | Pmsg->wparam = = Vk_return)        {            RETURN TRUE;        }    }    return CDialogEx::P retranslatemessage (PMSG);}

 

"Running Up"

If the compilation does not go wrong, it will look like this:

  

As long as the interface is left out, it is convenient to control the Cocos layer through the MFC layer control. As to what kind of tool to make, all rely on everyone to play ~

PostScript

Using this set of ideas, you can theoretically render cocos to any framework that supports calling C + + layer code.

Children's shoes that need to be rendered in C # WinForm Please wait for my next blog, which will talk about processing methods and the conversion of string to const char*.

Force rendering Cocos2d-x in MFC forms 3.6

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.