xLua使用注意事项

本文介绍了在C#中通过xlua进行Lua脚本加载、全局变量访问、Luatable映射到C#类型、接口和集合类型、以及使用delegate调用Lua函数的方法,同时涵盖了参数处理、枚举和委托的使用注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概述

先前并没有使用过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)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值