【Lua热更新】下篇 -- 更新中
上篇链接:【Lua热更新】上篇
文章目录
- 三、`xLua`热更新
- 📖1.概述
- 📚︎2.导入`xLua`框架
- 🔖3. C#调用`Lua`
- 3.1`Lua`解析器
- 3.2`Lua`文件夹的重定向
- 3.3`Lua`解析器管理器
- 3.4全局变量获取
- 3.5全局函数获取
- 3.6映射到List和Dictionary
- 3.7映射到类
- 3.8映射到接口
- 3.9映射到`LuaTable`
- 🔖4.`Lua`调用C#
- 🔖5.`xLua`热补丁
三、xLua热更新
📖1.概述
C#代码和Resoureces文件夹不能实现热更新,所以需要使用Lua通过AB包来进行更新脚本

📚︎2.导入xLua框架
🔹1.获得xLua包,GitHub地址:Tencent/xLua: xLua is a lua programming solution for C#
🔹2.导入文件夹

增加了xLua编辑栏导入成功
🔹3.点击Generate Code生成相关代码

🔹4.导入AssetBundles-Browser
GitHub地址:Unity-Technologies/ AssetBundles -Browser
如果有报错删除文件中的报错的Test文件即可
🔖3. C#调用Lua
3.1Lua解析器
Lua解析器让我们在Unity中执行Lua
🎒简述一下原理,就是Lua是一种解释型语言,代码在运行时才被解释器一行行动态翻译和执行,先由Lua编译器编译为字节码,然后交给Lua虚拟机执行,将Lua虚拟机源代码整合给Unity使用实现之间的交互
// 创建一个解析器
LuaEnv luaEnv = new LuaEnv();
// 使用Lua解析器执行Lua语言
luaEnv.DoString("print('First Lua')");
// Lua垃圾回收,手动释放对象
luaEnv.Tick();
// 销毁Lua解析器
luaEnv.Dispose();
🔹Lua默认调用Resources文件夹下的Lua脚本
执行以下代码会发现脚本报错没有找到脚本,原因是因为.lua不是Unity中的文件格式,所以我们需要给.lua在添加一个.txt后缀才能查找到,这个查找函数的源码估计是通过Resourece.Load加载,所以查找不到Lua文件
luaEnv.DoString("require('FistTest')");

❓存在的问题,从Resources文件夹下读取无法进行热更新,其次每个文件都要手动添加.txt后缀十分麻烦,所以后续需要进行文件夹的重定向和自动
3.2Lua文件夹的重定向
通过AddLoader方法进行重定向,进行自定义加载Lua文件规则
void Start()
{luaEnv.AddLoader(MyCustomLoader);luaEnv.DoString("require('FistTest')");
}private byte[] MyCustomLoader(ref string path)
{string newPath = Application.dataPath + "/Lua/" + path + ".lua";if (File.Exists(newPath))return File.ReadAllBytes(newPath);elseDebug.Log("重定向失败" + path);return null;
}
🎒我们看一下AddLoader源码
传入一个委托添加到 List 列表中,在我们使用require(文件名)时如果在列表中按顺序查找该文件,那么最后将在Resoureces文件夹中查找

