【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

官方不建议使用LuaTableLuaFunction会有垃圾占用,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热补丁

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/64540.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Gin-vue-admin(1):环境配置和安装

目录 环境配置如果443网络连接问题&#xff0c;需要添加代理服务器 后端运行前端运行 环境配置 git clone https://gitcode.com/gh_mirrors/gi/gin-vue-admin.git到server文件目录下 go mod tidygo mod tidy 是 Go 语言模块系统中的一个命令&#xff0c;用于维护 go.mod 文件…

C# 中的闭包

文章目录 前言一、闭包的基本概念二、匿名函数中的闭包1、定义和使用匿名函数2、匿名函数捕获外部变量3、闭包的生命周期 三、Lambda 表达式中的闭包1、定义和使用 Lambda 表达式2、Lambda 表达式捕获外部变量3、闭包的作用域 四、闭包的应用场景1、事件处理2、异步编程3、迭代…

视阅口译与其他口译的不同点

与其他口译不同 译员在进行视阅口译时可以看到书面材料&#xff0c;这与交替口译和同声口译有所不同。译员能够提前浏览文本内容&#xff0c;对主题、结构和专业词汇等有初步的了解。 压力相对较小 视阅口译相较于同声传译而言&#xff0c;时间压力没有那么紧迫。译员有一定…

功能篇:JAVA8实现数据去重

在Java 8中&#xff0c;有多种方法可以实现集合的去重。下面我将介绍几种常见的方法&#xff1a; ### 使用Set接口 最简单的方法是使用Set接口&#xff0c;因为根据定义&#xff0c;Set不允许重复元素。如果你有一个List并且想要去除其中的重复项&#xff0c;你可以将其转换为…

初学stm32 --- NVIC中断

目录 STM32 NVIC 中断优先级管理 NVIC_Type: ISER[8]&#xff1a; ICER[8]&#xff1a; ISPR[8]&#xff1a; ICPR[8]&#xff1a; IABR[8]&#xff1a; IP[240]&#xff1a; STM32 的中断分组&#xff1a; 中断优先级分组函数 NVIC_PriorityGroupConfig 中断初始化函…

网卡 TSO(提示发送端性能), LRO, GRO (提升接收端性能)功能

功能核心要点工作原理相关层级TSO (TCP Segmentation Offload)通过硬件完成TCP分段&#xff0c;减少CPU负担将大块数据分段为多个较小的TCP包&#xff0c;在网卡处进行分段处理网卡驱动&#xff0c;内核态LRO (Large Receive Offload)在网卡处合并多个TCP包&#xff0c;减少中断…

Dot Foods EDI 需求分析及对接流程

Dot Foods 是一家美国领先的食品和非食品产品的中间批发分销商&#xff0c;主要为食品服务、零售和分销行业的客户提供服务&#xff0c;是北美大型食品中间分销商之一。Dot Foods &#xff08;以下简称 Dot&#xff09;的业务模式是通过整合多个供应商的产品&#xff0c;为客户…

ic电路与mos管

最近调试步进电机发现不同的需求的情况下&#xff0c;使用是电路设计会不同&#xff0c;所以做个小结&#xff0c; 感叹现在的ai汇总真的厉害&#xff0c;目前daisy也经常使用&#xff0c;从之前的文言一心到现在的通义灵马和腾讯云ai&#xff0c;随着用户的增多可以明显感觉到…

MyBatis-Plus(一)

一、 MyBatis-Plus简介 1、简介 MyBatis-Plus &#xff08;简称 MP&#xff09;是一个 MyBatis的增强工具&#xff0c;在 MyBatis 的基础上只做增强不做改变&#xff0c;为 简化开发、提高效率而生。Mybatis-Plus提供了通用的Mapper和Service&#xff0c;可以在不编写任何SQ…

跟着AI 学AI开发二,本地部署自己的Chat GPT

这里要安装的是Open Web UI &#xff0c;用一张架构图说明AI 前端与后端的关系。 之前的Python 的方法已经做过多次介绍&#xff0c;这里不做赘述。 顺序&#xff1a;1&#xff0c;Ollama。 2&#xff0c;Docker。 3&#xff0c;Open WebUI。 Ollama 安装下载地址&#xff1…

