热更新解决方案3 —— xLua

news2024/12/18 1:24:16

概述

xLua框架导入和AB包相关准备

xLua导入

其它的导入

C#调用Lua

1.Lua解析器

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//引用命名空间
using XLua;

public class Lesson1_LuaEnv : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        //Lua解析器 能够让我们在Unity中执行Lua
        //一般情况下 保存它的唯一性
        LuaEnv env = new LuaEnv();

        //执行Lua语言
        //第二个参数是 如果lua出错 会打印第二个参数
        env.DoString("print('Hello world')", "Lesson1_LuaEnv");

        //通常我们会执行一个Lua脚本,将要执行的多行代码放入脚本中(Lua知识点:多脚本执行 require)
        //默认寻找脚本的路径 是在 Resources下 并且 因为在这里估计是通过 Resources.Load去加载Lua脚本的
        //所以只支持 txt、bytes等文件名后缀,只能在Lua脚本后缀再加上 txt才能执行
        env.DoString("require('Main')");

        //帮助我们清除Lua中我们没有手动释放的对象 垃圾回收
        //帧更新中定时执行 或者 切场景时执行
        env.Tick();

        //销毁Lua解析器
        //通常只会用一个解析器 所以这个函数用的比较少
        env.Dispose();
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

2.Lua文件加载重定向

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using XLua;

public class Lesson2_Loader : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        LuaEnv env = new LuaEnv();

        //xlua提供的一个 路径重定向 的方法
        //允许我们自定义 加载 Lua文件的规则
        //当我们执行Lua语言 require 时 相当于执行一个lua脚本
        //它就会 执行 我们自定义传入的这个函数
        env.AddLoader(MyCustomLoader);
        //最终我们其实 会去AB包中加载 lua文件

        env.DoString("require('Main')");
        env.DoString("require('ttt')");  //测试找不到时 会不会打印我们设置好的错误信息

    }

    // Update is called once per frame
    void Update()
    {
        
    }

    //自动执行
    private byte[] MyCustomLoader(ref string filePath)
    {
        //通过函数中的逻辑 去加载 Lua文件
        //传入的参数是 require执行的lua脚本文件名
        //拼接一个lua文件所在路径
        string path = Application.dataPath + "/Lua/" + filePath + ".lua";
        Debug.Log(path);

        //有路径 就去加载文件 
        //File 知识点 C#提供的文件读写的类
        //判断文件是否存在
        if (File.Exists(path))
        {
            return File.ReadAllBytes(path);   //这个方法会将文件中的数据转为二进制数组
        }

        
        return null;
    }
}

3.Lua解析器管理器

using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using XLua;

/// <summary>
/// Lua管理器
/// 提供 lua解析器
/// 保证解析器的唯一性
/// </summary>
public class LuaMgr : BaseManager<LuaMgr>
{
    //执行Lua语言的函数
    //释放垃圾
    //销毁
    //重定向
    private LuaEnv luaEnv;


    /// <summary>
    /// 得到Lua 中的_G   后面会频繁使用到
    /// </summary>
    public LuaTable Global
    {
        get
        {
            return luaEnv.Global;
        }
    }

    /// <summary>
    /// 初始化解析器
    /// </summary>
    public void Init()
    {
        //若已经初始化了 就直接return
        if (luaEnv != null)
            return;

        //初始化
        luaEnv = new LuaEnv();
        //加载lua脚本 重定向
        luaEnv.AddLoader(MyCustomLoader);
        luaEnv.AddLoader(MyCustomABLoader);  // 会依次执行
    }

    //Lua脚本会放在AB包
    //最终我们会通过加载AB包再加载其中的Lua脚本资源 来执行它
    //AB包中如果加载文本 后缀还是有一定限制的 .lua还是不能被识别
    //打包时 要把lua文件后缀改为 txt


    //自动执行
    private byte[] MyCustomLoader(ref string filePath)
    {
        //通过函数中的逻辑 去加载 Lua文件
        //传入的参数 是 require 执行的lua脚本文件名
        //拼接一个Lua文件所在路径
        string path = Application.dataPath + "/Lua/" + filePath + ".lua";

        //有路径 就去加载文件
        //File知识点 C#提供的文件读写的类
        //判断文件是否存在
        if (File.Exists(path))
            return File.ReadAllBytes(path);
        else
            Debug.Log("MyCustomLoader重定向失败,文件名为:" + filePath);

        return null;
    }