❓存在问题,最终我们需要在AB包中进行加载
3.3Lua解析器管理器
🔹通过实现Lua解析器管理器保证解析器的唯一性
👓️LuaMgr管理器源码
public class LuaMgr : BaseManager<LuaMgr>
{private const string LUA_PATH = "Lua";private LuaEnv luaEnv;public LuaTable Global {get => luaEnv.Global;} // 获得_G表// 初始化解析器public void Init(){if (luaEnv != null) return;luaEnv = new LuaEnv();luaEnv.AddLoader(MyCustomLoader); // 后缀.lua时日常开发重定向到的位置luaEnv.AddLoader(MyCustomABLoader); // 后缀.txt使用AB包时重定向到的位置}// 重定向加载AB包中的Lua脚本private byte[] MyCustomABLoader(ref string path){TextAsset txtLua = ABMgr.GetInstance().LoadRes<TextAsset>(LUA_PATH, path + ".lua");if (txtLua == null){Debug.LogError("在AB包中重定向失败");return null;}return txtLua.bytes;}private byte[] MyCustomLoader(ref string path){string newPath = Application.dataPath + "/"+ LUA_PATH + "/" + path + ".lua";if (File.Exists(newPath)){return File.ReadAllBytes(newPath);}else{Debug.LogError("重定向失败" + path);}return null;}// 执行lua语言public void DoString(string filename){if (luaEnv == null){Debug.LogError("解析器未初始化");return;}luaEnv.DoString(string.Format("require('{0}')", filename));}// 释放垃圾public void Tick(){if (luaEnv == null){Debug.LogError("解析器未初始化");return;}luaEnv.Tick();}// 销毁解析器public void Dispose(){luaEnv.Dispose();luaEnv = null;}
}
👓️BaseManager 源码
//1.C#中 泛型的知识
//2.设计模式中 单例模式的知识
public class BaseManager<T> where T:new()
{private static T instance;public static T GetInstance(){if (instance == null)instance = new T();return instance;}
}
❓现在只实现了重定向文件夹,并没有实现重定向AB包加载,接下来继续完成将Lua脚本放在AB包中,通过AB包加载脚本。需要注意的是AB包加载文本后缀不能使用.Lua,所以在打包时需要将后缀改为.txt,不过这一步后续将改成自动的
🔹我们将后缀暂时手动的修改后缀为.txt,进行进行打包,如果打包过程中存在报错,XLua → Clear Generated Code即可

打包成功后刷新界面