204页PPT金税四期监管要求与最新政策及风险防范-培训课件

这份资料是关于金税四期监管要求、最新政策及风险防范的培训课件&#xff0c;包含204页PPT内容。核心内容涉及金税四期的发展历程、与金税三期的主要变化、指挥决策系统、税务风险预警指标、重点监控行为、税务稽查重点领域、避税方法及处罚、以及企业自查和税务风险管控原理。…

超声波流量计的原理及应用简介

一 概念 超声波流量计是利用超声波测量管道或导管中流体流速的装置。 它们由放置在管道或管道相对两侧的两个超声波换能器组成&#xff0c;它们发射和接收超声波。 二 技术路径 1.时差 (TT) 超声波技术 最常见的技术是传输时间测量。超声波穿过流体返回到相反的传感器&#…

线性规划中的几种逻辑表达式

线性规划中的几种逻辑表达式 注意&#xff1a; 摘录字刘博士的《数学建模与数学规划》&#xff0c; 以便用时可查。 实际上Gurobi API 中自身放啊变的逻辑表达式函数&#xff0c;下面列出自定义的实现方式。 1 逻辑与 如果 x 1 1 x_1 1 x1​1, x 2 1 x_2 1 x2​1, 那…

knife4j增强swagger

背景 swagger很好用&#xff0c;但是UI不够友好 导入maven依赖 使用maven-search在common模块中导入依赖 <!-- knife4j --><dependency><groupId>com.github.xiaoymin</groupId><artifactId>knife4j-spring-boot-starter</artifactId&g…

iOS - 超好用的隐私清单修复脚本(持续更新)

文章目录 前言开发环境项目地址下载安装隐私访问报告隐私清单模板最后 前言 在早些时候&#xff0c;提交应用到App Store审核&#xff0c;大家应该都收到过类似这样的邮件&#xff1a; Although submission for App Store review was successful, you may want to correct th…

es使用knn向量检索中numCandidates和k应该如何配比更合适

在Elasticsearch&#xff08;ES&#xff09;中&#xff0c;KNN&#xff08;k-最近邻&#xff09;向量检索是一种高效的向量相似性搜索方法&#xff0c;广泛应用于推荐系统、图像搜索、自然语言处理等领域。在KNN检索中&#xff0c;k 和 numCandidates 是两个关键参数&#xff0…

记录一下自己对网络安全法的笔记

仅记录一下自己对网络安全法的笔记 文章来源&#xff08;https://www.cac.gov.cn/2016-11/07/c_1119867116_2.htm&#xff09;&#xff08;2016年11月7日第十二届全国人民代表大会常务委员会第二十四次会议通过&#xff09;该法自2017年6月1日起施行。《网络安全法》是我国第一…

mvc如何给action传递参数

步骤(常规方法) 新建一个控制器--LarsController.cs using Microsoft.AspNetCore.Mvc; namespace Blog.Controller; public class LarsController:Controller -----继承 {public IActionResult Index(){return View();} }获取id // program.cs中默认值是idpublic IAction…

Java爬虫️ 使用Jsoup库进行API请求有什么优势?

在Java的世界里&#xff0c;Jsoup库以其强大的HTML解析能力而闻名。它不仅仅是一个简单的解析器&#xff0c;更是一个功能齐全的工具箱&#xff0c;为开发者提供了从网页抓取到数据处理的一站式解决方案。本文将深入探讨使用Jsoup库进行API请求的优势&#xff0c;并提供代码示例…

NX二次开发通过内部函数获取面的面积MW_face_ask_area

获取动态库libmold.dll的路径 void TcharToChar(const TCHAR* tchar, char* _char) {int iLength; #if UNICODE//获取字节长度 iLength WideCharToMultiByte(CP_ACP, 0, tchar, -1, NULL, 0, NULL, NULL);//将tchar值赋给_char WideCharToMultiByte(CP_ACP, 0, tchar, -…