目录
Unity Lua 之 在 Unity 中 C#访问Lua 的全局变量、全局 table 和全局 function 的方法
一、简单介绍
二、C#访问Lua 官网相关知识
三、注意事项:
四、实现步骤
五、关键代码
Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。
xLua为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便的和C#相互调用。
xLua在功能、性能、易用性都有不少突破,这几方面分别最具代表性的是:
在Unity中使用xlua 的重要一个原因就是热更新,我们本着这个歌目的开始我们的学习。
这里指的是C#主动发起对Lua数据结构的访问。 本章涉及到的例子都可以在XLua\Tutorial\CSharpCallLua下找到。
获取一个全局基本数据类型 访问LuaEnv.Global就可以了,上面有个模版Get方法,可指定返回的类型。
luaenv.Global.Get("a")
luaenv.Global.Get("b")
luaenv.Global.Get("c")
访问一个全局的table
也是用上面的Get方法,那类型要指定成啥呢?
映射到普通class或struct
定义一个class,有对应于table的字段的public属性,而且有无参数构造函数即可,比如对于{f1 = 100, f2 = 100}可以定义一个包含public int f1;public int f2;的class。 这种方式下xLua会帮你new一个实例,并把对应的字段赋值过去。
table的属性可以多于或者少于class的属性。可以嵌套其它复杂类型。 要注意的是,这个过程是值拷贝,如果class比较复杂代价会比较大。而且修改class的字段值不会同步到table,反过来也不会。
这个功能可以通过把类型加到GCOptimize生成降低开销,详细可参见配置介绍文档。 那有没有引用方式的映射呢?有,下面这个就是:
映射到一个interface
这种方式依赖于生成代码(如果没生成代码会抛InvalidCastException异常),代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。
更轻量级的by value方式:映射到Dictionary<>,List<>
不想定义class或者interface的话,可以考虑用这个,前提table下key和value的类型都是一致的。
另外一种by ref方式:映射到LuaTable类
这种方式好处是不需要生成代码,但也有一些问题,比如慢,比方式2要慢一个数量级,比如没有类型检查。
访问一个全局的function
仍然是用Get方法,不同的是类型映射。
映射到delegate
这种是建议的方式,性能好很多,而且类型安全。缺点是要生成代码(如果没生成代码会抛InvalidCastException异常)。
delegate要怎样声明呢? 对于function的每个参数就声明一个输入类型的参数。 多返回值要怎么处理?从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数。
参数、返回值类型支持哪些呢?都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。
delegate的使用就更简单了,直接像个函数那样用就可以了。
映射到LuaFunction
这种方式的优缺点刚好和第一种相反。 使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。
使用建议
访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时把要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。table也类似。
如果lua测的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。
1、获取 Lua 中的全局变量的时候,注意数据类型要对上,不然可能获取不到正确的全局变量数据
2、在使用 class 获取 table 的时候,好像获取不到里面的函数(不知道就是这样,还是能获取到,只是方法不对)
3、在使用 interface 获取 table 的时候,注意 在 interface 前添加上 public(例如 public interface IPerson{}),不然可能会报访问不到
4、在使用 class 获取 table 的时候,修改值不会映射会lua中;而在使用 interface 获取 table 的时候,修改值会映射会lua中
5、注意 xlua (版本2.1.14)实话也不是很兼容 Unity 2018.4.12f1 调用 [CSharpCallLua] 会报错,报找不到
(如果你也有这样可以使用 Unity 2017.4.0f1 版本,测试可用)
6、使用 Delegate 获取 Lua 全局函数的时候,可以使用 ref 和 out 接受 lua 中多余的返回值
7、获取全局函数的时候,获取的函数变量, 函数变量使用完建议置空,需要反复使用,可以最后在把函数 = null
1、新建一个文本文件 CSharpGetLuaGlobalParamFunc.lua.txt
2、打开编写 lua 代码
3、新建 Unity 工程,新建一个 LuaTxt 文件夹,并把 文本文件 CSharpGetLuaGlobalParamFunc.lua.txt 导入
4、在工程中,新建脚本实现 C# 访问 Lua 的功能
5、把脚本挂载到场景中
6、运行场景,效果如下,获取到 Lua 中的属性和方法
1、CSharpGetLuaGlobalParamFunc.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
public class CSharpGetLuaGlobalParamFunc : MonoBehaviour
{
// Lua 环境变量参数
LuaEnv luaEnv;
// Start is called before the first frame update
void Start()
{
LuaInit();
luaEnv.DoString("require'CSharpGetLuaGlobalParamFunc'");
GetLuaGlobalParameters();
GetLuaGlobalTable_Method_Class();
GetLuaGlobalTable_Method_Interface();
GetLuaGlobalTable_Method_Dictionary_List();
GetLuaGlobalFunction_Method_Delegate();
GetLuaGlobalFunction_Method_LuaFunction();
}
///
/// 获取 Lua 全局变量
///
void GetLuaGlobalParameters() {
// 一定要注意类型,不然可能获取不到正确的值哈
float numberFloat = luaEnv.Global.Get("numberFloat");
int numberInt = luaEnv.Global.Get("numberInt");
string stringData = luaEnv.Global.Get("stringData");
bool isTrue = luaEnv.Global.Get("isTrue");
Debug.Log("numberFloat:" + numberFloat);
Debug.Log("numberInt:" + numberInt);
Debug.Log("stringData:" + stringData);
Debug.Log("isTrue:" + isTrue);
}
#region Get Table Class
///
/// 使用 Class 获取 Table 的内容
/// 前提:定义一个和 Table 对应的 Class 类
/// (好似 函数获取不到)
///
void GetLuaGlobalTable_Method_Class() {
Person person = luaEnv.Global.Get("person");
Debug.Log("GetLuaGlobalTable_Method_Class :");
Debug.Log("name:" + person.name + " age:" + person.age);
// 修改的值不会映射到 lua 里面
person.age = 20;
luaEnv.DoString("print(person.age)");
// class 方法获取好似获取不到函数
person.work(1, 2);
}
///
/// 与 Lua table 对应的 class 类
///
class Person
{
public string name;
public int age;
public void work(float a, float b) { }
}
#endregion
#region Get Table Interface
///
/// 与 Lua table 对应的 interface,请加上[CSharpCallLua] 修饰,并设置为 public 属性
///
[CSharpCallLua]
public interface IPerson
{
string name { get; set; }
int age { get; set; }
void work(float a, float b);
}
///
/// 使用 Class 获取 Table 的内容
/// 前提:定义一个和 Table 对应的 interface 类
///
void GetLuaGlobalTable_Method_Interface()
{
IPerson person = luaEnv.Global.Get("person");
Debug.Log("GetLuaGlobalTable_Method_Interface :");
Debug.Log("name:" + person.name + " age:" + person.age);
// 修改的值会映射到 lua 里面
person.age = 20;
luaEnv.DoString("print(person.age)");
// 执行函数
person.work(1, 2);
}
#endregion
#region Get Table Dictionary、List
///
/// 使用 Dictionary、List 获取 Table 内容
///
void GetLuaGlobalTable_Method_Dictionary_List() {
// 获取有变量的值
Dictionary dic = luaEnv.Global.Get>("person");
Debug.Log("Dictionary:");
foreach (string key in dic.Keys)
{
Debug.Log(key + ":" + dic[key]);
}
// 获取到 单一的值
List
2、CSharpGetLuaGlobalParamFunc.lua.txt
numberFloat = 20.05
numberInt = 5
stringData = "Hello Lua"
isTrue = false
person={
name = "Lua",
age = 18,
work=function(self,a,b)
print("Lua 工作:" + (a+b))
end
}
function sub(a,b)
print(a-b)
return a-b,a,b
end