重定向加载AB包
🔹这里建议实际开发中仍然使用.Lua后缀,那么将重定向读取普通文件夹,进行热更新时在修改为.txt后缀重定向到AB包文件夹
// 重定向加载AB包中的Lua脚本
private byte[] MyCustomABLoader(ref string path)
{string newPath = Application.streamingAssetsPath + "/"+ LUA_PATH;AssetBundle ab = AssetBundle.LoadFromFile(newPath); // 加载AB包TextAsset tx = ab.LoadAsset<TextAsset>(path + ".lua"); // 加载Lua文件return tx.bytes;
}
3.4全局变量获取
👓️Main.Lua源码
print("=== Main ===")
require('FirstTest') -- 在Unity中已经重定向
👓️FirstTest.Lua 源码
print("=== First Test ===")
myNumber = 1
myFloat = 1.2
myBool = true
myString = "OvO"
👓️Unity中代码
🔹无法直接获得local本地变量,因为不在_G表中
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoString("Main");// Get
int myNumber = LuaMgr.GetInstance().Global.Get<int>("myNumber");
float myFloat = LuaMgr.GetInstance().Global.Get<float>("myFloat");
bool myBool = LuaMgr.GetInstance().Global.Get<bool>("myBool");
string myString = LuaMgr.GetInstance().Global.Get<string>("myString");// Set
LuaMgr.GetInstance().Global.Set("myNumber", 99);
myNumber = LuaMgr.GetInstance().Global.Get<int>("myNumber");
3.5全局函数获取
👓️FirstTest.Lua 源码
myFun1 = function ()print("这是一个无参无返回的函数")
endmyFun2 = function (x)print("这是一个有参有返回的函数")return x + x
endmyFun3 = function ()print("这是一个多返回值的函数")return 99, "apple", 1.2f
endmyFun4 = function (x, ...)print("这是一个变长参数的函数")arg = {...}for k, v in pairs(arg) doprint(k, v)end
end
🔹无参数无返回函数获取
// Unity自带委托
UnityAction ua1 = LuaMgr.GetInstance().Global.Get<UnityAction>("myFun1");
ua1.Invoke();
// C#委托
Action ac = LuaMgr.GetInstance().Global.Get<Action>("myFun1");
// xLua提供:不建议使用
LuaFunction lf = LuaMgr.GetInstance().Global.Get<LuaFunction>("myFun1");
lf.Call();
🔹有参数有返回函数获取
定义一个委托,需要使用特性否则Lua无法识别,XLua → Generate Code在编译器重新生成一下代码否则也会报错
[CSharpCallLua]
public delegate int MyFunc2(int x);
调用函数
MyFunc2 ua2 = LuaMgr.GetInstance().Global.Get<myFunc2>("myFun2");
int x = ua2.Invoke(2);
print("返回值是" + x);
🔸或者使用C#封装的委托,如果报错就XLua → Generate Code
Func<int, int> ua2 = LuaMgr.GetInstance().Global.Get<Func<int, int>>("myFun2");
int x = ua2.Invoke(2);
print("返回值是" + x);
🔹多返回值函数获取
// 第一个返回值即委托返回值
// 之后的返回值使用out或者ref
[CSharpCallLua]
public delegate int MyFun3(int x, out string fruit, out float value);
调用
MyFun3 fc3 = LuaMgr.GetInstance().Global.Get<MyFun3>("myFun3");
string fruit;
float value;
int a = fc3.Invoke(12, out fruit, out value);
或者使用xLua自带的获取方法
LuaFunction lf3 = LuaMgr.GetInstance().Global.Get<LuaFunction>("myFun3");
object[] ojb = lf3.Call(100);
🔹多返回值函数获取
// 如果变长参数每个类型都不确定传入Object
[CSharpCallLua]
public delegate void MyFun4(string a, params int[] args);
3.6映射到List和Dictionary
👓️FirstTest.Lua 源码
testList = {1, 2, 3, 4}
testDic = {["a"] = 1,["b"] = 2,["c"] = 3
}
获取
LuaMgr.GetInstance().Init();
LuaMgr.GetInstance().DoLuaFile("Main");List<int> testList = LuaMgr.GetInstance().Global.Get<List<int>>("testList");
Dictionary<string, int> testDic = LuaMgr.GetInstance().Global.Get<Dictionary<string, int>>("testDic");
3.7映射到类
👓️FirstTest.Lua 源码
testClass = {name = "咪咪",age = 1,eat = function ()print("吃猫粮")end
}
🔹C#中声明该类,成员名称必须和lua中一致,类名可以不需要一致,成员必须是公共的
// 少于lua中的变量则获取不到,多余lua中的变量则不会赋值,并不会报错
public class TestClass
{public string name;public int age;public UnityAction eat;
}
3.8映射到接口
🔹声明接口,接口中不允许声明成员变量,所以使用成员属性来接收
🔹接口拷贝是引用拷贝,在C#中修改值Lua中也会被修改
[CSharpCallLua] // 需要特性
public interface MyInterface
{public string name { get; set; }public int age { get; set; }public UnityAction eat{ get; set; }
}
获取
MyInterface myInterface = LuaMgr.GetInstance().Global.Get<MyInterface>("TestClass");
3.9映射到LuaTable
官方不建议使用LuaTable和LuaFunction会有垃圾占用,LuaTable需要手动销毁否则会一致占用内存
LuaTable table = LuaMgr.GetInstance().Global.Get<LuaTable>("TestClass");
// 获得
string name = table.Get<string>("name");
int age = table.Get<int>("age");
UnityAction ac = table.Get<UnityAction>("eat");// 修改
table.Set("age", 55);
// 释放table垃圾
table.Dispose();
🔖4.Lua调用C#
🔖5.xLua热补丁
相关文章:
【Lua热更新】下篇 -- 更新中
上篇链接:【Lua热更新】上篇 文章目录 三、xLua热更新📖1.概述📚︎2.导入xLua框架🔖3. C#调用Lua3.1Lua解析器3.2Lua文件夹的重定向3.3Lua解析器管理器3.4全局变量获取3.5全局函数获取3.6映射到List和Dictionary3.7映射到类3.8映…...
射频测试入门学习(三)——程控仪器是怎样和电脑连接通信的
目录 一、程控仪器需要哪些条件 二、可程控仪器 三、专业的仪器通信软件、驱动 四、编程语言 五、电脑控制仪器条件汇总 六、仪器指令查询 七、结语 一、程控仪器需要哪些条件 1、需要具备硬件条件(可程控的仪器、个人计算机(PC)) 2、专业的仪器通信软件、驱动 3、…...
并发控制之Semaphore
Semaphore 作用 信号量,用于控制同时访问特定资源的线程数量,通过协调各个线程,以确保对共享资源的访问不会导致冲突或数据不一致等问题,有点类似令牌桶,内部维护一组许可证, acquire获取许可证…...
第R3周:RNN-心脏病预测
🍨 本文为🔗365天深度学习训练营 中的学习记录博客🍖 原作者:K同学啊 🍺要求: 1 本地读取并加载数据。 2 了解循环神经网络(RNN)的构建过程 3 测试集accuracy到达87% 🍻拔…...
【数值特性库】入口文件
数值特性库入口文件为lib.rs。该文件定义一系列数字特性的trait(特征),这些特性可以被不同的数字类型实现,从而提供一套通用的数值操作方法。下面是对代码中关键部分的解释: 一、基础设置 #![doc(html_root_url “h…...
RestTemplate实时接收Chunked编码传输的HTTP Response
学习调用AI接口的时候,流式响应都是使用的 Transfer-Encoding: chunked,图方便想用RestTemplate,但是平时用到的都是直接返回响应对象的类型。使用bing搜索到一种方式,使用下面的代码来读取,于是掉这个坑里了ÿ…...
GIT区域介绍及码云+GIt配置仓库
GIT区域介绍 创建文件夹git init 1、git有3个区域 工作区(working directory):项目的根目录,不包 括.git在内的其他文件暂存区(stage area):是一个看不见的区域,git add 命令就是将文…...
网络安全怎么学习
当我们谈论网络安全时,我们正在讨论的是保护我们的在线空间,这是我们所有人的共享责任。网络安全涉及保护我们的信息,防止被未经授权的人访问、披露、破坏或修改。 一、网络安全的基本概念 网络安全是一种保护:它涉及保护我们的设…...
PugiXML,一个高效且简单的 C++ XML 解析库!
嗨,大家好!我是一行。今天要给大家介绍 PugiXML,这可是 C 里处理 XML 数据的得力助手。它能轻松地读取、修改和写入 XML 文件,就像一个专业的 XML 小管家,不管是解析配置文件,还是处理网页数据,…...
Linux设备树的驱动开发
概述 本文介绍了platform框架下的设备驱动开发流程和方法,主要包括设备树、驱动程序和应用程序的开发。以随机数驱动为例,实现了应用程序调用库函数,通过系统调用陷入内核,最后执行硬件驱动,获取真随机数的过程。 添…...
连锁?下沉?AI?2025年餐饮新活力!
如果要用几个词来形容 2024 年的餐饮业,这些词大概率会是「卷、难、惨」,用著名商业顾问刘润的话来说就是「卷到极致」。虽然餐饮人在社交平台上叫苦连天,但当我们查看餐饮大盘数据时发现,大盘在涨,与个体餐饮人的实感…...
Javascript中如何实现函数缓存?函数缓存有哪些应用场景?
今天要聊的一个很经典的问题——如何在JavaScript中实现函数缓存,以及它有哪些应用场景。 我们先来明确一下,函数缓存是什么。简单来说,函数缓存是将函数的运算结果存储起来,以便下次用到相同的输入时,可以直接返回结…...
子页面访问父页面
子页面访问父页面的方式主要依赖于页面之间的关系,特别是它们是否处于同一域、是否是嵌套在 <iframe> 中、或者通过弹出窗口打开。下面是几种常见的子页面访问父页面的方法: 1. 通过 window.parent 访问父页面(适用于嵌套的 iframe&am…...
芯片级IO (Pad) Ring IP Checklist
SoC top顶层数字后端实现都会涉及到IO Ring (PAD Ring)的设计。这里面包括VDD IO,VDDIO IO, Signal IO, Corner IO,Filler IO,IO power cut cell等等。 数字后端零基础入门系列 | Innovus零基础LAB学习Day2 数字IC后端实现TOP F…...
计算机毕业设计论文指导
计算机毕业设计论文指导 计算机毕业设计辅导一站式!太香了💪 [赞R][赞R][赞R]嗨喽!计算机专业的宝子们! 计算机毕设辅导专业靠谱的他来了!! 是不是还在为选题程序不会做而感到苦难? 论文没思路赶…...
Electron-Vue 开发下 dev/prod/webpack server各种路径设置汇总
背景 在实际开发中,我发现团队对于这几个路径的设置上是纯靠猜的,通过一点点地尝试来找到可行的路径,这是不应该的,我们应该很清晰地了解这几个概念,以下通过截图和代码进行细节讲解。 npm run dev 下的路径如何处理&…...
Vue.js前端框架教程9:Vue插槽slot用法
文章目录 插槽(Slots)无名插槽(默认插槽)具名插槽reference 插槽使用 v-slot 的缩写语法 插槽(Slots) 在 Vue 中,插槽(Slots)是一种组件内容分发的机制,允许…...
初学stm32 --- NVIC中断
目录 STM32 NVIC 中断优先级管理 NVIC_Type: ISER[8]: ICER[8]: ISPR[8]: ICPR[8]: IABR[8]: IP[240]: STM32 的中断分组: 中断优先级分组函数 NVIC_PriorityGroupConfig 中断初始化函…...
Jest 入门指南:从零开始编写 JavaScript 单元测试
前言 在前端开发中,单元测试已经成为确保代码质量和稳定性的关键步骤。Jest 作为由 Facebook 开发和维护的功能强大的 JavaScript 测试框架,以其易于配置、丰富的功能和开箱即用的特性,成为众多开发者的首选工具。本文旨在引导你从零开始&am…...
【Java Web】Axios实现前后端数据异步交互
目录 一、Promise概述 二、Promise基本用法 三、async和await关键字 四、Axios介绍 4.1 Axios基本用法 4.2 Axios简化用法之get和post方法 五、Axios拦截器 六、跨域问题处理 一、Promise概述 axios是代替原生的ajax实现前后端数据交互的一套新解决方案,而…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
【力扣数据库知识手册笔记】索引
索引 索引的优缺点 优点1. 通过创建唯一性索引,可以保证数据库表中每一行数据的唯一性。2. 可以加快数据的检索速度(创建索引的主要原因)。3. 可以加速表和表之间的连接,实现数据的参考完整性。4. 可以在查询过程中,…...
微信小程序 - 手机震动
一、界面 <button type"primary" bindtap"shortVibrate">短震动</button> <button type"primary" bindtap"longVibrate">长震动</button> 二、js逻辑代码 注:文档 https://developers.weixin.qq…...
鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序
一、开发环境准备 工具安装: 下载安装DevEco Studio 4.0(支持HarmonyOS 5)配置HarmonyOS SDK 5.0确保Node.js版本≥14 项目初始化: ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...
vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
Redis数据倾斜问题解决
Redis 数据倾斜问题解析与解决方案 什么是 Redis 数据倾斜 Redis 数据倾斜指的是在 Redis 集群中,部分节点存储的数据量或访问量远高于其他节点,导致这些节点负载过高,影响整体性能。 数据倾斜的主要表现 部分节点内存使用率远高于其他节…...
WebRTC从入门到实践 - 零基础教程
WebRTC从入门到实践 - 零基础教程 目录 WebRTC简介 基础概念 工作原理 开发环境搭建 基础实践 三个实战案例 常见问题解答 1. WebRTC简介 1.1 什么是WebRTC? WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时语音…...
从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障
关键领域软件测试的"安全密码":Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天,软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力,从金融交易到交通管控,这些关乎国计民生的关键领域…...
Spring Security 认证流程——补充
一、认证流程概述 Spring Security 的认证流程基于 过滤器链(Filter Chain),核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤: 用户提交登录请求拦…...
