引言

    本篇简单介绍如何在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.mkAndroid.mk两个mk文件,下面是我使用的两个文件的内容,创建放在上面VS的工程里面即可,路径是在lua源码src的上一层目录。

# Application.mk
APP_PLATFORM = android-23
APP_ABI := armeabi-v7a arm64-v8a
APP_STL := stlport_shared
# Android.mk
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
MY_FILES_PATH := $(LOCAL_PATH)/src
MY_FILES_SUFFIX := %.c
MY_UN_INCLUDE := %lua.c %luac.c
# 递归遍历目录下的所有的文件
rwildcard=$(wildcard $1$2) $(foreach d,$(wildcard $1*),$(call rwildcard,$d/,$2))
# 获取相应的源文件
MY_ALL_FILES := $(foreach src_path,$(MY_FILES_PATH), $(call rwildcard,$(src_path),*.*) )
MY_SRC_LIST := $(filter $(MY_FILES_SUFFIX),$(MY_ALL_FILES))
MY_SRC_LIST := $(filter-out $(MY_UN_INCLUDE),$(MY_SRC_LIST))
MY_SRC_LIST := $(MY_SRC_LIST:$(LOCAL_PATH)/%=%)
LOCAL_SRC_FILES = $(MY_SRC_LIST)
#打印编译信息
$(warning 'src_list='$(LOCAL_SRC_FILES))
LOCAL_MODULE := CSharpLua
LOCAL_LDLIBS += -ldl
LOCAL_CFLAGS := $(L_CFLGAS)
include $(BUILD_SHARED_LIBRARY)

将上面的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[LuaDll.cs]

大部分的动态库中的接口直接使用以下这种方式即可使用,使用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);
}

  2. 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);
}

  3. 在lua源码中定义的宏代码是无法使用的,会提示找不到,需要在C#中手动实现,例如下面展示的2个宏。

#define lua_setglobal(L,s)  lua_setfield(L, LUA_GLOBALSINDEX, (s))
#define lua_getglobal(L,s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
[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);
}

  4. 如需要将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相互调用-Unity3D可用的更多相关文章

  1. Unity3D 预备知识:C#与Lua相互调用

    在使用Unity开发游戏以支持热更新的方案中,使用ULua是比较成熟的一种方案.那么,在使用ULua之前,我们必须先搞清楚,C#与Lua是怎样交互的了? 简单地说,c#调用lua, 是c# 通过Pin ...

  2. C#与lua相互调用

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

  3. Lua与C++相互调用

    {--1.环境--} 为了快速入手,使用了小巧快速的vc++6.0编译器 以及在官网下载了Lua安装包..官网地址{--http://10.21.210.18/seeyon/index.jsp--} ...

  4. uLua学习笔记(三):Unity3D和Lua之间的相互调用

    这篇笔记主要集中学习一下uLua和Unity3D之间相互调用的方法,我们导入了uLua之后,现在会弹出一个类似学习屏幕的东西,如下: 先赞一个! Unity3D调用Lua Unity3D调用Lua的方 ...

  5. C程序与Lua脚本相互调用

    Lua脚本是一种可用于C程序开发/测试的工具,本篇介绍一下C程序与Lua脚本如何进行相互调用,更加详细的操作参见<Programing in Lua>.本文分为3个部分:1.Windows ...

  6. Unity3D中C#和js方法相互调用

    通过查找资料,Unity3D中C#和js要相互调用彼此的方法,js文件必须放在"Standard Assets". "Pro Standard Assets" ...

  7. Unity3d 脚本相互调用

    unity中三种调用其他脚本函数的方法 第一种,被调用脚本函数为static类型,调用时直接用  脚本名.函数名().很不实用…… 第二种,GameObject.Find("脚本所在物体名& ...

  8. Unity3d 与IOS 相互调用

    Unity3d 与IOS 相互调用 @灰太龙 群63438968 我用的Unity3d 4.2版本,这一节说一下IOS与U3D的交互! 首先在U3D中写个方法:这个时候导出为ios代码必须是真机,模拟 ...

  9. iOS原生和H5的相互调用

    为什么现在越来越多的APP中开始出现H5页面? 1,H5页面开发效率更高,更改更加方便: 2,适当缩小APP安装包的大小: 3,蹭热点更加方便,比如五一,十一,双十一搞活动: 那么为什么说H5无法取代 ...

随机推荐

  1. 【Vulnhub练习】Acid

    靶机信息 下载链接 https://download.vulnhub.com/acid/Acid.rar 靶机说明 Welcome to the world of Acid. Fairy tails ...

  2. ModelSim10.7安装破解教程

    我打算开始学习ZYNQ,所以先安装好工具ModelSim10.7,很无奈的是倒腾了一个上午也没生产license的txt文档和破解成功. 上面是破解失败的截图,下面是生成不了license,点了直接闪 ...

  3. 【Java】这 35 个 Java 代码优化细节!

    前言 代码 优化 ,一个很重要的课题.可能有些人觉得没用,一些细小的地方有什么好修改的,改与不改对于代码的运行效率有什么影响呢?这个问题我是这么考虑的,就像大海里面的鲸鱼一样,它吃一条小虾米有用吗?没 ...

  4. JVM的小总结(转)

    ref:http://www.cnblogs.com/ityouknow/p/6482464.html 注1:看了大神:纯洁的微笑的JVM系列篇,发现好多地方还是似懂非懂,理解的并不透彻,jvm的调优 ...

  5. 使用SpringDataJdbc的@Query注解实现自动映射结果集 ----- RowMapper接口

    使用@Query注解的时候,常常需要写sql来映射非域类的实例,通常的做法就是 实现 RowMapper接口,然后new实例一个一个的设置值进去...为此.出世了自动映射工具类 注意事项:此抽象类只是 ...

  6. ThreadLocal 是什么?有什么用?

    ThreadLocal 是一个本地线程副本变量工具类.主要用于将私有线程和该线程存放的副本对象做一个映射,各个线程之间的变量互不干扰,在高并发场景下,可以实现无状态的调用,特别适用于各个线程依赖不通的 ...

  7. @Required 注解 ?

    这个注解表明 bean 的属性必须在配置的时候设置,通过一个 bean 定义的显式的 属性值或通过自动装配,若@Required 注解的 bean 属性未被设置,容器将抛出 BeanInitializ ...

  8. PowerDesigner生成MySQL脚本,表和字段进行转义

    打开Power Designer数据库建模工具,软件基本信息如下 如果PowerDesigner内置的(table_option)表物理操作没有,请看以下步骤 打开 Edit Current DBMS ...

  9. 使用Servlet编写增删改查

    第一步创建一个表 1 create database liyongzhendb default character set utf8 collate utf8_bin; 2 3 CREATE TABL ...

  10. Servlet的url-pattern配置

    url匹配规则 1)精确配置 精确匹配是指<servlet-mapping>中配置的值必须与请求中的url完全精确匹配. <servlet-mapping> <servl ...