Xlua和C#使用简介

Xlua

C#调用Lua

lua解析器

lua解析器可以让我们在unity中执行Lua,一般情况下,我们会保证它在unity中的唯一性。

//创建一个lua解析器
LuaEnv env = new LuaEnv();
//执行Lua语言
env.DoString("print('你好世界')");
//像上面这样一句一句的执行lua脚本是非常不好的,所以可以使用lua脚本加载的知识来解决这个问题。
//xlua默认的脚本寻找路径是在Resources文件夹中,但是由于unity并不能识别Lua文件,所以直接加载时会报错的。解决方案是我们将lua后缀多加一个txt,将其转换为文本文件。
env.DoString("require('Main')");
//此方法可以帮助我们清除Lua中我们没有手动释放的对象,相当于垃圾回收。
//我们可以在帧更新中定时执行,也可以在切换场景时执行。
env.Tick();
//销毁Lua解析器,一般会用的会比较少,因为我们之前说过了,我们基本会保证Lua解析器在unity中是全局唯一的。
env.Dispose();

lua文件重定向

//需要调用此方法来添加新的路径,参数为委托类型
//delegate byte[] CustomLoader(ref string filepath)
env.AddLoader(NewLoader);
//这里我们写一个方法并传进去
private byte[] NewLoader(ref string filepath)
{
    //路径拼接需要加上后缀
    string path = Application.dataPath + "/Lua/" + filepath + ".lua";
    if (File.Exists(path))
    {
        return File.ReadAllBytes(path);
    }
    return null;
}
//由于委托可以注册多个方法,所以xlua的文件重定向查找方案也是优先查找委托中所有的路径,最后查找默认路径Resources。
//一旦找到对应文件就会返回。不再继续

获取全局变量

//首先我们需要编辑一个test.lua脚本并书写一下信息
print("Test脚本");
testNumber = 1;
testBool = true;
testFloat = 1.2;
testString = "你好";
//在之前的LuaManager中我们添加了一个Global属性,用于获得lua解析器的_G表。
//我们调用xlua的Global中的get方法来获取lua解析器读取的lua脚本中_G表所存放的全局变量。
//注意,字符串内容需要与lua中的变量名一致。
//这里是值拷贝,就算使用临时变量接受了返回值并进行改变也不会影响Lua脚本中的值。
print(LuaManager.GetInstance.Global.Get<int>("testNumber"));
print(LuaManager.GetInstance.Global.Get<bool>("testBool"));
//虽然lua只有一种数值类型number,但是我们可以根据其内容来在C#中为其赋予类型
print(LuaManager.GetInstance.Global.Get<float>("testFloat"));
print(LuaManager.GetInstance.Global.Get<string>("testString"));
//想要改变Lua脚本中的值,我们需要使用Global提供给我们的Set方法。
LuaManager.GetInstance.Global.Set("testNumber",5);
print(LuaManager.GetInstance.Global.Get<int>("testNumber"));

获得全局函数

//我们在test.lua中添加新成员
testFunc1 = function()
	print("这是一个无参无返回值的函数");
end
testFunc2 = function(param)
	print("这是一个有参有返回值的函数");
	return param + 1;
end
testFunc3 = function()
	print("这是一个多返回值的函数");
	return 1,2,"123";
end
testFunc4 = function(...)
	arg = {...};
	print("这是一个有变长参数的函数");
	for k,v in pairs(arg) do
		print(k,v)
	end
end
//总的来说,获得lua的全局函数需要使用到委托。
//这里我们可以使用自定义委托,C#提供的标准委托,或是使用Xlua提供的委托。
//注意使用Xlua提供的委托可能发生拆箱装箱
//我们先来看第一种情况,无参无返回值,我们直接使用Action委托来接受。
Action func1 = LuaManager.GetInstance.Global.Get<Action>("testFunc1");
func1.Invoke();
//第二种情况,有参有返回值,我们直接使用Func委托来接受。
 Func<int, int> func2 = LuaManager.GetInstance.Global.Get<Func<int, int>>("testFunc2");
