Lua热更新原理与流程

目录

一、Lua热更新原理1、为什么要用lua做热更新,而不用C#?2、C#能不能实现热更新?

二、热更新流程1、在C#调用lua代码2、更新lua代码3、Hotfix补丁

三、XLua中的C#Api四、LuaMgr类参考四、总结

一、Lua热更新原理

利用第三方Lua热更新库,在Unity中嵌入lua解释器,利用解释器执行lua脚本。需要热更新的时候,从服务器上下载需要更新的代码文件

1、为什么要用lua做热更新,而不用C#?

C#语言本身是一种静态编译语言,它的代码在编译时会被转换成机器码,然后才能被计算机执行。这种静态编译的特性使得C#在运行时无法直接修改已编译的代码。另外,C#语言在运行时使用的是Common Language Runtime (CLR)环境,CLR负责将C#代码转换成可执行的机器码。CLR在运行时会对代码进行各种优化和安全检查,这些优化和检查过程也使得热更新变得困难。 Lua是一种动态脚本语言,它的代码在运行时会被解释器逐行执行,而不需要进行静态编译。这种动态性使得Lua可以在运行时修改已加载的代码,实现热更新。具体来说,Lua的解释器可以在运行时动态加载新的脚本文件,并替换已加载的代码模块。这种动态加载和替换的机制使得Lua可以实现热更新,即在程序运行过程中修改和更新代码,而无需重新启动整个程序。 其次,C#不能打包成AB包,而Lua可以,Lua结合AB包使用起来非常方便。

2、C#能不能实现热更新?

可以,利用反射实现 反射是什么?

C#反射是指在运行时动态地获取和操作程序集、类型、成员等信息的机制。通过反射,可以在运行时通过程序集中的元数据来实例化对象、调用方法、获取和设置属性等,而不需要在编译时明确知道这些类型和成员的具体信息,主要用到Type,Assembly,Activator三个类,这里不过多赘述,详细内容可以自行学习。

C#反射实现热更新 将需要频繁更改的逻辑部分独立出来做成DLL(动态链接库(Dynamic Link Library),可以包含类,函数等资源,让其他程序动态加载使用),通过反射机制加载这些DLL,主模块代码不作修改,热更新时用新的dll替换旧的dll,主模块加载dll就起到了更新的效果。不过c#反射只适用于Android热更新,iOS因为自身的安全机制,新申请的内存空间(用于修改后的代码使用)不允许进行写操作,所以目前c#的反射基本不被商业项目用作热更新。

二、热更新流程

1、在C#调用lua代码

1、下载热更新库(XLua,toLua等),导入Unity项目 2、创建基于Lua解释器的LuaMgr,用lua解释器来解释执行lua代码 3、在Unity中需要使用lua代码的地方用require(“文件名.lua”)的方式调用lua代码

2、更新lua代码

搭建一个ftp服务器,在需要进行更新检查的地方(如游戏主页面),连接到服务器,下载对比文件获取到文件名列表和md5码,与本地的对比文件进行对比 a、如果有新的文件名,则证明有新增文件,下载即可 b、如果文件名相同,md5码不同,则证明文件有改动,需要下载并覆盖本地文件 c、如果文件名和md5码完全一致,则证明不需要更新

3、Hotfix补丁

对于一开始用C#开发的项目,可以用热补丁的形式将C#代码里的方法替换为Lua里的方法

三、XLua中的C#Api

LuaEnv类 object[] DoString(string chunk, string chunkName = “chuck”, LuaTable env = null) 执行一个代码块。

T LoadString(string chunk, string chunkName = “chunk”, LuaTable env = null) 加载一个代码块,但不执行,只返回类型可以指定为一个delegate或者一个LuaFunction

LuaTable Global; 代表lua全局环境的LuaTable

void Tick() 清除Lua的未手动释放的LuaBase对象(比如:LuaTable, LuaFunction),以及其它一些事情。 需要定期调用,比如在MonoBehaviour的Update中调用。

void AddLoader(CustomLoader loader) 增加一个自定义loader

void Dispose() Dispose该LuaEnv

LuaTable类 T Get(string key) 获取在key下,类型为T的value,如果不存在或者类型不匹配,返回null;

T GetInPath(string path) 和Get的区别是,这个函数会识别path里头的“.”,比如var i = tbl.GetInPath(“a.b.c”)相当于在lua里头执行i = tbl.a.b.c,避免仅为了获取中间变量而多次调用Get,执行效率更高。

void SetInPath(string path, T val) 和GetInPaht对应的setter;

void Get(TKey key, out TValue value) 上面的API的Key都只能是string,而这个API无此限制;

void Set(TKey key, TValue value) 对应Get的setter;

T Cast() 把该table转成一个T指明的类型,可以是一个加了CSharpCallLua声明的interface,一个有默认构造函数的class或者struct,一个Dictionary,List等等。

void SetMetaTable(LuaTable metaTable) 设置metaTable为table的metatable

LuaFunction类 object[] Call(params object[] args) 以可变参数调用Lua函数,并返回该调用的返回值。

object[] Call(object[] args, Type[] returnTypes) 调用Lua函数,并指明返回参数的类型,系统会自动按指定类型进行转换。

void SetEnv(LuaTable env) 相当于lua的setfenv函数。

XLua.CSObjectWrap:用于将C#对象包装为Lua对象的工具类。

Register(Type type, LuaCSFunction creator):注册C#类型到Lua环境中。 CreateDelegate(LuaFunction func, Type delegateType):创建一个委托对象。

XLua.CSharpCallLuaAttribute:用于标记C#调用Lua函数的特性。

LuaCallCSharp:标记Lua函数可以被C#调用。 CSharpCallLua:标记C#函数可以被Lua调用。

四、LuaMgr类参考

其中ABMgr是我自己实现的一个加载AB包的管理器,原理不复杂,可以自行实现

public class LuaMgr : BaseManager

{

private LuaEnv luaEnv;

public LuaTable Global

{

get

{

return luaEnv.Global;

}

}

public void Init()

{

if (luaEnv != null)

{

return;

}

luaEnv = new LuaEnv();

luaEnv.AddLoader(MyLoader);

luaEnv.AddLoader(MyCustomABLoader);

}

public void DoLuaFile(string fileName)

{

string str = string.Format("require('{0}')", fileName);

DoString(str);

}

//自动执行

private byte[] MyLoader(ref string filePath)

{

string path = Application.dataPath + "/Lua/" + filePath + ".lua";

if (File.Exists(path))

{

return File.ReadAllBytes(path);

}

else

{

Debug.Log("文件重定向失败,文件名:"+filePath);

}

return null;

}

private byte[] MyCustomABLoader(ref string filePath)

{

TextAsset lua= ABMgr.GetInstance().LoadRes("lua", filePath + ".lua");

if (lua != null)

{

return lua.bytes;

}

else

{

Debug.Log("文件重定向失败,文件名:"+filePath);

return null;

}

}

public void DoString(string str)

{

if (luaEnv == null)

{

Debug.Log("解析器未初始化");

return;

}

luaEnv.DoString(str);

}

public void Tick()

{

luaEnv.Tick();

}

public void Dispose()

{

luaEnv.Dispose();

luaEnv = null;

}

}

四、总结

本期文章有点短,本来想再分析下lua的底层原理的,发现有点看不懂(; ̄ー ̄川,就先写这些吧