xLua 学习笔记

C#CallLua相关

获取Lua环境变量

  • 创建lua环境变量
LuaEnv env = new LuaEnv();
  • 获取lua中的_G表
env.Global;
  • 自定义加载器

    • 可自定义多个加载器,xLua会逐个加载器获取Lua代码,直到获得到Lua代码后返回
    • 如果自定义加载器无法获取lua代码,则会从默认地址加载lua代码
  • 调用lua代码

env.DoString("");

获取或修改Lua中_G中的全局变量

LuaEnv.Global.Get<T>();`
LuaEnv.Global.Set();

通过Get方法获取的参数为值类型,改变该值无法修改Lua中原变量的值

调用Lua函数

  1. 调用Lua中的无参无返回值的方法时,可使用以下四种方法
FuncNoReturn noReturn = LuaMgr.GetInstance().Global.Get<FuncNoReturn>("testFun1");
noReturn();

UnityAction unityAction = LuaMgr.GetInstance().Global.Get<UnityAction>("testFun1");
unityAction();

Action action = LuaMgr.GetInstance().Global.Get<Action>("testFun1");
action();

//不推荐使用此方法
LuaFunction lf =  LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun1");
lf.Call();
  1. 调用Lua中有参数有返回值得方法时,需要定义一个与该方法返回值、形参一致的deleget,添加[CSharpCallLua]特性,并Generate Code
--lua
--有参有返回
testFun2 = function(a)
print("有参有返回")
	return a + 1
end
[CSharpCallLua]
public delegate int FuncReturn(int a);

//有参有返回
FuncReturn funcReturn = LuaMgr.GetInstance().Global.Get<FuncReturn>("testFun2");
int val = funcReturn(2);
Debug.Log($"FuncReturn:{val}");
Func<int, int> sFun = LuaMgr.GetInstance().Global.Get<Func<int, int>>("testFun2");
Debug.Log($"FuncReturn:{sFun(20)}");
  1. 调用Lua中多返回值的方法时,需要在定义deleget时添加[CSharpCallLua]特性,deleget的返回值为Lua中方法的第一个返回值,后续的返回值用Out或ref标识第二个返回值及后续的返回值
--lua
testFun3 = function(a)
    print("多返回")
    return 1, "hhh", { 1, 2, 3 },a
end
 //多返回值
FuncMulitReturn funcMulitReturn = LuaMgr.GetInstance().Global.Get<FuncMulitReturn>("testFun3");
int result = funcMulitReturn(20, out string b, out int[] c, out int d);
Debug.Log($"funcMulitReturn:result:{result},b:{b},c:{c.Length},d:{d}");
输出:funcMulitReturn:result:1,b:hhh,c:3,d:20

4.调用Lua中的变长参数方法时,需要在定义deleget时添加[CSharpCallLua]特性,变长参数部分用params 关键字标识

--lua
--变长参数
testFun4 = function(a, ...)
    print("变长参数")
    arg = { ... }
    for i, v in pairs(arg) do
        print(i ..",".. v)
    end
    print(a)
end
[CSharpCallLua]
public delegate void FuncMultiArg(string a, params int[] b);
FuncMultiArg funcMultiArg = LuaMgr.GetInstance().Global.Get<FuncMultiArg>("testFun4");
funcMultiArg("ckb", new[] {1, 2, 3, 4, 5});

C#映射Lua中的table

  1. 通过Class或Interface映射table
    • 通过Class映射的table为浅拷贝,修改类中的值无法改变lua中的值
--lua
luaFunc = {
    f1 = 12, f2 = 34,
    1, 2, 3,
    add = function(self, a, b)
        print('d.add called')
        return a + b
    end
}
//通过class映射table。此类为浅拷贝,修改类中的值无法改变lua中table的值
public class tableClass
{
  public int a;
  public int b;
 }
  
tableClass tableClass = LuaMgr.GetInstance().Global.Get<tableClass>("luaFunc");
Debug.Log($"a:{tableClass.a},b:{tableClass.b}");
tableClass.a = 1000;
tableClass.b = 2000;
tableClass = LuaMgr.GetInstance().Global.Get<tableClass>("luaFunc");
Debug.Log($"Change____a:{tableClass.a},b:{tableClass.b}");
  • 通过Interface 映射的lua Table 为深拷贝,可同时修改lua中的值
  • 接口添加[CSharpCallLua]特性,需Generate Code
[CSharpCallLua]
public interface ITableClass
{
  int f1 { get; set; }
  int f2 { get; set; }
  int add(int a, int b);
}
ITableClass itable = LuaMgr.GetInstance().Global.Get<ITableClass>("luaFunc");
Debug.Log($"a:{itable.f1},b:{itable.f2}");
itable.f1 = 1000;
itable.f2 = 2000;
itable = LuaMgr.GetInstance().Global.Get<ITableClass>("luaFunc");
Debug.Log($"Change____a:{itable.f1},b:{itable.f2}");
Debug.Log($"Func add:{itable.add(20, 40)}");

LuaCallC#相关

lua无法直接访问c#,需要定义c#主入口,通过C#调用lua的主脚本,随后跑lua逻辑

  • 最好将常用的方法在单独的脚本中创建好。减少重复创建的消耗
  • 调用类的成员变量要用:
  • 调用mono脚本使用AddComponent(typeof(xxx))
  • lua调用C#的静态方法。用.
local callCSharp = CS.namespaces.Test
callCSharp.Speak2("222222")
  • lua调用C#的成员方法,需要先实例化类,然后用:调用
local call = callCSharp()
call:Speak("ccccc")

Lua处理C#枚举相关

  • 通过数值或字符串转换成枚举
myEnum.__CastFrom(1)
myEnum.__CastFrom("Atk")

Lua处理C#的List、Array、Dictionary

  1. Array相关
    • 数组相关操作和在C#侧的方法一致
    • 使用下标取数组的值时不是按照Lua的规则从1开始,而是从0开始
    • 要在Lua创建一个C#数组,只能通过调用C#中Array的静态方法进行创建
local array = CS.System.Array.CreateInstance(typeof(CS.System.Int32),10)
  1. List相关
    • List相关操作和在C#侧的方法一致
    • 在Lua创建一个C#的List
--在Lua中创建一个C#List
--先创建一个List类
local l = CS.System.Collections.Generic.List(CS.System.String)
--实例化这个类
local List = l()

  1. Dictionary相关
  • 创建一个C#的Dictionary
local d =CS.System.Collections.Generic.Dictionary(CS.System.String,CS.System.Int32)
local dicc = d()
  • 通过上边的方法创建出来的Dic无法用C#的 dic[key]的方式获取到value。只能用dic:get_Item(key)的方式获取
  • 根据key修改value 只能通过dic:set_Item(key,value)的方式

Lua调用C#拓展方法

  • 调用拓展方法,类似于调用C#的成员方法。但拓展类头部应添加[LuaCallCSharp] 特性,并Generate Code
//csharp
[LuaCallCSharp]
public static class Tools
{    
    public static void Move(this L4 obj)   
    {        
        Debug.Log($"{obj.name}---Move");    
    }
}
public class L4
{
    public string name = "MCNB";    
    public void Speak(string str)    
    {        
        Debug.Log($"{name}   str");    
    }    
    public static void Talk(string str)    
    {        
        Debug.Log($"Static:{str}");    
    }
}
--lua
local L4 = CS.L4
--调用静态方法
L4.Talk("ccc")
local _L = L4()
--调用成员方法
_L:Speak("ssssss")
--调用拓展方法
_L:Move()

Lua调用C#的函数传入ref和out参数

public class L5
{
    public int RefFunc(int a, ref int b, ref int c, int d)
    {
        b = a + d;
        c = a - d;
        return 100;
    }

    public int OutFunc(int a, out int b, out int c, int d)
    {
        b = a;
        c = d;
        return 200;
    }

    public int RefOutFunc(int a, out int b, ref int c)
    {
        b = a * 100;
        c = a * 200;
        return 300;
    }
}
l5 = CS.L5
local obj = l5()

--ref会以多返回值的方式返回lua
--如果函数存在返回值,第一个值为返回值,后续值为ref参数
local a,b,c = obj:RefFunc(1,2,3,4)
print(a..","..b..","..c)
输出: 100,5,-3
--out会以多返回值的方式返回lua
--如果函数存在返回值,第一个值为返回值,后续值为ref参数
--out参数可以不用传入参数
local a,b,c = obj:OutFunc(20,30)
print("返回值:"..a..",".."第一个out参数:"..b..",".."第二个out参数:"..c)
输出: 返回值:200,第一个out参数:20,第二个out参数:30

Lua调用C#重载函数

Lua支持调用C#的重载函数,在调用参数个数不同的重载函数时,可以正常调用。
当调用相同参数个数相同且均为数值类型,但精度不同时会出现意想不到的问题。

注意:可已通过反射的方式调用这种情况的函数,但并不推荐

public class L6
{
    public int Clac()
    {
        return 1;
    }
    
    public int Clac(int a, int b)
    {
        return a + b;
    }
    
    public int Clac(int a)
    {
        return a + 1;
    }

    public float Clac(float a)
    {
        return a;
    }
}
print("********************调用C#函数重载*********************")
local obj = CS.L6()
print(obj:Clac())
print(obj:Clac(2))
print(obj:Clac(3,6))
print(obj:Clac(2.2))

print("********************调用C#函数重载*********************")

--xLua调用C#的重载函数时,如果函数的参数个数一样,但精度不同,会出现问题
--可以通过反射的方式调用,但不建议使用
--通过函数名和参数类型获得方法的Type
local m1 = typeof(CS.L6):GetMethod("Clac",{typeof(CS.System.Int32)})
local m2 = typeof(CS.L6):GetMethod("Clac"{typeof(CS.System.Single)})

local f1 = xlua.tofunction(m1);
local f2 = xlua.tofunction(m2);

--成员函数传入的第一个参数为调用者的对象
--静态函数不用传
print(f1(obj,10))
print(f2(obj,10.2))

Lua访问C#二维数组

1.获取二维数组的行列数

public int[,] array = new int[2,3]{{1,2,3},{4,5,6}}
local obj = CS.L8()
print("行"..obj.array:GetLength(0))
print("列"..obj.array:GetLength(1))

2.通过下标获取二维数组元素和遍历二维数组

  • 注意:lua不支持通过[0,0]或[0][0] 的方式获取二维数组的元素
print("0,0元素:"..obj.array:GetValue(0,0))
--遍历二维数组
for i = 0 , obj.array:GetLength(0)-1 do
    for j = 0 , obj.array:GetLength(1)-1 do
        print("行:"..i..",列:"..j..",值:"..obj.array:GetValue(i,j))
    end
end

Lua判断C#中对象为空

  • lua中无法使用== 判断对象是否为空,只能用Equals(nil)来判断
  • 方法1:lua的公共方法中增加判空方法
function IsNull(obj)
    if obj == nil or obj:Equals(nil) then
        return true
    end
    return false
end
  • 方法2:C#为Object添加拓展方法,在lua调用
 public static bool IsNUll(this Object obj)
    {
        return obj == null;
    }

Lua调用C#协程

首先要在调用C#的协程,类需要继承自mono,因此创建一个空对象,在对象上添加一个继承自mono类的脚本

local obj = GameObject("协程测试")
local mono = obj:AddComponent(typeof(CS.LuaCallCSharp))

C#中开启协程用StartCoroutine()方法,在lua中同样可以使用这个方法

WaitForSeconds = CS.UnityEngine.WaitForSeconds
util = require("xlua.util")

func = function()
    local a = 1
    while true do
        print(a)
        a = a + 1
        --lua中的corotine.yield相当于C#种的yield return
        coroutine.yield(WaitForSeconds(1))
        if a > 10 then
            mono:StopCorotine(b)
        end
    end
end

--注意,如果要在lua用StartCoroutine开启一个协程,需要引用XLua提供的util脚本中的cs_generator方法,将一个lua中的方法转换成C#的协程方法
b =mono:StartCoroutine(util.cs_generator(func))

Lua调用C# 泛型函数

 public interface ITest
    {
        
    }
    public class TestFather
    {
        
    }

    public class TestChilder : TestFather,ITest
    {
        
    }
    
    public void TestFunc1<T>(T a, T b) where  T:TestFather
    {
        Debug.Log("有泛型约束的方法");
    }

    public void TestFunc2<T>(T a)
    {
        Debug.Log("没有约束的方法");
    }

    public void TestFunc3<T>() where T : TestFather
    {
        Debug.Log("有约束,没有参数");
    }
    
    public void TestFunc4<T>() where T : ITest
    {
        Debug.Log("接口约束,没有参数");
    }
local obj = CS.L12

local father = obj.TestFather
local child = obj.TestChilder

obj:TestFunc1(father,child)
--lua不支持没有约束的泛型函数
obj:TestFunc2(father)
--lua不支持没有参数的泛型函数
obj:TestFunc3()
--lua不支持约束是非class的泛型函数
obj:TestFunc4(child)

重要的知识点!!!

xLua官方推荐将每一个Lua 要调用的C#类都添加[LuaCallCSharp]特性,这样可以保证运行效率,否则xlua会通过反射调用Lua要用到的C#类。但是那些系统提供的方法和第三方库的方法,我们没办法在这些方法上添加特性标签,如果想在lua调用这些方法,应该怎么做呢?

举个例子:在UI上添加一个Slider控件,lua如何监听Slider控件的onValueChanged事件,打印它的数值变化?

lua这边的代码很容易搞定

local slider = GameObject.Find("Slider")
local sliderCom = slider:GetComponent(typeof(UI.Slider))
sliderCom.onValueChanged:AddListener(function(f)
    print(f)
end)

执行后,Unity报错
在这里插入图片描述
大致的意思是lua中为onValueChanged事件添加监听时传入的UnityAction 委托没有添加[CsharpCallLua]标签,这个委托是Unity自带的,我们无法直接为其添加这个标签,为了解决这个问题,可以在C#这边添加以下代码

public static class L10
{
	//把报错中提到的UnityAction<float>加到这个列表中
    [CSharpCallLua]
    public static List<Type> CsharpCallLua = new List<Type>()
    {
        typeof(UnityAction<float>)
    };
}

加好后别忘了GenerateCode。然后Lua便能愉快的监听到Slider的这个事件了

 热更新框架设计系列课程总体介绍:        本系列课程由《热更新框架设计之Xlua基础》、《热更新框架设计之热更流程与热补丁技术》、《热更新框架设计之游戏客户端框架》三套课程组成。 三套课程是一个不可分割有机的整体,笔者带领大家由浅入深逐级深入 ,在领悟热更精髓的基础之上,通过高端架构设计,设计出“低耦合”、“低侵入”、“高复用”性的游戏(VR/AR)客户端热更框架。                           温馨提示:            本套课程需要具备一定的框架理解与驾驭能力,为了更好的理解本作,强烈推荐广大学员首先学完必要的前导课程:“UI客户端框架设计”、“AssetBundle 框架设计”、“lua基础与中级篇”。《热更新框架设计之Xlua基础》课程介绍:       本课程为系列课程的xlua基础部分,本课程主要就xlua的优势、特点、环境搭建、以及lua文件加载、C#调用lua的各种方式、lua调用C#的各种技巧展开讨论与讲解,学后基本掌握xlua中常用的技术基础,为后续课程打下坚实的基础。 热更新系列(技术含量:中高级):A:《lua热更新技术中级篇》https://edu.csdn.net/course/detail/27087B:《热更新框架设计之热更流程与热补丁技术》https://edu.csdn.net/course/detail/27118C:《热更新框架设计之客户端热更框架(上)》https://edu.csdn.net/course/detail/27132D:《热更新框架设计之客户端热更框架(中)》https://edu.csdn.net/course/detail/27135E:《热更新框架设计之客户端热更框架(下)》https://edu.csdn.net/course/detail/27136
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值