func2.Invoke(10);
//第三种情况,多返回值的函数,我们需要自定义委托来接受。首先我们自定义委托
//当我们要使用自定义委托来接受lua中的全局函数时,我们需要为委托添加[CSharpCallLua]特性,并重新加载。
[CSharpCallLua]
public delegate void FuncHandler(out int paramA,out int paramB, out string paramC);
//使用此委托接受
FuncHandler func3 = LuaManager.GetInstance.Global.Get<FuncHandler>("testFunc3");
int paramA;
int paramB;
string paramC;
func3.Invoke(out paramA,out paramB,out paramC);
//第四种情况,要求输入一个变长参数,我们依旧需要自定义委托来接受。
[CSharpCallLua]
public delegate void FuncHandler2(params object[] array);
FuncHandler2 func4 = LuaManager.GetInstance.Global.Get<FuncHandler2>("testFunc4");
func4.Invoke(1,2,3,"qwe",true);

List,Dictionary映射Table

//使用List和Dictionary映射Table和之前说过的获得全局变量是一样的。
//来到lua脚本我们添加一些表
testList1  = {1,2,3,4,5,6};
testList2 = {1,2,3,"123",true,false};

testDic1 = {
	["1"] = 1,
	["2"] = 2,
	["3"] = 3,
	["4"] = 4,
}
testDic2 = {
	["1"] = 1,
	["2"] = 2,
	["3"] = true,
	["4"] = false,
}
//来到C#使用list或者dictionary直接接受
//注意这里的接受依旧是值拷贝
List<int> testList1 = LuaManager.GetInstance.Global.Get<List<int>>("testList1");
foreach (var item in testList1)
{
	print(item);
}

List<object> testList2 = LuaManager.GetInstance.Global.Get<List<object>>("testList2");
foreach (var item in testList2)
{
	print(item);
}
Dictionary<string, int> testDic1 = LuaManager.GetInstance.Global.Get<Dictionary<string, int>>("testDic1");
foreach (var key in testDic1.Keys)
{
	print($"{key}_{testDic1[key]}");
}
Dictionary<string, object> testDic2 = LuaManager.GetInstance.Global.Get<Dictionary<string, object>>("testDic2");
foreach (var key in testDic2.Keys)
{
	print($"{key}_{testDic2[key]}");
}

类映射Table

//使用C#类映射lua中的类表
//首先我们先在lua中写一个类表,其中包含number类型,string类型,boolean类型和function类型
testClass = {
	myInt = 1,
	myString = "你好",
	myBool = true,
	myFunc = function()
		print("testClass的函数");
	end
}
//接下来我们来书写C#中准备接受这个表的类
//其中,类名实没有要求的,但是在这个类中声明的成员变量名称需要和lua中的名称一致,这也是首次要求名称对应。
//类的成员变量需要是公共的,或者internal,其他类型将无法赋值。
//我们不需要将lua中声明的类型在这里全部声明;不论是多出来的变量还是缺少的变量,Xlua都会选择忽略。
public class CallLuaClass
    {
        public int myInt;
        public string myString;
        public bool myBool;
        public Action myFunc;
    }
//使用类映射table依旧是值拷贝

接口映射Table

接口映射Table和类大同小异,但在使用时需要注意:

  1. 接口需要统一使用属性来接受lua脚本中的成员。
  2. 使用接口映射Table时,需要和自定义委托一样加上特性并重新编译。
  3. 如果改变了接口的内容,一定要先清除在编译。
  4. 接口是引用拷贝,改变接口的值,lua中的值也会改变。

LuaTable,LuaFunction

首先要说的是,官方不推荐使用luaTable接收lua中的Table,也不推荐使用LuaFunction接收Lua中的function,他们执行效率低,且需要手动释放资源。

但需要注意的是。LuaTable和LuaFunction是引用拷贝,可以通过set方法改变lua脚本中的值。

*Lua调用C#

Lua使用C#类

print("*********Lua调用C#类相关知识点*********");
--对于lua调用C#的方法,我们一定是通过C#先进入Lua的
--通过DoString方法进入


--lua中使用C#的类非常简单
--固定套路
--CS.命名空间.类名
--Unity的类 比如 GameObject , Transform等等 可以通过CS.UnityEngine.类名进行调用
--CS.UnityEngine.GameObject
--CS.UnityEngine.Transform

print("通过C#中的类在Lua中实例化一个游戏对象");

--由于Lua中没有new,所以在Lua中我们有另外的语法来创建对象。
--在Lua中类名括号就是在实例化对象,默认的调用的相当于就是无参构造函数。
--调用无参构造函数
local gameObject1 = CS.UnityEngine.GameObject(); 
--调用有参构造函数
local gameObject2 = CS.UnityEngine.GameObject("有参构造函数对象");

