引擎开发

在你的引擎中嵌入Mono

Spread the love

周天的晚上总是睡不着觉啊……加上有点拉肚子……

写写东西吧

 

本文环境基于Mac,会CMake的话当然最好了,不会的话用VS应该也好搞,把.a或者.dylib换成dll就完事儿了

本来是想好好搞搞OpenGL,结果还没学完,就想自己写引擎了(?
我觉得这样学起来才比较快(虽然平时都没什么时间写……

获得需要的库

首先到Mono官网下载Mono,

然后安装Mono到本地

如果是在Mac上的话则是安装在/Library/Frameworks/Mono.framework

从Headers中可以获得头文件,在Library中可以获得相应的库。

我们如果希望在我们的程序中嵌入Mono则需要用到Mono相关的文件:

Headers/mono-2中包含了我们所需要的头文件

Libraries/libmonosgen-2.0.a或者Libraries/libmonosgen-2.0.dylib则是我们需要的库,取决于你需要动态库或者是静态库。

好了 已经有了我们需要的库与头文件,我们可以开始编写CMakeLists.txt了!

创建工程

我们使用CLion进行工程编辑

我们的目录结构如下

Include中包含了头文件

Libs包含了头文件

Resources中包含了我们需要用到的Managed Dll

Src包含了我们需要编写的代码

TestCSLib中则包含了C#工程

如图所示

现在我们开始编写CMake文件

cmake_minimum_required(VERSION 3.8)
project(TestMonoEmbed)
 
set(CMAKE_CXX_STANDARD  11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
include_directories(Include)
link_directories(Libs)
 
 
file(GLOB_RECURSE mono
        Include/*.h)
 
set(SOURCE_FILES
        ${mono}
        Src/main.cpp)
add_executable(TestMonoEmbed ${SOURCE_FILES})
target_link_libraries(TestMonoEmbed
        mono-2.0
        iconv
        "-framework Foundation"
        )

需要注意的是我们不仅仅依赖了mono-2.0的库还依赖了包括mac上的framework以及iconv库,在编写的时候漏掉会导致链接失败。

接下去我们就开始编写main.cpp了

使用Mono

话不多说我们先上代码:

我们先看看我们的C#代码是怎么样的。

using System;
 
namespace TestCSLib
{
    public class TestDll
    {
        public int Add(int a, int b)
        {
            return a + b;
        }
    }
}

实现了一个很简单的测试类。
其中是一个Add方法,我们的目标就是在C++中调用到这个方法。
那接下去我们就开始写C++代码:

#include 
#include "mono/metadata/mono-config.h"
#include "mono/metadata/assembly.h"
#include "mono/jit/jit.h"
 
int main() {
 
    mono_config_parse (NULL);
 
    // 初始化程序域
    MonoDomain* monoDomain = mono_jit_init_version("embedding_mono_domain",
                                                    "v4.0.30319");
 
    if(monoDomain == nullptr){
        std::cerr << "create monoDomain failed" << std::endl;
        return 1;
    }
 
    // 打开程序集
    MonoAssembly* assembly = mono_domain_assembly_open(monoDomain,
                                                       "TestCSLib.dll");
    if(assembly == nullptr){
        std::cerr << "create assembly failed" << std::endl;
        return 1;
    }
 
	//获取代码块
    MonoImage* monoImage = mono_assembly_get_image(assembly);
 
	//获取Class,我们需要传入命名空间以及类名
    MonoClass* entityClass = mono_class_from_name(monoImage,
                                                   "TestCSLib",
                                                   "TestDll");
 
	//实例化类
    MonoObject* instance = mono_object_new(monoDomain, entityClass);
 
    // 获取类中的构造函数
    MonoMethod* constructorMethod = mono_class_get_method_from_name(entityClass,
                                                                    ".ctor",
                                                                    0);
 
    MonoObject* exception = NULL;
    mono_runtime_invoke(constructorMethod, instance, nullptr, &exception);
 
    // 获取类中的相应方法
    MonoMethod* processMethod = mono_class_get_method_from_name(entityClass,
                                                                "Add",
                                                                2);
 
    exception = nullptr;
 
    void* args[2];
    args[0] = new int(5);
    args[1] = new int(100);
 
    // 调用方法
    // 如果调用的是静态方法,第二个参数必须为空
    MonoObject* result = mono_runtime_invoke(processMethod, instance, args, &exception);
 
 
    // 检查是否有错误
    if(exception)
    {
        std::cout << mono_string_to_utf8(mono_object_to_string(exception, nullptr))
                  << std::endl;
    }
 
	// 获得结果之后,我们发现是Int型,需要进行拆箱,以便输出结果
    int int_result = *(int*)mono_object_unbox (result);
 
    std::cout << int_result << std::endl;
 
 
    // 关闭Mono
    mono_jit_cleanup(monoDomain);
 
    return 0;
}

以上基本就是C++对Mono进行调用的过程

Mono调用C++

Mono调用C++其实就可以使用我以前文章中写到过得Swig工具进行生成,详细请看之前的文章:

Unity/C++混合编程全攻略——基础篇: http://www.resetoter.cn/?p=241

Unity/C++混合编程全攻略——Swig篇: http://www.resetoter.cn/?p=278

通过生成互操作代码的方式来使Mono可以调用C++

在引擎中的使用

在Unity中我们都知道有MonoBehavior,任何的C#代码的入口都是MonoBehavior,其实很好理解,因为是从C++调用过来的。

在Unity中,每当创建MonoBehavior的时候,如果该代码是第一次调用,则会通过反射将MonoBehavior中的Update、FixedUpdate等函数获得其指针并且存下来,然后定时得进行调用。

类似于前面写的:

	// 获取类中的相应方法
    MonoMethod* processMethod = mono_class_get_method_from_name(entityClass,
                                                                "Add",
                                                                2);

并且保存下来,然后通过几个BehaviorManager每一帧进行自动调用,这样就完成了在引擎中嵌入Mono的工作,我们也就可以在Mono上写自己的代码了,通过Swig,我们的Mono代码也可以使用C++的功能,类似于Unity中的Renderer、Transform等等。

是不是打开了新世界的大门呢?

赶紧自己也试一试吧!

3 thoughts on “在你的引擎中嵌入Mono

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.