目录
概述
先前并没有使用过xlua的更新方式,而且好久没有使用过lua。这次是一次同事分享经验中的笔记。不同的lua热更方案,应该都存在着共通的点。这里分享一下相关的内容:
文件加载
使用LuaEnv中的DoString方法来执行Lua脚本。
因为创建一个LuaEnv相当于创建了一个lua虚拟机,所以一个游戏最好只有一个LuaEnv。
// 执行lua代码
env.DoString("print(\"Hello World\")");
// 通过文件执行lua代码
TextAsset ta = Resources.Load<TextAsset>("Lua/helloworld.lua");
env.DoString(ta.text);
// 通过默认Loader执行lua代码
env.DoString("require 'Lua/helloworld'");
// 通过自定义Loader执行lua代码
// require实际上是调一个个的Loader去加载,有一个成功就不再往下尝试,全失败则报文件找不到。
private byte[] LuaLoader(ref string filePath)
{
string path = "Lua/" + filePath + ".lua";
print(path);
TextAsset ta = Resources.Load<TextAsset>(path);
return System.Text.Encoding.UTF8.GetBytes(ta.text);
}
env.AddLoader(LuaLoader);
env.DoString("require 'helloworld'");
官方建议的加载Lua脚本方式是:
整个程序就一个DoString(“require ‘main’”),然后在main.lua加载其它脚本(类似lua脚本的命令行执行:lua main.lua)。
C#访问Lua
普通全局变量
先加载lua脚本,再通过LuaEnv.Global.Get获取变量。
TextAsset ta = Resources.Load<TextAsset>("Lua/variable.lua");
env.DoString(ta.text);
int g_int = env.Global.Get<int>("g_int");
string g_str = env.Global.Get<string>("g_str");
bool g_bool = env.Global.Get<bool>("g_bool");
访问Lua table
需要注意的是,在构建C#中对应的结构时,接口一定要是public修饰,否则会报错。类最好也是,避免报错。
映射到普通class或struct
table的属性可以多于或者少于class的属性,也就是不一定都需要做映射。这个过程是值拷贝,如果class比较复杂代价会比较大。而且修改class的字段值不会同步到table,反过来也不会。
public class Person
{
public string name;
public int age;
}
Person p = env.Global.Get<Person>("g_tbl");
映射到接口
一定要加[CSharpCallLua]和public,否则会报错。
g_int = 1
g_str = "Hello World Variable"
g_bool = false
g_tbl = {name = "Juhnny", age = 12}
g_tbl.say = function(this, str)
print(str)
end
[CSharpCallLua]
public interface IPerson
{
string name { get; set; }
int age { get; set; }
public void say(string str);
}
IPerson p = env.Global.Get<IPerson>("g_tbl");
p.say("Yahoo");
映射到Dictionary和List
这种方法较方法2更为轻量级,但是要求table下key和value类型一致。
g_dic = {apple = "iPhone", flower = "Huawei"}
g_list = {1, 3, 5, 7, 9}
Dictionary<string, string> dic = env.Global.Get<Dictionary<string, string>>("g_dic");
foreach (var child in dic)
{
print(string.Format("Key is {0}, Value is {1}", child.Key, child.Value));
}
List<int> list = env.Global.Get<List<int>>("g_list");
foreach (var v in list)
{
print(v);
}
映射到LuaTable
这种方法比较慢,而且没有类型检查,不推荐使用。
LuaTable tab = env.Global.Get<LuaTable>("g_tbl");
string name = tab.Get<string>("name");
int age = tab.Get<int>("age");
print(string.Format("Name is {0}, age is {1}", name, age));
foreach (string key in tab.GetKeys())
{
print(tab.Get<object>(key));
}
访问Lua全局函数
使用delegate映射
这种是建议的方式,性能好很多,而且类型安全,但是要生成代码。支持带返回值,多返回值,甚至返回值都可以是delegate。
g_func1 = function()
print("FF")
end
g_func2 = function(num)
print(num)
return num, "Yoshida", "SE"
end
[CSharpCallLua]
public delegate int Func2(int num, out string name, out string company);
// 声明委托时,一定要加public和[CSharpCallLua]
// 无返回
Action func1 = env.Global.Get<Action>("g_func1");
func1();
func1 = null;
// 单返回值
Func2 func2 = env.Global.Get<Func2>("g_func2");
int num = func2(14);
func2 = null
// 多返回值
Func2 func2 = env.Global.Get<Func2>("g_func2");
string name;
string company;
int num = func2(14, out name, out company);
func2 = null
在过去的一些版本中,需要在不使用后,将接收到的函数销毁,否则会在LuaEnv.Dispose的时候报错。
使用LuaFunction
与上一种方法优缺点相反,不推荐使用。
LuaFunction func = env.Global.Get<LuaFunction>("g_func2");
object[] res = func.Call(14);
Lua调用C
// 读静态属性
CS.UnityEngine.Time.deltaTime
// 写静态属性
CS.UnityEngine.Time.timeScale = 0.5
// 调用静态方法
CS.UnityEngine.GameObject.Find('helloworld')
// 读写成员属性类似,但是访问成员方法要使用:
testobj:DMFunc()
其它注意事项
方法的参数处理
Lua调用侧的参数处理规则:C#的普通参数算一个输入形参,ref修饰的算一个输入形参,out不算,然后从左往右对应lua 调用侧的实参列表。
Lua调用侧的返回值处理规则:C#函数的返回值(如果有的话)算一个返回值,out算一个返回值,ref算一个返回值,然后从左往右对应lua的多返回值。
可变参数的处理
// 对于C#的如下方法:
void VariableParamsFunc(int a, params string[] strs)
// 可以在lua里头这样调用:
testobj:VariableParamsFunc(5, 'hello', 'john')
枚举
// 枚举值就像枚举类型下的静态属性一样。
testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)
上面的EnumTestFunc函数参数是Tutorial.TestEnum类型的。
枚举类支持__CastFrom方法,可以实现从一个整数或者字符串到枚举值的转换,例如:
CS.Tutorial.TestEnum.__CastFrom(1)
CS.Tutorial.TestEnum.__CastFrom('E1')
委托
-- 使用+-号来实现委托
-- 比如testobj里头有个事件定义是这样:public event Action TestEvent;
-- 增加事件回调
testobj:TestEvent('+', lua_event_callback)
--移除事件回调
testobj:TestEvent('-', lua_event_callback)