--为了方便使用并节约性能,定义全局变量存储C#中的类
--相当于取了一个别名
GameObject = CS.UnityEngine.GameObject;
local gameObject3 = GameObject("这是一个取了别名的游戏对象");

--类中的静态对象可以直接使用.运算符来调用
local gameObject4 = GameObject.Find("这是一个取了别名的游戏对象");

--得到对象中的成员变量,直接使用.运算符即可
print(gameObject4.transform.position);
Debug = CS.UnityEngine.Debug;
Debug.Log(gameObject4.transform.position);
--如果使用对象中的成员方法一定要使用:
Vector3 = CS.UnityEngine.Vector3;
gameObject4.transform:Translate(Vector3.right);
Debug.Log(gameObject4.transform.position);

--使用不继承Mono的普通类,首先在C#中创建一个Test类
Test = CS.Test;
--创建一个对象
local test = Test();
test:Speak("123");

--继承Mono的类。
--注意继承Mono的类是不可以直接new的,我们需要使用AddComponent方法
local gameObject5 = GameObject("加脚本测试");
--通过GameObject的AddComponent添加脚本
--Xlua提供了一个重要的方法typeof可以获得类的Type
--Xlua中不支持无参泛型函数,所以我们需要使用AddComponent的另一个重载
gameObject5:AddComponent(typeof(CS.Test1_LuaEnv));

Lua使用C#枚举

print("*********Lua调用C#枚举相关知识点*********");

--Lua调用C#的枚举,和调用C#的类相似,直接使用.运算符来使用其枚举值
--调用Unity中的默认枚举
--首先参照lua调用类,我们先将枚举的命名空间保存
PrimitiveType = CS.UnityEngine.PrimitiveType;
GameObject = CS.UnityEngine.GameObject;
--unity中创建默认几何体的代码
local obj = GameObject.CreatePrimitive(PrimitiveType.Cube);

--自定义枚举,使用方法和unity的默认枚举一样,但需要注意命名空间
--我们联合枚举转换一起来说
E_MyEnum = CS.E_MyEnum;
print(E_MyEnum.idle);
--数值转枚举
local enum1 = E_MyEnum.__CastFrom(1);
print(enum1);
--字符串转枚举
local enum2 = E_MyEnum.__CastFrom("atk");
print(enum2);

Lua使用C#数组,List,Dictionary

print("*********Lua调用C#数组相关知识点*********");
--需要注意,虽然我们在Lua中使用表来模拟数组,list,dictionary,但是当lua调用C#时,我们要遵守C#的使用规则。
--首先我们现在Lua中创建一个C#的数组
--这里我们调用C#中Array基类的CreateInstance方法来创建,具体参数信息需要查阅C#
--这里我们使用的方法第一个参数传入type,第二个参数传入容量
local array = CS.System.Array.CreateInstance(typeof(CS.System.Int32),10);
--获得数组的长度,这里调用了C#中的成员变量
print(array.Length);
--访问数组的元素
print(array[0]);
--数组遍历,这里需要注意,虽然Lua的索引是从1开始,但是我们使用的是C#的数组,其索引是从0开始的。
--注意Lua中的for循环遍历一定要减1
for i=1,array.Length - 1 do
	print(array[i]);
end

print("*********Lua调用C#List相关知识点*********");
--首先我们还是先创建一个List对象,这里有两种方式。
--老版本
--其中CS.System.Collections.Generic是List泛型的命名空间,List`1指的是此泛型有一个泛型参数。
local list = CS.System.Collections.Generic["List`1[System.String]"]();
list:Add("5555");
print(list[0]);
--新版本
local List_String =  CS.System.Collections.Generic.List(CS.System.String);
local list2 = List_String();
list2:Add("123");
print(list2[0]);
--输出长度
print(list2.Count);
print("*********Lua调用C#Dictionary相关知识点*********");
--我们还是先来创建一个字典
local dictionary_Int_Vector3 = CS.System.Collections.Generic.Dictionary(CS.System.String, CS.UnityEngine.Vector3);
local dic = dictionary_Int_Vector3();
dic:Add("123",CS.UnityEngine.Vector3.right);
--除了int类型的键,其他无法通过索引直接获得,这里会直接报空
print(dic["123"]);
--我们通过以下方法来获得键值
--1. TryGetValue
print(dic:TryGetValue("123"));
--2. get_Item, set_Item
print(dic:get_Item("123"));
dic:set_Item("123",nil);
print(dic:get_Item("123"));

