原生实现C#与Lua相互调用方法(Unity3D可用)

Lua是一种很好的扩展性语言,Lua解释器被设计成一个很容易嵌入到宿主程序的库,下面这篇文章主要给大家介绍了关于原生实现C#与Lua相互调用方法。

引言

本篇简单介绍如何在C#中执行Lua脚本,传递数据到Lua中使用,以及Lua中调用C#导出的方法等。在Unity中开发测试,并打IL2CPP的Android包在模拟器上运行通过。Lua版本使用的是Lua5.1.5。


一、编译Lua动态链接库

1. 编译Windows下使用的DLL文件

使用VS2015创建一个空的动态链接库项目,删除里面默认创建的几个文件(如果想自定义拓展可用保留),然后把Lua的源码拷贝进来,添加到项目工程中,编译宏需要配置LUA_BUILD_AS_DLL和_CRT_SECURE_NO_WARNINGS。然后就可以编译x86和x64的DLL动态库,整体步骤简单易操作。

2. 编译Android下使用的SO文件

通过NDK编译Android需要的so动态库,因此需要手写Application.mk和Android.mk两个mk文件,下面是我使用的两个文件的内容,创建放在上面VS的工程里面即可,路径是在lua源码src的上一层目录。

# Application.mk
APP_PLATFORM = android-23
APP_ABI := armeabi-v7a arm64-v8a
APP_STL := stlport_shared

将上面的mk文件放置完成后,打开CMD命令行,执行ndk编译。由于并不是在Android的jni项目目录,因此执行命令会有所不同,可以使用下面的命令执行生成,等待ndk执行完成后就生成了需要的so库。

ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk NDK_APPLICATION_MK=./Application.mk

二、编写C#使用的API

1. 动态链接库在Unity中的存放位置。

在Unity项目Assets目录里面创建Plugins目录,用于存放不同平台的DLL库。Windows需要的DLL存放的目录为Assets/Plugins/x86和Assets/Plugins/x86_64;Android需要的SO文件存放的目录为Assets/Android/[libs/arm64-v8a]括号里面的目录其实就是上面NDK编译后生成的路径。

2. 编写C#使用的API

大部分的动态库中的接口直接使用以下这种方式即可使用,使用IntPtr来表示lua_State*对象,传入参数char*可用使用byte[]或者string,但是会有一点点区别。

[DllImport("CSharpLua", EntryPoint = "luaL_newstate")]
public static extern IntPtr luaL_newstate();
[DllImport("CSharpLua", EntryPoint = "luaL_openlibs")]
public static extern void luaL_openlibs(IntPtr L);
[DllImport("CSharpLua", EntryPoint = "luaL_loadbuffer")]
public static extern int luaL_loadbuffer(IntPtr L, byte[] buff, uint size, string name);
[DllImport("CSharpLua", EntryPoint = "lua_call")]
public static extern void lua_call(IntPtr L, int nargs, int nresults);
[DllImport("CSharpLua", EntryPoint = "lua_pcall")]
public static extern int lua_pcall(IntPtr L, int nargs, int nresults, int errfunc); 

3.需要注意的几个地方

  1. 返回char*时,不可直接使用string替换,否则调用会导致崩溃,因此需要像下面代码展示的那样进行一下转换才可以使用。
[DllImport("CSharpLua", EntryPoint = "lua_tolstring")]
private static extern IntPtr _lua_tolstring(IntPtr L, int idx, ref uint size);
public static string lua_tolstring(IntPtr L, int idx, ref uint size)
{IntPtr buffer = _lua_tolstring(L, idx, ref size);return Marshal.PtrToStringAnsi(buffer);
}
  1. C#函数传递给Lua使用时,需要使用delegate委托类型。
public delegate int LuaFunction(IntPtr L);
[DllImport("CSharpLua", EntryPoint = "lua_pushcclosure")]
public static extern void lua_pushcclosure(IntPtr L, LuaFunction func, int idx);
public static void lua_pushcfunction(IntPtr L, LuaFunction func)
{lua_pushcclosure(L, func, 0);
}
  1. 在lua源码中定义的宏代码是无法使用的,会提示找不到,需要在C#中手动实现,例如下面展示的2个宏。
