【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);
    else
        Debug.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("这是一个无参无返回的函数")
end

myFun2 = function (x)
	print("这是一个有参有返回的函数")
	return x + x
end

myFun3 = function ()
	print("这是一个多返回值的函数")
	return 99, "apple", 1.2f
end

myFun4 = function (x, ...)
	print("这是一个变长参数的函数")
	arg = {...}
	for k, v in pairs(arg) do
		print(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热补丁

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于 uniapp 开发 android 播放 webrtc 流

一、播放rtsp协议流 如果 webrtc 流以 rtsp 协议返回&#xff0c;流地址如&#xff1a;rtsp://127.0.0.1:5115/session.mpg&#xff0c;uniapp的 <video> 编译到android上直接就能播放&#xff0c;但通常会有2-3秒的延迟。 二、播放webrtc协议流 如果 webrtc 流以 webrt…

Redis应用—7.大Value处理方案

大纲 1.⽅案设计 2.安装与配置环境 1.⽅案设计 步骤一&#xff1a;首先需要配置一个crontab定时调度shell脚本&#xff0c;然后该脚本每天凌晨会通过rdbtools⼯具解析Redis的RDB⽂件&#xff0c;接着对解析出的内容进行过滤&#xff0c;把RDB⽂件中的⼤key导出到CSV⽂件。 步…

Kafka常见面试题+详细解释,易理解。

目录 题库 1.Kafka中的ISR(InSyncRepli)、OSR(OutSyncRepli)、AR(AllRepli)代表什么&#xff1f; 2.Kafka中的HW、LEO等分别代表什么&#xff1f; 3.Kafka的用途有哪些&#xff1f;使用场景如何&#xff1f; 4.Kafka中是怎么体现消息顺序性的&#xff1f; 5.“消费组中的…

泷羽sec学习打卡-brupsuite7搭建IP炮台

声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都 与本人无关,切莫逾越法律红线,否则后果自负 关于brupsuite的那些事儿-Brup-IP炮台搭建 搭建炮台服务端安装zmap1、更新系统和安装基础依赖&#xff…

知网研学 | 知网文献(CAJ+PDF)批量下载

知网文献&#xff08;CAJPDF&#xff09;批量下载 一、知网研学安装二、插件及脚本安装三、CAJ批量下载四、脚本下载及PDF批量下载浏览器取消拦截窗口 一、知网研学安装 批量下载知网文件&#xff0c;格式为es6文件&#xff0c;需使用知网研学软件打开&#xff0c;故需先安装该…

强化学习的产业界探索

RL China 2024的会议上&#xff0c;赵鉴博士发表了一个有关强化学习的产业界探索的Topic&#xff0c;我看完之后结合他的ppt和我的看法谈下强化学习的应用落地。 强化学习在很多领域都有应用&#xff0c;据我所知&#xff0c;在好几个热点中都有强化学习的身影&#xff0c;而且…

【RAG实战】Prompting vs. RAG vs. Finetuning: 如何选择LLM应用选择最佳方案

在构建基于大型语言模型&#xff08;LLM&#xff09;的应用时&#xff0c;通常不可能立即使用模型而无需任何调整。为了保持高实用性&#xff0c;我们可以选择以下几种方法之一&#xff1a; Prompt Engineering&#xff08;提示工程&#xff09;Fine-tuning&#xff08;微调&a…

小白入门 · 腾讯云轻量服务器部署 Hadoop 3.3.6

1. 安装JDK1.8 官网下载安装包&#xff0c;上传至服务器&#xff0c;解压tar包 http://planetone.online/downloads/java/jdk/jdk-8u221-linux-x64.tar.gz上传安装包至服务器并解压到指定目录&#xff1a; tar -zxvf jdk-8u221-linux-x64.tar.gz -C /opt/module/2. 配置java…

sql-labs 练习笔记

简介# 这个节不涉及过多原理&#xff0c;而主要是靶场练习&#xff0c;想获得更多原理请见上篇博客。 基础知识# 如何闭合括号# 如果有报错则直接利用报错信息进行&#xff0c;否则尝试常见的闭合形式&#xff0c;简单的可以直接使用 bool 探测&#xff0c;而困难一点的可以…

NOTEBOOK_11 汽车电子设备分享(工作经验)

汽车电子设备分享 摘要 本文主要列出汽车电子应用的一些实验设备和生产设备&#xff0c;部分会给予一定推荐。目录 摘要一、通用工具&#xff1a;二、测量与测试仪器2.1测量仪器2.2无线通讯测量仪器2.3元器件测试仪2.4安规测试仪2.5电源供应器2.6电磁兼容测试设备2.7可靠性环境…

10.1k高星 GitHub 库:告别JSON错误:Outlines如何提升大模型的结构化输出

在人工智能和大语言模型(LLM)的应用中,如何高效、可靠地从模型输出中提取结构化数据,成为了一个至关重要的课题。本篇文章将介绍 GitHub 高星开源库 Outlines,并分析它如何帮助开发者解决大模型在生成结构化数据时面临的挑战。我们将探讨大模型的非结构化输出给 AI 应用带…

【计算机毕设】基于Python预制菜可视化数据分析预测推荐系统(完整系统源码+数据库+详细部署教程)✅

目录 【计算机毕设】基于Python预制菜可视化数据分析预测推荐系统&#xff08;完整系统源码数据库详细部署教程&#xff09;✅源码获取方式在文章末尾 一、项目背景 二、研究目的 三、项目意义 四、项目功能 五、项目创新点 六、开发技术介绍 七、数据库设计 八、项目…

设计模式期末复习

一、设计模式的概念以及分类 二、设计模式的主题和意图 三、面向对象程序设计原则&#xff0c;记住名字&#xff0c;还要理解它的使用场景以及如何用&#xff1f; 四、松耦合、紧耦合、强关联、弱关联、静态复用、动态复用的概念&#xff0c;还有静态委派&#xff0c;动态委…

ELK系列-(六)Redis也能作为消息队列?(上)

一、前文回顾 &#x1f50d; 在前面的ELK系列中&#xff0c;我们已经搭建了ELK的核心组件&#xff0c;包括&#xff1a; ELK系列-&#xff08;一&#xff09;Docker部署ELK核心组件ELK系列-&#xff08;二&#xff09;LogStash数据处理的瑞士军刀ELK系列-&#xff08;三&…

二进制分析的新兴趋势:塑造安全的移动应用

在当今快速发展的数字世界中&#xff0c;保障移动应用的安全性变得尤为重要。随着移动技术的广泛应用&#xff0c;安全性需求也日益增强。二进制分析作为确保移动应用安全和合规性的重要手段&#xff0c;通过对已编译的应用进行深入分析&#xff0c;能够发现源代码中难以察觉的…

Pikachu-XXE靶场(注入攻击)

1.攻击测试 <?xml version"1.0"?> <!DOCTYPE foo [ <!ENTITY xxe "a" > ]> <foo>&xxe;</foo> 2.查看文件 <?xml version"1.0"?> <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///E:/ph…

CSS学习记录12

CSS浮动 CSSfloat属性规定元素如何浮动 CSSclear属性规定哪些元素可以在清除的元素旁边以及在哪一侧浮动。 float属性 float属性用于定位和格式化内容&#xff0c;例如让图像向左浮动到容器的文本那里。 float属性可以设置以下值之一&#xff1a; left - 元素浮动到其容器…

概率论得学习和整理30: 用EXCEL 描述泊松分布 poisson distribution

目录 1 泊松分布的基本内容 1.1 泊松分布的关键点 1.1.1 属于离散分布 1.1.2 泊松分布的特点&#xff1a;每个子区间内概率相等 &#xff0c; λ就是平均概率 1.2 核心参数 1.3 pmf公式 1.4 期望和方差 2 例1&#xff1a;用EXCEL计算泊松分布的概率 3 比较λ不同值时…

leetcode212. 单词搜索 II

给定一个 m x n 二维字符网格 board 和一个单词&#xff08;字符串&#xff09;列表 words&#xff0c; 返回所有二维网格上的单词 。 单词必须按照字母顺序&#xff0c;通过 相邻的单元格 内的字母构成&#xff0c;其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一…

Y20030009基于Java+springboot+MySQL+uniapp框架的待办事项提醒微信小程序的设计与实现 源码 文档 PPT

待办事项提醒小程序 1.摘要2.开发目的和意义3.系统功能设计4.系统界面截图5.源码获取 1.摘要 随着现代人的工作和生活压力越来越大&#xff0c;人们的精力和时间也越来越有限。在这样的情况下&#xff0c;很容易忘记一些很重要的行程&#xff0c;有时会导致严重的后果&#xf…