本来想着一个文章全都写完,后来才发现,失策了!!!,加上自己感觉要是太长了,后面自己复习也懒得去看,那就细分成三个文章咯!毕竟有些地方还是有些区别的

获取全局变量

其实获取全局变量还是比较容易的,首先在项目的Lua文件中创建两个lua脚本

1
2
3
-- main.lua
print("主lua脚本_main.lua_文件启动")
require("test")-- 这里为了结合require的知识点
1
2
3
4
5
6
-- test.lua
print("test.lua脚本调用")
testNumber=1
testBool=true
testFloat=1.25
testString="123"

下面就可以直接用c#来获取lua文件夹中的main.lua和test.lua了

1
2
3
4
//首先还是初始化lua解析器
LuaMgr.GetInstance().Init();
//执行main.lua
LuaMgr.GetInstance().DoLuaFile("main");
知识点: 下面获取到全局变量,就用到了lua的相关知识点中的_G表将我们申明的所有全局变量都存储在其中,但是Lua脚本中的本地局部变量的是无法直接获取的 所以我们就可以用_G表来获取全局变量了
1
2
3
4
5
6
7
8
9
10
int i = LuaMgr.GetInstance().Global.Get<int>("testNumber");
Debug.Log("testNumber:" + i);
bool b = LuaMgr.GetInstance().Global.Get<bool>("testBool");
Debug.Log("testBool:" + b);
float f = LuaMgr.GetInstance().Global.Get<float>("testFloat");
Debug.Log("testFloat:" + f);
double d = LuaMgr.GetInstance().Global.Get<double>("testFloat");
Debug.Log("testFloat_double:" + d);
string s = LuaMgr.GetInstance().Global.Get<string>("testString");
Debug.Log("testString:" + s);

这样非常明显的看出来,虽然说在lua中的整型,浮点型等都是number,但是可以在c#中转化为相应的类型

对于赋值操作可通过Set(“变量名”,”所要赋的值”)来实现

1
2
3
LuaMgr.GetInstance().Global.Set("testNumber", 55);
int i2 = LuaMgr.GetInstance().Global.Get<int>("testNumber");
Debug.Log("testNumber_i2:" + i2);

获取全局函数/方法

其实常见的方法类型无外乎就是以下类型

  • 普通方法(无返回值,无参数,此方法一般应用于相关逻辑的处理)
  • 有参数有返回值
  • 多返回值
  • 方法中不定参数

无参无返回

1
2
3
funcFun=function()
print("无参无返回")
end

c#中调用lua中的方法一般有几种方式

  • 通过自定义委托来实现函数的调用
1
2
3
4
5
6
7
8
9
10
首先需要创建一个委托
public delegate void CallBackOne();
//初始化lua解析器
LuaMgr.GetInstance().Init();
//执行lua文件,这里以test为例
LuaMgr.GetInstance().DoLuaFile("test");
//委托【这里通过_G表来获取到funcFun】
CallBackOne callBackOne = LuaMgr.GetInstance().Global.Get<CallBackOne>("funcFun");
//执行即可
callBackOne(); //到这里就完成了lua中的方法的调用

wucan_wufanhui.jpg (1347×51) (gmcj0816.top)

  • 通过Unity3D自带的委托来实现函数的调用
1
2
3
4
5
6
7
//初始化lua解析器
LuaMgr.GetInstance().Init();
//执行lua文件,这里以test为例
LuaMgr.GetInstance().DoLuaFile("test");
//unity3d自带的委托,记得这里需要添加引用:using UnityEngine.Events;
UnityAction unityAction_One = LuaMgr.GetInstance().Global.Get<UnityAction>("funcFun");
unityAction_One();//这里结果与上方结果一致,在此不再进行重复提供
  • 通过C#中提供的委托来实现函数的调用
1
2
3
4
5
6
7
//初始化lua解析器
LuaMgr.GetInstance().Init();
//执行lua文件,这里以test为例
LuaMgr.GetInstance().DoLuaFile("test");
//c#提供的委托
Action action_One = LuaMgr.GetInstance().Global.Get<Action>("funcFun");
action_One();//这里结果与上方结果一致,在此不再进行重复提供
  • 通过Lua解析器自带的来实现函数的调用

Lua解析器也是提供了获取函数的方式,但是官方也有提到这种方式会造成一些垃圾,因此建议少用