[DllImport("CSharpLua", EntryPoint = "lua_getfield")]
public static extern void lua_getfield(IntPtr L, int idx, string s);
public static void lua_getglobal(IntPtr L, string s)
{lua_getfield(L, LUA_GLOBALSINDEX, s);
}
[DllImport("CSharpLua", EntryPoint = "lua_setfield")]
public static extern void lua_setfield(IntPtr L, int idx, string s);
public static void lua_setglobal(IntPtr L, string s)
{lua_setfield(L, LUA_GLOBALSINDEX, s);
}
  1. 如需要将C#的类实例对象即userdata传递给lua,需要在C#中转换成IntPtr后传递,Lua返回的则需要通过IntPtr转换回C#的实例对象。
[DllImport("CSharpLua", EntryPoint = "lua_pushlightuserdata")]
public static extern void _lua_pushlightuserdata(IntPtr L, IntPtr p);
public static void lua_pushlightuserdata<T>(IntPtr L, T p)
{IntPtr obj = Marshal.GetIUnknownForObject(p);_lua_pushlightuserdata(L, obj);
}
[DllImport("CSharpLua", EntryPoint = "lua_touserdata")]
public static extern IntPtr _lua_touserdata(IntPtr L, int idx);
public static T lua_touserdata<T>(IntPtr L, int idx)
{IntPtr p = _lua_touserdata(L, idx);return (T)Marshal.GetObjectForIUnknown(p);
}

三、C#与Lua的相互调用举例

1. C#中创建Lua环境

IntPtr L = LuaDll.luaL_newstate();
LuaDll.luaL_openlibs(L);

2. 加载Lua代码并执行,调用Lua的函数及向Lua传递参数。

var data = Resources.Load<TextAsset>(lua_file);
int rc = LuaDll.luaL_loadbuffer(L, data.bytes, (uint)data.bytes.Length, lua_file);
rc = LuaDll.lua_pcall(L, 0, 0, 0)
LuaDll.lua_getglobal(L, "main");
// 传递参数
LuaDll.lua_pushinteger(L, 3333);
LuaDll.lua_pushnumber(L, 3.3);
// 执行main方法
int i = LuaDll.lua_pcall(L, 2, 0, 0);

3. 将C#函数提供给Lua使用,需要使用静态方法参考上面LuaFunction的定义。

LuaDll.lua_pushcfunction(L, LuaPrint);
LuaDll.lua_setglobal(L, "print");
[MonoPInvokeCallback]   // 这个主要是在Android上需要。
static int LuaPrint(IntPtr L)
{Debug.Log(".....");return 0;
}

4. Lua代码调用C#方法并提供回调,由C#函数调用。

static int FindAndBind(IntPtr L)
{GameObject go = LuaDll.lua_touserdata<GameObject>(L, 1);string path = LuaDll.lua_tostring(L, 2);// 这里将lua的函数放到LUA_REGISTRYINDEX上int idx = LuaDll.luaL_refEx(L);Transform t = go.transform.Find(path);Button btn = t.GetComponent<Button>();btn.onClick.AddListener(delegate() {// 从LUA_REGISTRYINDEX栈获取lua的函数进行执行。LuaDll.lua_rawgeti(L, LuaDll.LUA_REGISTRYINDEX, idx);LuaDll.lua_pcall(L, 0, 0, 0);});return 0;
}

四、总结

总体来说交互调用还是比较的简单方便,跟使用C/C++与Lua交互差不多。我仅仅简单使用Lua源码进行编译动态库使用,可以方便的替换各个版本的lua进行使用。C#导出方法给Lua使用也相对简单,但是Unity中使用Lua的时候,不可能每个类例如GameObject、Transform等都手动写导出的代码给Lua使用。这块就可以去看tolua、xlua的实现,需要考虑很多东西。

到此这篇关于原生实现C#与Lua相互调用方法的文章就介绍到这了。

