上一篇文章
Unity XLua学习笔记(二):C#调用Lua
创建一个Lua脚本
Lua脚本
LuaCallCSharp.lua脚本内容:
local obj1=CS.UnityEngine.GameObject()
local obj2=CS.UnityEngine.GameObject("测试游戏对象")
Main.lua脚本内容:
require("LuaCallCSharp")
在Unity中用C#脚本调用(执行Main.lua脚本)
XLuaMgr.GetInstance().Init();
XLuaMgr.GetInstance().DoLuaFile("Main");
运行后,场景中创建了相应的GameObject
在Lua中调用C#的类和方法还有变量,需要打全名
所以为了方便可以声明变量,将全名赋值给这个变量方便调用
GameObject=CS.UnityEngine.GameObject
Debug=CS.UnityEngine.Debug
Vector3=CS.UnityEngine.Vector3
例如
--类中的静态对象 可以直接使用.来调用
obj2.transform.position=Vector3(100,100,100)
local obj3= GameObject.Find("测试游戏对象")
Debug.Log(obj3.transform.position)
运行后打印
Lua调用C#中的类和方法
不管是调用自定义类还是Unity中自带的类,在调用成员方法时,都要用“:”,而不是“.”
调用静态方法用“.”
C#自定义类
public class Test
{
public void Speak(string str)
{
Debug.Log("Test:"+str);
}
}
namespace TestNameSpace
{
public class Test2
{
public void Speak(string str)
{
Debug.Log("Test2:" + str);
}
}
}
Lua中调用C#及Unity中的类
--调用Unity自带方法
local obj4= GameObject.Find("测试游戏对象")
obj4.transform:Translate(Vector3.right)
Debug.Log(obj4.transform.position)
--调用C#自定义类,有命名空间需要打上命名空间
local t1=CS.Test()
t1:Speak("testSpeak")
local t2=CS.TestNameSpace.Test2()
t2:Speak("test2Speak")
运行Unity打印
Lua调用C#中的自定义枚举和Unity中的枚举
C#代码
/// <summary>
/// 自定义测试枚举
/// </summary>
public enum E_MyEnum
{
Idle,
Move,
Atk
}
Lua代码
--枚举调用
--调用Unity当中的枚举
--枚举的调用规则和 类的调用规则是一样的
--CS.命名空间.枚举名.枚举成员
--也支持取别名
PrimitiveType=CS.UnityEngine.PrimitiveType
GameObject=CS.UnityEngine.GameObject
local obj=GameObject.CreatePrimitive(PrimitiveType.Cube)
E_MyEnum=CS.E_MyEnum
local e = E_MyEnum.Idle
print(e)
--枚举转换相关
--数值转枚举
local a=E_MyEnum.__CastFrom(1)
print(a)
--字符串转枚举
local b=E_MyEnum.__CastFrom("Atk")
print(b)
Unity打印
Lua调用C#数组相关
C#中的测试类
public class ArrayTest
{
public int[] array=new int[5]{1,2,3,4,5};
public List<int> list=new List<int>();
public Dictionary<int,string> dic=new Dictionary<int, string>();
public void Test()
{
}
}
lua调用
local arrayTestObj=CS.ArrayTest()
--访问C#中的数组长度
print("array数组长度:"..arrayTestObj.array.Length)
--访问元素
print("访问元素:"..arrayTestObj.array[0])
for i=0,arrayTestObj.array.Length-1 do
print("array数组第"..i.."个元素的值:"..arrayTestObj.array[i])
end
--在lua中创建一个C#狐族
local lua_CSharpList=CS.System.Array.CreateInstance(typeof(CS.System.Int32),10)
print("lua创建C#数组:"..lua_CSharpList.Length)
--这里的索引遵循C#
print("lua创建C#数组,第1个元素:"..lua_CSharpList[0])
print("lua创建C#数组,第2个元素:"..lua_CSharpList[1])
运行后打印信息
Lua调用扩展方法
C#扩展测试类
//想要在Lua中使用拓展方法 一定要在工具类前面加上特性
//建议 Lua中要使用的类 都加上该性能 可以提升性能
//如果不加该特性 除了拓展方法对应的类 其他的类的使用都不会报错
//但是Lua是通过反射的机制去调用的C#类 效率较低
[LuaCallCSharp]
public static class Tools
{
//Lesson4的拓展方法
public static void Move(this ExtensionTestClass obj)
{
Debug.Log(obj.name + "移动");
}
}
/// <summary>
/// 扩展方法测试类
/// </summary>
public class ExtensionTestClass
{
public string name = "ABC";
public void Speak(string str)
{
Debug.Log(str);
}
public static void Eat()
{
Debug.Log("吃东西");
}
}
Lua脚本调用
--使用静态方法
--CS.命名空间.类名.静态方法名()
CS.ExtensionTestClass.Eat()
--成员方法 实例化出来用
local obj=CS.ExtensionTestClass()
--成员方法 一定用冒号
obj:Speak("ABC:39023sidjflkq0e")
--使用拓展方法 和使用成员方法一致
--要调用 C#某个类的拓展方法 那一定要在拓展方法的静态类前面加上LuaCallCSharp特性
obj:Move()
运行Unity打印信息
Lua调用C#含有out和ref的方法
C#测试脚本
public class RefOutFunTestClass
{
public int RefFun(int a,ref int b,ref int c,int d)
{
b = a + d;
c = a - d;
return 100;
}
public int OutFun(int a, out int b, out int c, int d)
{
b = a;
c = d;
return 200;
}
public int RefOutFun(int a,out int b,ref int c)
{
b = a * 10;
c = a * 20;
return 300;
}
}
Lua脚本调用
RefOutFunTestClass=CS.RefOutFunTestClass
local obj=RefOutFunTestClass()
print("********************Lua调用C# ref********************")
--ref参数 会以多返回值的形式返回给lua
--如果函数存在返回值 那么第一个值 就是该返回值
--之后的返回值 就是ref的结果 从左到右一一对应
--ref参数 需要传入一个默认值(0,0) 占位置(a接收的是函数默认的返回值)
--a相当于函数返回值
--b 第一个ref
--c 第二个ref
local a,b,c = obj:RefFun(1,0,0,1)
print(a)
print(b)
print(c)
print("********************Lua调用C# out********************")
--out参数 会以多返回值的形式返回给lua
--如果函数存在返回值 那么第一个值就是该返回值
--之后的返回值 就是out的结果 从左到右一一队形
--out参数 不需要传占位置的值
local a,b,c=obj:OutFun(20,30)
print(a)
print(b)
print(c)
print("********************Lua调用C# ref out********************")
--混合使用时 综合上面的规则
--ref需站位 out不用传
--第一个是函数的返回值 之后 从左到右依次对应ref或者out
local a,b,c=obj:RefOutFun(20,1)
print(a)--300
print(b)--200
print(c)--400
运行Unity打印
Lua调用C#的重载函数
C#测试类
public class OverloadedFuncTest
{
public int Calc()
{
return 100;
}
public int Calc(int a,int b)
{
return a + b;
}
public int Calc(int a)
{
return a;
}
public float Calc(float a)
{
return a;
}
}
Lua脚本调用
local obj =CS.OverloadedFuncTest()
--虽然Lua自己不支持写重载函数
--但是Lua支持调用C#中的重载函数
print(obj:Calc())
print(obj:Calc(15,1))
--lua虽然支持调用C#重载函数
--但是因为Lua中的数值类型 只有Number
--对C#中多精度的重载函数支持不好
--在使用时 可能出现意想不到的问题
print(obj:Calc(10))
--这里float的输出结果为0
print(obj:Calc(10.2))
--解决重载函数含糊的问题
--XLua提供了解决方案 反射机制
--这种方法只做了解 尽量别用
--Type是反射的关键类
--得到指定函数的相关信息
local m1= typeof(CS.OverloadedFuncTest):GetMethod("Calc",{typeof(CS.System.Int32)})
local m2= typeof(CS.OverloadedFuncTest):GetMethod("Calc",{typeof(CS.System.Single)})
--通过xLua提供的一个方法 把它转成lua函数来使用
--一般我们转依次 然后重复使用
local f1=xlua.tofunction(m1)
local f2=xlua.tofunction(m2)
--成员方法 第一个参数传对象
--静态方法 不用传对象
print(f1(obj,10))
print(f2(obj,10.2))
需要注意的是,Lua对应重载函数支持的并不好,有可能会出现问题
Lua调用C#委托
C#测试代码
public class CallDelTesTClass
{
public UnityAction del;
public event UnityAction eventAction;
public void DoEvent()
{
if (eventAction != null)
{
eventAction();
}
}
public void ClearEvent()
{
eventAction = null;
}
}
Lua代码调用
print("********************Lua调用C# 委托事件********************")
local obj=CS.CallDelTesTClass()
--委托是用来装函数的
--使用C#中的委托 就是用来装Lua函数的
local fun=function()
print("Lua函数Fun")
end
--Lua中没有复合运算符 不能+=
--如果第一次往委托中加函数 因为是nil
--所以第一次 要先等=
obj.del=fun
--obj.del=obj.del+fun
obj.del=obj.del+fun
--不建议这么写 不好减去 最好哈市先声明函数再加
obj.del=obj.del+function()
print("临时声明的函数")
end
obj.del()
print("********************开始减函数********************")
obj.del=obj.del-fun
obj.del=obj.del-fun
--委托执行
obj.del()
print("********************清空函数********************")
--清空所有存储的函数
obj.del=nil
--清空过后得先等
obj.del=fun
--调用
obj.del()
print("********************Lua调用C# 事件相关********************")
local fun2=function()
print("事件加的函数")
end
print("*************事件加函数**************")
--事件加减函数 和委托非常不一样
--lua中使用C#事件 加函数
--有点类似使用成员方法 冒号事件名
obj:eventAction("+",fun2)
--最好最好不要匿名这样写,假如要去掉该函数时,就无法知道要去掉那个函数了
obj:eventAction("+",function()
print("事件加的匿名函数")
end)
obj:DoEvent()
print("*************事件减函数**************")
obj:eventAction("-",fun2)
obj:DoEvent()
print("*************事件清除**************")
--清事件,不能直接设空
--obj.eventAction=nil
--obj.DoEvent()
obj:ClearEvent()
obj:DoEvent()
Unity运行打印信息
Lua遍历C#二维数组
C#测试代码
public class Array2DTraversalTestClass
{
public int[,] array = new int[2, 3] { { 1, 2, 3 }, { 4, 5, 6 } };
}
lua脚本调用
local obj=CS.Array2DTraversalTestClass()
--获取长度
print("行"..obj.array:GetLength(0))
print("列"..obj.array:GetLength(1))
--获取元素
--不能通过[0,0]或者[0][0]访问元素,会报错
print(obj.array:GetValue(0,0))
print(obj.array:GetValue(1,0))
print("**************************************")
for i=0,obj.array:GetLength(0)-1 do
for j=0,obj.array:GetLength(1)-1 do
print(obj.array:GetValue(i,j))
end
end
unity运行打印信息
Lua的nil和C#null
lua的nil和C#null无法进行比较,lua中提供了Equals()来进行比较,或者在C#中编写一个用于比较的方法
local obj=GameObject("测试加脚本")
--得到身上的刚体组件 如果没有 就加 有就不管
Rigidbody=CS.UnityEngine.Rigidbody
local rig =obj:GetComponent(typeof(Rigidbody))
print(rig)
--判断空
--nil和null 没法进行==比较
--第一种方法
--但是若rig本来就是空的话,会报错
--if rig:Equals(nil) then
--第二种方法 写一个判空的全局方法
--判空全局函数
--if IsNull(rig) then
if IsNull(rig) then
print("123")
rig=obj:AddComponent(typeof(Rigidbody))
end
print(rig)
全局方法
function IsNull(obj)
if obj==nil or obj:Equals(nil) then
return true
end
return false
end
运行后
lua给Unity中的组件绑定事件
在场景中创建一个Slider
Lua代码
UI=CS.UnityEngine.UI
local slider=GameObject.Find("Slider")
print(slider)
local sliderScript = slider:GetComponent(typeof(UI.Slider))
print(sliderScript)
sliderScript.onValueChanged:AddListener(function (f)
print(f)
end)
需要注意的是,Lua并不支持参数为float的委托
需要加一段C#代码
public static class SliderEventTestClass
{
[CSharpCallLua]
public static List<Type> csharpCallLuaList = new List<Type>()
{
typeof(UnityAction<float>)
};
}
再执行XLua------>Generate Code
运行后,拖动滑动条,打印了Slider的数值
lua使用Unity中的协程
准备一个继承MonoBehaviour的类
public class LuaCallCSharp : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
Lua调用
--xlua提供的一个工具表
--一定是要通过require调用之后才能用
local u=require("xlua.util")
--C#中协程启动都是通过继承了Mono的l类 通过里面的启动函数StartCoroutine
GameObject=CS.UnityEngine.GameObject
WaitForSeconds=CS.UnityEngine.WaitForSeconds
--在场景中新建一个空物体 然后挂一个脚本上去 脚本继承Mono使用它来开启协程
local obj=GameObject("Coroutine")
local mono=obj:AddComponent(typeof(CS.LuaCallCSharp))
fun=function()
local a=1
while true do
--lua中 不能直接使用 C#中的 yield return
--就使用lua中的协程返回
coroutine.yield(WaitForSeconds(1))
print(a)
a=a+1
if a>10 then
mono:StopCoroutine(b)
end
end
end
--我们不能直接将 lua函数传入到开启协程中!!!!
--如果要把lua函数当做协程函数传入
--必须 先调用 xlua.util中的cs_generator(lua函数)
b= mono:StartCoroutine(u.cs_generator(fun))
Lua调用泛型方法
C#测试类
public class T_TestClass
{
public interface ITest
{
}
public class TestFather
{
}
public class TestChild:TestFather,ITest
{
}
public void TestFun1<T>(T a, T b) where T:TestFather
{
Debug.Log("有参数有约束的泛型方法");
}
public void TestFun2<T>(T a)
{
Debug.Log("有参数,没有约束");
}
public void TesFun3<T>() where T:TestFather
{
Debug.Log("有约束,但是没有参数的泛型函数");
}
public void TestFun<T>(T a) where T : ITest
{
Debug.Log("有约束,有参数,但是约束不是类是接口");
}
}
Lua脚本调用
local obj=CS.T_TestClass()
local child=CS.T_TestClass.TestChild()
local father=CS.T_TestClass.TestFather()
--支持有约束有参数的泛型函数
obj:TestFun1(child,father)
obj:TestFun1(father,child)
--lua中不支持 没有约束的泛型函数
--obj:TestFun2(child)
--lua中不支持 有约束 但是没有参数的泛型函数
--obj:TestFun3()
--lua中不支持 非class的约束
--obj:TestFun4(child)
--PlayerSettings中设置打包方式
--Mono打包 这种方式支持使用
--IL2cpp打包 如果泛型参数是引用类型才可以使用
--IL2cpp打包 如果泛型参数是值类型,除非C#那边已经调用过了 同类型的泛型参数 lua中才能够被使用
--补充知识 让上面 不支持使用的泛型函数 变得能用
--有一定的使用限制,性能较低,不推荐使用
--得到通用函数
--设置泛型类型再使用
--xlua.get_generic_method(类,"函数名")
local testFun2= xlua.get_generic_method(CS.T_TestClass,"TestFun2")
local testFun2_R= testFun2(CS.System.Int32)
--调用
--成员方法 第一个参数 传调用函数的对象
--静态方法 不用传
testFun2_R(obj,1)
Unity运行打印