[Xlua] C#调用Lua

阅前须知

由于本文为个人笔记,再加上本人喜欢将说明放在注释中,所以只有一些特别重要的内容才会写在代码块外部,整体的内容都是以代码为主

Lua调用C

1. LuaEnv

image

使用LuaEnv的DoString方法执行lua脚本

  • 由于C#字符串使用"String"​所以在DoString中编写的lua代码需要使用''包裹字符串
  • 默认DoString在Resource文件夹下加载,不能加载lua文件,只能加载TextAsset,所以要给lua脚本加上.txt后缀
  • LuaEnv建议全局唯一
void Start()
{
    //Lua解析器 能够让我们在Unity中执行Lua
    //一般情况下 保持全局唯一
    LuaEnv env = new LuaEnv();

    //执行Lua语言
    env.DoString("print('Hello World')");
    /*
    执行一个Lua脚本 
    多脚本执行使用require
    默认寻找脚本的路径为Resources 
    因该是通过 Resources.Load加载Lua脚本  TextAsset等
    所以Lua脚本加txt后缀 
    */

    env.DoString("require('Main')");

    //清除Lua中没有手动释放的对象 垃圾回收
    //帧更新中定时执行 或者 切场景时执行
    env.Tick();

    //销毁Lua解析器 
    env.Dispose();
}

2. CunstomLoader:自定义lua脚本加载器

image

利用CunstomLoder可以自定义lua脚本的加载方式,Xlua会按照顺序依次去尝试加载

  • 添加CunstomLoder使用env.AddLoader(MyCustomLoader);​ ,可以添加多个Loder
private byte[] MyCustomLoader(ref string fillPath)
{

    string path = Application.dataPath + "/Lua/" + fillPath + ".lua";
    Debug.Log($"load lua file from path: {path}");

    if (!File.Exists(path))
    {
        Debug.LogError("Lua File is not found");
        return null;
    }
    return File.ReadAllBytes(path);

}

3. Global.Set()和Global.Get()

image

使用Global.Set()和Global.Get() 获取和设置_G表中的变量

  1. 使用方法

    LuaMgr.GetInstance().Global.Set("testNumber", 55);
    int i2 = LuaMgr.GetInstance().Global.Get<int>("testNumber");
  2. 支持double=float之类的自动转换,但因为lua中只有number,要注意溢出问题

    double d = LuaMgr.GetInstance().Global.Get<double>("testFloat");
  3. 获取变量的方式为值拷贝

4. [重要] C#调用Lua的Function

image

使用 委托或LuaFunction借助Global.Get<>()获取lua中的全局方法(_G)

lua代码如下:

testFun =function()
    print('无参数无返回值方法')
end
testFun2=function(a)
    print('有参数有返回值')
    return a+1
end
testFun3 =function(a)
    print("多返回值")
    return a+1,10,false,"123",a
end
testFun4 =function (a,...)
    print('变长参数' .. a)
    arg={...}
    for k,v in pairs(arg) do
        print(k,v)
    end  
end

对于无参数可无返回值的方法:

//对于无参数无返回值UnityAction、自定义delegate等都可以
Action action = LuaMgr.GetInstance().Global.Get<Action>("testFun");
LuaFunction lf3 = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun3");

对于有参数有返回值的方法:

  1. 添加CSharpCallLua特性
[CSharpCallLua]
public delegate int CustomCall2(int a);

  1. 采取相同的方式获取方法(注意返回值、参数要能对应)
CustomCall2 call2 = LuaMgr.GetInstance().Global.Get<CustomCall2>("testFun2");
Debug.Log("有参有返回" + call2(10));

  1. 在运行脚本前需要 Xlua->Generate Code

image

  1. 对于多返回值方法 使用ref、out
//out 必须在方法返回前被赋值
int b;
bool c;
string d;
int e;
Debug.Log("第一个返回值" + call3(100, out b, out c, out d, out e) + $",{b},{c},{d},{e}");

CustomCall4 call4 = LuaMgr.GetInstance().Global.Get<CustomCall4>("testFun3");
//ref需要先初始化,在作为参数传入,可以通过引用在方法中修改原有数
int b1 = 0;
bool c1 = false;
string d1 = "1";
int e1 = 0;
Debug.Log("第一个返回值" + call4(200, ref b, ref c, ref d, ref e) + $",{b1},{c1},{d1},{e1}");
  1. 变长参数