    //重定向加载AB包的Lua脚本
    private byte[] MyCustomABLoader(ref string filePath)
    {
        //Debug.Log("进入AB包加载 重定向函数");
        从AB包中加载lua文件
        加载AB包
        //string path = Application.streamingAssetsPath + "/lua";
        //AssetBundle ab = AssetBundle.LoadFromFile(path);

        加载Lua文件 返回
        //TextAsset tx = ab.LoadAsset<TextAsset>(filePath + ".lua");  //改变了文件名所以.lua也是文件名的一部分
        加载Lua文件 byte数组
        //return tx.bytes;

        string path = Application.streamingAssetsPath + "/lua";
        TextAsset lua = ABMgr.GetInstance().LoadRes<TextAsset>("lua", filePath + ".lua");
        if (lua != null)
            return lua.bytes;
        else
            Debug.Log("MyCustomABLoader重定向失败,文件名为:" + filePath);

        return null;
    }

    /// <summary>
    /// 传入lua文件名 就可以执行luo脚本 (就是DoString 的优化)
    /// </summary>
    /// <param name="fileName"></param>
    public void DoLuaFile(string fileName)
    {
        string str = string.Format("require('{0}')", fileName);
        DoString(str);
    }

    /// <summary>
    /// 执行Lua语言
    /// </summary>
    /// <param name="str"></param>
    public void DoString(string str)
    {
        if(luaEnv == null)
        {
            Debug.Log("解析器未初始化");
            return;
        }
        luaEnv.DoString(str);
    }

    /// <summary>
    /// 释放资源
    /// </summary>
    public void Tick()
    {
        if (luaEnv == null)
        {
            Debug.Log("解析器未初始化");
            return;
        }
        luaEnv.Tick();
    }

    /// <summary>
    /// 销毁解析器
    /// </summary>
    public void Dispose()
    {
        if (luaEnv == null)
        {
            Debug.Log("解析器未初始化");
            return;
        }
        luaEnv.Dispose();
        luaEnv = null;
    }

}

测试

4.全局变量获取

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson4_CallVariable : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        LuaMgr.GetInstance().Init();

        LuaMgr.GetInstance().DoLuaFile("Main");

        //我们通过C# 没办法直接获取本地局部变量
        int local = LuaMgr.GetInstance().Global.Get<int>("testLocal");
        Debug.Log("testLocal:" + local);

        //使用lua解析器luaenv中的 Global属性
        int i = LuaMgr.GetInstance().Global.Get<int>("testNumber");
        Debug.Log("testNumber:" + i);

        i = 10;
        //值copy 不会影响原来Lua中的值
        int i2 = LuaMgr.GetInstance().Global.Get<int>("testNumber");
        Debug.Log("testNumber:" + i2);
        //改值
        LuaMgr.GetInstance().Global.Set("testNumber", 55);
        i2 = LuaMgr.GetInstance().Global.Get<int>("testNumber");
        Debug.Log("testNumber:" + i2);

        bool b = LuaMgr.GetInstance().Global.Get<bool>("testBool");
        Debug.Log("testBool:" + b);

        float f = LuaMgr.GetInstance().Global.Get<float>("testFloat");
        Debug.Log("testFloat:" + f);

        double d = LuaMgr.GetInstance().Global.Get<double>("testFloat");
        Debug.Log("testFloat_Double:" + d);

        string str = LuaMgr.GetInstance().Global.Get<string>("testString");
        Debug.Log("testString:" + str);

    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

5.全局函数获取

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using XLua;

public class Lesson5_CollFunction : MonoBehaviour
{

    //无参无返回值的委托
    public delegate void CustomCall();

    //有参有返回的委托
    //该特性是在XLua命名空间中的
    //加了过后 要在编辑器里 生成 Lua代码
    [CSharpCallLua]
    public delegate int CustomCall2(int a);

    //多返回值委托
    [CSharpCallLua]
    public delegate int CustomCall3(int a, out int b, out bool c, out string d, out int e);
    [CSharpCallLua]
    public delegate int CustomCall4(int a, ref int b, ref bool c, ref string d, ref int e);

    //变长参数委托
    //变长参数的类型 是根据实际情况来定的 如果想要什么类型都接就传 obj
    [CSharpCallLua]
    public delegate void CustomCall5(string a, params int[] args);