1
2
3
4
5
6
7
//初始化lua解析器
LuaMgr.GetInstance().Init();
//执行lua文件,这里以test为例
LuaMgr.GetInstance().DoLuaFile("test");
//类型于xLua自带的一种处理的方式
LuaFunction luaFunction_One = LuaMgr.GetInstance().Global.Get<LuaFunction>("funcFun");
luaFunction_One.Call();//这里结果与上方结果一致,在此不再进行重复提供

有参有返回

1
2
3
4
funcFun2=function(a)
print("有参有返回")
return a+1
end

这里对于有参数或者有返回的用法是有一些不一样的,因为对于无参无返回是系统内置好的,但是对于有参有返回,多返回以及不定参数,由于系统并未自带,因此需要点击

这时在xLua/Gen文件夹中会生成相应的文件,这里可以简单理解为依赖

但是为了保险起见呢,最好在自定义委托时按照以下写法设置

1
2
[CSharpCallLua]
public delegate int CallBackTwo(int a);
  • 自定义委托实现
1
2
CallBackTwo callBackTwo = LuaMgr.GetInstance().Global.Get<CallBackTwo>("funcFun2");
Debug.Log("有参数有返回值:" + callBackTwo(10));
  • c# 自带的泛型委托
1
2
CallBackTwo callBackTwo = LuaMgr.GetInstance().Global.Get<CallBackTwo>("funcFun2");
Debug.Log("有参数有返回值:" + callBackTwo(10)); //c#自带的泛型委托
  • 系统自带的泛型

    此方式依赖于于using System(public delegate TResult Func(T arg);),提供了一种有参有返回值泛型的方法Func<值类型,……返回值的类型>,这里可以对多个参数赋值,但是最后一个必然是需要返回的值的类型

1
2
Func<int, int> sFun = LuaMgr.GetInstance().Global.Get<Func<int, int>>("funcFun2");
Debug.Log("c#自带的有参数有返回值:" + sFun(20));
  • Lua解析器自带的委托
1
2
3
LuaFunction luaFunction_Two = LuaMgr.GetInstance().Global.Get<LuaFunction>("funcFun2");
//这里由于其只有一个返回值,则打印便是数组中的第一个参数
Debug.Log("xLua提供的有参有会返回值的:" + luaFunction_Two.Call(30)[0]);

多返回值

这里需要用到一个知识点,也就是out和ref的用法

知识点: 在C#中通过使用方法来获取返回值时,通常只能得到一个返回值。因此,当一个方法需要返回多个值的时候,就需要用到ref和out。

ref 关键字使参数按引用传递。其效果是,当控制权传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。若要使用 ref 参数,则方法定义和调用方法都必须显式使用 ref 关键字。(简单来说就是在参数前加ref,并需要先对参数初始化)

out 关键字会导致参数通过引用来传递。这与 ref 关键字类似,不同之处在于 ref 要求变量必须在传递之前进行初始化。若要使用 out 参数,方法定义和调用方法都必须显式使用 out 关键字(简单来说,与ref一样需要在参数前加out,但是比ref不需要先对参数初始化)

1
2
3
4
funcFun3=function(a)
print("多返回值")
return 1,2,true,"哈信息",a
end

定义与lua中相同的委托

1
2
3
//因为在上面lua中定义的函数只有一个参数,所以第一个作为默认的返回值,后面几个相当于作为提取
[CSharpCallLua]
public delegate int CallBackFour(int a, ref int b, ref bool c, ref string d, ref int e);

需要先对相关参数变量进行初始化

1
2
3
4
5
6
7
int b1 = 0;
bool c1 = true;
string d1 = "";
int e1 = 0;
CallBackFour call4 = LuaMgr.GetInstance().Global.Get<CallBackFour>("funcFun3");
Debug.Log("使用ref" + call4(200, ref b1, ref c1, ref d1, ref e1));
Debug.Log(b1 + "_" + c1 + "_" + d1 + "_" + e1);

定义与lua中相同的委托

1
2
[CSharpCallLua]
public delegate int CallBackThree(int a, out int b, out bool c, out string d, out int e)

创建相关参数变量

1
2
3
4
5
6
7
CallBackThree call3 = LuaMgr.GetInstance().Global.Get<CallBackThree>("funcFun3");
int b;
bool c;
string d;
int e;
Debug.Log("第一个返回值:" + call3(100, out b, out c, out d, out e));
Debug.Log(b + "_" + c + "_" + d + "_" + e);