CustomCall5 call5 = LuaMgr.GetInstance().Global.Get<CustomCall5>("testFun4");
//int为变长部分
call5("1", 1, 2, 3, 4, 5);

LuaFunction(不建议使用,效率低)

LuaFunction lf3 = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun3");
object[] objects = lf3.Call(300);
for (int i = 0; i < objects.Length; i++)
{
    Debug.Log(objects[i]);
}

5. C#使用List和Dictionary映射Table

image

里的List和Dictionary其实代表的就是Lua的Table,对应我们将Table类比List和Dictionary去使用的情况(因为Lua中只有table)

  • List用于映射没有自定义索引的表
  • Dictionary用于映射有自定义索引的表
  • 值拷贝

lua代码如下:

testList={1,2,3,4,5,6}
testList2={"123","123",true,1,1.2}
testDic={
["1"] = 1,
["2"] = 2,
["3"]=3,
["4"]=4,

}
testDic2={

    ["1"]=1,
    [true]=1,
    [false]=true,
    ["123"]=false
}

C#代码如下

//同一类型List
List<int> list = LuaMgr.GetInstance().Global.Get<List<int>>("testList");
Debug.Log("*******************List************************");
for (int i = 0; i < list.Count; ++i)
{
    Debug.Log(list[i]);
}
//值拷贝 浅拷贝 不会改变lua中的内容
list[0] = 100;
List<int> list2 = LuaMgr.GetInstance().Global.Get<List<int>>("testList");
Debug.Log(list2[0]);

//不指定类型 object
List<object> list3 = LuaMgr.GetInstance().Global.Get<List<object>>("testList2");
Debug.Log("*******************List object************************");
for (int i = 0; i < list3.Count; ++i)
{
    Debug.Log(list3[i]);
}

Debug.Log("*******************Dictionary************************");
Dictionary<string, int> dic = LuaMgr.GetInstance().Global.Get<Dictionary<string, int>>("testDic");
foreach (string item in dic.Keys)
{
    Debug.Log(item + "_" + dic[item]);
}
dic["1"] = 100000;
//值拷贝 不会改变lua中的内容
Dictionary<string, int> dic2 = LuaMgr.GetInstance().Global.Get<Dictionary<string, int>>("testDic");
Debug.Log(dic2["1"]);

Debug.Log("*******************Dictionary object************************");
Dictionary<object, object> dic3 = LuaMgr.GetInstance().Global.Get<Dictionary<object, object>>("testDic2");
foreach (object item in dic3.Keys)
{
    Debug.Log(item + "_" + dic3[item]);
}

输出符合预期,由于太长就不展示了

C#使用Class、Interface、LuaTable映射Lua的Table

image

后续映射的Table对应的lua代码均为:

testTable={

    testInt=2,
    testBool=true,
    testFloat=1.2,
    testString="123",
    testFunc= function()
        print("table s func")
    end,
    testFun2= function(a)
        print("testFun2" .. a)
    end,
    testInTable={
        testInInt=10
    }
}

使用Class映射Table

  • 保证参数名一致
  • 映射的类对应的参数、方法可多可少,例如可以增加额外的方法,或者减少对于某一个索引的映射(无非就是获取不到)
  • 对于有参数或有返回值的方法仍然要使用CsharpCallLua特性并Generate Code

C#代码:

[CSharpCallLua]
//有参数或有返回值的方法同样要添加特性
//记得Generate Code
public delegate void TestFun2(int a);
/// <summary>
/// 使用自定义类映射Table,变量名需一致
/// </summary>
public class CallLuaClass
{
    //变量名需和lua保持一致
    //必须是public,private和protected无法赋值
    public int testInt;
    public bool testBool;
    public float testFloat;
    public string testString;

    public UnityAction testFunc;
    public TestFun2 testFun2;
    public TestInClass testInTable;
    //Table映射类中的内容可多可少

