热更新解决方案3 —— xLua
概述




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 updatevoid 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 framevoid 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 updatevoid 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 framevoid 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(){//若已经初始化了 就直接returnif (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);elseDebug.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;elseDebug.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 updatevoid 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 framevoid 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 updatevoid 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外面不需要初始化)//outCustomCall3 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);//refCustomCall4 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);//XLuaLuaFunction lf4 = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun4");lf4.Call("456", 5, 6, 7, 99);}// Update is called once per framevoid 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 updatevoid Start(){LuaMgr.GetInstance().Init();LuaMgr.GetInstance().DoLuaFile("Main");//同一类型ListList<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]);//不指定类型的话 就用objectList<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"]);//不指定类型 还是用 objectDebug.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 framevoid 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 updatevoid 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 framevoid 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 updatevoid 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 framevoid 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 updatevoid 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 framevoid 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 doprint(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 doprint(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) doprint(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) doprint(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.Lesson5local 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")
endprint("********加函数********")
--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("事件加的函数")
endprint("********事件加函数********")
--事件加减函数 和 委托非常不一样
--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的类 通过里面的启动函数StartCoroutineGameObject = CS.UnityEngine.GameObject
WaitForSeconds = CS.UnityEngine.WaitForSeconds--在场景中新建一个空物体 然后挂一个脚本上去 脚本继承momo使用它来开启协程
local obj = GameObject("Coroutine")
local mono = obj:AddComponent(typeof(CS.LuaCallCSharp))--希望用来被开启的协程函数
fun = function()local a = 1while true do--lua中 不能直接使用 C#中的 yield return--那就使用lua中的协程返回coroutine.yield(WaitForSeconds(1)) --后面的每隔一秒打印其实就是我们C#的方法print(a)a = a + 1if a > 10 thenmono:StopCoroutine(b)endend
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背包小实践
相关文章:
热更新解决方案3 —— xLua
概述 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 fi…...
如何让ai在游戏中更像一个人?
开题开了一整年是我没想到的,还因此延毕了……我重新梳理一下我想做的研究以及相关痕迹。 我2023年3月找到的导师。起初我发现了在玩RTS游戏中会出现很多固定的套路,选手为此要做大量的练习,我就在想如何把这部分内容借助状态机这种流程给…...
websocket_asyncio
WebSocket 和 asyncio 指南 简介 本指南涵盖了使用 Python 中的 websockets 库进行 WebSocket 编程的基础知识,以及 asyncio 在异步非阻塞 I/O 中的作用。它提供了构建高效 WebSocket 服务端和客户端的知识,以及 asyncio 的特性和优势。 1. 什么是 WebS…...
如何在NGINX中实现基于IP的访问控制(IP黑白名单)?
大家好,我是锋哥。今天分享关于【如何在NGINX中实现基于IP的访问控制(IP黑白名单)?】面试题。希望对大家有帮助; 如何在NGINX中实现基于IP的访问控制(IP黑白名单)? 1000道 互联网大…...
Y3编辑器文档4:触发器1(界面及使用简介、变量作用域、入门案例)
文章目录 一、触发器简介1.1 触发器界面1.2 ECA语句编辑及快捷键1.3 参数设置1.4 变量设置1.5 实体触发器1.6 触发器复用 二、触发器的多层结构2.1 子触发器(在游戏内对新的事件进行注册)2.2 触发器变量作用域 三、入门案例3.1 使用触发器实现瞬间移动3.…...
echarts图表自定义配置(二)——代码封装
下图是初版,火山图的代码。可以看出,里面的变量,逻辑,函数存在冗余,基本上都是改了参数,同样的get和set,去刷新图表;对于往后继续开发十几二十个图表,会很麻烦。因此需要…...
02、10个富士胶片模拟的设置
二色彩 1、色彩的加减控制全局的饱和度增减; 2、色彩效果只提升暖色系饱和度; 3、FX蓝色大幅度提升蓝色系饱和度; 4、三个参数都不改变颜色的色相。 2.1 色彩 色彩调整的是拍摄画面整体的色彩饱和程度 2.2色彩效果 调整的是画面中暖色…...
鸿蒙系统-前端0帧起手
鸿蒙系统-前端0帧起手 先search 一番 找到对应的入门文档1. 运行项目遇到问题 如下 (手动设计npm 的 registry 运行 npm config set registry https://registry.npmjs.org/)2.运行后不支持一些模拟器 配置一下(如下图,运行成功&am…...
211-基于FMC的1路1.5G ADC 1路 2.5G DAC子卡
一、板卡概述 FMC-1AD-1DA-1SYNC是我司自主研发的一款1路1G AD采集、1路2.5G DA回放的FMC、1路AD同步信号子卡。板卡采用标准FMC子卡架构,可方便地与其他FMC板卡实现高速互联,可广泛用于高频模拟信号采集等领域。 二、功能介绍 2.1 原理框图 2.2 硬件…...
获取微信用户openid
附上开发文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/Wechat_webpage_authorization.html 开发之前,准备事项 一个已认证过的服务号|基本信息配置js域名和网站授权域名配置最后确认当前账号网页授权功能是否开通,没有开通的无法获取到用户授权开发人…...
MultiRECloudSim使用
MultiRECloudSim使用 简介 MultiRECloudSim是一个用于云计算环境下的模拟器相关工具,它主要用于模拟和评估云计算中的资源分配、任务调度等多种场景。它可能是基于CloudSim这个基础的云计算模拟器进行扩展而来,CloudSim提供了基本的云计算模拟功能,如数据中心、虚拟机、任务…...
智能设备安全-固件逆向分析
固件逆向分析实验报告-20241022 使用固件常用逆向分析工具,对提供的固件进行文件系统提取,并记录逆向分析实验过程,提交实验报告(报告要求图文并茂,对涉及到的关键步骤附截图说明)。具体任务如下࿱…...
【小白包会的】使用supervisor 管理docker内多进程
使用supervisor 管理docker内多进程 一般情况下,一个docker是仅仅运行一个服务的 但是有的情况中,希望一个docker中运行多个进程,运行多个服务,也就是一个docker容器执行多个服务。 调研了一下,发现可以通过**super…...
使用navicat新旧版本,连接PostgreSQL高版本报错问题图文解决办法
使用navicat新旧版本,连接PostgreSQL高版本报错问题图文解决办法 一、问题现象:二、出现原因三、解决方法:1、升级Navicat版本:2、使用低版本的postgreSQL:3、修改Navicat的dll二进制文件:navicat版本15nav…...
IDEA 未启用lombok插件的Bug
项目中maven已引用了lombok依赖,之前运行没有问题的,但有时启动会提示: java: You arent using a compiler supported by lombok, so lombok will not work and has been disabled. Your processor is: com.sun.proxy.$Proxy8 Lombok support…...
认识GO--gRPC的metadata
参考: 写给go开发者的gRPC教程-metadata-CSDN博客https://blog.csdn.net/kevin_tech/article/details/129395177?ops_request_misc%257B%2522request%255Fid%2522%253A%25221f2f2e26f48c755c33344ccb171a49fc%2522%252C%2522scm%2522%253A%252220140713.130102334…...
2024年安徽省职业院校技能大赛信息安全管理与评估
一、赛项名称 赛项名称:信息安全管理与评估 英文名称:Information Security Management and Evaluation 赛项组别:高职组 赛项归属:电子信息大类 二、竞赛目标 通过赛项检验参赛选手熟悉信息安全行业标准规范和信息 安全测试员新职…...
Perl 引用
Perl 引用 Perl,作为一种灵活而强大的编程语言,广泛用于系统管理、网络编程、GUI开发等领域。在Perl编程中,引用(References)是一个核心概念,它允许变量引用其他数据,从而创建复杂的数据结构&a…...
RT-Thread启动过程 :从汇编开始的启动流程
这个系列参考了《嵌入式实时操作系统RT-Thread设计与实现》,会详细介绍RT-Thread的启动流程,即是如何从零开始在开发板上运行起一个RTOS内核的。本文将会以 ch32v307VCT6 开发板为例展开进行详细介绍。主要包括:startup.S、初始化与系统相关的…...
Scala—“==“和“equals“用法(附与Java对比)
Scala 字符串比较—""和"equals"用法 Scala 的 在 Scala 中, 是一个方法调用,实际上等价于调用 equals 方法。不仅适用于字符串,还可以用于任何类型,并且自动处理 null。 Demo: Java 的 在 J…...
模型参数、模型存储精度、参数与显存
模型参数量衡量单位 M:百万(Million) B:十亿(Billion) 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的,但是一个参数所表示多少字节不一定,需要看这个参数以什么…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
【Go】3、Go语言进阶与依赖管理
前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程,它的核心机制是 Goroutine 协程、Channel 通道,并基于CSP(Communicating Sequential Processes࿰…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
面向无人机海岸带生态系统监测的语义分割基准数据集
描述:海岸带生态系统的监测是维护生态平衡和可持续发展的重要任务。语义分割技术在遥感影像中的应用为海岸带生态系统的精准监测提供了有效手段。然而,目前该领域仍面临一个挑战,即缺乏公开的专门面向海岸带生态系统的语义分割基准数据集。受…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
永磁同步电机无速度算法--基于卡尔曼滤波器的滑模观测器
一、原理介绍 传统滑模观测器采用如下结构: 传统SMO中LPF会带来相位延迟和幅值衰减,并且需要额外的相位补偿。 采用扩展卡尔曼滤波器代替常用低通滤波器(LPF),可以去除高次谐波,并且不用相位补偿就可以获得一个误差较小的转子位…...
