目录
创建C#入口
首先我们新建一个C#脚本,取名为Main,因为Lua中没有办法直接访问C#,一定是先从C#中调用lua再将核心逻辑让lua来编写
cs
public class Main : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
}
}
Lua调用类
创建调用类的脚本,然后再Main的脚本中调用
Lua
require("Lesson1CallClass")
接着来编写调用类的lua脚本
Lua
print("----------------------Lua调用C#类相关知识---------------------")
--lua中使用C#的类
--固定格式:CS.命名空间.类名
--Unity的类 比如 GameObject Transform等------ CS.UnityEngine.类名
--CS.UnityEngine.GameObject
--通过C#中的类实例化一个对象
--lua中没有new,直接类名括括号就是实例化对象
--默认调用的,相当于就是无参构造
local obj1=CS.UnityEngine.GameObject()
local obj2=CS.UnityEngine.GameObject("xxx")
--为了方便使用与节约性能,定义全局变量存储C#的类
GameObject=CS.UnityEngine.GameObject
local obj3=GameObject("yyyyy")
--类中的静态对象可以直接使用.来调用
local obj4=GameObject.Find("xxx")
--得到其中的成员变量也是直接.对象
print(obj4.transform.position)
CS.UnityEngine.Debug.Log(obj4.transform.position)
--使用对象中的成员方法,要加:调用
Vector3=CS.UnityEngine.Vector3
obj4.transform:Translate(Vector3.right)
CS.UnityEngine.Debug.Log(obj4.transform.position)
运行出现为:1.场景中出现空物体 2.打印信息 3.位置坐标发生修改
刚才调用的都是U3d中自带的类,现在我们尝试C#自定义类
cs
public class Test
{
public void Speak(string str)
{
Debug.Log("Test" + str);
}
}
namespace tx
{
public class Test2
{
public void Speak(string str)
{
Debug.Log("Test2"+str);
}
}
}
Lua
--调用没有在命名空间中的自定义类
local t1=CS.Test()
t1:Speak("11111111")
--调用在命名空间中的自定义类
local t2=CS.tx.Test2()
t2:Speak("2222222")
继承Mono的类:
Lua
--继承Mono的类,是不能直接new的
local obj5=GameObject("加脚本Test")
--通过GameObject的AddComponent添加脚本,由于lua中不支持无参泛型函数,我们使用另一个重载
--xlua提供一个方法typeof 得到类的type
obj5:AddComponent(typeof(CS.LuaCallCSharp))
运行可以看到
Lua调用枚举
新建一个lua脚本在Main中调用,然后写
Lua
--枚举调用
--调用Unity中的枚举
--枚举调用规则与调用类相似:CS.命名空间.枚举名.枚举成员
PrimitiveType=CS.UnityEngine.PrimitiveType
GameObject=CS.UnityEngine.GameObject
local obj = GameObject.CreatePrimitive(PrimitiveType.Cube)
这样在场景中就会出现一个Cube
如果是自定义脚本,在C#中
cs
public enum myem
{
Idel,
Move,
Jump,
Atk
}
然后在lua中我们调用并进行枚举的一系列转换,利用.__CastFrom(),可以看到
Lua
--自定义枚举
myem=CS.myem
local x=myem.Idel
print(x)
--枚举转换相关rgs
--数值转枚举
local a=myem.__CastFrom(1)
print(a)
--字符串转枚举
local b=myem.__CastFrom("Atk")
print(b)
Lua调用数组,列表,字典
数组,列表,字典在lua中的使用都遵循C#的规则,我们先在C#中创建
cs
public int[] arr=new int[5] { 1, 2, 3 ,7,6};
public List<int> list=new List<int>();
public Dictionary<int,string> dic = new Dictionary<int,string>();
对于数组,在lua中长度的获取,元素遍历如下:
Lua
local obj =CS.Lesson3()
--长度获取,C#怎么用,lua怎么用
print(obj.arr.Length)
--访问元素,虽然lua中数组从1开始,但是访问的数组是C#的规则,从0开始
print(obj.arr[1])
--遍历
--最大值一定也减一
for i=0,obj.arr.Length-1 do
print(obj.arr[i])
end
lua中创建一个C#的数组,可以用表表示list与数组
Lua
--创建C#中的数组使用Array类中的静态方法
local arr2=CS.System.Array.CreateInstance(typeof(CS.System.Int32),10)
print(arr2.length)
print(arr2[1])
同理在lua中向表中添加元素,遍历列表
Lua
obj.list:Add(1)
obj.list:Add(5)
obj.list:Add(3)
print(obj.list.Count)
--遍历
for i=0,obj.list.Count-1 do
print(obj.list[i])
end
在lua中创建一个list对象
Lua
--相当于得到了List<string>的一个类别名,需要再实例化
local list_String=CS.System.Collections.Generic.List(CS.System.String)
local list3=list_String()
list3:Add(222222222222222)
print(list3[0])
在lua中向字典中添加元素,遍历字典
Lua
--使用与C#一致
obj.dic:Add(1,"xxx")
print(obj.dic[1])
--遍历
for k,v in pairs(obj.dic) do
print(k,v)
end
在lua中创建一个字典对象
Lua
local Dic_String_Vector3=CS.System.Collections.Generic.Dictionary(CS.System.String,CS.UnityEngine.Vector3)
local dic2=Dic_String_Vector3()
dic2:Add("124",CS.UnityEngine.Vector3.right)
for k,v in pairs(dic2) do
print(k,v)
end
--要通过下面方法来获取
print(dic2:get_Item("124"))
--改变
dic2:set_Item("124",676)
print(dic2:get_Item("124"))
Lua调用C#拓展方法
我们先在C#中新建一个类,并写出对应的拓展方法
如果要在lua中使用拓展方法,要在类前面加上特性[LuaCallCSharp] (建议lua中要使用的类都加上该特性,可以提升性能,因为lua是通过反射的机制调用C的类,效率低)
cs
[LuaCallCSharp]
public static class Tools
{
//Lesson4的拓展方法
public static void Move(this Lesson4 obj)
{
Debug.Log(obj.name + "移动");
}
}
public class Lesson4
{
public string name = "cc";
public void Speak(string str)
{
Debug.Log(str);
}
public static void Eat()
{
Debug.Log("eat");
}
}
之后在lua中调用
Lua
Lesson4=CS.Lesson4
--使用静态方法
--CS.命名空间.类名.静态方法()
Lesson4.Eat()
--成员方法 实例化出来用
local obj=Lesson4()
--成员方法调用用冒号:
obj:Speak("hhhhhhhhhh")
--使用拓展方法
obj:Move()
Lua调用C#Ref与Out知识
在C#中定义三个方法
cs
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中访问他们
Lua
Lesson5=CS.Lesson5
local obj = Lesson5()
对于ref, 会以多返回值形式返回给lua,如果函数存在返回值,那么第一个值就是该返回值,之后的返回值就是ref的结果。ref参数需要传一个默认值占位置,如果不传会默认传0
Lua
local a,b,c=obj:RefFun(1,0,0,1)
print(a)
print(b)
print(c)
对于out, 会以多返回值形式返回给lua,如果函数存在返回值,那么第一个值就是该返回值,之后的返回值就是out的结果,Out参数不需要传一个默认值占位置
Lua
local a,b,c=obj:OutFun(15,78)
print(a)
print(b)
print(c)
如果我们混用,则是结合两个的规则
Lua
local a,b,c=obj:RefOutFun(10,5)
Lua调用C#函数重载
先在C#中写出重载函数
cs
public int fun1()
{
return 111;
}
public int fun1(int a,int b)
{
return a + b;
}
public int fun1(int a)
{
return a ;
}
public float fun1( float b)
{
return b;
}
虽然lua自己不支持写重载函数,但是支持调用C#中的重载函数
Lua
local obj = CS.Lesson6()
print(obj:fun1())
print(obj:fun1(45,45))
由于lua中数值类型只有Number,对C#中多精度的重载函数支持不好,在使用时,可能会出现一些问题,如:
Lua
print(obj:fun1(45))
print(obj:fun1(4.45))
解决方法:通过反射,但是效率低,尽量不用
Lua
--得到指定函数的相关信息
local m1=typeof(CS.Lesson6):GetMethod("fun1",{typeof(CS.System.Int32)})
local m2=typeof(CS.Lesson6):GetMethod("fun1",{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调用C#委托与事件
先在C#中定义委托与事件
cs
public class Lesson7
{
//申明
public UnityAction del;
public event UnityAction evAction;
public void DoEvent()
{
if (evAction != null)
{
evAction();
}
}
}
委托在lua中与在C#中差不多,但是如果第一次往委托中加函数,会是nil,不能直接+,要先=
Lua
local obj = CS.Lesson7()
--委托用于装函数,执行C#中的委托就是用来装lua函数的
local fun=function ()
print("Lua函数Fun")
end
--lua中有复合运算符,不能+=
--如果第一次往委托中加函数,会是nil,不能直接+
--obj.del=obj.del+fun
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()
--清空所有存储的函数
obj.del=nil
obj.del=fun
obj.del()
事件在C#中与lua中差别很大,使用类似于成员方法
Lua
local fun2=function ( )
print("事件加的函数")
end
--事件+-函数与委托不一样,类似使用成员方法
obj:evAction("+",fun2)
obj:evAction("+",function ( )
print("临时申明")--但是不建议
end)
obj:DoEvent()
obj:evAction("-",fun2)
obj:DoEvent()
--清事件不能直接nil,只能在C#中加一个方法然后调用
Lua调用C#二维数组
cs
public int[,] arr = new int[2, 3] { { 1, 2, 3 }, { 3, 4, 9 } };
在lua中调用,获取长度,元素以及遍历
Lua
local obj=CS.Lesson8()
--获取长度
print("行"..obj.arr:GetLength(0))
print("列"..obj.arr:GetLength(1))
--获取元素
print(obj.arr:GetValue(0,0))
print(obj.arr:GetValue(1,2))
--遍历数组
for i=0,obj.arr:GetLength(0)-1 do
for j=0,obj.arr:GetLength(1)-1 do
print(obj.arr:GetValue(i,j))
end
end
Lua调用C#中nil与null的差距
nil 与null是没办法进行==比较的,假设往场景对象上加一个脚本,如果存在就不加,如果不存在再加,解决方法如下:
Lua
GameObject=CS.UnityEngine.GameObject
Rigidbody=CS.UnityEngine.Rigidbody
local obj=GameObject("测试脚本")
--得到刚体组件,如果没有就加
local rig=obj:GetComponent(typeof(Rigidbody))
print(rig)
if rig ==nil then
rig=obj:AddComponent(typeof(Rigidbody))
end
print(rig)
此时打印如下:
方法1:
Lua
if rig:Equals(nil) then
rig=obj:AddComponent(typeof(Rigidbody))
end
print(rig)
方法2:
Lua
先在Main中写判空全局函数
function IsNull( obj )
if obj==nil or obj:Equals(nil) then
return true
end
return false
end
在lua中:
if IsNull(rig) then
rig=obj:AddComponent(typeof(Rigidbody))
end
print(rig)
方法3:在C#中写一个拓展方法在lua中调用
cs
[LuaCallCSharp]
public static class Lesson9
{
public static bool IsNUll(this Object obj)
{
return obj==null;
}
}
Lua
if rig:IsNUll() then
rig=obj:AddComponent(typeof(Rigidbody))
end
print(rig)
Lua调用C#中让系类型与lua能够互相访问
在前面我们学习了两个特性[CSharpCallLua]和[LuaCallCSharp],其中[CSharpCallLua]主要是用于接口与委托,[LuaCallCSharp]用在拓展方法前面,也可以在每个被Lua调用的类都加,这样可以提升性能。但是对于一些系统类是无法进行修改的。比如我们要调用UI中Slider,我们在场景中创建一个Slider,然后在Lua中写
Lua
GameObject=CS.UnityEngine.GameObject
UI=CS.UnityEngine.UI
local slider=GameObject.Find("Slider")
print(slider)
local sliderSp=slider:GetComponent(typeof(UI.Slider))
print(sliderSp)
sliderSp.onValueChanged:AddListener(function ( f )
print(f)
end)
这样前面两个能够正常打印但是第三个会报错。此时就需要我们在前面加上[CSharpCallLua],我们可以新建一个静态类,使用 XLua进行 C# 和 Lua 的交互配置,可以把所有特性汇总到这里。注意在保存后需要重新生成代码
cs
public static class Lesson10
{
[CSharpCallLua]
public static List<Type> csharpcalllua = new List<Type>() {
typeof(UnityAction<float>)
};
[LuaCallCSharp]
public static List<Type> luacallcsharp = new List<Type>() {
typeof(GameObject),
typeof(Rigidbody)
};
}
Lua调用C#协程
在lua中调用协程与C#中一样
Lua
--C#中协程启动通过继承mono类,通过里面的启动函数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
end
end
mono:StartCoroutine(fun)
此时在u3d中执行会报错 我们不能直接将lua函数传入开启到协程中
可以采用xlua中的util工具表,这样才能正常启动协程
Lua
util=require("xlua.util")
b=mono:StartCoroutine(util.cs_generator(fun))
停止协程可以在判断条件里面加如
Lua
fun=function ( )
local a=1
while true do
--lua中不能直接使用C#中的yield return ,使用lua中的协程返回
coroutine.yield(WaitForSeconds(1))
print(a)
a=a+1
--关闭协程,与C#一样
if a>10 then
mono:StopCoroutine(b)
end
end
end
Lua调用C#泛型
先在C#中准备好多种泛型函数
cs
public class Lesson12
{
public interface Itest
{
}
public class Father
{
}
public class Child:Father, Itest
{
}
public void testFun1<T>(T a,T b) where T : Father
{
Debug.Log("有参数有约束的泛型方法");
}
public void testFun2<T>(T a)
{
Debug.Log("有参数无约束的泛型方法");
}
public void testFun3<T>( ) where T : Father
{
Debug.Log("无参数有约束的泛型方法");
}
public void testFun4<T>(T a) where T : Itest
{
Debug.Log("有参数有约束的泛型方法 ,约束不是类是接口");
}
}
然后逐个在lua中调用看看是否可用
Lua
local obj=CS.Lesson12()
local child=CS.Lesson12.Child()
local father=CS.Lesson12.Father()
--支持有参数有约束的泛型方法
obj:testFun1(child,father)
obj:testFun1(father,child)
--不支持无约束的泛型方法
--obj:testFun2(child)
--不支持有约束无参数的泛型方法
--obj:testFun3()
--不支持非class的泛型方法
--obj:testFun4(child)
如果想要不支持的泛型函数被调用,可以使用下面方法:
Lua
--解决方法:得到通用函数,设置泛型类型再使用
--xlua.get_generic_method(类,"函数名")
local test2=xlua.get_generic_method(CS.Lesson12,"testFun2")
local test2_R=test2(CS.System.Int32)
--调用,成员方法第一个参数传调用函数的对象;静态方法不用传
test2_R(obj,1)
但是有一定限制:如果使用mono进行打包则支持使用,如果使用IL2CPP打包,则要求泛型类型为引用类型才能使用、如果为值类型,则需要C#已经调用过同类型的泛型lua中才能使用