    // Start is called before the first frame update
    void Start()
    {
        LuaMgr.GetInstance().Init();

        LuaMgr.GetInstance().DoLuaFile("Main");

        //无参无返回的获取
        //用什么来接收他们呢 —— 委托最合适
        CustomCall call = LuaMgr.GetInstance().Global.Get<CustomCall>("testFun");
        call();
        //Unity自带委托
        UnityAction ua = LuaMgr.GetInstance().Global.Get<UnityAction>("testFun");
        ua();
        //C#提供的委托
        Action ac = LuaMgr.GetInstance().Global.Get<Action>("testFun");
        ac();
        //XLua提供的一种 获取函数的方法 (尽量少用)
        LuaFunction lf = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun");
        lf.Call();

        //有参有返回
        CustomCall2 call2 = LuaMgr.GetInstance().Global.Get<CustomCall2>("testFun2");
        Debug.Log("有参有返回:" + call2(10));
        //C#自带的泛型委托 (一般情况下我们都不会自己去申明委托,基本上都是有C#或Unity自带的委托)
        Func<int, int> sFun = LuaMgr.GetInstance().Global.Get<Func<int, int>>("testFun2");
        Debug.Log("有参有返回值:" + sFun(20));
        //XLua提供的
        LuaFunction lf2 = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun2");
        Debug.Log("有参有返回值:" + lf2.Call(30)[0]);

        //多返回值
        //使用out 和 ref 来接收  (out外面不需要初始化)
        //out
        CustomCall3 call3 = LuaMgr.GetInstance().Global.Get<CustomCall3>("testFun3");
        int b;
        bool c;
        string d;
        int e;
        Debug.Log("第一个返回值:" + call3(100, out b, out c, out d, out e));
        Debug.Log(b + "_" + c + "_" + d + "_" + e);
        //ref
        CustomCall4 call4 = LuaMgr.GetInstance().Global.Get<CustomCall4>("testFun3");
        int b1 = 0;
        bool c1 = true;
        string d1 = "";
        int e1 = 0;
        Debug.Log("第一个返回值:" + call4(200, ref b1, ref c1, ref d1, ref e1));
        Debug.Log(b1 + "_" + c1 + "_" + d1 + "_" + e1);
        //XLua    (用于多返回值比较适用, 但是会产生垃圾,所以不是很建议使用)
        LuaFunction lf3 = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun3");
        object[] objs = lf3.Call(1000);
        for (int i = 0; i < objs.Length; i++)
        {
            Debug.Log("第" + i + "个返回值是: " + objs[i]);
        }

        //变长参数
        CustomCall5 call5 = LuaMgr.GetInstance().Global.Get<CustomCall5>("testFun4");
        call5("123", 1, 2, 3, 4, 5, 5, 66, 666);
        //XLua
        LuaFunction lf4 = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun4");
        lf4.Call("456", 5, 6, 7, 99);

    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

6.映射到List和Dictionary

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Lesson6_CollListDic : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        LuaMgr.GetInstance().Init();

        LuaMgr.GetInstance().DoLuaFile("Main");

        //同一类型List
        List<int> list = LuaMgr.GetInstance().Global.Get<List<int>>("testList");
        Debug.Log("**********list**************");
        for (int i = 0; i < list.Count; i++)
        {
            Debug.Log(list[i]);
        }
        //值拷贝(浅拷贝) 不会改变lua中的内容
        list[0] = 100;
        List<int> list2 = LuaMgr.GetInstance().Global.Get<List<int>>("testList");
        Debug.Log(list[0]);

        //不指定类型的话 就用object
        List<object> list3 = LuaMgr.GetInstance().Global.Get<List<object>>("testList2");
        Debug.Log("*********list object***************");
        for (int i = 0; i < list3.Count; i++)
        {
            Debug.Log(list3[i]);
        }


        Debug.Log("*********Dictionary***************");
        Dictionary<string, int> dic = LuaMgr.GetInstance().Global.Get<Dictionary<string, int>>("testDic");
        foreach (string item in dic.Keys)
        {
            Debug.Log(item + "_" + dic[item]);
        }
        //检测是不是 值拷贝  —— 还是值拷贝
        dic["1"] = 10000;
        Dictionary<string, int> dic2 = LuaMgr.GetInstance().Global.Get<Dictionary<string, int>>("testDic");
        Debug.Log(dic2["1"]);

        //不指定类型 还是用 object
        Debug.Log("*********Dictionary***************");
        Dictionary<object, object> dic3 = LuaMgr.GetInstance().Global.Get<Dictionary<object, object>>("testDic2");
        foreach (object item in dic3.Keys)
        {
            Debug.Log(item + "_" + dic3[item]);
        }

    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

7.映射到类

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

public class CallLuaClass
{
    //在这个类中去声明成员变量
    //名字一定要和 Lua那么的一样
    //要公共的  (私有和保护的 没办法赋值)
    //这个自定义中的 变量 可以更多也可以更少
    //如果变量比 lua中的少 就会忽略它
    //如果多了 不会赋值 也相当于忽略
    public int testInt;
    public bool testBool;
    //public float testFloat;
    public string testString;

    public int i;

    public UnityAction testFun;

    public CallLuaInClass testInClass;
}

public class CallLuaInClass
{
    public int testInInt;
}

public class Lesson7_CallClass : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        LuaMgr.GetInstance().Init();

        LuaMgr.GetInstance().DoLuaFile("Main");

        CallLuaClass obj = LuaMgr.GetInstance().Global.Get<CallLuaClass>("testClass");
        Debug.Log(obj.testInt);
        Debug.Log(obj.testBool);
        //Debug.Log(obj.testFloat);
        Debug.Log(obj.testString);
        Debug.Log(obj.i);
        Debug.Log("嵌套里的:" + obj.testInClass.testInInt);
        obj.testFun();

        //测试 是否是值拷贝
        //值拷贝 改变它 不会改变Lua表里的内容
        obj.testInt = 100;
        CallLuaClass obj2 = LuaMgr.GetInstance().Global.Get<CallLuaClass>("testClass");
        Debug.Log(obj2.testInt);

    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

8.映射到接口

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using XLua;

//接口中是不允许有成员变量的 但是可以有属性
//我们可以用属性来接收
//接口和类规则一样 其中的属性多了少了 不影响结果 无非就是忽略
//嵌套几乎和类一样 无非 是要遵循接口的规则
[CSharpCallLua]
public interface ICSharpCallInterface
{
    int testInt
    {
        get;
        set;
    }

    bool testBool
    {
        get;
        set;
    }

    //float testFloat
    //{
    //    get;
    //    set;
    //}

    string testString
    {
        get;
        set;
    }

    UnityAction testFun
    {
        get;
        set;
    }

    float testFloat22
    {
        get;
        set;
    }
}

public class Lesson8_CallInterface : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        LuaMgr.GetInstance().Init();

        LuaMgr.GetInstance().DoLuaFile("Main");

        ICSharpCallInterface obj = LuaMgr.GetInstance().Global.Get<ICSharpCallInterface>("testClass");
        Debug.Log(obj.testInt);
        Debug.Log(obj.testBool);
        Debug.Log("新加的:" + obj.testFloat22);
        Debug.Log(obj.testString);
        obj.testFun();


        //测试 是否 值拷贝
        //接口拷贝 是引用拷贝 改了值 lua表中的值也变了
        obj.testInt = 1000;
        ICSharpCallInterface obj2 = LuaMgr.GetInstance().Global.Get<ICSharpCallInterface>("testClass");
        Debug.Log(obj2.testInt);  // 1000

    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

9.映射到LuaTable

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using XLua;

public class Lesson9_CallLuaTable : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        LuaMgr.GetInstance().Init();

        LuaMgr.GetInstance().DoLuaFile("Main");

        //官方不建议使用LuaTable 和 LuaFunction  因为效率低 并且会产生垃圾
        //引用对象
        LuaTable table = LuaMgr.GetInstance().Global.Get<LuaTable>("testClass");
        Debug.Log(table.Get<int>("testInt"));
        Debug.Log(table.Get<bool>("testBool"));
        Debug.Log(table.Get<float>("testFloat"));
        Debug.Log(table.Get<string>("testString"));

        table.Get<LuaFunction>("testFun").Call();

        //引用拷贝
        table.Set("testInt", 55);
        LuaTable table2 = LuaMgr.GetInstance().Global.Get<LuaTable>("testClass");
        Debug.Log(table2.Get<int>("testInt"));

        //用完 table 记得要 清除它 不然会产生垃圾
        table.Dispose();
        table2.Dispose();
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

10.CSharpCallLua特性什么时候用

Lua调用C#

1.类

print("************Lua 调用 C#类相关知识点************")

--lua中使用C#的类非常简单
--固定套路:
--CS.命名空间.类名
--Unity的类 比如 GameObject Transform等等 —— CS.UnityEngine.类名
--CS.UnityEngine.GameObject

--通过C#中的类 实例化一个对象 lua中没有new 所以我们直接 类名括号就是实例化对象
--默认调用的 相当于就是无参构造
local obj1 = CS.UnityEngine.GameObject()
local obj1 = CS.UnityEngine.GameObject("Sunset")  --带传参

--为了方便使用 并且节约性能 定义全局变量存储 C#中的类
--这样定义 相当于是取了一个别名
GameObject = CS.UnityEngine.GameObject
local obj3 = GameObject("山有木兮")

--类中的静态对象 可以直接使用 .来调用
local obj4 = GameObject.Find("Sunset")

--得到对象中的成员变量 直接对象 . 即可
print(obj4.transform.position)
Debug = CS.UnityEngine.Debug
Debug.Log(obj4.transform.position)

Vector3 = CS.UnityEngine.Vector3
--如果使用对象中的 成员方法 !!! 一定要加 冒号 :
obj4.transform:Translate(Vector3.right)
Debug.Log(obj4.transform.position)

--自定义类 使用方法 相同 只是命名空间不同而已
local t = CS.Test1()
t:Speak("说话")

local t2 = CS.Sunset.Test2()
t2:Speak("说话")

--继承了Mono的类
--继承了Mono的类 是不能直接 new
local obj5 = GameObject("加脚本测试")
--通过GameObject的 AddComponent 添加脚本
--xlua提供了一个重要方法 typeof 可以得到类的Type
--xlua中不支持 无参泛型函数 所以 我们要使用另一个重载
obj5:AddComponent(typeof(CS.LuaCallCSharp))

2.枚举

3.数组、List和Dictionary

print("**********Lua调用C# 数组 相关知识点********")

-- 实例化出对象
local obj = CS.Lesson3()

--Lua使用C#数组相关知识
--长度 userdata
--C#怎么用 lua就怎么用 不能使用#去获取长度
print(obj.array.Length)

--访问元素
print(obj.array[0])

--遍历要注意 虽然lua中索引从1开始
--但是数组是C#那边的规则 所以 还是按C#的0开始
--注意最大值 一定要减1
for i=0,obj.array.Length-1 do
	print(obj.array[i])
end

--Lua中创建一个C#的数组 Lua中表示数组和List可以用比表
--但是若要使用C#中???
--创建C#中的数组 使用 Array类中的静态方法即可
--该方法就是创建一个数组,第一个参数是数组类型,第二个参数是数组大小
local array2 = CS.System.Array.CreateInstance(typeof(CS.System.Int32), 10)
print(array2.Length)
print(array2[0])
print(array2[1])
print(array2)

print("**********Lua调用C# list 相关知识点********")
--调用成员方法 用冒号
obj.list:Add(1)
obj.list:Add(2)
obj.list:Add(3)
--长度
print(obj.list.Count)
--遍历
for i = 0,obj.list.Count-1 do
	print(obj.list[i])
end
print(obj.list)

--在Lua中创建一个List对象
--老版本中
local list2 = CS.System.Collections.Generic["List`1[System.String]"]()
print(list2)
list2:Add("123")
print(list2[0])

--新版本 > v2.1.12
--相当于得到一个 List<string> 的一个类别名 需要再实例化
local List_String = CS.System.Collections.Generic.List(CS.System.String)
local list3 = List_String()
list3:Add("555555")
print(list3[0])

print("**********Lua调用C# Dictionary 相关知识点********")
--使用和C#一致
obj.dic:Add(1, "123")
print(obj.dic[1])

--遍历
for k,v in pairs(obj.dic) do
	print(k,v)
end

--在Lua中创建一个字典对象
--也分老版本和新版本
--这里只讲解 新版本(老版本可以去了解)
local Dic_String_Vector3 = CS.System.Collections.Generic.Dictionary(CS.System.String, CS.UnityEngine.Vector3)
local dic2 = Dic_String_Vector3()
dic2:Add("123",CS.UnityEngine.Vector3.right)
for k,v in pairs(dic2) do
	print(k,v)
end
--在Lua中创建的字典 直接通过键中括号得 是得不到 返回的是nil
print(dic2["123"])
--字典里还有一个方法也能够正常得到值
print(dic2:TryGetValue("123"))  -- 返回两个值 true 值
--如果要通过键获取值 要通过这个固定方法
print(dic2:get_Item("123"))
dic2:set_Item("123", nil)
print(dic2:get_Item("123"))

4.函数(拓展方法)

5.函数(ref 和 out)

print("***********Lua调用C# ref 方法相关知识点***********")

Lesson5 = CS.Lesson5

local obj = Lesson5()

--ref参数 会以多返回值的形式返回给lua
--如果函数存在返回值 那么第一个值 就是该返回值
--之后的返回值 就是ref的结果 从左到右一一对应
--ref参数 需要传入一个默认值 占位置
--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)


--混合使用时 综合上面的规则
--ref 需要占位  out不用传
--第一个是函数的返回值 之后 从左到右依次对应ref或者out
local a,b,c = obj:RefOutFun(20, 1)  --ref 的占位值不一定要是0,可以用其他数
print(a)  --300
print(b)  --200
print(c)  --400

6.函数(重载)

print("**********Lua调用C# 重载函数相关知识点**********")

Lesson6 = CS.Lesson6
local obj = Lesson6()

--虽然Lua自己不支持写重载函数
--但是Lua支持调用C#中的重要函数
print(obj:Calc())
print(obj:Calc(15, 1))

--Lua虽然支持调用C#重载函数
--但是因为Lua中的数值类型 只有Number
--对C#中多精度的重载函数支持的不好  会傻傻分不清
--在使用时 可能出现意向不到的问题
print(obj:Calc(10))
print(obj:Calc(10.2))


--解决重载函数含糊的问题
--xlua提供了解决方案 通过反射机制
--这种方法只做了解 尽量别用 (因为效率低)
--Type是反射的关键类
--得到指定函数的相关信息
local m1 = typeof(CS.Lesson6):GetMethod("Calc", {typeof(CS.System.Int32)})
local m2 = typeof(CS.Lesson6):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))