Lua使用C#拓展方法

print("*********Lua调用C#List相关知识点*********");
local ExMethodDemo = CS.ExMethodDemo();
--Lua调用C#的拓展方法时,需要在拓展方法的类上面加上[LuacallCSharp]这个特性,并重新加载
--使用拓展方法和使用成员方法一样使用:,毕竟拓展方法本身就是将自己作为参数传进去了。
ExMethodDemo:Eat();
--[[
	总结:
	1. 想要在Lua中使用拓展方法,就一定要加上[LuacallCSharp]特性。
	2. 建议Lua中要使用的C#类都使用这个特性,可以提升性能。
	3. Xlua是通过反射来调用C#类的,当我们加了上述特性并重新加载代码后,相当于提前做了工作。
]]

Lua使用ref和out

print("*********Lua调用C#ref和out相关知识点*********");
local RefAndOut = CS.RefAndOut();
--C#中的ref参数会以多返回值的形式返回给Lua。
--如果函数存在返回值,则第一个默认返回该返回值。
--之后返回的结果就是ref的结果,从左到右一一对应。
--ref参数需要传入一个默认值来占位,这点和C#中使用是非常相似的。
local paramA,paramB,paramC = RefAndOut:RefTest(1,0,0,1);
print(paramA);
print(paramB);
print(paramC);
--C#中的out参数会以多返回值的形式返回给Lua。
--如果函数存在返回值,则第一个默认返回该返回值。
--之后返回的结果就是out的结果,从左到右一一对应。
--与ref不同,out参数不需要占位,当然如果你传入参数也没有事。
local paramA,paramB,paramC = RefAndOut:OutTest(2,1);
print(paramA);
print(paramB);
print(paramC);
--ref和out的综合使用,总体区别在于ref需要占位,out不需要
local paramA,paramB,paramC = RefAndOut:RefOut(2,1,1,1);
print(paramA);
print(paramB);
print(paramC);

Lua使用C#重载方法

print("*********Lua调用C#重载方法相关知识点*********");

--虽然lua自己不支持重载函数的书写,但是可以调用C#中的重载函数
--原因是lua中对于相同变量的函数赋值会覆盖之前赋值的函数,所以不能重载函数是,但如果调用C#的话很明显就不会存在这个问题。
local CallOverrideFunc = CS.LuaCallOverride();
CallOverrideFunc:CallOverrideWithParam();
--注意,由于lua中在数值类型上只支持number类型,所以对于C#中参数个数相同,数值精度不同的重载无法正确获得。
--我们要尽量避免这种写法,或者使用Xlua提供给我们的反射解决方案。
--需要注意的是,此方案毕竟是通过反射实现的,性能较差,不到万不得已的情况下不要使用。
print(CallOverrideFunc:CallOverrideWithParam(10));
print(CallOverrideFunc:CallOverrideWithParam(10.2));
--反射解决方案
--首先通过反射获得需要使用的方法
local method1 = typeof(CS.LuaCallOverride):GetMethod("CallOverrideWithParam", {typeof(CS.System.Int32)});
local method2 = typeof(CS.LuaCallOverride):GetMethod("CallOverrideWithParam", {typeof(CS.System.Single)});
--通过Xlua提供的tofunction方法转换为lua函数
--一般我们只转换一次,之后重复使用
local luafunc1 = xlua.tofunction(method1);
local luafunc2 = xlua.tofunction(method2);
--转换完成后,需要注意:
--如果目标方法为成员方法,则第一个参数需要传调用对象。
--如果目标方法为静态方法,则不需要。
print(luafunc1(CallOverrideFunc,10));
print(luafunc2(CallOverrideFunc,10.2));

Lua使用C#委托事件

print("*********Lua调用C#委托事件相关知识点*********");
--Lua使用C#委托事件的语法基本上和C#中的使用是一致的。
local LuaCallDelegate = CS.LuaCallDelegate();
local func1 = function()
	print("Lua使用c#委托调用lua的方法");
end
--Lua中使用C#委托,将其当作成员变量来使用,使用的规则和C#中一样,先等号赋值,再+=
--需要注意,lua中没有+=运算,需要我们老老实实的写A = A + B这种形式。
LuaCallDelegate.DelegateTest = func1;
--lua中使用C#委托,也可以直接添加lua函数,这种添加方法有点类似于在C#中添加匿名方法或者lambda表达式。
--不建议使用,因为这种添加形式只能加不能减。
LuaCallDelegate.DelegateTest = LuaCallDelegate.DelegateTest + function()
	print("lua使用C#委托添加的临时变量");