    public int testInt2 = 1;
    public void Test()
    {
        Debug.Log($"testInt value is {testInt}");
    }
}
public class TestInClass
{
    public int testInInt;
}
public class Learn7_CallTable : MonoBehaviour
{
    void Start()
    {
        LuaMgr.GetInstance().Init();
        LuaMgr.GetInstance().DoLuaFile("Main");
        //仍然是值拷贝
        CallLuaClass callLuaClass = LuaMgr.GetInstance().Global.Get<CallLuaClass>("testTable");
        Debug.Log(callLuaClass.testInt2);
        callLuaClass.Test();
        callLuaClass.testFun2(1000);
        Debug.Log(callLuaClass.testInTable.testInInt);
    }
}

输出如下:

image

使用Interface映射Table

image

  • 接口必须要使用CsharpCallLua特性
  • 对于Lua中的变量接口要使用属性来映射
  • 非常不一样的一点就是,接口映射是引用拷贝!!!!

C#代码:


using UnityEngine;
using UnityEngine.Events;
using XLua;

/// <summary>
/// 接口映射Table
/// </summary>
//1.如果使用接口映射表需使用Attribute
//2.属性和方法同样可少可多
//3.如果接口结构更改,需要清空Xlua代码,重新生成
[CSharpCallLua]
public interface CsharpCallInterface
{
    int testInt { get; set; }
    bool testBool { get; set; }

    float testFloat { get; set; }

    string testString { get; set; }

    UnityAction testFunc { get; set; }


}
public class Learn8_CallInterface : MonoBehaviour
{

    void Start()
    {
        LuaMgr.GetInstance().Init();
        LuaMgr.GetInstance().DoLuaFile("Main");
        CsharpCallInterface callLuaClass = LuaMgr.GetInstance().Global.Get<CsharpCallInterface>("testTable");
        callLuaClass.testFunc();
        Debug.Log(callLuaClass.testInt);
        callLuaClass.testInt = 10000;
        //使用接口映射Table 引用拷贝!
        CsharpCallInterface callLuaClass2 = LuaMgr.GetInstance().Global.Get<CsharpCallInterface>("testTable");
        Debug.Log(callLuaClass2.testInt);
    }

}

输出:

image

使用LuaTable(不推荐,效率低)

image

  • LuaTable使用完要使用Dispose显示释放
  • 仍然为引用拷贝
using UnityEngine;
using XLua;

/// <summary>
/// 不推荐使用LuaTable,效率低
/// </summary>
public class Learn9_CallLuaTable : MonoBehaviour
{
    void Start()
    {
        LuaMgr.GetInstance().Init();
        LuaMgr.GetInstance().DoLuaFile("Main");

        LuaTable table = LuaMgr.GetInstance().Global.Get<LuaTable>("testTable");
        Debug.Log(table.Get<int>("testInt"));
        Debug.Log(table.Get<bool>("testBool"));

        Debug.Log(table.Get<float>("testFloat"));

        Debug.Log(table.Get<string>("testString"));

        table.Get<LuaFunction>("testFunc").Call();

        //引用拷贝
        table.Set("testInt", 1000);
        LuaTable table2 = LuaMgr.GetInstance().Global.Get<LuaTable>("testTable");
        Debug.Log(table2.Get<int>("testInt"));

        //显示释放
        table.Dispose();
        table2.Dispose();

    }
}

总结

  • CsharpCallLua特性的使用

    1. 有参数或有返回值的自定义委托
    2. 接口
  • 引用拷贝

    1. 接口映射Table
    2. LuaTable映射Table

本文是关于Xlua框架中C#调用Lua的详细指南,主要内容包括:

  1. LuaEnv基础:介绍如何使用LuaEnv解析器执行Lua脚本,注意事项如字符串包裹方式、加载路径限制等。
  2. 自定义加载器:通过CustomLoader实现自定义Lua脚本加载路径和方式,支持多加载器顺序尝试。
  3. 全局变量交互:使用Global.Set()/Get()方法在C#和Lua的_G表间传递数据,注意值拷贝和类型转换问题。
  4. 函数调用:重点讲解四种Lua函数调用场景:

    • 无参无返回值:直接使用Action委托
    • 有参有返回值:需添加[CSharpCallLua]特性并生成代码
    • 多返回值:通过out/ref参数获取
    • 变长参数:使用LuaFunction处理
      文中包含大量代码示例和关键注释,特别强调Xlua的代码生成步骤和类型安全注意事项。
最后修改:2025 年 06 月 20 日
如果觉得我的文章对你有用,请随意赞赏