7.委托和事件

print("**********Lua调用C# 委托 相关知识点********")

local obj = CS.Lesson7()

--委托是用来装函数的
--使用C#中的委托 就是用来装lua函数的
local fun = function()
	print("Lua函数Fun")
end

print("********加函数********")
--Lua中没有复合运算符 不能 +=
--如果第一次往委托中加函数 因为是nil 不能直接+
--所以第一次 要先等 +
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("********事件清除********")
--请事件 不能直接设空
--因为在C#中事件是不允许在外部等于和调用的
--obj:eventAction = nil
--但是我们可以在这个事件的类的内部写一个清除的方法出来调用
obj:ClearEvent()
obj:DoEvent()

8.特殊问题(二维数组遍历)

9.特殊问题(null和ni比较)

拓展方法记得加特性

10.特殊问题(让系统类型和Lua能相互访问)

11.协程

print("************Lua调用C# 协程 相关知识点**************")
--xlua提供的一个工具表
--一定是要通过require 调用后 才能用
util = require("xlua.util")

--C#中协程启动都是通过继承了Mono的类 通过里面的启动函数StartCoroutine

GameObject = CS.UnityEngine.GameObject
WaitForSeconds = CS.UnityEngine.WaitForSeconds

--在场景中新建一个空物体 然后挂一个脚本上去 脚本继承momo使用它来开启协程
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)) --后面的每隔一秒打印其实就是我们C#的方法
		print(a)
		a = a + 1
		if a > 10 then
			mono:StopCoroutine(b)
		end
	end