原生实现C#与Lua相互调用方法相关推荐

  1. C#与lua相互调用

    C#与lua相互调用 Lua是一种很好的扩展性语言,Lua解释器被设计成一个很容易嵌入到宿主程序的库.LuaInterface则用于实现Lua和CLR的混合编程. (一)C#调用Lua 测试环境:在V ...

  2. maven项目多模块相互调用方法

    maven项目多模块相互调用方法 问题背景 解决方法 方法1 方法2 问题背景 maven项目包括多个子模块,类似下图: 现在[web]模块某个类想要调用[service]模块的某个类的某个方法. 如 ...

  3. unity 热更新:C#与Lua相互调用(转载)

    一.基本原理 简单地说,c#调用lua, 是c# 通过Pinvoke方式调用了lua的dll(一个C库),然后这个dll执行了lua脚本. ULua = Lua + LuaJit(解析器.解释器) + ...

  4. 父子页面iframe相互调用方法详解

    2019独角兽企业重金招聘Python工程师标准>>> 如果你采用嵌入iframe机制,不可避免的要用到各个iframe页面之间方法和属性的相互调用.这里介绍的是兼容各个浏览器的if ...

  5. react17同源iframe父子页面相互调用方法

    最近制作一个大数据展示页面,需要嵌套第三方html,并进行相应的交互. 如下图: 其中A页面需要与第三方iframe进行交互,同时iframe选择公司或者园区时会回显到树选择器中. 先上解决办法 1, ...

  6. iframe父页面子页面相互调用方法

    父页面寻找子页面元素: window.frames["iframeName"].document.getElementsByClassName('div')[0] $(docume ...

  7. C++与Lua相互调用

    C++调用Lua函数: http://blog.csdn.net/lodger007/article/details/836897 Lua调用C++函数: http://blog.csdn.net/L ...

  8. pyqt 多窗口之间的相互调用方法

    在编程开发中,一个程序不可避免的需要多窗口操作来实现具体的功能. 实现此功能的基本步骤(以三个窗口为例,使用主窗口调用其它两个窗口) # 主窗口 from PyQt5 import QtCore, Q ...

  9. python pyqt5 多窗口_pyqt 多窗口之间的相互调用方法

    * 在编程开发中,一个程序不可避免的需要多窗口操作来实现具体的功能. 实现此功能的基本步骤(以三个窗口为例,使用主窗口调用其它两个窗口) # 主窗口 from PyQt5 import QtCore, ...

最新文章

  1. python 网页cookie的使用
  2. Spring 多数据源事务配置问题
  3. ipad php mysql_如何用PHP/MySQL为 iOS App 写一个简单的web服务器(译) PART1
  4. CSS-解决苹果点击高亮、安卓select灰色背景(select下拉框在IOS中背景变黑、出现阴影问题)
  5. 为什么手机上传图片这么慢 前端_怎样在手机上就能把图片压缩到100K以下?
  6. VMWare安装黑苹果Mac OS
  7. CodeChef Ada Pawns
  8. Python基础教程:使用dict和set
  9. python的类方法_python 如何调用类的方法
  10. Flex学习的利器《Hello Flex4》
  11. Oracle 创建函数的权限
  12. PL/SQL编程基础(一):PL/SQL语法简介(匿名PL/SQL块)
  13. Ibatis查询条件对于特殊字符的处理方法
  14. linux 版本的scipy,linux安装scipy
  15. c语言 单词长度统计,编写一个程序,打印输入中单词长度的直方图
  16. multisim中轻触开关在哪_现货供应轻触开关|品质确保|厂家直销
  17. 通用计算机遵循什么原则,计算机组成原理——计算机系统概述考研题
  18. 短信接口怎么对接?看完这篇文章你就知道了!
  19. 替代 NetMeeting 的多人屏幕共享工具
  20. 【STM32H7的DSP教程】第8章 DSP定点数和浮点数(重要)

热门文章

  1. 通过学习Python+request,爬取《雪中悍刀行》小说
  2. PostgreSQL--读懂执行计划(一)
  3. AUTOSAR从入门到精通-【应用篇】基于AUTOSAR架构的混合动力电池 ECU 软件开发
  4. 鸿蒙系统分辨率,5G基带、8K分辨率、鸿蒙系统、升降摄像头,华为智慧屏叱咤电视?...
  5. Win10任务栏不显示蓝牙图标 - 解决方案
  6. Mybatis与Spring集成原理
  7. linux 下redis启动命令
  8. properties介绍
  9. excel密码忘记了怎么办
  10. 学计算机的学生有什么优势,学习的压力对学生有什么好处,和坏处