end
LuaCallDelegate.DelegateTest();
--注销回调函数
LuaCallDelegate.DelegateTest = LuaCallDelegate.DelegateTest - func1;
LuaCallDelegate.DelegateTest();
--清空委托链,相当于C#中调用 = null
LuaCallDelegate.DelegateTest = nil;

local func2 = function()
	print("事件添加的函数");
end
--在事件的添加上,lua不同于C#
--事件添加函数使用对象名:事件名("+",函数名)的方式来添加
--注销事件同理
LuaCallDelegate:EventTest("+",func2);
LuaCallDelegate:OnEventStart();
--同样可以使用此方法来添加匿名函数
LuaCallDelegate:EventTest("+",function()
	print("这是使用事件添加的匿名函数");
end
);
LuaCallDelegate:OnEventStart();
--注销事件
LuaCallDelegate:EventTest("-",func2);
LuaCallDelegate:OnEventStart();
--由于事件不能在类外调用委托链清空,所以我们将委托链清空的方法封装在事件申明类中。
LuaCallDelegate:ClearEvent();
LuaCallDelegate:OnEventStart();

Lua使用C#获得二维数组的值

print("*********Lua调用C#二维数组相关知识点*********");
--Lua使用C#的数组,并直接通过索引查找数组元素只适用于一维数组。
--想要获得多维数组的元素需要使用Array基类中的一个获取元素的方法GetValue。
--同理如果我们想要设置多维数组的值也可以通过方法SetValue来实现。
--注意这里的GetSet都为成员方法。
local Call2Array = CS.Call2Array();
--获得C#2维数组的行长度
print(Call2Array.My2Array:GetLength(0));
--获得C#2维数组的列长度
print(Call2Array.My2Array:GetLength(1));
--获得C#2维数组的元素,我们需要通过GetValue方法来实现。
--获得索引为(0,0)的元素
 print(Call2Array.My2Array:GetValue(0,0));
 --获得索引为(1,2)的元素
 print(Call2Array.My2Array:GetValue(1,2));

 --遍历C#二维数组
for i=0,Call2Array.My2Array:GetLength(0) - 1 do
	for j=0, Call2Array.My2Array:GetLength(1) - 1 do
		print(Call2Array.My2Array:GetValue(i,j));
	end
end

Lua调用C#nil和null的比较

print("*********Lua调用C#nil和null相关知识点*********");
--首先我们需要明白:C#中的null和lua中的nil是不能判等的。
--我们用一个开发中比较常见的问题来说明这个问题。
--问题:判断一个游戏物体是否存在rigidbody,如果不存在需要添加一个tigidbody组件。
local GameObject = CS.UnityEngine.GameObject;
local Rigidbody = CS.UnityEngine.Rigidbody;
local go = GameObject("添加Rigidbody");
local rigi = go:GetComponent(typeof(Rigidbody));
--解决方式一:使用Equals
if rigi:Equals(nil) then
	go:AddComponent(typeof(Rigidbody));
end
--再次判断go物体上是否有Rigidbody组件
local rigi = go:GetComponent(typeof(Rigidbody));
--解决方式二:在Main中或者调用Main的调用函数中书写全局判空函数并使用。
--[[
function IsNull(param)
	if param == nil or param:Equals(nil) then
		return true;
	end
	return false;
end
]]
if IsNull(rigi) then
	print("123");
	go:AddComponent(typeof(Rigidbody));
end
--再次判断go物体上是否有Rigidbody组件
local rigi = go:GetComponent(typeof(Rigidbody));
--解决方式三:在C#中为Object书写判空拓展方法并使用。
--[[
[LuaCallCSharp]
public static class NilAndNull{
    public static bool IsNull(this Object obj)
    {
        return obj == null;
    }
}
]]
if rigi:IsNull() then
	print("123");
	go:AddComponent(typeof(Rigidbody));
end

CSharpCallLua和LuaCallCsharp特性

使用时机:

CSharpCallLua:当我们使用C#的接口和委托接收lua代码时(CSharp为主场),我们需要为C#类提供这个特性。另外一种情况是当我们需要使用Lua调用C#的系统类型的时候,同样需要使用到该特性。

