【续】强行在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++项目

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

    

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

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

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

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

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

    

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

    

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

    

    

    附加包含目录中加入:

    $(EngineRoot)cocos\audio\include
    $(EngineRoot)external
    $(EngineRoot)external\chipmunk\include\chipmunk
    $(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即可。

时间: 2024-08-28 16:21:46

【续】强行在C# Winform中渲染Cocos2d-x 3.6的相关文章

(转载)强行在MFC窗体中渲染Cocos2d-x 3.6

强行在MFC窗体中渲染Cocos2d-x 3.6 GuyaWeiren2015-06-29 15:14:063696 次阅读 [前言] 把Cocos2d-x渲染到另一个应用程序框架中的方法,在2.x时代有很多大神已经实现了,而3.x的做法网上几乎找不着.这两天抽空强行折腾了一下,不敢独享,贴出来供大家参考. [已知存在的问题] 程序退出时会发生非常严重的内存泄漏,博主检查了很久,但技术不够暂时无法解决.如果有大神能搞定,求告知一下做法,谢谢! 在程序从开始运行到关闭期间,有且仅有一个Cocos2

强行在MFC窗体中渲染Cocos2d-x 3.6

[前言] 把Cocos2dx渲染到另一个应用程序框架中的方法,在2.x中有很多大神已经实现了,而3.x的做法网上几乎找不着.这两天抽空强行折腾了一下,不敢独享,贴出来供大家参考. [已知存在的问题] 程序退出时会发生非常严重的内存泄漏,博主检查了很久,但技术不够暂时无法解决.如果有大神能搞定,求告知一下做法,谢谢! 在程序从开始运行到关闭期间,有且仅有一个cocos2dx窗体存在时可以选择性无视内存泄漏.如果非常在意这一点,建议使用cocos2d-x 2.2.6这个版本,放在MFC中的内存泄漏很

在 WinForm 中使用 Direct2D

在 C# 的 WinForm 应用中,界面的绘制使用的是 GDI+.不过在一些特别的应用中,可能需要用硬件加速来提高绘制的效率.下面就来介绍两种在 WinForm 应用中嵌入 Direct2D 的方法. 这里所谓的“嵌入”,指的是只有窗口的某一部分应用 Direct2D 绘制(用一些控件承载),而不是整个窗口都使用 Direct2D 绘制.这是一种混合方案,需要用硬件加速的部分由自己来绘制,其它部分仍然可以使用现有的 WinForm 技术. 至于 Direct2D 的类库,我仍然使用 Sharp

C# Winform中执行post操作并获取返回的XML类型的数据

/// <summary> /// 返回指定日期的订单数据 /// </summary> /// <param name="StartDate">起始日期</param> /// <param name="EndDate">结束日期</param> /// <returns>DataTable</returns> public System.Data.DataTable

另一种在WINFORM中使用XNA的方法

之前在写化学分子模型制作程序的时候,使用一种方法,将WINFORM控件嵌入到XNA窗体中,从而实现了即使用WINFORM窗体控件又使用XNA.最近在写另一个物理运动学课件制作程序,同样使用XNA,但从另一个角度实现了WINFORM控件和XNA共存,并且在编码上更简单一些. 一.创建XNA工程并添加窗体 向工程添加窗体MainForm,并修改GAME1为MainGame.我们把XNA内容绘制到MainForm上,其实绘制到任何有句柄的控件都可以,即使我们绘制到桌面也未尝不可.但更少的控件能够使我们

WPF与Winform中的不同(1)

1. 部分控件的Text属性,变成了 Content属性 如: winform中,Button.Text = "abc"; wpf中,Button.Content = "abcd"; 2. wpf中控件的控件属性,由 boolean类型的Visiable 改成 枚举类型的 Visibility winform中,Button.Visable = true (false); wpf中,Button.Visibility = System.Windows.Visibil

C#在WinForm中怎样让多行TEXTBOX的换行

要让一个Windows Form的TextBox显示多行文本就得把它的Multiline属性设置为true.  要让TextBox里面的文本换行大家往往会想到直接在要换行的地方加个转义的换行符"\n": [csharp] view plaincopyprint? this.textBoxDescription.Text = "操作说明\nESC\t最小化\nALT+F4\t退出\nShift+F6\t设置访问地址"; this.textBoxDescription.

C# WinForm中Show与ShowDialog

show()与showDialog()的区别 2010-06-05 14:22:51|  分类: c#学习|举报|字号 订阅 A.WinForm中窗体显示  显示窗体可以有以下2种方法:  Form.ShowDialog方法 (窗体显示为模式窗体)  Form.Show方法 (窗体显示为无模式窗体) 2者具体区别如下:  1.在调用Form.Show方法后,Show方法后面的代码会立即执行  2.在调用Form.ShowDialog方法后,直到关闭对话框后,才执行此方法后面的代码  3.当窗体显

Winform中打开网页页面的方法

1.首先比较简单的我们知道有类似的方法如下 System.Diagnostics.Process.Start("http://www.baidu.com"); 2.比较灵活一点,可以定义窗口大小,我们要实现网页中脚本打开页面的方法,即window.open 那么,我们必然会想,如何调用页面的脚本呢?其实可以利用WebBrowser来实现 //连接 string url ="http://www.baidu.com"; //定义脚本 string script =@&