不定参数

1
2
3
4
5
6
7
8
funcFun4=function(a,...)
print("变长参数")
print(a)
arg={...}
for k,v in pairs(arg) do
print(k,v)
end
end

C#映射到Lua

列表和字典映射

列表映射Lua

简述:List可以包含指定类型的元素。它提供编译时类型检查,并且不执行装箱/拆箱,因为它是泛型的。

首先还是需要在lua中实现一个列表的效果,这个还是比较简单,因为列表是可以存相同类型的数据,也可存储不同类型的数据,而lua中是以table来实现相应的效果,这里为了模拟实际中常见的使用创建两个table

1
2
3
--映射到List
testList={1,2,3,4,5,6}
testListTwo={"123","456",true,1,1,2}

在c#中使用首先还是需要初始化lua解析器

1
2
3
//初始化lua解析器
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("main");

然后我们就可以用大G表去获取到两个lua中的table表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//因为在lua中的testList,很容易可以看出全都是存的number,所以完全可以用int来存
List<int> listOne = LuaMgr.GetInstance().Global.Get<List<int>>("testList");
foreach (var item in listOne)
{
//打印每一个值
Debug.Log(item);
}
//因为在lua中的testListTwo,很容易可以看出全都是存的number,string,boolean值,所以可以用object来存
List<object> listTwo = LuaMgr.GetInstance().Global.Get<List<object>>("testListTwo");
foreach (var item in listTwo)
{
//打印每一个值
Debug.Log(item);
}

字典映射Lua

其实对于字典来说,在lua中基本就是table1套table2,table2中有两个变量,一个用来存键,一个用来值

1
2
3
4
5
6
7
8
9
10
11
12
13
--映射到Dictionary
testDic={
["1"]=1,
["2"]=2,
["3"]=3,
["4"]=4
}
testDicTwo={
["1"]=1,
[true]=1,
[false]=true,
["456"]=false
}

在c#中使用首先还是需要初始化lua解析器

1
2
3
//初始化lua解析器
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("main");

然后创建字典用大G表获取到testDic和testDicTwo

1
2
3
4
5
6
7
8
Dictionary<string, int> dic = LuaMgr.GetInstance().Global.Get<Dictionary<string, int>>("testDic");
foreach (string item in dic.Keys) {
Debug.Log(item + "_" + dic[item]);
}
Dictionary<object, object> dicTwo = LuaMgr.GetInstance().Global.Get<Dictionary<object, object>>("testDicTwo");
foreach (object item in dicTwo.Keys) {
Debug.Log(item + "_" + dicTwo[item]);
}

类映射Lua的table

基本使用

在lua中创建类基本和c#中创建一个类相比较有些类似,只是在表中加入参数和函数

1
2
3
4
5
6
7
8
9
testClass={
testInt=2, --Number
testBool=true, --boolean
testFloat=1.2, --Number
testString="123", --String
testFun=function() --function
print("123456789")
end
}

对于在C#中如何使用,基本就是在创建相同名称的公有变量(这里需要注意私有和保护的变量是无法获取到的,而且公有变量是可多可少的,如果某个变量不需要,可以不写,结果只会被忽略得不到值而已,多出的变量也不会赋值,并且自动忽略,不会影响整体的效果,而类属于值传递,并不会造成lua变量的值的变化)

1
2
3
4
5
6
7
8
9
10
--我们先创建和lua中相同名字的类
public class CallLuaClass
{
//这里我们先不忽略,将所有变量都打印一下看看
public int testInt;
public bool testBool;
public float testFloat;
public float testString;
public UnityAction testFun;
}
1
2
3
4
5
6
7
8
9
10
11
12
//初始化Lua解析器
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
//通过_G表获取到testClass这个表
CallLuaClass obj = LuaMgr.GetInstance().Global.Get<CallLuaClass>("testClass");
//获取其中的变量
Debug.Log(obj.testInt);
Debug.Log(obj.testBool);
Debug.Log(obj.testFloat);
Debug.Log(obj.testString);
//执行函数
obj.testFun();

这里我们可以通过以下的截图看出结果

当我们注释掉testFloat这个变量,如下

1
2
3
4
5
6
7
8
9
10
11
--我们先创建和lua中相同名字的类
public class CallLuaClass
{
//这里我们先不忽略,将所有变量都打印一下看看
public int testInt;
public bool testBool;
//public float testFloat;//省略这个变量
public float testFloatTwo;//添加一个不存在的变量
public float testString;
public UnityAction testFun;
}

