xlua的基本调用
using UnityEngine;
using System.Collections;
using XLua;
namespace Tutorial
{
public class ByString : MonoBehaviour
{
LuaEnv luaenv = null;
// Use this for initialization
void Start()
{
luaenv = new LuaEnv();
luaenv.DoString("print('hello world')");
}
// Update is called once per frame
void Update()
{
if (luaenv != null)
{
luaenv.Tick();
}
}
void OnDestroy()
{
luaenv.Dispose();
}
}
}
Xlua实现最简单的热更新
1.安装xlua且开启Hotfix在project->Player处点other Settings
当工程中 Script /脚本文件有变更的时候
例如增加/删除/增加标签[Hotfix]/增加函数标签[LuaCallCSharp]
注意:只要有任何变动,就需要重新生成和注入脚本
另外如果出现花式报错时,就清除所有,并重新生成和注入
2.自定义Loader
为了测试在工程中调用 Lua 文件
新建一个脚本 ChinarHotFix ,并挂载当前场景
新建一个 Lua 文件放在一个目录下(因为我们自定义的Loader,是要指定该目录中的Lua文件的)
Lua文件的后缀名,要与函数中的 路径后缀 保持一致
C#代码
using System.IO;
using System.Text;
using UnityEngine;
using XLua;
/// <summary>
/// 热更新测试脚本——该脚本新建一个 Lua环境,并完成对 Lua脚本的指向调用
/// </summary>
public class ChinarHotFix : MonoBehaviour
{
private LuaEnv luaEnv; //声明一个Lua环境对象
void Start()
{
luaEnv = new LuaEnv(); //实例化一个
luaEnv.AddLoader(ChinarLoader); //添加Loader
luaEnv.DoString("require'ChinarLuaTest'"); //引用名为: ChinarLuaTest 的 Lua 脚本
}
/// <summary>
/// 自定义一个 Loader
/// </summary>
/// <param name="luaFileName">Lua文件名</param>
/// <returns>字节组</returns>
private byte[] ChinarLoader(ref string luaFileName)
{
return Encoding.UTF8.GetBytes(File.ReadAllText(@"C:\Users\Administrator\Desktop\ChinarXLuaDemo\LuaFiles\" + luaFileName + ".lua")); //读指定目录下的 Lua 文件,并返回字节组
}
}
lua代码
print("Chinar")
释放Lua环境之前
运行后需要对 LuaEnv 环境进行释放
释放 LuaEnv 之前还要反注册,那些注册到C#中的回调函数
不然就会造成 LuaEnv 已经释放了,但是 Xlua 机制中的 Delegate 中的函数回调并没有被释放
直接新建一个 Lua 脚本,专门管理并释放 Delegate 中的函数的释放
例如:你通过Lua脚本 xlua.hotfix(CS.ChinarTest,'ChinarTestMethod',function) 注册到 C# 中的函数
则通过新建一个 Lua 脚本ChinarDispose.lua,写上 xlua.hotfix(CS.ChinarTest,'ChinarTestMethod',nil) 质空,即可完成释放
注意:每通过Lua脚本修改一个C#函数,都需要在ChinarDispose.lua脚本中添加对应函数的释放/删除操作
声明。
委托/回调:C#委托、事件、回调函数及结合运用详解_c# 事假、委托和回调组合用法-CSDN博客
更改后,可进行双清、释放的 C# 文件
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using XLua;
using System.IO;
using System.Text;
public class ChinarHotFix : MonoBehaviour
{
// Start is called before the first frame update
private LuaEnv luaEnv; //声明一个Lua环境对象
void Start()
{
luaEnv = new LuaEnv(); //实列化
luaEnv.AddLoader(ChinarLoader); //添加一个Loader
luaEnv.DoString("require'ChinarLuaTest'"); //引用名为:chinarLuaTest的lua脚本
}
/// <summary>
/// 自定义一个 Loader
/// </summary>
/// <param name="luaFileName">Lua文件名</param>
/// <returns>字节组</returns>
private byte[] ChinarLoader(ref string luaFileName)
{
return Encoding.UTF8.GetBytes(File.ReadAllText(@"E:\test\Assets\G_Resources\Scripts\Lua\test lua--csharp\" + luaFileName + ".lua")); //读指定目录下的 Lua 文件,并返回字节组
}
/// <summary>
/// 释放掉函数
/// 此函数会在 OnDestroy 之前调用
/// </summary>
private void OnDisable()
{
luaEnv.Dostring("require'ChinarDispose'");
}
private void OnDestroy()
{
luaEnv.Dispose();
}
}
lua
xlua.hotfix(CS.ChinarTest,'ChinarTestMethod',nil)
--xlua.hotfix(CS.ChinarTest1,'ChinarTestMethod1',nil)只要有修改C#中对应函数,都需要在这里完成释放操作
--xlua.hotfix(CS.ChinarTest2,'ChinarTestMethod2',nil)
--xlua.hotfix(CS.ChinarTest3,'ChinarTestMethod3',nil)
私有变量
正常情况,我们是无法直接通过 Lua 直接访问到 C# 中的私有变量的
然而 Xlua 机制为我们提供了一个非常简便的解决方案
当我们需要访问某个 C# 类中的私有变量时,只需要在 Lua 代码中加上一句话
xlua.private_accessible(CS.ChinarTest)
然后,我们就可以访问到 C# ChinarTest类中的私有变量了
using UnityEngine;
using XLua;
[Hotfix]
public class ChinarTest: MonoBehaviour
{
private int Number = 666;//私有数字变量--例如这么一个私有变量
}
lua加上
xlua.private_accessible(CS.ChinarTest)--只有加上这句话,才可以访问C#对应类中的私有变量
Lua与C#的相互调用(xLua)_c# lua-CSDN博客
C#与Lua数据通信机制
C#如何调用xlua
无论是Lua调用C#,还是C#调用Lua,都需要一个通信机制,来完成数据的传递。而Lua本身就是由C语言编写的,所以它出生自带一个和C/C++的通信机制。
Lua和C/C++的数据交互通过栈进行,操作数据时,首先将数据拷贝到"栈"上,然后获取数据,栈中的每个数据通过索引值进行定位,索引值为正时表示相对于栈底的偏移索引,索引值为负时表示相对于栈顶的偏移索引,索引值以1或-1为起始值,因此栈顶索引值永远为-1, 栈底索引值永远为1 。 “栈"相当于数据在Lua和C/C++之间的中转地。每种数据都有相应的存取接口
.dill科普:
.dll文件是Dynamic Link Library(动态链接库)文件的缩写,它是一种共享库文件,包含了程序所需的代码和数据。与静态链接库不同,动态链接库可以在程序运行时动态加载,使得程序的内存占用更小,同时也方便了程序的更新和维护。
动态链接库和静态链接库的区别
静态链接库(.LIB) 由函数和数据编译而成的一个二进制文件。使用时,在编译链接阶段,由链接器从库中复制这些函数和数据,并把他们与应用程序的其他模块组合起来创建最终的可执行文件。由于静态链接库中的程序和数据已经被复制并应用到可执行文件中,因此发布产品时不需要发布使用的静态库文件。
动态链接库 (.DLL) 包含被可执行程序或其他DLL调用来完成某项工作的函数,不可以直接运行,也不可以接收消息。动态链接库一半包含两个文件,引入库文件(.LIB)和动态链接库文件(.DLL)。使用时,在编译链接阶段,只需要链接引入库文件,动态链接库中的函数和数据并不复制到程序中,在运行阶段去访问DLL文件中的函数。由于动态链接库中函数和数据并没有被复制,因此发布产品时,必须包含动态链接库文件。
引入库文件 (.LIB): 包含该动态链接库包含的函数和变量的符号名。注意:虽然引入库文件和静态链接库文件的后缀名相同(.LIB),但是他们之间有着本质的区别,不可混淆。
动态链接库文件 (.DLL): 包含该动态链接库实际的函数和数据。在程序运行阶段,加载该文件,并将该文件映射到进程地址空间中,然后访问该文件中的相应函数。
详解释看这个为什么动态链接.dll和.lib都需要(详解静、动态链接库)_windows的动态链接库为什么多个lib文件-CSDN博客
传递Lua table到C#
以TestXLua类为例来看Lua table是如何被传递的,TestXLua有一个LuaTable类型的静态变量,LuaTable是C#这边定义的一个类,封装了一些对Lua table的操作
public class TestXLua
{
public static LuaTable tab;
}
在点击Generate Code之后,部分生成代码如下所示。为tab变量生成了对应的set和get包裹方法
static int _g_get_tab(RealStatePtr L)
{
try {
ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
translator.Push(L, TestXLua.tab);
} catch(System.Exception gen_e) {
return LuaAPI.luaL_error(L, "c# exception:" + gen_e);
}
return 1;
}
[MonoPInvokeCallbackAttribute(typeof(LuaCSFunction))]
static int _s_set_tab(RealStatePtr L)
{
try {
ObjectTranslator translator = ObjectTranslatorPool.Instance.Find(L);
TestXLua.tab = (XLua.LuaTable)translator.GetObject(L, 1, typeof(XLua.LuaTable));
} catch(System.Exception gen_e) {
return LuaAPI.luaL_error(L, "c# exception:" + gen_e);
}
return 0;
}
给tab静态变量赋一个lua table ,table中包含一个 num=1的key-value
local t={
num=1
}
CS.TextXLua.tab=t;
上述代码在赋值时,最终会调用到_s_set_tab包裹方法(具体原理可以查看这里),Lua这边调用_s_set_tab前,会先将参数table t压入到栈中,因此_s_set_tab内部需要通过translator.GetObject拿到这个table,并将其赋值给tab静态变量
// ObjectTranslator.cs
public object GetObject(RealStatePtr L, int index, Type type)
{
int udata = LuaAPI.xlua_tocsobj_safe(L, index);
if (udata != -1)
{
// 对C#对象的处理
object obj = objects.Get(udata);
RawObject rawObject = obj as RawObject;
return rawObject == null ? obj : rawObject.Target;
}
else
{
if (LuaAPI.lua_type(L, index) == LuaTypes.LUA_TUSERDATA)
{
GetCSObject get;
int type_id = LuaAPI.xlua_gettypeid(L, index);
if (type_id != -1 && type_id == decimal_type_id)
{
decimal d;
Get(L, index, out d);
return d;
}
Type type_of_struct;
if (type_id != -1 && typeMap.TryGetValue(type_id, out type_of_struct) && type.IsAssignableFrom(type_of_struct) && custom_get_funcs.TryGetValue(type, out get))
{
return get(L, index);
}
}
return (objectCasters.GetCaster(type)(L, index, null));
}
}
GetObject方法负责从栈中获取指定类型的对象,对于LuaTable类型是通过objectCasters.GetCaster获取转换器后,通过转换器函数转换得到
// ObjectTranslator.cs
public ObjectCast GetCaster(Type type)
{
if (type.IsByRef) type = type.GetElementType(); // 如果是按引用传递的,则使用引用的对象的type
Type underlyingType = Nullable.GetUnderlyingType(type);
if (underlyingType != null)
{
return genNullableCaster(GetCaster(underlyingType));
}
ObjectCast oc;
if (!castersMap.TryGetValue(type, out oc))
{
oc = genCaster(type);
castersMap.Add(type, oc);
}
return oc;
}
xLua已经默认在castersMap中为一些类型定义好了转换函数,其中就包括LuaTable类型
// ObjectCasters.cs
public ObjectCasters(ObjectTranslator translator)
{
this.translator = translator;
castersMap[typeof(char)] = charCaster;
castersMap[typeof(sbyte)] = sbyteCaster;
castersMap[typeof(byte)] = byteCaster;
castersMap[typeof(short)] = shortCaster;
castersMap[typeof(ushort)] = ushortCaster;
castersMap[typeof(int)] = intCaster;
castersMap[typeof(uint)] = uintCaster;
castersMap[typeof(long)] = longCaster;
castersMap[typeof(ulong)] = ulongCaster;
castersMap[typeof(double)] = getDouble;
castersMap[typeof(float)] = floatCaster;
castersMap[typeof(decimal)] = decimalCaster;
castersMap[typeof(bool)] = getBoolean;
castersMap[typeof(string)] = getString;
castersMap[typeof(object)] = getObject;
castersMap[typeof(byte[])] = getBytes;
castersMap[typeof(IntPtr)] = getIntptr;
//special type
castersMap[typeof(LuaTable)] = getLuaTable;
castersMap[typeof(LuaFunction)] = getLuaFunction;
}
LuaTable对应的转换函数是getLuaTable
// ObjectCasters.cs
private object getLuaTable(RealStatePtr L, int idx, object target)
{
if (LuaAPI.lua_type(L, idx) == LuaTypes.LUA_TUSERDATA)
{
object obj = translator.SafeGetCSObj(L, idx);
return (obj != null && obj is LuaTable) ? obj : null;
}
if (!LuaAPI.lua_istable(L, idx))
{
return null;
}
// 处理普通table类型
LuaAPI.lua_pushvalue(L, idx);
return new LuaTable(LuaAPI.luaL_ref(L), translator.luaEnv);
}
getLuaTable的主要逻辑是将idx处的table通过luaL_ref添加到Lua注册表中并得到指向该table的索引,然后创建LuaTable对象保存该索引。也就是说Lua table在C#这边对应的是LuaTable对象,它们之间通过一个索引关联起来,这个索引表示Lua table在Lua注册表中的引用,利用这个索引可以获取到Lua table。拿到Lua table后,就可以继续访问Lua table的内容了
// CS测试代码
int num = TestXLua.tab.Get<int>("num");
对Lua table的访问操作都被封装在LuaTable的Get方法中