end

--我们不能直接将 lua函数传入到C#开启协程中!!!
--mono:StartCoroutine(fun)
--如果要把lua函数当中协程函数传入
--必须 先调用 xlua.util中的 cs_generator(lua函数)
--它的返回值才是一个协程函数
--关闭的话 我们就先存起它来
b = mono:StartCoroutine(util.cs_generator(fun))

12.泛型函数

print("**************Lua调用C# 泛型函数相关知识点***************")

local obj = CS.Lesson12()

local child = CS.Lesson12.TestChild()
local father = CS.Lesson12.TestFather()

--支持有约束有参数的泛型函数
obj:TestFun1(child, fater)
obj:TestFun1(father, child)

--lua中不支持 没有约束的泛型函数
--obj:TestFun2(child)  -- 会报错

--lua中不支持 有约束 但是没有参数的泛型函数
--obj:TestFun3()

--lua中不支持 非class的约束 (如不支持 接口约束)
--obj:TestFun4()


--下面的方法有一定的使用限制
--Mono打包 这种方式支持使用
--il2cpp打包 如果泛型参数是引用类型才可以使用
--il2cpp打包 如果泛型参数是值类型,除非C#那边已经调用过了同类型的泛型参数 lua中才能够被使用


--补充知识 让上面 不支持使用的泛型函数 变得能用
--得到通用的函数 
--设置泛型类型再使用
--xlua.get_generic_method(类,"函数名")
local testFun2 = xlua.get_generic_method(CS.Lesson12, "TestFun2")
local testFun2_R = testFun2(CS.System.Int32)
--调用
--成员方法 第一个参数 传调用函数的对象
--静态方法 不用传
testFun2_R(obj, 1)

