Unity--XLua调用C#

打印 上一主题 下一主题

主题 530|帖子 530|积分 1605

Unity–XLua调用C#

由于Unity/C# 和lua是两种语言,两种语言的特性不一样,因此,如果要互相调用的话,必要第三方作桥梁. 因此,为了在Unity中/C#中使用lua的特性,必要在Unity中安装插件,Xlua/toLua作为桥梁. 通过该桥梁可以使得lua中的数据传递Unity中,Unity中的数据传递到lua中,或者进行类/成员函数/成员变量/静态函数的调用. 以下是在关系图,下图是Lua调用Unity/C#的一个大抵过程, 本文重要介绍xlua在Unity中的使用以及lua如何调用C#

由于是lua调用Unity/C#,因此,许多逻辑都是在lua中书写的,在C#中准备部分数据,以便lua调用
1. lua调用C#中的类

说明: 由于C#存在类地点定名空间,因此,差别定名空间下的类大概名称是相同的,因此在使用中的是必要注意.此外,为了简写定名空间,我们可以给C#中的类名区别名. 即 CS.Namespace.ClassName 具体可以为:CS.UnityEngine.GameObject
比方:
  1. GameObject = CS.UnityEngine.GameObject
  2. Transform = CS.UnityEngine.GameObject.Transfrom
复制代码
由于在C#中,要使用类,我们一般的方式是使用new的方式实例化对象,在lua中没有new的方式,因此在lua中使用()就相称于无参构造
比方:
  1. local obj1 = CS.UnityEngine.GameObject()
  2. local obje2 = CS.UnityEngine.GameObject("Go")
复制代码
如前说的,我们可以取别名 因此,上面的lua代码可以写为
  1. -- 取别名
  2. GameObject = CS.UnityEngine.GameObject
  3. -- 使用别名创建对象
  4. local obj = GameObject()
复制代码
使用类中的静态变量和静态方法
我们可以通过类名.的方法调用
比方:
  1. -- 取别名
  2. GameObject = CS.UnityEngine.GameObject
  3. -- 实例化对象
  4. local obj = GameObject()
  5. -- 使用成员属性并打印出来
  6. print(obj.transform.position)
  7. -- 使用点的方式调用静态珊瑚
  8. local go = GameObject.Find("Go");
  9. -- 取别名
  10. Debug = CS.UnityEngine.Debug
  11. -- 调用Deug中的Log函数
  12. Debug.Log(go.transform.position)
复制代码
调用实例化对象中的成员属性和成员方法
调用成员属性的时候直接使用点的方式即可,掉用对象中的成员方法必要使用冒号:的方式进行调用. 同理,我们自己写的类也是如此.
比方:
  1. -- 取别名
  2. GameObject = CS.UnityEngine.GameObject
  3. Vector3 = CS.UnityEngine.Vector3
  4. Debug = CS.UnityEngine.Debug
  5. --实例化对象
  6. local obj = GameObject()
  7. -- 调用成员函数
  8. obj.transform:Translate(Vector3.right)
复制代码
Unity中的继续了Mono的类
由于Unity的Mono本身不能通过new的方式进行实例化,因此,我们在必要通过名称是实例化GameObject的时候必要填写参数,在C#中使用的泛型的方式,但是在lua中无法使用泛型,因此,必要调用c#中的重载的方法.
比方:
  1. GameObject = CS.UnityEngine.GameObject
  2. local obj = GameObject("更名的GameObject")
复制代码

2.lua调用C#中的枚举

lua调用C#中的枚举和调用类/类的实例中的属性是一样的. 调用自定义枚举也是如此.
比方:
  1. -- 取别名
  2. PrimitiveType = CS.UnityEngine.PrimitiveType
  3. GameObject = CS.UnityEngine.GameObject
  4. Debug = CS.UnityEngine.Debug
  5. -- 使用Unity中的创建内置的游戏物体 -- PrimitiveType.Cube在Unity中就是枚举
  6. local obj1 = GameObject.CreatePrimitive(PrimitiveType.Cube)
  7. -- 自定义枚举
  8. AnimationEnum = CS.E_AnimationEnum
  9. local obj2 = AnimationEnum.Idle
  10. print(obj2)
复制代码
必要注意的是,我们在C#中使用枚举,是直接使用枚举对象或者枚举.枚举值进行调用,在lua中,必要转换才能被使用
转换方法如下
  1. .__CastFrom(数字/字符串)
复制代码
根据函数名称,我们知道,传入参数是一个数字,或者字符串,得到的是是一个枚举
比方:
C#中的自定义枚举
  1. public Enum E_AnimationEnum
  2. {
  3.     Idle,
  4.     Walk,
  5.     Run,
  6.     Jump
  7. }