LuaCallCsharp:当我们使用Lua调用C#时(Lua为主场),我们建议所有被lua调用的类都加上这个特性。

这两个特性保证了自定义C#类和lua代码之间的调用,但我们不能通过这两个特性实现系统类型和lua代码之间的调用。

Lua使用C#系统类型

上面对于两个特性的比较已经说明了其使用时机,但是我们注意到,当我们想用lua调用Csharp中系统类型时。我们没有办法为系统类型添加特性,这时我们就需要用到xlua的特性列表。

print("*********Lua调用C#系统类型相关知识点*********");
--这里我们用unity中的Slider为例
local GameObject = CS.UnityEngine.GameObject;
local Slider = CS.UnityEngine.UI.Slider;
local go = GameObject.Find("Slider");
print(go);
local sliderScript = go:GetComponent(typeof(Slider));
print(sliderScript);
--[[
这里直接添加监听事件,会直接报错提示需要为UnityAction添加[CSharpCallLua]的特性。
但我们知道我们是不可以改变系统类型的,所以我们需要用到xlua提供的特性列表:
	[CSharpCallLua]
    public static List<Type> CSharpCallLuaList = new List<Type>()
    {
        typeof(UnityAction<float>),
    };
    [LuaCallCSharp]
    public static List<Type> LuaCallCSharpList = new List<Type>()
    {
        typeof(GameObject),
        typeof(Rigidbody),
    };
    这两个分别为[CSharpCallLua]特性列表和[LuaCallCSharp]特性列表。
    我们只需要在里面写我们想要添加特性的类型就可以了,之后重新生成代码,Xlua就会帮我们构建可以使用的中间代码。
    特性列表还有一大用途就是集中添加我们需要申明特性的类,这会使我们的代码更加专业和简洁。
    完成特性列表的书写后我们就可以通过lua来调用C#或者unity中的系统类型了。
    需要注意:
    特性列表和特性列表所在的类一定都为静态的。
]]
sliderScript.onValueChanged:AddListener(function(value)
	print(value);
end);

Lua使用C#协程

print("*********Lua调用C#协程相关知识点*********");
local GameObject = CS.UnityEngine.GameObject;
local WaitForSeconds = CS.UnityEngine.WaitForSeconds;
--Lua调用C#的协程需要注意几点:
--[[
	1. 建议将所有用到的和C#协程有关的类都预先保存
	2. Lua不能直接使用C#协程中的yield return
	3. 虽然我们使用lua调用C#的StartCoroutine来开启协程,但是协程函数的书写使用Lua语法书写
	4. lua协程不能直接传入StartCoroutine中,我们需要接用xlua为我们提供的util工具表来实现
	5. 由于调用的是C#的协程启动和停止相关的函数,所以除过协程本身的书写,其他也还是遵循C#的规则
]]
--加载xlua提供给我们的util工具表
util = require("xlua.util");
--首先我们先来创建一个游戏物体
local go = GameObject("Coroutine");
local mono = go:AddComponent(typeof(CS.CsharpCallLua.CallCoroutine));

--书写lua协程
func = function()
	local param = 1;
	while true do
		--使用lua的yield来暂停协程
		coroutine.yield(WaitForSeconds(1));
		print(param);
		param = param + 1;
		--如果param大于10,我们就停止协程
		if param > 10 then
			mono:StopCoroutine(cor);
		end
	end
end

--使用C#的StartCoroutine开启lua协程,这里使用util工具表辅助实现
cor = mono:StartCoroutine(util.cs_generator(func));

Lua使用C#泛型函数

print("*********Lua调用C#泛型相关知识点*********");
local callT = CS.CallT();
local chlid = CS.CallT.InnerChild();
local father = CS.CallT.InnerFather();

--lua仅支持有约束有参数且约束为类的泛型函数
callT:TestFunc1(child,father);
callT:TestFunc1(father,child);

--lua不支持没有参数的泛型函数
--callT:TestFunc2();
--lua不支持没有约束的泛型函数
--callT:TestFunc3(child);
--lua不支持非class的约束
--callT:TestFunc4();

