UH2S2L6——全局函数获取

本章代码关键字

1
2
3
4
[CSharpCallLua]            //使委托可以接收Lua方法的特性,添加特性后需要使用Generate Code
LuaFunction //xLua提供的委托,可以接收各种类型的Lua全局函数,建议少用,因为会产生垃圾
luaFunction.Call() //执行委托,需要传入参数,会返回object数组
luaFunction.Dispose() //销毁luaFunction,若不销毁,则将一直占用内存!

全局函数的获取

  • 无参无返回

    • 自定义委托
    • Action
    • UnityAction
    • LuaFunction
  • 有参有返回

    • 自定义委托
    • Func
    • LuaFunction
  • 多返回

    • 自定义委托

      • Out
      • Ref
    • LuaFunction

  • 变长参数

    • 自定义委托
    • LuaFunction

先在Lua声明不同的函数(无参无返回,有参有返回,多返回值,变长参数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
TestFun = function()
print("无参无返回")
end

TestFun2 = function(a)
print("有参有返回")
return a + 1
end

TestFun3 = function(a)
return 1, 2, false, "123", a
end

TestFun4 = function(a, ...)
print("a")
arg = { ... }
for key, value in pairs(arg) do
print(key, value)
end
end

无参无返回值

我们可以像调用全局变量一样调用全局方法,就是需要使用委托来接收它
这个委托可以是我们自己声明的,也可以使用Unity提供的,也可以使用C#提供的,
xLua也提供了对应的变量类型接收方法,调用它需要使用Call方法(官方建议少用,因为会产生垃圾,性能不佳)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//无参无返回值的委托
public delegate void CustomCall();

public class L5_CallFunction : MonoBehaviour
{
void Start()
{
LuaManager.Instance.Init();
LuaManager.Instance.DoLuaFile("Main");

CustomCall call = LuaManager.Instance.Global.Get<CustomCall>("TestFun");
call();
UnityAction ua = LuaManager.Instance.Global.Get<UnityAction>("TestFun");
ua();
Action action = LuaManager.Instance.Global.Get<Action>("TestFun");
action();
LuaFunction lf = LuaManager.Instance.Global.Get<LuaFunction>("TestFun");
lf.Call();
}
}

有参有返回值

和上面的无参无返回值函数的调用大同小异,但是需要做额外的步骤
首先是委托需要有对应的参数与返回值类型,并且加上特性[CSharpCallLua]
并且在工具栏点击 XLua - Clear Generate Code 选项,生成对应的C#代码,该委托才能被识别!

[CSharpCallLua]​特性有不在类声明前添加也可以使用的方法,对不能直接修改代码的类也有效,详见:让系统类型和Lua能互相访问

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//无参无返回值的委托
public delegate void CustomCall();

//有参有返回值的委托
[CSharpCallLua]
public delegate int CustomCall2(int a);

public class L5_CallFunction : MonoBehaviour
{
void Start()
{
LuaManager.Instance.Init();
LuaManager.Instance.DoLuaFile("Main");
CustomCall2 call2 = LuaManager.Instance.Global.Get<CustomCall2>("TestFun2");
Debug.Log("有参有返回值:" + call2(10));
}
}

image

C#也自带了有参有返回值的委托Func<>,可以直接使用,泛型参数第一个一定是返回值,后面都是参数

1
2
3
4
CustomCall2 call2 = LuaManager.Instance.Global.Get<CustomCall2>("TestFun2");
Debug.Log("有参有返回值:" + call2(10));
Func<int, int> sFun = LuaManager.Instance.Global.Get<Func<int, int>>("TestFun2");
Debug.Log("有参有返回值:" + sFun(20));

也可以使用XLua提供的委托,执行时需要传入参数,它会返回object[],返回值从这个数组获取

1
2
LuaFunction lf2 = LuaManager.Instance.Global.Get<LuaFunction>("TestFun2");
Debug.Log("有参有返回值:" + lf2.Call(30)[0]);

image

多返回值

C#不支持函数多返回值,我们需要使用out​和ref​来接收,也就是通过传入的参数变量来接收返回值
具体声明方式是,用out修饰接收返回值参数,将接收返回值的变量传入到方法中,这些变量在这些过方法后就会接收到方法的返回值
(同样的,这种新声明出来的委托还是需要加上特性并执行Generate Code)

1
2
3
4
//多返回值的委托
[CSharpCallLua]
public delegate int CustomCall3(int a, out int b, out bool c, out string d, out int e);
//前面的a是传入Lua方法的参数,后面的参数用来接收返回值的参数

函数的第一个返回值还是通过委托本身返回出来,剩下的返回值就是通过参数传递变量来接收返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//多返回值的委托
[CSharpCallLua]
public delegate int CustomCall3(int a, out int b, out bool c, out string d, out int e);

public class L5_CallFunction : MonoBehaviour
{
void Start()
{
LuaManager.Instance.Init();
LuaManager.Instance.DoLuaFile("Main");
CustomCall3 call3 = LuaManager.Instance.Global.Get<CustomCall3>("TestFun3");
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);
}
}