按照上面的方式进行打印

1
2
3
4
5
6
7
8
9
10
11
12
13
//初始化Lua解析器
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
//通过_G表获取到testClass这个表
CallLuaClass obj = LuaMgr.GetInstance().Global.Get<CallLuaClass>("testClass");
//获取其中的变量
Debug.Log(obj.testInt);
Debug.Log(obj.testBool);
//Debug.Log(obj.testFloat);
Debug.Log(obj.testFloatTwo);
Debug.Log(obj.testString);
//执行函数
obj.testFun();

这里很容易会发现,结果不会受影响,并且没有找到的testFloatTwo这个变量结果也并没有进行赋值,而是默认值为0

进阶

其实在实际的开发过程中,不可能单单是一类,也存在类中在套着类的情况,这种情况对于lua中的写法来说,无外乎就是table套table,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
testClass={
testInt=2,
testBool=true,
testFloat=1.2,
testString="123",
testFun=function()
print("123456789")
end,
--table中在套一个table
testInClass= {
testInInt=5
}
}

在C#来说就是在创建一个Class类,然后在类中添加,例如(需要注意:lua中的表名要和c#中类名要保持一致)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class CallLuaClass
{
public int testInt;
public bool testBool;
//public float testFloat;
public float testString;
public UnityAction testFun;
public CallLuaInClass testInClass;
public void Test()
{
Debug.Log(testInt);
}
}
public class CallLuaInClass
{
public int testInInt;
}

具体的调用

1
2
3
4
5
6
7
8
9
10
11
12
13
//初始化Lua解析器
LuaMgr.GetInstance().Init();
//加载lua脚本
LuaMgr.GetInstance().DoLuaFile("Main");
//获取到testClass这个表并与CallLuaClass意义对应
CallLuaClass obj = LuaMgr.GetInstance().Global.Get<CallLuaClass>("testClass");
Debug.Log(obj.testInt);
Debug.Log(obj.testBool);
//Debug.Log(obj.testFloat);
Debug.Log(obj.testString);
Debug.Log(obj.i);
Debug.Log("嵌套:" + obj.testInClass.testInInt);
obj.testFun();--这个基本变量不是很大,就不附上结果截图了

接口映射Lua的table

其实接口和类规则一样,也遵循公有变量是可多可少的,如果某个变量不需要,可以不写,结果只会被忽略得不到值而已,多出的变量也不会赋值,并且自动忽略,不会影响整体的效果,但是由于接口拷贝是引用拷贝,对于数值的赋值会影响到lua中的参数数值

,例如 ### 首先创建一个接口 接口中是不允许有成员变量的,我们用属性来接受,而且用接口来接收lua参数是需要添加[CSharpCallLua]
1
2
3
4
5
6
7
8
9
//这里记得Genetate Code
[CSharpCallLua]
public interface ICSharpCallInterface
{
int testInt{ get;set; }
bool testBool{ get;set; }
string testString { get;set; }
UnityAction testFun { get;set; }
}

示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
//初始化Lua解析器
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
ICSharpCallInterface obj = LuaMgr.GetInstance().Global.Get<ICSharpCallInterface>("testClass");
Debug.Log(obj.testInt);
Debug.Log(obj.testBool);
Debug.Log(obj.testString);
obj.testFun();
//接口拷贝 是引用拷贝 改了值 lua表中的值也变了
obj.testInt = 10000;
ICSharpCallInterface obj2 = LuaMgr.GetInstance().Global.Get<ICSharpCallInterface>("testClass");
Debug.Log(obj2.testInt);

LuaTable映射table

其实这种方式,在官方的文档中是不建议使用的,因为其会造成多余的垃圾,如果不进行清理,会造成垃圾的存留,或多或少的造成效率的降低

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//初始化Lua解析器
LuaMgr.GetInstance().DoLuaFile("Main");
LuaTable table = LuaMgr.GetInstance().Global.Get<LuaTable>("testClas");
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>("testFun").Call();
//改 引用
table.Set("testInt", 55);
Debug.Log(table.Get<int>("testInt"));
LuaTable table2 = LuaMgr.GetInstance().Global.Get<LuaTable>("testClas");
Debug.Log(table2.Get<int>("testInt"));
//销毁解析器
table.Dispose();
table2.Dispose();