--补充知识:xlua为我们提供了对应的转换方法,但是慎用
--[[
	此方法的有一定的限制:
	1. 如果我们的游戏项目是Mono打包则这种方式是完全支持的;但如果我们的项目是IL2CPP打包,则只有引用类型可以使用。
]]
--[[
	1. 使用get_generic_method方法得到通用函数
		xlua.get_generic_method(类, 函数名字符串);
	2. 使用通用函数设置泛型类型
	3. 调用设置了泛型类型的通用函数。

]]
local testFunc3 = xlua.get_generic_method(CS.CallT, "TestFunc3");
local testFunc3_R = testFunc3(CS.CallT.InnerFather);
--[[
	调用:
	1. 成员方法:第一个参数需要穿阿如调用函数的对象。
	2. 静态方法:不需要传调用对象。
]]
testFunc3_R(callT, father);
## C#Lua编程支持 xLua为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力,借助xLua,这些Lua代码可以方便的和C#相互调用。 ## xLua的突破 xLua在功能、性能、易用性都有不少突破,这几方面分别最具代表性的是: * 可以运行时把C#实现(方法,操作符,属性,事件等等)替换成lua实现; * 出色的GC优化,自定义struct,枚举在LuaC#间传递无C# gc alloc; * 编辑器下无需生成代码,开发更轻量; 更详细的特性、平台支持介绍请看[这](Assets/XLua/Doc/features.md)。 ## 安装 打开zip包,你会看到一个Assets目录,这目录就对应Unity工程的Assets目录,保持这目录结构放到你的Unity工程。 如果希望安装到其它目录,请看[FAQ](Assets/XLua/Doc/faq.md)相关介绍。 ## 文档 * [常见问题解答](Assets/XLua/Doc/faq.md):常见问题都总结在这,初使用大多数问题都可以在这找到答案。 * (必看)[XLua教程](Assets/XLua/Doc/XLua教程.md):教程,其配套代码[这](Assets/XLua/Tutorial/)。 * (必看)[XLua的配置](Assets/XLua/Doc/configure.md):介绍如何配置xLua。 * [热补丁操作指南](Assets/XLua/Doc/hotfix.md):介绍如何使用热补丁特性。 * [XLua增加删除第三方lua库](Assets/XLua/Doc/XLua增加删除第三方lua库.md):如何增删第三方lua扩展库。 * [XLua API](Assets/XLua/Doc/XLua_API.md):API文档。 * [生成引擎二次开发指南](Assets/XLua/Doc/custom_generate.md):介绍如何做生成引擎的二次开发。 ## 快速入门 一个完整的例子仅需3行代码: 安装好xLua,建一个MonoBehaviour拖到场景,在Start加入如下代码: ```csharp XLua.LuaEnv luaenv = new XLua.LuaEnv(); luaenv.DoString("CS.UnityEngine.Debug.Log('hello world')"); luaenv.Dispose(); ``` 1、DoString参数为string,可输入任意合法的Lua代码,本示例在lua调用C#的UnityEngine.Debug.Log打印了个日志。 2、一个LuaEnv实例对应Lua虚拟机,出于开销的考虑,建议全局唯一。 C#主动调用lua也很简单,比如要调用lua的系统函数,推荐方式是: * 声明 ```csharp [XLua.CSharpCallLua] public delegate double LuaMax(double a, double b); ``` * 绑定 ```csharp var max = luaenv.Global.GetInPath("math.max"); ``` * 调用 ```csharp Debug.Log("max:" + max(32, 12)); ``` 建议绑定一次,重复使用。生成了代码的话,调用max是不产生gc alloc的。 ## 热补丁 * 侵入性小,老项目原有代码不做任何调整就可使用。 * 运行时影响小,不打补丁基本和原有程序一样。 * 出问题了可以用Lua来打补丁,这时才会走到lua代码逻辑; [这](Assets/XLua/Doc/hotfix.md)是使用指南。 ## lua5.3 vs luajit xLua有两个版本,分别集成了lua5.3和luajit,一个项目只能选择其一。这两个版本C#代码是一样的,不同的是Plugins部分。 lua5.3的特性更丰富些,比如支持原生64位整数,支持苹果bitcode,支持utf8等。出现问题因为是纯c代码,也好定位。比起luajit,lua对安装包的影响也更小。 而luajit胜在性能,如果其jit不出问题的话,可以比lua高一个数量级。目前luajit作者不打算维护luajit,在找人接替其维护,后续发展不太明朗。 项目可以根据自己情况判断哪个更适合。因为目前lua53版本使用较多,所以xLua工程Plugins目录下默认配套是lua53版本。 ## 更多示例 * [01_Helloworld](Assets/XLua/Examples/01_Helloworld/):
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值