Unity XLua学习笔记(三):Lua调用C#

上一篇文章
Unity XLua学习笔记(二):C#调用Lua
创建一个Lua脚本
Unity XLua学习笔记(三):Lua调用C#_第1张图片
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
Unity XLua学习笔记(三):Lua调用C#_第2张图片
在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打印
Unity XLua学习笔记(三):Lua调用C#_第3张图片

Lua调用C#中的自定义枚举和Unity中的枚举

C#代码

/// 
/// 自定义测试枚举
/// 
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打印
Unity XLua学习笔记(三):Lua调用C#_第4张图片

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])

运行后打印信息
Unity XLua学习笔记(三):Lua调用C#_第5张图片
Lua调用扩展方法
C#扩展测试类

//想要在Lua中使用拓展方法 一定要在工具类前面加上特性
//建议 Lua中要使用的类 都加上该性能 可以提升性能
//如果不加该特性 除了拓展方法对应的类 其他的类的使用都不会报错
//但是Lua是通过反射的机制去调用的C#类 效率较低
[LuaCallCSharp]
public static class Tools
{
    //Lesson4的拓展方法
    public static void Move(this ExtensionTestClass obj)
    {
        Debug.Log(obj.name + "移动");
    }
}
/// 
/// 扩展方法测试类
/// 
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打印信息
Unity XLua学习笔记(三):Lua调用C#_第6张图片

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打印
Unity XLua学习笔记(三):Lua调用C#_第7张图片

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对应重载函数支持的并不好,有可能会出现问题
Unity XLua学习笔记(三):Lua调用C#_第8张图片

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运行打印信息
Unity XLua学习笔记(三):Lua调用C#_第9张图片

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运行打印信息
Unity XLua学习笔记(三):Lua调用C#_第10张图片

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

运行后
Unity XLua学习笔记(三):Lua调用C#_第11张图片
Unity XLua学习笔记(三):Lua调用C#_第12张图片

lua给Unity中的组件绑定事件

在场景中创建一个Slider
Unity XLua学习笔记(三):Lua调用C#_第13张图片

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的数值
Unity XLua学习笔记(三):Lua调用C#_第14张图片

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运行打印
Unity XLua学习笔记(三):Lua调用C#_第15张图片

你可能感兴趣的:(lua,unity,c#)