image

ref​修饰参数的效果是一样的,唯一的区别是,ref传入的变量必须初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//多返回值的委托
[CSharpCallLua]
public delegate int CustomCall3(int a, out int b, out bool c, out string d, out int e);
[CSharpCallLua]
public delegate int CustomCall4(int a, ref int b, ref bool c, ref string d, ref int e);

public class L5_CallFunction : MonoBehaviour
{
void Start()
{
LuaManager.Instance.Init();
LuaManager.Instance.DoLuaFile("Main");
CustomCall3 call3 = LuaManager.Instance.Global.Get<CustomCall3>("TestFun3");
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);

CustomCall4 call4 = LuaManager.Instance.Global.Get<CustomCall4>("TestFun3");
int b1 = 0;
bool c1 = true;
string d1 = "";
int e1 = 0;
Debug.Log("第一个返回值:" + call4(200, ref b1, ref c1, ref d1, ref e1));
Debug.Log("剩余的返回值:" + b1 + "_" + c1 + "_" + d1 + "_" + e1);
}
}

image

这里同样可以使用XLua提供的委托,执行时需要传入参数,它会返回object[],返回值从这个数组获取(少用)

1
2
3
4
5
6
LuaFunction lf3 = LuaManager.Instance.Global.Get<LuaFunction>("TestFun3");
object[] objs = lf3.Call(1000);
for (int i = 0; i < objs.Length; i++)
{
Debug.Log("第" + i + "个返回值是:" + objs[i]);
}

变长参数

调用变长参数函数的委托,我们在参数列表使用C#提供的变长参数即可,
只不过和Lua相比C#的变长参数需要我们指定类型,如果类型不确定,可以使用object
如果可以确定是单一类型,就直接使用对应的类型即可
(同样的,这种新声明出来的委托还是需要加上特性并执行Generate Code)

1
2
3
//变长参数
[CSharpCallLua]
public delegate void CustomCall5(string a, params int[] args); //变长参数的类型,是根据实际情况来决定的

这里同样可以使用XLua提供的委托,执行时传入对应的参数即可(少用)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
//变长参数
[CSharpCallLua]
public delegate void CustomCall5(string a, params int[] args); //变长参数的类型,是根据实际情况来决定的

public class L5_CallFunction : MonoBehaviour
{
void Start()
{
LuaManager.Instance.Init();
LuaManager.Instance.DoLuaFile("Main");

CustomCall5 call5 = LuaManager.Instance.Global.Get<CustomCall5>("TestFun4");
call5("传入了这些参数:", 1, 2, 3, 4, 5, 666, 7, 88, 999);
LuaFunction lf4 = LuaManager.Instance.Global.Get<LuaFunction>("TestFun4");
lf4.Call("传入了这些参数:", 3, 4, 54321, 6666, 7, 88, 9);
}
}

LuaFunction​的销毁

LuaFunction​的调用完毕时,应当手动销毁,否则将一直占用内存,造成内存泄露,LuaTable​同理
Lua解析器的tick()​就是会释放我们没有手动销毁的LuaTable​,LuaFunction

1
lf.Dispose();