完结:接xLua背包小实践

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2261332.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Rk3588 FFmpeg 拉流 RTSP, 硬解码转RGB

RK3588 ,基于FFmpeg, 拉取RTSP,使用 h264_rkmpp 实现硬解码. ⚡️ RK3588 编译ffmpeg参考: Ubuntu x64 架构, 交叉编译aarch64 FFmpeg mpp Code RTSPvoid hardwave_init(AVCo

谷粒商城—分布式高级①.md

1. ELASTICSEARCH 1、安装elastic search dokcer中安装elastic search (1)下载ealastic search和kibana docker pull elasticsearch:7.6.2 docker pull kibana:7.6.2(2)配置 mkdir -p /mydata/elasticsearch/config mkdir -p /mydata/elasticsearch/data echo "h…

OpenCV圆形标定板检测算法findGrid原理详解

OpenCV的findGrid函数检测圆形标定板的流程如下: class CirclesGridClusterFinder {CirclesGridClusterFinder(const CirclesGridClusterFinder&); public:CirclesGridClusterFinder

30. Three.js案例-绘制并渲染圆弧

30. Three.js案例-绘制并渲染圆弧 实现效果 知识点 WebGLRenderer WebGLRenderer 是 Three.js 中用于渲染 3D 场景的核心类。它利用 WebGL 技术在浏览器中渲染 3D 图形。 构造器 new THREE.WebGLRenderer(parameters) 参数类型描述parametersObject可选参数对象&#xff…

STM32F407ZGT6-UCOSIII笔记4:时间片轮转调度

本文学习与程序编写基于 正点原子的 STM32F1 UCOS开发手册 编写熟悉一下 UCOSIII系统的 时间片轮转调度 文章提供测试代码讲解、完整工程下载、测试效果图 目录 解决上文的卡系统问题&#xff1a; 使能时间片轮转调度&#xff1a; 任务初始化定义更改&#xff1a; 文件结构…

谭浩强C++课后练习(更新中)

基于过程的程序设计 第1章 C的初步知识 1. 请根据你的了解&#xff0c;叙述C的特点。C对C有哪些发展? 2. 一个 C程序是由哪几部分构成的?其中的每一部分起什么作用? 3. 从接到一个任务到得到最终结果&#xff0c;一般要经过几个步骤? 4. 请说明编辑、编译、连接的作用…

单元测试知识总结

我们希望每段代码都是自测试的&#xff0c;每次改动之后&#xff0c;都能自动发现对现有功能的影响。 1 测试要求 在对软件单元进行动态测试之前&#xff0c;应对软件单元的源代码进行静态测试&#xff1b; 应建立测试软件单元的环境&#xff0c;如数据准备、桩模块、模拟器…

前后端跨域问题(CROS)

前端 在src中创建util文件&#xff0c;写request.js文件&#xff1a; request.js代码如下&#xff1a; import axios from axios import { ElMessage } from element-plus;const request axios.create({// baseURL: /api, // 注意&#xff01;&#xff01; 这里是全局统一加…

【数据结构——查找】顺序查找(头歌实践教学平台习题)【合集】

目录&#x1f60b; 任务描述 相关知识 测试说明 我的通关代码: 测试结果&#xff1a; 任务描述 本关任务&#xff1a;实现顺序查找的算法。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.根据输入数据建立顺序表&#xff0c;2.顺序表的输出&#xff0c;…

Android 车载虚拟化底层技术-Kernel 5.15 -Android13(multi-cards)技术实现

系列文章请扫点击如下链接&#xff01; Android Display Graphics系列文章-汇总 本文主要包括部分&#xff1a; 一、Android13的Kernel 5.15版本 1.1 Kernel 5.15 情况说明 1.2 前置条件 二、QCM61*5 plane配置 2.1 multi-card配置 2.2 移植msm-lease 2.3 配置信息确认…

【FFmpeg】FFmpeg 内存结构 ⑥ ( 搭建开发环境 | AVPacket 创建与释放代码分析 | AVPacket 内存使用注意事项 )

文章目录 一、搭建开发环境1、开发环境搭建参考2、项目搭建 二、AVPacket 创建与释放代码分析1、AVPacket 创建与释放代码2、Qt 单步调试方法3、单步调试 - 分析 AVPacket 创建与销毁代码 三、AVPacket 内存使用注意事项1、谨慎使用 av_init_packet 函数2、av_init_packet 函数…

C# DLT645 97/07数据采集工具

电表模拟器 97协议测试 07协议测试 private void btnSend_Click(object sender, EventArgs e) {string addr txtAddr.Text.Trim();string data txtDataFlg.Text.Trim();byte control 0x01;switch (cmbControl.SelectedIndex){case 0: control (byte)0x01; break;// 97协议c…

颜色代码表: 一站式配色方案设计工具集网站

大家好&#xff0c;我是一名设计师&#xff0c;同时也是一名开发者。平时的工作中&#xff0c;相信很多设计师和我一样经常遇到一个问题&#xff1a;设计配色方案时&#xff0c;工具太分散了。寻找颜色搭配灵感需要去一个网站&#xff0c;颜色代码转换要开另一个&#xff0c;检…

Android显示系统(13)- 向SurfaceFlinger提交Buffer

Android显示系统&#xff08;01&#xff09;- 架构分析 Android显示系统&#xff08;02&#xff09;- OpenGL ES - 概述 Android显示系统&#xff08;03&#xff09;- OpenGL ES - GLSurfaceView的使用 Android显示系统&#xff08;04&#xff09;- OpenGL ES - Shader绘制三角…

WebSocket 与 Server-Sent Events (SSE) 的对比与应用

目录 ✨WebSocket&#xff1a;全双工通信的利器&#x1f4cc;什么是 WebSocket&#xff1f;&#x1f4cc;WebSocket 的特点&#x1f4cc;WebSocket 的优点&#x1f4cc;WebSocket 的缺点&#x1f4cc;WebSocket 的适用场景 ✨Server-Sent Events (SSE)&#xff1a;单向推送的轻…

Mysql 深度分页查询优化

Mysql 分页优化 1. 问题根源 问题&#xff1a; mysql在数据量大的时候&#xff0c;深度分页数据偏移量会增大&#xff0c;导致查询效率越来越低。 问题根源&#xff1a; 当使用 LIMIT 和 OFFSET 进行分页时&#xff0c;MySQL 必须扫描 OFFSET LIMIT 行&#xff0c;然后丢弃前…

SpringBoot - 动态端口切换黑魔法

文章目录 关键技术点核心原理Code 关键技术点 利用 Spring Boot 内嵌 Servlet 容器 和 动态端口切换 的方式实现平滑更新的方案&#xff0c;关键技术点如下&#xff1a; Servlet 容器重新绑定端口&#xff1a;Spring Boot 使用 ServletWebServerFactory 动态设置新端口。零停…

linux(CentOS8)安装PostgreSQL16详解

文章目录 1 下载安装包2 安装3 修改远程连接4 开放端口 1 下载安装包 官网下载地址&#xff1a;https://www.postgresql.org/download/ 选择对应版本 2 安装 #yum源 yum -y install wget https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redha…

spring学习(spring-bean实例化(无参构造与有参构造方法实现)详解)

目录 一、spring容器之bean的实例化。 &#xff08;1&#xff09;"bean"基本概念。 &#xff08;2&#xff09;spring-bean实例化的几种方式。 二、spring容器使用"构造方法"的方式实例化bean。 &#xff08;1&#xff09;无参构造方法实例化bean。 &#…

ElasticSearch学习5

基本Rest命令说明&#xff1a; method url地址 描述 PUT&#xff08;创建,修改&#xff09; localhost:9200/索引名称/类型名称/文档id 创建文档&#xff08;指定文档id&#xff09; POST&#xff08;创建&#xff09; localhost:9200/索引名称/类型名称 创建文档&…