C#CallLua相关
获取Lua环境变量
- 创建lua环境变量
LuaEnv env = new LuaEnv();
- 获取lua中的_G表
env.Global;
-
自定义加载器
- 可自定义多个加载器,xLua会逐个加载器获取Lua代码,直到获得到Lua代码后返回
- 如果自定义加载器无法获取lua代码,则会从默认地址加载lua代码
-
调用lua代码
env.DoString("");
获取或修改Lua中_G中的全局变量
LuaEnv.Global.Get<T>();`
LuaEnv.Global.Set();
通过Get方法获取的参数为值类型,改变该值无法修改Lua中原变量的值
调用Lua函数
- 调用Lua中的无参无返回值的方法时,可使用以下四种方法
FuncNoReturn noReturn = LuaMgr.GetInstance().Global.Get<FuncNoReturn>("testFun1");
noReturn();
UnityAction unityAction = LuaMgr.GetInstance().Global.Get<UnityAction>("testFun1");
unityAction();
Action action = LuaMgr.GetInstance().Global.Get<Action>("testFun1");
action();
//不推荐使用此方法
LuaFunction lf = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun1");
lf.Call();
- 调用Lua中有参数有返回值得方法时,需要定义一个与该方法返回值、形参一致的deleget,添加[CSharpCallLua]特性,并Generate Code
--lua
--有参有返回
testFun2 = function(a)
print("有参有返回")
return a + 1
end
[CSharpCallLua]
public delegate int FuncReturn(int a);
//有参有返回
FuncReturn funcReturn = LuaMgr.GetInstance().Global.Get<FuncReturn>("testFun2");
int val = funcReturn(2);
Debug.Log($"FuncReturn:{val}");
Func<int, int> sFun = LuaMgr.GetInstance().Global.Get<Func<int, int>>("testFun2");
Debug.Log($"FuncReturn:{sFun(20)}");
- 调用Lua中多返回值的方法时,需要在定义deleget时添加[CSharpCallLua]特性,deleget的返回值为Lua中方法的第一个返回值,后续的返回值用Out或ref标识第二个返回值及后续的返回值
--lua
testFun3 = function(a)
print("多返回")
return 1, "hhh", { 1, 2, 3 },a
end
//多返回值
FuncMulitReturn funcMulitReturn = LuaMgr.GetInstance().Global.Get<FuncMulitReturn>("testFun3");
int result = funcMulitReturn(20, out string b, out int[] c, out int d);
Debug.Log($"funcMulitReturn:result:{result},b:{b},c:{c.Length},d:{d}");
输出:funcMulitReturn:result:1,b:hhh,c:3,d:20
4.调用Lua中的变长参数方法时,需要在定义deleget时添加[CSharpCallLua]特性,变长参数部分用params 关键字标识
--lua
--变长参数
testFun4 = function(a, ...)
print("变长参数")
arg = { ... }
for i, v in pairs(arg) do
print(i ..",".. v)
end
print(a)
end
[CSharpCallLua]
public delegate void FuncMultiArg(string a, params int[] b);
FuncMultiArg funcMultiArg = LuaMgr.GetInstance().Global.Get<FuncMultiArg>("testFun4");
funcMultiArg("ckb", new[] {1, 2, 3, 4, 5});
C#映射Lua中的table
- 通过Class或Interface映射table
- 通过Class映射的table为浅拷贝,修改类中的值无法改变lua中的值
--lua
luaFunc = {
f1 = 12, f2 = 34,
1, 2, 3,
add = function(self, a, b)
print('d.add called')
return a + b
end
}
//通过class映射table。此类为浅拷贝,修改类中的值无法改变lua中table的值
public class tableClass
{
public int a;
public int b;
}
tableClass tableClass = LuaMgr.GetInstance().Global.Get<tableClass>("luaFunc");
Debug.Log($"a:{tableClass.a},b:{tableClass.b}");
tableClass.a = 1000;
tableClass.b = 2000;
tableClass = LuaMgr.GetInstance().Global.Get<tableClass>("luaFunc");
Debug.Log($"Change____a:{tableClass.a},b:{tableClass.b}");
- 通过Interface 映射的lua Table 为深拷贝,可同时修改lua中的值
- 接口添加[CSharpCallLua]特性,需Generate Code
[CSharpCallLua]
public interface ITableClass
{
int f1 { get; set; }
int f2 { get; set; }
int add(int a, int b);
}
ITableClass itable = LuaMgr.GetInstance().Global.Get<ITableClass>("luaFunc");
Debug.Log($"a:{itable.f1},b:{itable.f2}");
itable.f1 = 1000;
itable.f2 = 2000;
itable = LuaMgr.GetInstance().Global.Get<ITableClass>("luaFunc");
Debug.Log($"Change____a:{itable.f1},b:{itable.f2}");
Debug.Log($"Func add:{itable.add(20, 40)}");
LuaCallC#相关
lua无法直接访问c#,需要定义c#主入口,通过C#调用lua的主脚本,随后跑lua逻辑
- 最好将常用的方法在单独的脚本中创建好。减少重复创建的消耗
- 调用类的成员变量要用:
- 调用mono脚本使用AddComponent(typeof(xxx))
- lua调用C#的静态方法。用.
local callCSharp = CS.namespaces.Test
callCSharp.Speak2("222222")
- lua调用C#的成员方法,需要先实例化类,然后用:调用
local call = callCSharp()
call:Speak("ccccc")
Lua处理C#枚举相关
- 通过数值或字符串转换成枚举
myEnum.__CastFrom(1)
myEnum.__CastFrom("Atk")
Lua处理C#的List、Array、Dictionary
- Array相关
- 数组相关操作和在C#侧的方法一致
- 使用下标取数组的值时不是按照Lua的规则从1开始,而是从0开始
- 要在Lua创建一个C#数组,只能通过调用C#中Array的静态方法进行创建
local array = CS.System.Array.CreateInstance(typeof(CS.System.Int32),10)
- List相关
- List相关操作和在C#侧的方法一致
- 在Lua创建一个C#的List
--在Lua中创建一个C#List
--先创建一个List类
local l = CS.System.Collections.Generic.List(CS.System.String)
--实例化这个类
local List = l()
- Dictionary相关
- 创建一个C#的Dictionary
local d =CS.System.Collections.Generic.Dictionary(CS.System.String,CS.System.Int32)
local dicc = d()
- 通过上边的方法创建出来的Dic无法用C#的 dic[key]的方式获取到value。只能用dic:get_Item(key)的方式获取
- 根据key修改value 只能通过dic:set_Item(key,value)的方式
Lua调用C#拓展方法
- 调用拓展方法,类似于调用C#的成员方法。但拓展类头部应添加[LuaCallCSharp] 特性,并Generate Code
//csharp
[LuaCallCSharp]
public static class Tools
{
public static void Move(this L4 obj)
{
Debug.Log($"{obj.name}---Move");
}
}
public class L4
{
public string name = "MCNB";
public void Speak(string str)
{
Debug.Log($"{name} str");
}
public static void Talk(string str)
{
Debug.Log($"Static:{str}");
}
}
--lua
local L4 = CS.L4
--调用静态方法
L4.Talk("ccc")
local _L = L4()
--调用成员方法
_L:Speak("ssssss")
--调用拓展方法
_L:Move()
Lua调用C#的函数传入ref和out参数
public class L5
{
public int RefFunc(int a, ref int b, ref int c, int d)
{
b = a + d;
c = a - d;
return 100;
}
public int OutFunc(int a, out int b, out int c, int d)
{
b = a;
c = d;
return 200;
}
public int RefOutFunc(int a, out int b, ref int c)
{
b = a * 100;
c = a * 200;
return 300;
}
}
l5 = CS.L5
local obj = l5()
--ref会以多返回值的方式返回lua
--如果函数存在返回值,第一个值为返回值,后续值为ref参数
local a,b,c = obj:RefFunc(1,2,3,4)
print(a..","..b..","..c)
输出: 100,5,-3
--out会以多返回值的方式返回lua
--如果函数存在返回值,第一个值为返回值,后续值为ref参数
--out参数可以不用传入参数
local a,b,c = obj:OutFunc(20,30)
print("返回值:"..a..",".."第一个out参数:"..b..",".."第二个out参数:"..c)
输出: 返回值:200,第一个out参数:20,第二个out参数:30
Lua调用C#重载函数
Lua支持调用C#的重载函数,在调用参数个数不同的重载函数时,可以正常调用。
当调用相同参数个数相同且均为数值类型,但精度不同时会出现意想不到的问题。
注意:可已通过反射的方式调用这种情况的函数,但并不推荐
public class L6
{
public int Clac()
{
return 1;
}
public int Clac(int a, int b)
{
return a + b;
}
public int Clac(int a)
{
return a + 1;
}
public float Clac(float a)
{
return a;
}
}
print("********************调用C#函数重载*********************")
local obj = CS.L6()
print(obj:Clac())
print(obj:Clac(2))
print(obj:Clac(3,6))
print(obj:Clac(2.2))
print("********************调用C#函数重载*********************")
--xLua调用C#的重载函数时,如果函数的参数个数一样,但精度不同,会出现问题
--可以通过反射的方式调用,但不建议使用
--通过函数名和参数类型获得方法的Type
local m1 = typeof(CS.L6):GetMethod("Clac",{typeof(CS.System.Int32)})
local m2 = typeof(CS.L6):GetMethod("Clac"{typeof(CS.System.Single)})
local f1 = xlua.tofunction(m1);
local f2 = xlua.tofunction(m2);
--成员函数传入的第一个参数为调用者的对象
--静态函数不用传
print(f1(obj,10))
print(f2(obj,10.2))
Lua访问C#二维数组
1.获取二维数组的行列数
public int[,] array = new int[2,3]{{1,2,3},{4,5,6}}
local obj = CS.L8()
print("行"..obj.array:GetLength(0))
print("列"..obj.array:GetLength(1))
2.通过下标获取二维数组元素和遍历二维数组
- 注意:lua不支持通过[0,0]或[0][0] 的方式获取二维数组的元素
print("0,0元素:"..obj.array:GetValue(0,0))
--遍历二维数组
for i = 0 , obj.array:GetLength(0)-1 do
for j = 0 , obj.array:GetLength(1)-1 do
print("行:"..i..",列:"..j..",值:"..obj.array:GetValue(i,j))
end
end
Lua判断C#中对象为空
- lua中无法使用== 判断对象是否为空,只能用Equals(nil)来判断
- 方法1:lua的公共方法中增加判空方法
function IsNull(obj)
if obj == nil or obj:Equals(nil) then
return true
end
return false
end
- 方法2:C#为Object添加拓展方法,在lua调用
public static bool IsNUll(this Object obj)
{
return obj == null;
}
Lua调用C#协程
首先要在调用C#的协程,类需要继承自mono,因此创建一个空对象,在对象上添加一个继承自mono类的脚本
local obj = GameObject("协程测试")
local mono = obj:AddComponent(typeof(CS.LuaCallCSharp))
C#中开启协程用StartCoroutine()方法,在lua中同样可以使用这个方法
WaitForSeconds = CS.UnityEngine.WaitForSeconds
util = require("xlua.util")
func = function()
local a = 1
while true do
print(a)
a = a + 1
--lua中的corotine.yield相当于C#种的yield return
coroutine.yield(WaitForSeconds(1))
if a > 10 then
mono:StopCorotine(b)
end
end
end
--注意,如果要在lua用StartCoroutine开启一个协程,需要引用XLua提供的util脚本中的cs_generator方法,将一个lua中的方法转换成C#的协程方法
b =mono:StartCoroutine(util.cs_generator(func))
Lua调用C# 泛型函数
public interface ITest
{
}
public class TestFather
{
}
public class TestChilder : TestFather,ITest
{
}
public void TestFunc1<T>(T a, T b) where T:TestFather
{
Debug.Log("有泛型约束的方法");
}
public void TestFunc2<T>(T a)
{
Debug.Log("没有约束的方法");
}
public void TestFunc3<T>() where T : TestFather
{
Debug.Log("有约束,没有参数");
}
public void TestFunc4<T>() where T : ITest
{
Debug.Log("接口约束,没有参数");
}
local obj = CS.L12
local father = obj.TestFather
local child = obj.TestChilder
obj:TestFunc1(father,child)
--lua不支持没有约束的泛型函数
obj:TestFunc2(father)
--lua不支持没有参数的泛型函数
obj:TestFunc3()
--lua不支持约束是非class的泛型函数
obj:TestFunc4(child)
重要的知识点!!!
xLua官方推荐将每一个Lua 要调用的C#类都添加[LuaCallCSharp]特性,这样可以保证运行效率,否则xlua会通过反射调用Lua要用到的C#类。但是那些系统提供的方法和第三方库的方法,我们没办法在这些方法上添加特性标签,如果想在lua调用这些方法,应该怎么做呢?
举个例子:在UI上添加一个Slider控件,lua如何监听Slider控件的onValueChanged事件,打印它的数值变化?
lua这边的代码很容易搞定
local slider = GameObject.Find("Slider")
local sliderCom = slider:GetComponent(typeof(UI.Slider))
sliderCom.onValueChanged:AddListener(function(f)
print(f)
end)
执行后,Unity报错
大致的意思是lua中为onValueChanged事件添加监听时传入的UnityAction 委托没有添加[CsharpCallLua]标签,这个委托是Unity自带的,我们无法直接为其添加这个标签,为了解决这个问题,可以在C#这边添加以下代码
public static class L10
{
//把报错中提到的UnityAction<float>加到这个列表中
[CSharpCallLua]
public static List<Type> CsharpCallLua = new List<Type>()
{
typeof(UnityAction<float>)
};
}
加好后别忘了GenerateCode。然后Lua便能愉快的监听到Slider的这个事件了