zoukankan      html  css  js  c++  java
  • 【续】强行在C# Winform中渲染Cocos2d-x 3.6

    【前言】

      上一篇讲了怎么把Cocos2d-x 3.6渲染进MFC窗体,这里来讲一下怎么在C# Winform中做到同样的功能。如果你不熟悉MFC的使用但对C# Winform比较在行,请往下看。

      这一篇是作为上一篇的副属文,所以文中提到的部分操作需要在上一篇中找……博主懒逼不在这复制粘贴了。


    【核心思想】

      同上一章不同的是,C#是托管环境,并不能直接用“对象.方法()”这样的形式来访问Cocos层的代码。我们需要在其间建立一个DLL层(C++编写)作为Cocos层的接口,让C#通过接口来控制Cocos层。


    【需要的工具】

      1、    安装了C#组件的Visual Studio 2013

      2、    Cocos2d-x 3.6

      3、    GLFW (下载地址:点我

      4、    CMake(下载地址:点我


    【操作步骤】

      1、    创建C# Winform项目

        .NET的版本随意,使用默认的即可。

        

      2、    拷贝必要文件

        参考上一篇

      3、    创建空的C++项目

        VS2013创建的C++ DLL项目默认是Win8.1平台的,不知道里面装了什么奇怪的东西进去……于是手动创建干净的DLL项目。项目名称自定,我使用的是“App”

        

        添加完成后,将App项目设为C#项目的依赖项。

      4、    在解决方案中加入Cocos项目

        将libcocos2d,libbox2d,libspine加入解决方案中,并把libcocos2d设为App项目的依赖项。

      5、    修改C++项目的属性

        在属性管理器(视图——属性管理器)中为项目添加cocos2dx的两个属性表。属性表位于解决方案目录cocos2dcocos2d:

        

        常规页面,按照打框处设置:

        

        调试页面,设置工作目录:

        

        

        附加包含目录中加入:

        $(EngineRoot)cocosaudioinclude
        $(EngineRoot)external
        $(EngineRoot)externalchipmunkincludechipmunk
        $(EngineRoot)extensions
        ..Classes
        ..
        %(AdditionalIncludeDirectories)
        $(_COCOS_HEADER_WIN32_BEGIN)
        $(_COCOS_HEADER_WIN32_END)

      

        预处理器定义中加入:

        _WIN32
        _WINDOWS
        COCOS2D_DEBUG=1
        _CRT_SECURE_NO_WARNINGS

        附加库目录中加入:

        $(_COCOS_LIB_PATH_WIN32_BEGIN)

        $(_COCOS_LIB_PATH_WIN32_END)

      

        附加依赖项加入:

        $(_COCOS_LIB_WIN32_BEGIN)

        $(_COCOS_LIB_WIN32_END)

        libcocos2d.lib

      6、    修改GLFW

        同上一篇

      7、    修改Cocos层

        同上一篇,以及修改CCFileUtils-win32.cpp 59行的方法:

    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;
        }
    }
    

      

      8、    为C++项目添加代码

        首先添加一个标准DLL源文件dllmain.cpp(名字必须是这个):

    // dllmain.cpp : Defines the entry point for the DLL application.
    #include <windows.h>
    
    BOOL APIENTRY DllMain( HMODULE hModule,DWORD  ul_reason_for_call,LPVOID lpReserved)
    {
    	switch (ul_reason_for_call)
    	{
    	case DLL_PROCESS_ATTACH:
    	case DLL_THREAD_ATTACH:
    		break;
    	case DLL_THREAD_DETACH:
    	case DLL_PROCESS_DETACH:
    		//instance will be deleted automatically
    		break;
    	}
    	return TRUE;
    }
    

      

        然后将Classes文件夹中的源码加入到项目中:

        

        最后添加接口(文件名自定,我使用的API.h和API.cpp),头文件:

    #pragma once
    
    #define DLLEXPORT __declspec(dllexport) 
    
    extern "C"
    {
      DLLEXPORT void Initialize(HWND parent);   DLLEXPORT void MainLoop();   DLLEXPORT void Destory(); };

        实现:

    #include "API.h"
    
    #include "cocos2d.h"
    #include "AppDelegate.h"
    
    extern "C"
    {
        AppDelegate app;
        DLLEXPORT void Initialize(HWND parent)
        {
            cocos2d::GLViewImpl::SetParent(parent);
            cocos2d::Application::getInstance()->run();
        }
    
    
        DLLEXPORT void MainLoop()
        {
            auto director = cocos2d::Director::getInstance();
            director->mainLoop();
            director->getOpenGLView()->pollEvents();
        }
    
    
        DLLEXPORT void Destory()
        {
            auto director = cocos2d::Director::getInstance();
            director->getOpenGLView()->release();
            director->end();
            director->mainLoop();
        }
    }

        之后可以根据需求在接口中添加更多的函数。

      ⑨、    修改C#项目的属性

        设置输出路径:

        

        设置工作目录和启用本机代码调试(勾上后可以调试C++层):

        

      10、    为C#项目添加代码

        添加一个调用DLL代码的类,我使用的名字叫NativeInterface:

    using System;
    using System.Runtime.InteropServices;
    
    
    namespace Cocos2dxCSharp
    {
        class NativeInterface
        {
            const string DLL_NAME = "App.dll";
    
            [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
            public static extern void Initialize(int parent);
    
            [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
            public static extern void MainLoop();
    
            [DllImport(DLL_NAME, CallingConvention = CallingConvention.Cdecl)]
            public static extern void Destory();
        }
    }

        在窗体编辑器中,对窗体添加Load和FormClosing两个事件响应方法,再拖一个Panel控件和一个Timer控件到窗体上。Timer控件的Interval值设为10,并添加Tick事件的响应方法。

        然后完成方法:

    private void Form1_Load(object sender, EventArgs e)
    {
        this.timer1.Start();
        NativeInterface.Initialize(this.panel1.Handle.ToInt32());
    }
    
    
    private void Form1_FormClosing(object sender, FormClosingEventArgs e)
    {
        this.timer1.Stop();
        NativeInterface.Destory();
    }
    
    
    private void timer1_Tick(object sender, EventArgs e)
    {
        NativeInterface.MainLoop();
    }
    

      

      11、    运行起来

        如果编译没有出错的话,运行起来会看到这个样子:

        


    【字符串传递处理】

      普通的数据类型(int,float这些)是可以直接作为参数或返回值传递的。虽然C#中的string类型和C++中的const char*类型也是对应的,但是在调试过程中,如果不做处理会报错。

      因为调试时C#的托管堆栈和C++的DLL堆栈并不属于同一块内存,就好比你拿着“城隍庙”这个地址,在成都找到的是各种电子元件,在上海找到的是各种小吃。

      这里有篇博文讲了参数如何传递,我大概整理了一下:

      1、字符串作为参数
        C++:参数为char*
        C#:参数为string,用[MarshalAs(UnmanagedType.LPStr)]修饰

    [DllImport("A.dll)"]
    static extern void Function([MarshalAs(UnmanagedType.LPStr)]string val);
    

      

      2、字符串作为返回值
        C++:返回值为char*
        C#:返回值为string,使用[return:MarshalAs(UnmanagedType.LPStr)]修饰

    [DllImport("A.dll)"]
    [return:MarshalAs(UnmanagedType.LPStr)]
    static extern string Function();
    

      

      3、字符串作为输入输出参数
        C++:参数为char*
        C#:参数为byte[](那篇博文提到的用StringBuilder我这里传不了,不解)

    [DllImport("A.dll)"]
    static extern void Function(byte[] intoutVal);
    

      如果要传递宽字符,使用UnmanagedType.LPWStr即可。

  • 相关阅读:
    Java IO流
    Java中Char和Byte的区别
    Java常用类
    View
    3 View
    View
    3 View
    3 View视图 URLconf
    2 Model层
    2 Model层-模型成员
  • 原文地址:https://www.cnblogs.com/GuyaWeiren/p/4606419.html
Copyright © 2011-2022 走看看