OpenGL-脚本-C#

Mono

就我个人的体验来说,C#简单易学、开发速度快,用C#编程是会让人上瘾的,这里想用C#做脚本语言,自然而然就需要借助Mono了。不过因为一些理论和定义又生涩又占篇幅,我不愿记录太多,留下关键字,便于搜索就足够了。使用Mono还有一个重要原因就是Mono可以植入到程序里面,有些软件有一个超级不好的体验就是在执行它之前,需要安装一堆的组件。我也就希望借助mono减少这些不好的体验。
Mono:http://www.mono-project.com/
Embedding Mono:http://www.mono-project.com/docs/advanced/embedding/

集成Mono

  • 下载mono:http://www.mono-project.com/download/stable/
    安装之后,可以将mono的文件夹拷出来,因为原来的Mono差不多占400M+的空间,所以我们可以根据自己的需求删除一些多余的东西,我大概删了一部分精简到190M,我看了一下Unity的,它精简到了90M,可能我还有部分没有删掉。
  • 将精简后的mono拷贝到自己的工程的相对路径下,其他还是跟C++工程的配置一样,添加mono的头文件路径,库路径,并添加附加依赖项,我这里是mono-2.0-sgen.lib
  • 在工程目录下新建一个C#脚本
using System;
namespace NameSp
{
    public class MainTest
    {
        public void Main()
        {
        }
    }
}
  • 在上面Embedding Mono的文档中知道要想在mono runtime中执行c#脚本,需要先编译.cs文件,我这里都使用的相对路径,其实我们可以把这个接口留给wxwidgets的一个编译按钮,可以手动点击编译,然后再运行,这里为了快速下一步,我都是运行直接编译运行。
//编译脚本
void LoadCSharpLibrary::Compile()
{
    const std::string scriptPath("MainTest.cs");
    std::string command = "..\\..\\Libraries\\Mono\\bin\\mcs " + scriptPath + " -t:library";

    //Compile the script
    system(command.c_str());
}
  • 运行C#,好多示例都是直接运行的静态函数,我这里就故意没有定义MainTestMain函数为静态函数,让我们去生成一个对象再来调用。其实mono的操作感觉跟反射的使用有点类似。最后调用这两个函数,因为我没有在MainTest做任何明显的操作,所以没反应也正常,只要没报错,就先继续往下写。
void LoadCSharpLibrary::Load()
{
    // Program.cs所编译dll所在的位置
    const char* managed_binary_path = "MainTest.dll";

    mono_set_dirs("..\\..\\Libraries\\Mono\\lib",
        "..\\..\\Libraries\\Mono\\etc");

    //获取应用域
    domain = mono_jit_init("Test");

    //加载程序集ManagedLibrary.dll
    MonoAssembly* assembly = mono_domain_assembly_open(domain, managed_binary_path);
    MonoImage* image = mono_assembly_get_image(assembly);

    //获取MonoClass
    MonoClass* main_class = mono_class_from_name(image, "NameSp", "MainTest");

    //获取要调用的MonoMethodDesc,主要调用过程
    MonoMethodDesc* entry_point_method_desc = mono_method_desc_new("NameSp.MainTest::Main()", true);
    MonoMethod* entry_point_method = mono_method_desc_search_in_class(entry_point_method_desc, main_class);
    mono_method_desc_free(entry_point_method_desc);

    MonoObject *instance = mono_object_new(domain, main_class);

    //调用方法
    mono_runtime_invoke(entry_point_method, instance, NULL, NULL);

    //释放应用域
    mono_jit_cleanup(domain);
}

C++与C#的交互

之所以能使用脚本能进行快速开发还有个很重要的原因就是我们给它封装足够多的接口,下面就来试试C#调用C++的接口。

  • 首先看看之前设置三角形的角度函数
//三角形的变换矩阵
glm::mat4 trans;
//缩放0.5倍
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
//逆时针旋转90度
trans = glm::rotate(trans, 90.0f, glm::vec3(0.0, 0.0, 1.0));
//向x轴位移1.5
trans = glm::translate(trans, glm::vec3(1.2, 0.0f, 0.0));
  • 这里示例都比较简单,我准备将90.0f提出来作为一个变量,给C#去设置,定义一个角度的变量,和设置角度的函数
//角度
static int _angle;
static void SetAngle(float angle);
  • C++设置变量
//设置初始值
int OpenGLCanvas::_angle = 0;
//设置角度
void OpenGLCanvas::SetAngle(float angle)
{
    _angle = angle;
}

//...
//三角形的变换矩阵
glm::mat4 trans;
//缩放0.5倍
trans = glm::scale(trans, glm::vec3(0.5, 0.5, 0.5));
const float angle = _angle;
//逆时针旋转角度
trans = glm::rotate(trans, angle, glm::vec3(0.0, 0.0, 1.0));
//向x轴位移1.5
trans = glm::translate(trans, glm::vec3(1.2, 0.0f, 0.0));
//...
  • C#中声明C++函数,并开始调用它
using System;
using System.Runtime.CompilerServices;
namespace NameSp
{
    public class MainTest
    {
        [MethodImpl(MethodImplOptions.InternalCall)]
        public extern static void SetAngle(float angle);

        public void Main()
        {
            SetAngle(0.0f);
        }
    }
}
  • 需要再在mono的接口中做一下函数绑定,我认为mono_add_internal_call的理解就可以简单一点,就是在调用NameSp.MainTest::SetAngle的时候,调用OpenGLCanvas::SetAngle
void LoadCSharpLibrary::Load()
{
    // Program.cs所编译dll所在的位置
    const char* managed_binary_path = "MainTest.dll";

    mono_set_dirs("..\\..\\Libraries\\Mono\\lib",
        "..\\..\\Libraries\\Mono\\etc");

    //获取应用域
    domain = mono_jit_init("Test");

    //加载程序集ManagedLibrary.dll
    MonoAssembly* assembly = mono_domain_assembly_open(domain, managed_binary_path);
    MonoImage* image = mono_assembly_get_image(assembly);

    //获取MonoClass
    MonoClass* main_class = mono_class_from_name(image, "NameSp", "MainTest");

    //获取要调用的MonoMethodDesc
    MonoMethodDesc* entry_point_method_desc = mono_method_desc_new("NameSp.MainTest::Main()", true);
    MonoMethod* entry_point_method = mono_method_desc_search_in_class(entry_point_method_desc, main_class);
    mono_method_desc_free(entry_point_method_desc);

    //函数绑定
    mono_add_internal_call("NameSp.MainTest::SetAngle", reinterpret_cast<void*>(OpenGLCanvas::SetAngle));

    MonoObject *instance = mono_object_new(domain, main_class);

    //调用方法
    mono_runtime_invoke(entry_point_method, instance, NULL, NULL);

    //释放应用域
    mono_jit_cleanup(domain);
}
  • 编译运行, 正正的一个三角形
    这里写图片描述
  • 然后我们再将C#的设置角度改变一下
using System;
using System.Runtime.CompilerServices;
namespace NameSp
{
    public class MainTest
    {
        [MethodImpl(MethodImplOptions.InternalCall)]
        public extern static void SetAngle(float angle);

        public void Main()
        {
            SetAngle(135.0f);
        }
    }
}
  • 截图可以看到三角形确实旋转了,就证明我们的mono已经植入成功了
    这里写图片描述

扩展

用脚本还有一个不错的想法就是自己可以将脚本封装成可视化编程,后面可以来试试

猜你喜欢

转载自blog.csdn.net/qq992817263/article/details/79507042