复制代码
lua代码:
  1. AnimationEnum = CS.E_AnimationEnum
  2. local obj = AnimationEnum.Idle
  3. print(obj)
  4. -- obj中是userdata,保存的是C#/C++中的原始数据
  5. -- 数字枚举转换
  6. local a = AnimationEnum.__CastFrom(1)
  7. print(a)
  8. -- 打印出来是:Idle : 0
  9. -- 字符串转枚举
  10. local b = AnimationEnum.__CastFrom("Jump")
  11. print(b)
  12. -- 打印出来是:Walk : 1
复制代码

3.lua调用C#的数组/List/Dictionary

1.ua调用数组

在C#中的代码如下:
ArrayTest.cs
  1. public Class AttayTest
  2. {
  3.     public int[] array = new int[9] { 1,2,3,4,5,6,7,8,9 };
  4. }
复制代码
遍历数组
在C#中使用.Length进行获得数组的长度,在lua中调用的时候也可以,因为数组基础功能了Array这个类,该类中有Length的属性,因此,在 lua中可以直接调用
  1. -- 取别名
  2. Debug = CS.UnityEngine.Debug
  3. -- 实例化对象
  4. local obj = CS.AttayTest()
  5. -- 遍历c#中的数组
  6. for i = 0,obj.array.Length - 1 do
  7.         print("index: " .. obj.array[i])
  8. end
复制代码
我们已经会了遍历数组中的的元素,如安在lua中创建C#数组呢
根据上面提到的Array类的方法中,我们发现有CreateInstance函数来实现创建各种类型的数组. 该函数原型如下,且有6个重载,特家具
  1. public static Array CreateInstance(Type elementType,int length);
  2. public static Array CreateInstance(Type elementType,int length1,int length2);
  3. public static Array CreateInstance(Type elementType,int length1,int length2,int length3);
  4. public static Array CreateInstance(Type elementType,params int[] lengths);
  5. public static Array CreateInstance(Type elementType,int[] lengths,int[] lowerBounds);
  6. public static Array CreateInstance(Type elementType,params long[] lengths);
复制代码
这六个函数都是Array类的静态方法,用于创建指定类型和维度的数组实例。它们在System定名空间中定义,可以在C#代码中直接调用。下面是对每个函数的详细解释:

  • CreateInstance(Type elementType,int length):

    • 这个方法创建一个一维数组,其元素类型由elementType指定,长度由length指定。

  • CreateInstance(Type elementType,int length1,int length2):

    • 这个方法创建一个二维数组,元素类型由elementType指定,第一维的长度由length1指定,第二维的长度由length2指定。

  • CreateInstance(Type elementType,int length1,int length2,int length3):

    • 这个方法创建一个三维数组,元素类型由elementType指定,第一、二、三维的长度分别由length1、length2、length3指定。

  • CreateInstance(Type elementType,params int[] lengths):

    • 这个方法是最通用的创建数组的方法,可以创建任意维度的数组。elementType指定元素类型,lengths是一个参数数组,每个元素对应数组的一个维度长度。

  • CreateInstance(Type elementType,int[] lengths,int[] lowerBounds):

    • 这个方法与上一个方法雷同,但它允许指定每个维度的下界(即数组的起始索引)。lengths指定每个维度的长度,lowerBounds指定每个维度的起始索引。

  • CreateInstance(Type elementType,params long[] lengths):

    • 这个方法与CreateInstance(Type elementType,params int[] lengths)雷同,但它担当long类型的参数,允许创建长度非常大的数组。

根据CreateInstance的第一个参数,我相识到必要传入一个泛型,因此 我们在lua中创建数组的时候,必要指定类型
由于xlua对泛型的支持不算友爱,因此,必要使用typeof()来指定类型
比方:
  1. -- 给系统中Int32的类型取别名
  2. Int = CS.System.Int32
  3. -- 使用重载函数创建一维数组
  4. local obj = CS.System.Array.CreateInstance(typeof(Int),8)
  5. -- 遍历以为数组
  6. for i = 0,obj.Length - 1 do
  7.         obj[i] = i * 2
  8.         print("lua创建c#中的数组,index = " .. i .. "value = ".. obj[i])
  9. end
复制代码
2.lua调用List

在C#的代码TestList.cs中有一个空的list
  1. public class TestList
  2. {
  3.     public List<int> list = new List<int>();
  4. }
