图中灰色的无法实现热更新,而Lua代码可以打包成AB包,并上传到资源服务器,
当进入游戏检测是否有资源需要更新,需要则会从资源服务器下载。
学习目标
1.导入xLua框架
2.C#调用Lua
3.Lua调用C#
4.xLua热补丁
xLua框架导入和AB包相关准备
xLua导入
去github上下载xlua,将Assets文件夹中的Plugins和Xlua文件夹赋值到Unity的Assets文件夹中,生成XLua代码
AB包导入
Unity Asset Bundle Browser 工具 - Unity 手册,git导入该工具。新版Unity已经将AB包封装在Addressables中
单例模式基类导入
导入资源包中的Base文件夹。
AB包管理器的导入
导入ABMgr.cs
C#调用Lua
使用建议
-
访问lua全局数据,特别是table以及function,代价比较大,建议尽量少做,比如在初始化时把要调用的lua function获取一次(映射到delegate)后,保存下来,后续直接调用该delegate即可。table也类似。
-
如果lua侧的实现的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一个专门的模块负责xlua的初始化以及delegate、interface的映射,然后把这些delegate和interface设置到要用到它们的地方。
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
//require('脚本名')
//默认寻找脚本路径的是Resources文件夹下的
//估计是通过Resources.Load去加载Lua脚本 txt bytes等等可以识别
//所以Lua脚本后缀要加上 txt
env.DoString("require('Main')");
//帮助我们清除Lua中我们没有手动释放的对象 垃圾回收
//帧更新中定时执行 或者 切场景时执行
env.Tick();
//销毁Lua解析器
env.Dispose();
}
}
关键点:默认Lua脚本路径在Resources文件夹下
注意调用Lua脚本估计是使用Resources的Load方法,它不能识别lua,所以要加上后缀txt,这样就可以执行
Lua文件加载重定向
使用require加载lua脚本,只能实现加载Resources文件下的脚本,这不能实现热更新。
因此要进行加载重定向
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脚本
//它就会执行我们自定义 传入的函数
//参数是一个返回值为byte[] 的委托
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脚本文件名
Debug.Log(filePath);
//拼接一个lua文件所在路径
//Application.dataPath 可以得到Assets路径
//记得加上lua后缀
string path = Application.dataPath + "/Lua/" + filePath + ".lua";
Debug.Log(path);
//有路径 就去加载文件
//File知识点 C#提供的文件读写的类
//需要 System.IO命名空间
//判断文件是否存在
if (File.Exists(path))
{
return File.ReadAllBytes(path);
}
else
{
Debug.Log("MyCutomLoader重定向失败,文件名为" + filePath);
}
return null;
}
}
require查找顺序,因为委托可以添加多个自定义函数,因此可以依次通过自定义函数找文件
Lua解析器管理器
基本完成
LuaMgr
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>
/// 初始化解析器
/// </summary>
public void Init()
{
//已经初始化了 不初始化
if (luaEnv != null) return;
//初始化
luaEnv = new LuaEnv();
//加载lua脚本 重定向
luaEnv.AddLoader(MyCustomLoader);
}
private byte[] MyCustomLoader(ref string filePath)
{
//通过函数中的逻辑 去加载lua文件
//传入的参数是 require执行的Lua脚本文件名
//拼接一个lua文件所在路径
//Application.dataPath 可以得到Assets路径
//记得加上lua后缀
string path = Application.dataPath + "/Lua/" + filePath + ".lua";
//有路径 就去加载文件
//File知识点 C#提供的文件读写的类
//需要 System.IO命名空间
//判断文件是否存在
if (File.Exists(path))
{
return File.ReadAllBytes(path);
}
else
{
Debug.Log("MyCutomLoader重定向失败,文件名为" + filePath);
}
return null;
}
/// <summary>
/// 执行lua语言
/// </summary>
/// <param name="str"></param>
public void DoString(string str)
{
if(luaEnv == null)
{
Debug.Log("解析器未初始化");
return;
}
luaEnv.DoString(str);
}
/// <summary>
/// 释放lua垃圾
/// </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;
}
}
测试代码
void Start()
{
//初始化解析器
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoString("require('Main')");
}
彻底完成(实现AB包加载)
将脚本后缀加上.txt(ab包同样不能识别.lua),然后如图打包(路径改为PC因为AB包的管理器设置为PC路径)
build时可能出现大量报错,是xlua生成代码出错
清空生成代码后再build
更新LuaMgr
public void Init()
{
//已经初始化了 不初始化
if (luaEnv != null) return;
//初始化
luaEnv = new LuaEnv();
//加载lua脚本 重定向
luaEnv.AddLoader(MyCustomLoader);
luaEnv.AddLoader(MyCustomABLoader);
}
private byte[] MyCustomLoader(ref string filePath)
{
//通过函数中的逻辑 去加载lua文件
//传入的参数是 require执行的Lua脚本文件名
//拼接一个lua文件所在路径
//Application.dataPath 可以得到Assets路径
//记得加上lua后缀
string path = Application.dataPath + "/Lua/" + filePath + ".lua";
//有路径 就去加载文件
//File知识点 C#提供的文件读写的类
//需要 System.IO命名空间
//判断文件是否存在
if (File.Exists(path))
{
return File.ReadAllBytes(path);
}
else
{
Debug.Log("MycutomLoader重定向失败,文件名为" + filePath);
}
return null;
}
//Lua脚本会放在AB包中
//最终我们会通过加载AB包 再加载其中的Lua脚本资源 来执行它
//AB包中 如果要加载文本 后缀还是有一定的限制 .lua不能被识别
//所以还是要改后缀为txt
private byte[] MyCustomABLoader(ref string filePath)
{
//没有AB包管理器时
/*Debug.Log("进入AB包加载重定向函数");
//从AB包中加载lua文件
//加载AB包
string path = Application.streamingAssetsPath + "/lua";
AssetBundle ab = AssetBundle.LoadFromFile(path);
//加载Lua文件 返回
//因为后缀为txt,补上.lua
TextAsset tx = ab.LoadAsset<TextAsset>(filePath + ".lua");
//加载Lua文件 byte数组
return tx.bytes;*/
//使用AB包管理器
//同步加载(不能异步,重定向要求马上返回)
//通过AB包管理器加载的lua脚本资源
TextAsset lua = ABMgr.GetInstance().LoadRes<TextAsset>("lua", filePath + ".lua");
if (lua != null) return lua.bytes;
else Debug.Log("MyCustomABLoader重定向失败,文件名为:" + filePath);
return null;
}
实际开发过程中不会使用加载AB包的方式来测试,只有最终要发布时,测试AB包功能时才会使用。在之后实践中,会写一个Lua文件后缀修改小工具。
优化
/// <summary>
/// 传入lua文件名 执行lua脚本
/// </summary>
/// <param name="fileName"></param>
public void DoLuaFile(string fileName)
{
string str = string.Format("require('{0}')", fileName);
DoString(str);
}
/// <summary>
/// 得到Lua中的_G
/// </summary>
public LuaTable Global
{
get { return luaEnv.Global; }
}
全局变量的获取
更新Main.lua
print("主Lua脚本启动")
--Unity中写lua执行
--xlua帮我们处理
--只要执行lua脚本 都会自动的进入我们的重定向函数中找文件
require("test")
编写test.lua
print("test.Lua")
testNumber = 1
testBool = true
testFloat = 1.2
testString = "123"
--通过C# 没办法直接获取局部变量
local testLocal = 10
CallVariable
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");
//使用lua解析器luaenv中的Global属性
int i = LuaMgr.GetInstance().Global.Get<int>("testNumber");
print("testNumber: "+ i);
i = 10;
//不会影响,因为值类型是值拷贝,不会影响原来Lua中的值
//改值
LuaMgr.GetInstance().Global.Set("testNumber",55);
int i2 = LuaMgr.GetInstance().Global.Get<int>("testNumber");
print("testNumber_i2: " + i2);
bool b = LuaMgr.GetInstance().Global.Get<bool>("testBool");
print("testBool: " + b);
float f = LuaMgr.GetInstance().Global.Get<float>("testFloat");
print("testFloat: " + f);
double d = LuaMgr.GetInstance().Global.Get<double>("testFloat");
print("testFloat_Double: " + d);
string s = LuaMgr.GetInstance().Global.Get<string>("testString");
print("testString: " + s);
//会报错,无法获得局部变量
//int local = LuaMgr.GetInstance().Global.Get<int>("testLocal");
//print("testLocal: " + local);
}
// Update is called once per frame
void Update()
{
}
}
Lua中只有Number一种类型,但可以根据它具体的值 用对应的C#类型存储。
全局函数的获取
以下取自XLua教程
访问一个全局的function
仍然是用Get方法,不同的是类型映射。
映射到delegate
这种是建议的方式,性能好很多,而且类型安全。缺点是要生成代码(如果没生成代码会抛InvalidCastException异常)。
delegate要怎样声明呢? 对于function的每个参数就声明一个输入类型的参数。 多返回值要怎么处理?从左往右映射到c#的输出参数,输出参数包括返回值,out参数,ref参数。
参数、返回值类型支持哪些呢?都支持,各种复杂类型,out,ref修饰的,甚至可以返回另外一个delegate。
delegate的使用就更简单了,直接像个函数那样用就可以了
映射到LuaFunction
这种方式的优缺点刚好和第一种相反。 使用也简单,LuaFunction上有个变参的Call函数,可以传任意类型,任意个数的参数,返回值是object的数组,对应于lua的多返回值。
更新test.Lua
print("test.Lua")
testNumber = 1
testBool = true
testFloat = 1.2
testString = "123"
--通过C# 没办法直接获取局部变量
local testLocal = 10
--无参无返回
testFun = function ()
print("无参无返回")
end
--有参有返回
testFun2 = function(a)
print("有参有返回")
return a + 1
end
--多返回
testFun3 = function(a)
print("多返回值")
return 1,2,false,"123",a
end
--变长参数
testFun4 = function(a,...)
print(a)
arg = {...}
for k,v in pairs(arg) do
print(k,v)
end
end
无参无返回
//无参无返回值的委托 XLua认识这种委托
public delegate void CustomCall();
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();
}
有参有返回
//有参有返回的委托
//该特性在 XLua命名空间中
[CSharpCallLua]
public delegate int CustomCall2(int a);
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
//有参有返回
CustomCall2 call2 = LuaMgr.GetInstance().Global.Get<CustomCall2>("testFun1");
print("有参有返回:" + call2(10));
//C#自带泛型委托,方便我们使用
Func<int,int> sFun = LuaMgr.GetInstance().Global.Get<Func<int,int>>("testFun2");
print("有参有返回:" + sFun(20));
//XLua提供的 一种函数的方式 少用
LuaFunction lf2 = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun2");
print("有参有返回" + lf2.Call(30)[0]);
}
注意生成XLua代码,因为需要特性,XLua会根据特性生成相应的代码,使这个有参有返回的委托能够被识别。
多返回值
[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);
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
//多返回值
//使用out 和 ref 来接受
CustomCall3 call3 = LuaMgr.GetInstance().Global.Get<CustomCall3>("testFun3");
int b;
bool c;
string d;
int e;
print("第一个返回值: " + call3(100, out b, out c, out d, out e));
print("后面的返回值:" + b + "_" + c + "_" + d + "_" + e);
CustomCall4 call4 = LuaMgr.GetInstance().Global.Get<CustomCall4>("testFun3");
//使用ref要初始化
int b1 = 0;
bool c1 = true;
string d1 = "";
int e1 = 0;
print("第一个返回值: " + call4(200, ref b1, ref c1, ref d1, ref e1));
print("后面的返回值:" + 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++)
{
print("第" + i + "个返回值是:" + objs[i]);
}
}
每次都要记得生成XLua代码
变长参数
//object[] args 可以根据是否确定使用哪种类型的参数来改变,比如只传int
//则可以使用 int[] args
[CSharpCallLua]
public delegate void CustomCall5(string a, params object[] args);
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
//变长参数
CustomCall5 call5 = LuaMgr.GetInstance().Global.Get<CustomCall5>("testFun4");
call5("123", 1, 2, 3, 4, 555, 666);
LuaFunction lf4 = LuaMgr.GetInstance().Global.Get<LuaFunction>("testFun4");
lf4.Call("456",1,2,3,114514);
}
List和Dictionary映射table
更新test.lua
--List
testList = {1,2,3,4,5,6}
testList2 = {"123","123",true,1,1.2}
--Dictionary
testDic = {
["1"] = 1,
["2"] = 2,
["3"] = 3,
["4"] = 4
}
testDic2 = {
["1"] = 1,
[true] = 1,
[false] = true,
["123"] = false
}
CallListDic
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(list2[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]);
}
//值拷贝 浅拷贝 不会改变lua中的内容
dic["1"] = 10000;
Dictionary<string, int> dic2 = LuaMgr.GetInstance().Global.Get<Dictionary<string, int>>("testDic");
Debug.Log(dic2["1"]);
//不指定类型
Dictionary<object,object> dic3 = LuaMgr.GetInstance().Global.Get<Dictionary<object,object>>("testDic2");
Debug.Log("********************Dictionary object********************");
foreach (object item in dic3.Keys)
{
Debug.Log(item + "_" + dic3[item]);
}
}
都是值拷贝,不会影响lua中的值。
类映射table
更新test.lua
testClass = {
testInt = 2,
testBool = true,
testFloat = 1.2,
testString = "123",
testFun = function()
print("12312312312")
end,
testInClass = {
testInInt = 3
}
}
CallClass
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 CallLuaInClass testInClass;
public UnityAction testFun;
public void Test()
{
Debug.Log(testInt);
}
}
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");
print("*************Class*************");
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.testInClass.testInInt);
obj.testFun();
//值拷贝 改变他不会改变lua里的值
obj.testInt = 100;
CallLuaClass obj2 = LuaMgr.GetInstance().Global.Get<CallLuaClass>("testClass");
Debug.Log(obj2.testInt);
}
}
接口映射table
需要在接口上加上[CSharpCallLua],生成xlua代码
一旦修改接口 都要记得清空xlua代码 再重新生成
接口的拷贝是引用改变,改变值会使lua中也改变。
更新test.lua
testInterface = {
testInt = 2,
testBool = true,
testFloat = 1.2,
testString = "123",
testFun = function()
print("12312312312")
end
}
CallInterface
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using XLua;
//接口中不允许有成员变量
//我们用属性来接受
//接口默认public
//嵌套几乎和类一样 无非要遵循接口规则。
[CSharpCallLua]
public interface ICSharpCallInterface
{
//同样可以 少 或 多
//一旦修改接口 都要记得清空xlua代码 再重新生成
int testInt { get; set; }
string testString { get; set; }
bool testBool { get; set; }
float testFloat { get; set; }
UnityAction testFun { 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>("testInterface");
Debug.Log(obj.testInt);
Debug.Log(obj.testBool);
Debug.Log(obj.testFloat);
Debug.Log(obj.testString);
obj.testFun();
//接口拷贝 是引用拷贝 改了值 lua也会改变
obj.testInt = 10000;
ICSharpCallInterface obj1 = LuaMgr.GetInstance().Global.Get<ICSharpCallInterface>("testInterface");
Debug.Log(obj1.testInt);
}
// Update is called once per frame
void Update()
{
}
}
LuaTable映射table
这种方式好处是不需要生成代码,但也有一些问题,比如慢,比方式2要慢一个数量级,比如没有类型检查。---xLua教程
更新test.lua
testLuaTable = {
testInt = 2,
testBool = true,
testFloat = 1.2,
testString = "123",
testFun = function()
print("12312312312")
end
}
CallLuaTable
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
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>("testLuaTable");
//Global是个大LuaTable,因此使用方法相同 用get,set
Debug.Log(table.Get<int>("testInt"));
Debug.Log(table.Get<float>("testFloat"));
Debug.Log(table.Get<bool>("testBool"));
Debug.Log(table.Get<string>("testString"));
table.Get<LuaFunction>("testFun").Call();
//改
table.Set("testInt", 100);
Debug.Log(table.Get<int>("testInt"));
LuaTable table2 = LuaMgr.GetInstance().Global.Get<LuaTable>("testLuaTable");
Debug.Log(table2.Get<int>("testInt"));
table.Dispose();
table2.Dispose();
}
// Update is called once per frame
void Update()
{
}
}
Lua调用C#
调用类
更新Main
/// <summary>
/// Lua没办法直接访问C# 一定是先从C#调用Lua脚本后
/// 才把核心逻辑 交给Lua编写
/// </summary>
public class Main : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
}
}
LuaCallCSharp
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test
{
public void Speak(string str)
{
Debug.Log(str);
}
}
namespace MrJin
{
public class Test2
{
public void Speak(string str)
{
Debug.Log(str);
}
}
}
public class LuaCallCSharp : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
更新Main.lua
print("主Lua脚本启动")
--Unity中写lua执行
--xlua帮我们处理
--只要执行lua脚本 都会自动的进入我们的重定向函数中找文件
--require("test")
require("lesson1_CallClass")
创建lesson1_CallClass
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("azhe") --生成名字叫azhe的空物体
--为了方便使用 并且节约性能 定义全局变量存储 C#中的类
--相当于取了别名
GameObject = CS.UnityEngine.GameObject
local ob3 = GameObject("Azhe1") --生成Azhe1物体
--类中的静态对象 可以直接使用.来调用
local obj4 = GameObject.Find("azhe")
--得到对象中的成员变量 直接对象. 即可
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)
--自定义类 使用方法 相同 只是命名空间不同而已
--调用没有命名空间的Test类
local t = CS.Test()
t:Speak("Test说话")
--有命名空间的类
local t2 = CS.MrJin.Test2()
t2:Speak("Test2说话")
--继承Mono的类
--继承了Mono的类 是不能直接new
local obj5 = GameObject("加脚本测试")
--通过GameObject的 AddComponent 添加脚本
--Xlua提供了一个重要方法 typeof 可以得到类的Type
--lua不支持无参的泛型函数 所以不能用 AddComponent<LuaCallCSharp>()
--要使用另一个重载
obj5:AddComponent(typeof(CS.LuaCallCSharp))
调用C#枚举
Main.lua
print("主Lua脚本启动")
--Unity中写lua执行
--xlua帮我们处理
--只要执行lua脚本 都会自动的进入我们的重定向函数中找文件
--require("test")
--require("lesson1_CallClass)
--不注释上一条 可以使用上一条定义的全局变量,如GameObject别名
require("Lesson2_CallEnum")
Lesson2_CallEnum.lua
print("************Lua调用C#枚举相关知识点*************")
--枚举调用
--调用Unity当中的枚举
--枚举的调用规则 和 类的调用规则一样
--CS.命名空间.枚举名.枚举成员
--也支持取别名
--Unity自带的枚举,包含Capsule,Cube,Plane等物体
PrimitiveType = CS.UnityEngine.PrimitiveType
GameObject = CS.UnityEngine.GameObject
--使用GameObject.CreatePrimitive()方法来测试
local obj = GameObject.CreatePrimitive(PrimitiveType.Cube)
--自定义枚举
E_MyEnum = CS.E_MyEnum
local c = E_MyEnum.Idle
print(c)
--枚举转换相关
--数值转枚举
local a = E_MyEnum.__CastFrom(1)
print(a)
--字符串转枚举
local b = E_MyEnum.__CastFrom("Atk")
print(b)
更新LuaCallCSharp
/// <summary>
/// 自定义测试枚举
/// </summary>
public enum E_MyEnum
{
Idle,
Move,
Atk
}
Lua使用C#数组 list和字典
Main.lua
--require("Lesson2_CallEnum")
require("Lesson3_CallArray")
LuaCallCSharp
public class Lesson3
{
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>();
}
#endregion
Lesson3_LuaCallArray
print("************Lua调用C# 数组 List 字典 相关知识点*************")
local obj = CS.Lesson3()
--Lua使用C#数组相关知识
--长度 userdata
--C#怎么用 lua就怎么用 不能使用#来获取长度
print(obj.array.Length)
--访问元素
print(obj.array[0])
--遍历要注意 虽然lua中索引从1开始
--但是数组是C#那边的规则 所以 还是得按C#的来
--注意最大值 一定要减一 lua中可以取到最大
for i = 0, obj.array.Length-1 do
print(obj.array[i])
end
--Lua中创建一个C#的数组 lua中表示数组和List可以用表
--但是要使用C#中的
--数组实际是Array类
--所以要使用Array类中的CreateInstance方法 参数一 类型, 参数二 长度
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对象
--老版本
--CS.System.Collections.Generic为List的命名空间
--表示创建1个参数的List泛型里面参数是类型 String
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("5555")
print(list3[0])
print("************Lua调用C# 字典 相关知识点*************")
--使用和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中创建的字典 直接通过[]是得不到的
print(dic2["123"]) --输出了nil
--该方法多返回值
print(dic2:TryGetValue("123")) -- true (1,0,0)
--如果要通过键获取值,要通过这个固定方法
print(dic2:get_Item("123")) --(1,0,0)
--同理设置值
dic2:set_Item("123",nil)
print(dic2:get_Item("123")) --(0,0,0)
调用C#拓展方法
需要特性[LuaCallCSharp],命名空间xlua
记得生成代码
//想要在Lua中使用扩展方法 一定要在工具类前面加上特性
//建议 lua中要使用的类 都加上该特性 可以提升性能
//如果不加该特性 除了扩展方法对应的类 其他类的使用 都不会报错
//但lua是用反射机制 调用 c# 类 效率低
//加上特性并生成代码后,会生成对应的C#代码,提升效率
仍有问题 自定义类可以加上特性,但非自定义类怎么加呢?在后面特殊问题有解答
更新luaCallCSharp
#region 拓展方法
//想要在Lua中使用扩展方法 一定要在工具类前面加上特性
//建议 lua中要使用的类 都加上该特性 可以提升性能
//如果不加该特性 除了扩展方法对应的类 其他类的使用 都不会报错
//但lua是用反射机制 调用 c# 类 效率低
//加上特性并生成代码后,会生成对应的C#代码,提升效率
[LuaCallCSharp]
public static class Tools
{
//Lesson4的扩展方法
public static void Move(this Lesson4 obj)
{
Debug.Log(obj.name + "移动");
}
}
public class Lesson4
{
public string name = "azhe";
public void Speak(string str)
{
Debug.Log(str);
}
public static void Eat()
{
Debug.Log("吃东西");
}
}
#endregion
Lesson4_CallFunction
print("************Lua调用C# 扩展方法 相关知识点*************")
Lesson4 = CS.Lesson4
--使用静态方法
--CS.命名空间.类名.静态方法名()
Lesson4.Eat()
--成员方法 实例化出来用
local obj = Lesson4()
--成员方法 一定要用冒号
obj:Speak("ahhhhhhh")
--使用扩展方法 和使用成员方法 一致
obj:Move()
调用C# ref和out函数
更新LuaCallCSharp
#region ref和out
public class Lesson5
{
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;
}
}
#endregion
Lesson5_CallFunction
print("************Lua调用C# ref和out方法 相关知识点*************")
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)
--local a,b,c = obj:RefFun(1, 1) 这样也不会报错,但相当于第三第四个参数为0
print(a)
print(b)
print(c)
-- out 参数 会以 多返回值的形式返回给lua
-- 如果函数存在返回值,第一个值就是函数默认返回值
-- 之后的返回值 就是out的结果 从左到右一一对应
-- out参数 不需要传占位置的值 !!!!!
local a,b,c = obj:OutFun(20,30)
print(a)
print(b)
print(c)
--混合使用
--out省略,ref要占位
--第一个是函数的返回值 之后 从左到右依次对应ref或out
local a,b,c = obj:RefOutFun(20,1)
print(a)
print(b)
print(c)
使用C#重载函数
luaCallCSharp
#region 函数重载
public class Lesson6
{
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;
}
}
#endregion
Lesson6_CallFunction
print("************Lua调用C# 重载函数 相关知识点*************")
local obj = CS.Lesson6()
--Lua支持调用C#中的重载函数
--虽然lua 自己不支持写重载函数
print(obj:Calc())
print(obj:Calc(15,1))
--lua虽然支持调用C#重载函数
--但因为lua中的数值类型只有 number
--对c#中多精度 的重载函数 支持不好 傻傻分不清
--在使用时,可能出现意想不到的问题
print(obj:Calc(10))
print(obj:Calc(10.2)) --输出0
--解决重载函数含糊的问题
--xlua提供了解决方案 反射机制
--这种方式只做了解 尽量别用 效率低
--Type是反射的 关键类
--得到指定函数的相关信息
local m1 = typeof(CS.Lesson6):GetMethod("Calc",{typeof(CS.System.Int32)})
--得到float的信息
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))
使用C#委托和事件
luaCallCSharp
#region 委托和事件
public class Lesson7
{
//申明委托和事件
public UnityAction del;
public event UnityAction eventAction;
//事件外部不能直接调用,所以提供一个方法
public void DoEvent()
{
if(eventAction != null)
eventAction();
}
public void ClearEvent()
{
eventAction = null;
}
}
#endregion
Lesson7_CallDel
print("************Lua调用C# 委托 相关知识点*************")
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()
obj.del = obj.del - fun
obj.del = obj.del - fun
obj.del()
--清空所有函数
obj.del = nil
--清空后要先等
obj.del = fun
obj.del()
print("************Lua调用C# 事件 相关知识点*************")
local fun2 = function()
print("事件加的函数")
end
--事件加减函数 和 委托非常不一样 用冒号
--lua中使用C#事件 加函数
--有点类似使用成员方法 冒号事件名("+",函数变量)
obj:eventAction("+",fun2)
--最好别这样写
obj:eventAction("+",function()
print("事件加的匿名函数")
end)
obj:DoEvent()
obj:eventAction("-",fun2)
obj:DoEvent()
--清事件 不能直接设置为nil 外部不能访问
--可以在C#中写一个方法来置空
obj:ClearEvent()
obj:DoEvent()
使用C#二维数组
#region 二维数组遍历
public class Lesson8
{
public int[,] array = new int[2, 3] { { 1, 2, 3 }, { 4, 5, 6 } };
}
#endregion
print("************Lua调用C# 二维数组 相关知识点*************")
local obj = CS.Lesson8()
--获取长度
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
null和nil的比较
有三种可行方法
不可行(也最容易想到)
print("************Lua调用C# null和nil比较 相关知识点*************")
--往场景对象上添加一个脚本 如果存在就不加 如果不存在再加
GameObject = CS.UnityEngine.GameObject
Rigidbody = CS.UnityEngine.Rigidbody
local obj = GameObject("测试加脚本")
--得到身上的刚体组件 如果没有 就加
local rig = obj:GetComponent(typeof(Rigidbody))
print(rig)
--判断空
--nil和null 没法进行==比较
if rig == nil then
print("第一种") --没有进入if中 失败!!!!
rig = obj:AddComponent(typeof(Rigidbody))
end
print(rig)
第一种可行
if rig:Equals(nil) then
print("第二种") --进入if中
rig = obj:AddComponent(typeof(Rigidbody))
end
print(rig)
--但这样也不够好,万一rig就是nil,则无法调用方法,会报错
使用了Object中的Equals方法
第二种方法(解决第一种的缺陷)
--所以可以再Main函数中添加一个全局方法判断
if isNull(rig) then
print("第三种") --进入if中
rig = obj:AddComponent(typeof(Rigidbody))
end
print(rig)
----------------------------------
--Main函数中
function isNull(obj)
if obj == nil or obj:Equals(nil) then
return true
end
return false
end
第三种方法(在C#中解决)
#region 判空
//为 object 扩展一个方法
[LuaCallCSharp]
public static class Lesson9
{
//扩展一个为object判空的方法 主要给lua用 lua没法用null和nil比较
public static bool IsNull(this Object obj)
{
return obj == null;
}
}
#endregion
--还有一种方法 在 C#中为Object提供一个扩展方法来判空
if rig:IsNull() then
print("第四种") --进入if中
rig = obj:AddComponent(typeof(Rigidbody))
end
print(rig)
lua和系统类型或委托相互使用
系统类型不能主动添加CSharpCallLua或LuaCallCSharp这样的特性
GameObject = CS.UnityEngine.GameObject
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)
--以上会报错说需要CSharpCallLua的特性,但是系统类是无法更改的
更新luacallCsharp
#region 系统类型加特性
public static class Lesson10
{
[CSharpCallLua]
public static List<Type> csharpCallLuaList = new List<Type>()
{
typeof(UnityAction<float>)
};
[LuaCallCSharp]
public static List<Type> luaCallCsharpList = new List<Type>()
{
typeof(GameObject),
typeof(Rigidbody)
};
}
#endregion
记得生成xlua代码
使用C#协程
Lesson10_Coroutine
print("************Lua调用C# 协程 相关知识点*************")
--xlua提供的一个工具表
--一定要通过require调用之后 才能用
util = require("xlua.util")
--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
if a > 10 then
--停止协程 和C#中一样
mono:StopCorotine(b)
end
end
end
--我们不能直接将 lua函数传入到 开启协程中!!!!!!
--mono:StartCoroutine(fun) -- 会报错
--如果要把lua函数当作协程函数传入
--必须 先调用 xlua.util中的cs_generator(lua函数)
b = mono:StartCoroutine(util.cs_generator(fun))
使用泛型函数
LuaCallCsharp
#region 调用泛型方法
public class Lesson12
{
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 TestFun3<T>() where T : TestFather
{
Debug.Log("无参数 但有约束的泛型方法");
}
public void TestFun4<T>(T a) where T :ITest
{
Debug.Log("有参数有约束, 但是约束不是类");
}
}
#endregion
Lesson12_T
print("************Lua调用C# 泛型函数相关知识点*************")
local obj = CS.Lesson12()
local child = CS.Lesson12.TestChild()
local father = CS.Lesson12.TestFather()
--支持有约束 有参数的 泛型函数
obj:TestFun1(child,father)
obj:TestFun1(father,child)
--不支持没有约束的泛型函数
--obj:TestFun2(child)
--不支持 有约束 但是没有参数的泛型函数
--obj:TestFun3()
--lua中不支持 非class的约束
--obj:TestFun4(child)
--有一定的使用限制
--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)
关于LuaCallCSharp和CSharpCallLua特性
CSharpCallLua 适用于委托 接口
LuaCallCsharp 适用于 扩展方法 建议每个被lua调用的类都加 可以提升性能
热补丁
热补丁,可以让原本用C#写的内容不再被执行,而去执行新写的lua代码的逻辑。
第一个热补丁
第一步 加特性
第二步 加宏
注意第一次使用热补丁,要在项目的File-Build Settings - Player Settings - Player - Other Settings - Scripting Define Symbols加上热补丁的宏HOTFIX_ENABLE
第三-四步 生成代码 注入热补丁
生成代码后,点击Hotfix Inject In Editor(加入宏后出现的),但这样还不够,点击它会报错提示需要下载tools,在xlua文件夹下存在Tools文件夹
将其导入进项目文件夹下,注意要与Assets同级
这样就可以注入成功
详细代码
HotfixMain.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
[Hotfix]
public class HotfixMain : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
print(Add(10, 20));
Speak("阿喆不想学习");
}
public int Add(int a, int b)
{
return 0;
}
public static void Speak(string str)
{
Debug.Log("哈哈哈");
}
}
Hotfix_Lesson1.lua
print("*********第一个热补丁***********")
--直接写好代码 运行 是会报错的
--需要4个操作
--1.加特性
--2.加宏
--3.生成代码
--4.hotfix 注入 在注入时可能报错,提示你要引入Tools
--热补丁的缺点:只要我们修改了热补丁类的代码,就要重新注入
--lua当中 热补丁代码固定写法
--xlua.hotfix(类,"函数名",lua函数)
--成员函数 记得把自己传进来
xlua.hotfix(CS.HotfixMain,"Add",function(self,a,b)
return a + b
end)
--静态函数不用传自己
xlua.hotfix(CS.HotfixMain,"Speak",function(str)
print(str)
end)
注意热补丁的缺点:只要我们修改了热补丁类的代码,就要重新注入
注意如果是在类中新增加函数,而不是单纯改原函数逻辑,还要生成代码。
多函数替换(构造、析构)
构造函数热补丁特点
先调用了原逻辑,后调用lua逻辑
HotfixMain.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
//没继承Mono的类
[Hotfix]
public class HotfixTest
{
public HotfixTest()
{
Debug.Log("HotfixTest构造函数");
}
public void Speak(string str)
{
Debug.Log(str);
}
~HotfixTest() { }
}
[Hotfix]
public class HotfixMain : MonoBehaviour
{
HotfixTest hotTest;
// Start is called before the first frame update
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
print(Add(10, 20));
Speak("阿喆不想学习");
hotTest = new HotfixTest();
hotTest.Speak("哈哈哈哈");
}
private void Update()
{
}
public int Add(int a, int b)
{
return 0;
}
public static void Speak(string str)
{
Debug.Log("哈哈哈");
}
}
Hotfix_Lesson2.lua
print("*********多函数替换***********")
--lua当中 热补丁代码固定写法
--xlua.hotfix(类,"函数名",lua函数)
--xlua.hotfix(类,{函数名 = 函数, 函数名 = 函数, ....})
xlua.hotfix(CS.HotfixMain,{
Update = function(self)
print(os.time())
end,
Add = function(self,a,b)
return a + b
end,
Speak = function(a)
print(a)
end
})
xlua.hotfix(CS.HotfixTest,{
--构造函数 热补丁固定写法!!!!
--他们和别的函数不同 不是替换 是先调用原逻辑 再调用lua的逻辑
[".ctor"] = function()
print("lua热补丁构造函数")
end,
Speak = function(self,a)
print("阿喆说:"..a)
end,
--析构函数固定写法Finalize
Finalize = function()
print("lua热补丁析构函数")
end
})
协程函数替换
更新HotfixMain
[Hotfix]
public class HotfixMain : MonoBehaviour
{
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
StartCoroutine(TestCoroutine());
}
IEnumerator TestCoroutine()
{
while (true)
{
yield return new WaitForSeconds(1f);
Debug.Log("C#协程打印一次");
}
}
}
Hotfix_Lesson3.lua
print("*********协程函数替换***********")
--要在lua中配合C#协程函数 必使用它
util = require("xlua.util")
--xlua.hotfix(类,{函数名 = 函数, 函数名 = 函数, ....})
xlua.hotfix(CS.HotfixMain,{
TestCoroutine = function(self)
--返回一个正儿八经的 xlua处理过的lua写成函数
return util.cs_generator(function()
while true do
coroutine.yield(CS.UnityEngine.WaitForSeconds(1))
print("lua打补丁后的协程函数")
end
end)
end
})
--如果为打了Hotfix特性的C#类中新增加函数
--不能只注入 必须要先生成代码 再注入 不然注入会报错
索引器和属性替换
新增的原C#索引器与属性
[Hotfix]
public class HotfixMain : MonoBehaviour
{
public int[] array = new int[] { 1, 2, 3 };
//属性
public int Age
{
get { return 0; }
set { Debug.Log(value); }
}
//索引器
public int this[int index]
{
get {
if(index >= array.Length || index < 0)
{
Debug.Log("索引不正确");
return 0;
}
return array[index];
}
set
{
array[index] = value;
}
}
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
this.Age = 100;
Debug.Log(Age);
this[99] = 100;
Debug.Log(this[9999]);
}
}
Hotfix_Lesson4
print("*********属性和索引器替换***********")
xlua.hotfix(CS.HotfixMain,{
--如果是属性进行热补丁重定向
--set_属性名 是设置属性 的方法
--get_属性名 是得到属性 的方法
set_Age = function(self,v)
print("lua重定向的属性:"..v)
end,
get_Age = function(self)
return 10
end,
--索引器固定写法
--set_Item 设置索引器
--get_Item 通过索引器获取
set_Item = function (self, index, v )
print("lua重定向索引器,索引:"..index.."值"..v)
end,
get_Item = function(self,index)
print("lua重定向索引器")
return 999
end
})
事件替换
更新事件
[Hotfix]
public class HotfixMain : MonoBehaviour
{
//事件
event UnityAction myEvent;
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
myEvent += TestTest;
myEvent -= TestTest;
}
private void TestTest()
{
}
}
Hotfix_Lesson5
print("*********事件加减替换***********")
xlua.hotfix(CS.HotfixMain,{
--add_事件名 代表着事件加操作
--remove_事件名 减操作
add_myEvent = function(self,del)
print(del)
print("添加事件函数")
--有的人会去尝试使用lua使用C#事件的方法去添加
--在事件加减的重定向lua函数中
--千万不要把传入的委托往事件里存
--否则会死循环
--会把传入的 函数 存在lua中!!!!!!!!!!!!
--self:myEvent("+",del)
end,
remove_myEvent = function (self, del)
print(del)
print("移除事件函数")
end
})
泛型类替换
更新泛型类
[Hotfix]
public class HotfixTest2<T>
{
public void Test(T str)
{
Debug.Log(str);
}
}
[Hotfix]
public class HotfixMain : MonoBehaviour
{
void Start()
{
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");
HotfixTest2<string> t1 = new HotfixTest2<string>();
t1.Test("123");
HotfixTest2<int> t2 = new HotfixTest2<int>();
t2.Test(100);
}
}
Hotfix_Lesson6
print("*********泛型类的替换***********")
--泛型类 T是可以变化 那lua中应该如何替换呢?
--lua中的替换 要一个一个来
xlua.hotfix(CS.HotfixTest2(CS.System.String),{
Test = function(self,str)
print("lua中打的补丁"..str)
end
})
xlua.hotfix(CS.HotfixTest2(CS.System.Int32),{
Test = function(self,str)
print("lua中打的补丁"..str)
end
})
注意事项
1.成员方法记得加self
2.事件加减替换 不要把传入的委托往事件里存