复制代码
在lua中调用list的成员函数
  1. -- 实例化对象
  2. local obj = CS.TestList()
  3. -- 添加数据
  4. obj.list: Add(1)
  5. obj.list: Add(10)
  6. obj.list: Add(100)
  7. -- 遍历list
  8. for i = 0,obj.list.Count - 1 do
  9.         print("list: " .. obj.list[i])
  10. end
复制代码
在lua中创建list对象
版本1->旧版本
使用CS.System.Collections.Generic的方式构建lList,但还不是List类的实例
比方: 构建了一个List的类,list中的类型System.String,即体系中的String类型
  1. CS.System.Collections.Generic["List`1[System.String]"]
复制代码
使用该类构建list对象
  1. -- 使用()构建list对象CS.System.Collections.Generic["List`1[System.String]"]
  2. ()-- 或者分开使用List_String = CS.System.Collections.Generic["List`1[System.String]"]
  3. -- 实例化对象local stringList = List_String()
复制代码
版本2->新版本
省去了"List1[System.String]"这一串,使用CS.System.String`代替
  1. local List_String = CS.System.Collections.Generic.List(CS.System.String)
  2. -- 实例化对象
  3. local list = List_String()
  4. -- 添加数据
  5. list3: Add("Pi")
  6. list3: Add("Banana")
  7. list3: Add("chese")
  8. -- 打印数据
  9. print(list[1])
  10. print(list[2])
复制代码
3.lua调用Dictionary

在C#的代码TestDictionary.cs中有如下代码. 其中包罗了一个key为string类型的键,int类型的Value
  1. public class TestDictionary
  2. {
  3.     // Dictionary
  4.     public Dictionary<string,int> dic = new Dictionary<string,int>();
  5. }
复制代码
调用c#中的字典
  1. -- 实例化对象
  2. local obj = CS.TestDictionary()
  3. -- 给c#中的字典添加数据
  4. obj.dic: Add("One",1)
  5. obj.dic: Add("two",2)
  6. obj.dic: Add("three",3)
  7. obj.dic: Add("four",4)
复制代码
Lua中遍历C#中的字典. 在C#中遍历的时候使用的是foreach通过迭代器的方式遍历,在lua中没有foreach,因此只能使用for配送pais来遍历
  1. -- 实例化对象
  2. local obj = CS.TestDictionary()
  3. -- 给c#中的字典添加数据
  4. obj.dic: Add("One",1)
  5. obj.dic: Add("two",2)
  6. obj.dic: Add("three",3)
  7. obj.dic: Add("four",4)
  8. -- 遍历字典-- 遍历使用for循环,c#中使用的是foreachfor k,v in pairs(obj.dic) do        print(k,v)end
复制代码
在lua中创建Dictionary,使用CS.System.Collections.Generic.Dictionary(key类型,value类型)来构建字典
比方,在lua创建一个string为key,Vector3为value的键Dictionary,lua代码如下
  1. -- 取别名
  2. Vector3 = CS.UnityEngine.Vector3
  3. String = CS.System.String
  4. -- 构建字典类
  5. local Dic_String_Vector3 = CS.System.Collections.Generic.Dictionary(String,Vector3)
  6. -- 实例化字典
  7. local dic2 = Dic_String_Vector3()
  8. -- 添加数据
  9. dic2: Add("Zero",Vector3.Zero)
  10. dic2: Add("Forward",Vector3.rorward)
  11. dic2: Add("Right",Vector3.right)
  12. -- 遍历字典
  13. for k,v in pairs(dic2) do
  14.         print(k,v)
  15. end
复制代码
[注意]: 在C#中可以通过[key]的方式来获取值,比方:dic["Zero"]获取值,但是在lua中无法使用该方法,必要使用固定写法get_Item(键值)
比方:
  1. print(dic2:get_Item("Right"))
复制代码
同时,还有别的一种获取值的方式TryGetValue(键名)
  1. print(dic2: TryGetValue("Forward"))
复制代码
使用TryGetValue的方式获得值,先返回的是一个bool值,表现是否得到了key对应的值,如果能得到Value,然后返回对应的值
修改自定中的值,通过set_Item的方式修改值
比方:
  1. -- 使用功能set_Item来设置值
  2. dic: set_Item("Right",Vector3.left)
  3. print(dic:get_Item("Right"))
复制代码

4.lua调用扩展方法

在C#中,在不改变源代码的情况下给我们自己的类添加其他功能,我们是通过c#的特性添加扩展方法,因此,在lua中也是可以可以调用c#中的扩展方法.
在C#代码中有如下代码:
  1. [XLua.LuaCallCSharp]
  2. public static class Tools
  3. {
  4.     // 拓展方法 必须是静态的,且有this指针
  5.     // 这是一个扩展方法,它为 TestExtend 类型添加了一个名为 ToUpperFirstLetter 的新方法
  6.     public static string ToUpperFirstLetter(this TestExtend obj)
  7.     {
  8.         // 如果字符串不为空,则转换首字母为大写
  9.         if (!string.IsNullOrEmpty(obj.s1))
  10.         {
  11.             return char.ToUpper(obj.s1[0]) + obj.s1.Substring(1);
  12.         }
  13.         return obj.s1;
  14.     }
  15. }
  16. public class TestExtend
  17. {
  18.     public string s1 = "i am writing this letter to inform you of my recent situation";
  19.     public string s2 = "we've been experiencing a hurricane here recently,so we've been very busy lately";
  20.     // 静态成员方法
  21.     public static void Greating()
  22.     {
  23.         Debug.Log("Daily Greating");
  24.     }
  25.    
  26.     public void SendLetter(string userName,string emailAddress,string subject)
  27.     {
  28.         Debug.Log("sendign the letter with the subject:" + subject + "to user: " + userName + "(" + emailAddress + ")");
  29.     }
  30. }
复制代码
在lua中的代码如下:
  1. TestExtend = CS.TestExtend
  2. -- 先调用静态方法
  3. TestExtend.Greating()
  4. -- 实例化对象
  5. local obj = TestExtend()
  6. -- 调用成员方法
  7. obj:SendLetter("Caron4","xxx@gmail.com","Daily Greetings")
  8. -- 调用扩展方法
  9. -- 需要再添加xLua特性
  10. -- [XLua.LuaCallCSharp]
  11. -- 建议添加上,可以提升速度,因为lua调用c#是通过反射的方式调用的
  12. local str = obj:ToUpperFirstLetter();
  13. print(str)
复制代码

5.lua调用C#中含有ref out修饰参数的函数

在C#中,ref 和 out 是两个用于参数传递的关键字,它们允许方法通过引用传递参数,而不是通过值传递。这种传递方式允许方法修改传入参数的值,并在方法外部保持这些更改. ref和out的区别,在于ref必要初始化,out不必要初始化。
在lua中如调用呢? 以下是C#中的代码。
  1. public class TestREfOut
  2. {
  3.     // 普通函数
  4.     public void NormalFun(int a,int b,int c)
  5.     {
  6.         Debug.Log($"a = {a},b = {b},c = {c}");
  7.     }
  8.     // 被ref修饰的参数-需要初始化
  9.     public int RefFun(int a,ref int b,ref int c,int d)
  10.     {
  11.         b = a + d;
  12.         c = a - d;
  13.         return 200;
  14.     }
  15.     // 被out修饰的参数-不需要初始化
  16.     public int OutFun(int a,out int b,out int c,int d)
  17.     {
  18.         b = a;
  19.         c = d;
  20.         return 300;
  21.     }
  22.     // 被ref 和 out同时修饰的函数
  23.     public int RefOutFun(int a,ref int b,out int c)
  24.     {
  25.         b = a * 10;
  26.         c = a * 20;
  27.         return 400;
  28.     }
  29. }
复制代码
以下是lua中的调用代码,在调用的术时候,必要注意C#中的函数是否有返回值,如果有返回值,返回的第一个参数是函数的返回值,而不是被ref或者out修饰的值
  1. -- 别名
  2. TestREfOut = CS.TestREfOut
  3. local obj = TestREfOut()
  4. -- 调用普通函数
  5. obj: NormalFun(100,200,300);
  6. -- 调用ref的函数
  7. local a,b,c = obj:RefFun(10,0,0,20)
  8. print("ref返回值" .. a)         -- 打印 200
  9. print("ref返回值" .. b)        -- 打印 30
  10. print("ref返回值" .. c)         -- 打印 10
  11. -- 调用out的函数
  12. -- C#中的函数签名 4个参数,1个返回值
  13. -- public int RefFun(int a,ref int b,ref int c,int d)
  14. -- public int OutFun(int a,out int b,out int c,int d)
  15. local a,b,c = obj:OutFun(10,20)
  16. print("out返回值" .. a)         -- 打印 300
  17. print("out返回值" .. b)        -- 打印 10
  18. print("out返回值" .. c)         -- 打印 20
  19. -- 混合调用ref和out的函数
  20. -- C#中的函数签名 3个参数,1个返回值
  21. -- public int RefOutFun(int a,out int b,ref int c)
  22. local a,b,c = obj:RefOutFun(10,0)
  23. print("refout返回值" .. a)                 -- 打印 400
  24. print("refout返回值" .. b)                -- 打印 100
  25. print("refout返回值" .. c)                -- 打印 200
复制代码

6.lua调用C#中的重载函数

在C#中我们可以使用名称相同,参数类型差别和返回值差别来进行实现重载函数. 在lua中中调用,必要特备注意.
以下是C#中的代码
  1. public class TestOverride
  2. {
  3.     // 普通函数
  4.     public int Calc()
  5.     {
  6.         return 100;
  7.     }
  8.     // 有返回值,有两个参数的重载
  9.     public int Calc(int num1,int num2)
  10.     {
  11.         return num1 + num2;
  12.     }
  13.     // 有返回值和一个参数的重载
  14.     public int Calc(int num)
  15.     {
  16.         return num;
  17.     }
  18.     // 有返回值和一个类型不同的参数的重载
  19.     public float Calc(float num)
  20.     {
  21.         return num;
  22.     }
  23. }
复制代码
在lua是支持重载函数的,但是由于lua中的中保存数字的类型只有一种,因此我们在调用饭后float/double等类型的重载函数必要注意. 以下是具体代码.
  1. -- 取别名
  2. TestOverride = CS.TestOverride
  3. -- 实例化对象
  4. local obj = TestOverride();
  5. -- 调用普通的成员函数
  6. local a = obj:Calc()
  7. print("a = ".. a)
  8. local b = obj:Calc(100,200)
  9. print("b = " ..b)
  10. -- a和b都被打印出来,说明是支持重载的
复制代码
调用精度较高的重载函数的时候,,会出现问题
比方:
  1. -- 取别名
  2. TestOverride = CS.TestOverride
  3. -- 实例化对象
  4. local obj = TestOverride();
  5. -- 调用重载函数
  6. local c = obj:Calc(300)
  7. print("c = " .. c)
  8. -- 调用精度较高的重载函数
  9. local d = obj:Calc(300.569)
  10. print("d = ".. d)
  11. -- 的打印的结果为0  是因为lua中的数字存储的类型只有number一种类型
  12. -- 因此不建议使用精度不同的重载函数
复制代码
为了将解决进度的问题,我们必要使用反射来获取类型
先通过GetMethod获取类中的方法,在GetMethod中实则参数的类型,将函数转换一下,然后在使用,注意转换为float的时候, 类型必要填写为CS.System.Single
比方:
  1. local m1 =typeof(CS.TestOverride):GetMethod("Calc",{typeof(CS.System.Int32)})
复制代码
  1. -- 取别名
  2. TestOverride = CS.TestOverride
  3. -- 实例化对象
  4. local obj = TestOverride();
  5. -- 转换重载函数
  6. local m1 = typeof(CS.TestOverride):GetMethod("Calc",{typeof(CS.System.Int32)})
  7. local m2 = typeof(CS.TestOverride):GetMethod("Calc",{typeof(CS.System.Single)})
  8. -- 一般只转换一次,然后重复使用
  9. local f1 = xlua.tofunction(m1)
  10. local f2 = xlua.tofunction(m2)
  11. -- 调用的时候,如果是成员方法的话,第一个参数传对象
  12. -- 如果是静态方法的话,就不用传对象
  13. print(f1(obj,10))
  14. print(f2(obj,10.2))
复制代码

7.Lua调用C#中的委托和事件

Unity中的游戏开发中,大型项目往往是lua项目,而游戏中的UI的交互往往是通过lua调用的. 因此UI的交互这种事情可以交给lua来做,比如,按钮点击,滑块调节.
以下是C#中的代码
  1. public class TestDelegate
  2. {
  3.     // 申明委托
  4.     public UnityAction del;
  5. }
复制代码
由于委托/事件具有添加,移除,实验和销毁等基本功能,添加和移除使用+=/-=来操作的,由于在lua总没有复合运算符,因此我们子啊进行添加事件或者移除事件的时候必要做一些修改. 调用委托和C#中的一样
以下是有关委托的干系的操作
lua中的代码如下
  1. -- 别名
  2. TestDelegate = CS.TestDelegate
  3. local obj = TestDelegate();
  4. -- lua中的函数,待添加到委托中
  5. local fun = function()
  6.         print("lua函数Fun")
  7. end
  8. -- 第一次使用/添加的时候需要等号=赋值
  9. obj.del = fun
  10. -- 使用 = 也算添加进去了一次函数
  11. print("lua使用+给C#的委托添加函数")
  12. obj.del = obj.del + fun
  13. obj.del = obj.del + fun
  14. -- 调用
  15. obj.del()
  16. -- 添加匿名函数
  17. obj.del = obj.del + function ()
  18.         print("lua给C#委托添加了匿名函数")
  19. end
  20. -- 执行委托
  21. obj.del()
  22. -- 移除委托中的函数
  23. print("lua端移除委托中的函数")
  24. obj.del = obj.del - fun
  25. obj.del = obj.del - fun
  26. obj.del = obj.del - fun
  27. -- 执行委托
  28. obj.del()
  29. -- 发现还是打印的的"lua给C#委托添加了匿名函数"
  30. -- 这说明,委托中无法移除匿名函数,但是可以使用nil来清空
  31. -- 清空委托
  32. print("lua将C#中的委托置为nil等于清空了委托,也包括匿名函数")
  33. obj.del = nil
复制代码
以下是有关事件的干系的操作
  1. public class TestEvent
  2. {
  3.     // 声明事件
  4.    public event UnityAction eventAction;
  5.    
  6.     public void DoEvent()
  7.     {
  8.         if(eventAction!= null)
  9.         {
  10.             // 执行事件
  11.             eventAction();
  12.         }
  13.     }
  14.         // 清空事件
  15.     public void ClearEvent()
  16.     {
  17.         eventAction = null;
  18.         Debug.Log("事件清空完毕");
  19.     }
  20. }
复制代码
lua调用C#中的事与调用委托非常不一样
以下是lua中调用C#中的事件的代码
  1. -- 取别名
  2. TestEvent = CS.TestEvent
  3. -- 实例化对象
  4. local obj = TestEvent();
  5. -- 待添加到事件中的函数
  6. local fun2 = function()
  7.         print("lua给C#事件添加函数")
  8. end
  9. -- lua中使用c#事件添加函数,有点类似与调用成员方法 冒号: 事件名("+",函数变量)
  10. obj:eventAction("+",fun2)
  11. -- 调用的时候不能直接调用,需要在c#写函数,lua调用函数的方式调用事件
  12. obj:DoEvent()
  13. -- 添加匿名函数
  14. obj:eventAction("+",function()
  15.         print("Lua给事件添加匿名函数")
  16. end)
  17. obj:DoEvent()
  18. print("移除事件事件中的函数")
  19. obj:eventAction("-",fun2)
  20. obj:eventAction("-",fun2)
  21. -- 移除后再执行事件
  22. obj:DoEvent()
  23. -- lua中不管是委托还是事件,不建议使用添加匿名函数
  24. -- lua中的事件,需要清空的话,需要在C#中写一个函数进行清空
  25. -- 然后在lua中调用,换句话说,lua不支持直接清空c#的事件
  26. print("lua调用C#中清空事件的函数")
  27. -- 清空事件
  28. obj:ClearEvent();
复制代码

8.lua调用C#中的二维数组

这是为了不充电前面的3的干系内容. 在C#中使用二维数组很简单,但是在lua中调用必要做一些改变
以下是C#中的代码
  1. public class TestTWoDimAttary
  2. {
  3.     // 一个二维数组
  4.     public int[,] array = new int[2,3] { { 1,2,3 },{ 4,5,6 } };
  5.     // 构造函数
  6.     public TestTWoDimAttary(){  }
  7. }
复制代码


  • lua中 必要通过GetLength(维度)来获取长度
  • lua中通过GetValue获取二维数组中的值
  • lua中通过SetValue设置/修改二维数组中的值
lua代码如下
  1. local obj = CS.TestTWoDimAttary()
  2. -- 获取二维数组的长度
  3. print("行的长度:" .. obj.array:GetLength(0))
  4. print("列的长度:" .. obj.array:GetLength(1))
  5. -- 获取值
  6. print("通过getValue的方式获取值:" .. obj.array:GetValue(0,0))
  7. print("通过getValue的方式获取值:" .. obj.array:GetValue(1,0))
  8. --lua遍历c#中的数组
  9. for i = 0,obj.array: GetLength(0) - 1 do
  10.         for j = 0,obj.array:GetLength(1) -1  do
  11.                 print(obj.array: GetValue(i,j))
  12.         end
  13. end
复制代码
9.lua中的nil和c#中的null比较

在lua和C#的对象映射中,C#中对空的判断是null而自lua中对空的判断是nil
两者都是空,但是并不代表可以使用.
比方: 检测GameObject上是否有个组件, 没有该组件,就添加组件,使用C#中的Equal()方法进行判断
  1. -- 别名
  2. GameObject = CS.UnityEngine.GameObject
  3. Rigidbody = CS.UnityEngine.Rigidbody
  4. BoxCollider = CS.UnityEngine.BoxCollider
  5. --
  6. local obj = GameObject("添加刚体的物体")
  7. -- 判断实例化的物体上是否有刚体组件
  8. local rig = obj: GetComponent(typeof(Rigidbody))
  9. -- 注意 需要使用Equals(nil)来判断,
  10. if rig:Equals(nil) then
  11.     rig = obj: AddComponent(typeof(Rigidbody))
  12. end
复制代码
由于lua中的某对象很有大概就是nil值,因此,我们可以在lua中进行优化,提供判断是否空的兼容性,即在开头添加以下函数进行判断. 当然这种工具列的函数,非常建议卸载一个文件中,然后使用require的方式引用该文件,在必要判断是否为空的地方调用即可
  1. --方法2: 使用写全局函数nil和Equals(nil)进行判断
  2. function IsNull(obj)
  3.         if obj == nil or obj:Equals(nil) then
  4.                 return true
  5.         end
  6.         return false
  7. end
复制代码
除了上面的方法,我们还可以在C#中进行做处理,代码如下,以下代码是给了Unity中的GameObject添加了拓展方法,并使用了[LuaCallCSharp]进行标记,表现lua调用C#中的代码. 必要再Unity编辑器中重新使用XLua生成代码
  1. // 扩展方法
  2. [LuaCallCSharp]
  3. public static class class9
  4. {
  5.     public static bool IsNull(this UnityEngine.Object obj)
  6.     {
  7.         return obj == null;   
  8.     }
  9. }
复制代码

10.lua调用C#中的体系函数

除了可以大概调用Unity中的体系函数,我们也必要调用C#中的体系函数,我们的一般做法是给代码添加特性[LuaCallCSharp]或者[CSharpCallLua],然而,.Net提供的代码我们无法添加特性,结合扩展的思想,我们可以将我们必要的内容放在一个自定义类中,对自定义类进行扩展即可.
以下代码定义了一个名为 TestSystem 的公共静态类,其中包罗两个静态列表:CSharpCallLuaList 和 LuaCallCSharpList。这两个列表用于在差别的编程情况(C# 和 Lua)之间进行类型交互。该静态类的设计允许我们在 C# 和 Lua 之间轻松地传递和操作数据,特殊是在使用 Lua 脚本进行游戏开发时。通过这种方式,开发者可以在 C# 中编写核心逻辑,同时利用 Lua 的灵活性和易于修改的特性来快速迭代游戏设计。
当然,你可以自定名称,放在指定的文件夹中
  1. public static class TestSystem
  2. {
  3.     [CSharpCallLua]
  4.     public static List<Type> CSharpCallLuaList = new List<Type>()
  5.     {
  6.         // 需要什么特性就添加什么特性
  7.         typeof(UnityAction<float>),
  8.         typeof(UnityAction<int>),
  9.         typeof(UnityAction<double>)
  10.     };
  11.     [LuaCallCSharp]
  12.     public static List<Type> LuaCallCSharpList = new List<Type>()
  13.     {
  14.         // 需要什么特性就添加什么特性
  15.         typeof(GameObject),
  16.         typeof(Transform),
  17.         typeof(Image)
  18.     };
  19. }
复制代码
在Unity的场景中创建一个Slider,
我们想通过lua得到当前Slider的值,在C#中是通过AddListener()的方式进行监听,由于是体系AddListener是体系提供的函数,无法添加特性,因此,我们可以使用上面的扩展类进行或者,通过在List中添加即可. 同时,也必要重新生成代码
  1. -- 别名
  2. GameObject = CS.UnityEngine.GameObject
  3. UI = CS.UnityEngine.UI
  4. -- 查找Slider的GameObject
  5. local slider = GameObject.Find("Slider")
  6. print(slider)
  7. -- 获取slider上的脚本slider
  8. local sliderScript = slider:GetComponent(typeof(UI.Slider))
  9. -- 绑定slider上的value的值变化情况
  10. sliderScript.onValueChanged:AddListener(function (sliderValue)
  11.         print("slider's Value: " .. sliderValue)
  12. end)
复制代码

11.lua调用C#中的协程

lua无法直接开启Unity中的协程,必要使用XLua中提供更多工具类,将lua的协程函数进行转换,然后再调用
工具类如下require("xlua.util")
具体流程如下:

  • 准备好了协程函数和工具与类
  • 使用工具转换协程函数,并使用lua的开启协程
  • 保存协程
  • 在必要的时候关闭协程
在lua中的代码如下:
  1. -- 引入工具
  2. util = require("xlua.util")
  3. -- 别名
  4. GameObject = CS.UnityEngine.GameObject
  5. WaitForSeconds = CS.UnityEngine.WaitForSeconds
  6. -- 创建新的物体
  7. local obj = GameObject("Coroutine")
  8. -- 添加脚本
  9. local mono = obj:AddComponent(typeof(CS.LuaCallCSharp))
  10. -- 希望被开启的协程函数
  11. fun = function ()
  12.         local a = 1
  13.         while true do
  14.                 -- 使用lua中的协程返回
  15.                 coroutine.yield(WaitForSeconds(1))
  16.         
  17.                 print("当前a的值:" .. a)
  18.                 a = a + 1
  19.                
  20.                 if a > 10 then
  21.             -- 关闭协程,和C#中的一样
  22.                         mono:StopCoroutine(cor)
  23.                 end
  24.         end
  25. end
  26. -- 准备好了协程函数和工具与类
  27. -- 使用工具转换协程函数,并使用lua的开启协程
  28. -- 保存协程
  29. cor = mono:StartCoroutine(util.cs_generator(fun))
复制代码

12.lua调用C#中的泛型

由于lua中没有泛型,只能通过typeof获得类型. 因此,在使用泛型的方向上,我们可以使用typeof来解决无法直接使用泛型的问题
代码解释:
除了常见的值类型的泛型,int,short,long,float,double,bool,string,enum等,还有我们之定义的类和接口,这些都可以作为泛型,因此,在C#中是可以制动推到的,且在匿名函数中可以大概字节表现出来. 但是在lua中缺有以下限制. lua中他默认支持有参数,有约束的泛型. 其他条件的就会被限制,究竟lua是轻量级的脚本语言
以下是C#中各种泛型的代码.
  1. public class TestT
  2. {
  3.     // 内部类和接口
  4.     public interface ITest { }
  5.     public class TestFather : ITest { }
  6.     public class TestChild : TestFather,ITest { }
  7.     // 普通函数
  8.     public void Test()
  9.     {
  10.         Debug.Log("普通测试函数");
  11.     }
  12.         // 有参数,有约束
  13.     public void TestFun1<T>(T a,T b) where T : TestFather
  14.     {
  15.         Debug.Log("有参数,有约束的泛型方法");
  16.     }
  17.         // 有参数,无约束
  18.     public void TestFun2<T>(T a)
  19.     {
  20.         Debug.Log("有参数,没有约束的泛型方法");
  21.     }
  22.         // 无参数,有约束
  23.     public void TestFun3<T>() where T: TestFather
  24.     {
  25.         Debug.Log("无参数,有约束的泛型方法");
  26.     }
  27.         // 有参数,有约束是接口
  28.     public void TestFun4<T>(T a) where T : ITest
  29.     {
  30.         Debug.Log("有参数,有约束,但是约束是接口");
  31.     }
  32. }
复制代码
以下是lua中的测试代码
  1. -- 实例化对象
  2. local obj = CS.TestT()
  3. -- 实例化对象
  4. local father = CS.TestT.TestFather()
  5. local child = CS.TestT.TestChild()
  6. -- 普通函数
  7. print("test")
  8. obj:Test()
  9. -- 有参数,有约束的泛型方法调用
  10. obj: TestFun1(child,father)
  11. obj: TestFun1(father,child)
  12. -- 可以被打印出来
  13. -- 有参数,没有约束的泛型方法
  14. -- lua中不支持有参数没有约束的泛型
  15. -- obj: TestFun2(child)
  16. -- 无参数,有约束的泛型方法
  17. -- obj: TestFun3()
  18. -- lua中不支持无参数有约束的泛型
  19. -- 有参数,有约束,约束是接口
  20. -- lua中不支持约束是接口的泛型
  21. -- obj: TestFun4()
复制代码
在上面的代码中,有很多代码的调用是被注释,这是由于lua无法直接调用这些代码
因此,为相识决上面的问题,我们必要通过xlua工具进行转换,然后才可以通过调用.
同时,如果使用xlua的工作转换了,打包的时候就只能通过Mono的方式进行打包,如果使用il2cpp的方式,泛型使只能是引用类型,如果值值类型的话,除非C#那边已经调用过了 同类型的泛型参数,lua中才能被使用
否者 lua中无法使用
以下代码是xLua工具转换了的泛型,使用 xlua.get_generic_method(类名,函数名)进行转换
  1. -- 转换填写参数: 类名,带有泛型的方法名
  2. local testFun2 = xlua.get_generic_method(CS.TestT,"TestFun2")
  3. -- 确定泛型的类型
  4. local testFun2_Reall = testFun2(CS.System.Int32)
  5. -- 转换完毕后调用
  6. -- 调用
  7. -- 如果是成员方法,第一个参数是 使用函数的对象
  8. -- 如果是静态方法,不用传使用函数的对象
  9. testFun2_Reall(obj,11)
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

大连全瓷种植牙齿制作中心

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表