【Unity】浅尝xlua热更新插件
前言
之前的学习中了解到了一些热更新的知识,本想系统地学习基于xLua的热更新框架,但时间紧迫,遂浅尝辄止。在此记录一下相关知识。
什么是热更新
这里有一篇博客讲得很好,指路->【Unity框架】基于XLua框架搭建与AssetBundle的使用流程。
从云端下载资源包,这些新资源包会被自动整合进现有软件中,达到动态更新软件的目的,而无需重新下载整个软件。而支持热更新的脚本可以打包成AB包上传到服务器,这就能够实现热更新。
最主流的Unity热更新方式的有三种:
- XLua
- ILRuntime
- huatuo
前者是腾讯开源,网上教程很多、十分成熟、好评如潮,但要熟悉新语言lua;
中者用C#来作为热更语言,最大的优势是项目可以用同一个语言来进行开发;
后者是Unity全平台原生c#热更新方案,据说它特性完整、零成本、高性能、低内存、近乎完美。
xLua是什么
2017年初的一篇文章中这样写道:
2016年12月末,xLua刚刚实现新的突破:全平台支持用Lua修复C#代码bug。
目前Unity下的Lua热更新方案大多都是要求要热更新的部分一开始就要用Lua语言实现,不足之处在于:
1、接入成本高,有的项目已经用C#写完了,这时要接入需要把需要热更的地方用Lua重新实现;
2、即使一开始就接入了,也存在同时用两种语言开发难度较大的问题;
3、Lua性能不如C#;
xLua热补丁技术支持在运行时把一个C#实现(函数,操作符,属性,事件,或者整个类)替换成Lua实现,意味着你可以:
1、平时用C#开发;
2、运行也是C#,性能秒杀Lua;
3、有bug的地方下发个Lua脚本fix了,下次整体更新时可以把Lua的实现换回正确的C#实现,更新时甚至可以做到不重启游戏;
这个新特性iOS,Android,Window,Mac都测试通过了,目前在做一些易用性优化。
Lua:Lua解释执行时,性能接近于native
xLua:xLua是Lua在C#环境( .net)的解决方案
xLua功能特性:
- 可以运行时把C#实现(方法,操作符,属性,事件等等)替换成lua实现;
- 出色的GC优化,自定义struct,枚举在Lua和C#间传递无C# gc alloc;
- 编辑器下无需生成代码,开发更轻量;
详细说明见此处。
如何向项目中引入xLua
从github中下载ZIP文件后,解压,进入Assets文件夹,看到有如下四个文件。
把这四个文件直接复制进Unity项目中的Asset文件夹下,注意不要错放到Asset的子文件中。等待Unity编译完就可以了。
基于xLua的纯Lua开发框架的基本原则
-
游戏场景中不放任何游戏对象。避免冲突,用代码生成对象。
-
运行时仅有一个场景,不涉及场景切换。策划、美术、程序分离。
-
不手动向预制体上挂载代码。去编辑器化,便于维护代码。
-
以纯AssetsBundle代替Resources做资源管理。
a:方便更新 :更新代码(lua->脚本代码ab包)+更新资源(AssetsBundle)
b:方便打包:场景+场景依赖的资源;Resources文件夹下的所有资源打入安装包
-
制定一个纯Lua开发的组件化框架,方便编写业务逻辑与运算,尽可能减少Lua调用C#提升性能。
-
建立调试模式以加载Lua代码与资源:AssetsDatabase加载资源,供Editor模式下使用。
基本框架
目录结构
- AssetsPackage:用于存放所有游戏资源。
- Scenes:用于存放游戏场景。
- Scripts:用于存放框架性质的C#代码。
- LuaScripts:用于存放Lua代码。
- StreammingAssets:用于存放ab包。若不打空包,则将ab包放入该文件夹内,和安装包一起打包。
- Editor:用于扩展编辑器。
框架启动全流程
- GameLaunch单例
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class GameLaunch : MonoSingleton<GameLaunch>
{
public void Awake()
{
//初始化游戏框架:声音管理,资源管理,网络管理等
}
IEnumerator CheckHotUpdate()
{
//热更新游戏资源+代码
//end
yield return null;
}
IEnumerator GameStart()
{
yield return this.StartCoroutine(this.CheckHotUpdate());
//进入游戏,通过Lua虚拟机进入Lua逻辑代码
Debug.Log("GameStart");
//end
}
protected override void OnStart()
{
StartCoroutine(this.GameStart());
}
}
独立化Lua脚本开发
-
内置Lua虚拟机。Managers->xLuaManager
-
将Lua代码脱离于C#代码,虚拟机加载我们的文本代码并执行。
虚拟机装载执行第一个Lua脚本,main.lua
-
自定义一个Lua代码装载器(LuaScriptLoader),当我们请求装载代码时,到项目指定的路径下装载Lua代码。
-
装载方式:调试模式直接在LuaScripts下加载;发布模式LuaScripts->ab包->从ab包中加载Lua内容。
如下是代码示例。
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using Unity.VisualScripting;
using UnityEditor;
using UnityEngine;
using XLua;
public class xLuaManager : MonoSingleton<xLuaManager>
{
private static string luaScriptsFolder = "LuaScripts";
private LuaEnv env = null;
private bool isGameStarted = false;
private void Awake()
{
this.InitLuaEnv();
}
public byte[] LuaScriptLoader(ref string filepath)
{
//Debug.Log("#" + filepath);
string scriptPath = string.Empty;
filepath = filepath.Replace(".", "/") + ".lua.txt";
# if UNITY_EDITOR//调试模式
scriptPath = Path.Combine(Application.dataPath, luaScriptsFolder);
scriptPath = Path.Combine(scriptPath, filepath);
//byte[] data = GameUtility.SafeReadAllBytes(scriptPath);
byte[] data = File.ReadAllBytes(scriptPath);
return data;
#endif
//发布模式
return null;
}
private void InitLuaEnv()
{
this.env = new LuaEnv();
this.env.AddLoader(this.LuaScriptLoader);
}
protected override void OnStart()
{
base.OnStart();
}
public void EnterGame()
{
//游戏正式开始
this.isGameStarted = true;
//进入游戏逻辑
//Lua代码:print("helloworld!");
this.env.DoString("require (\"main\")");
this.env.DoString("main.init()");
}
private void Update()
{
if(this.isGameStarted)
this.env.DoString("main.update()");
}
private void FixedUpdate()
{
if (this.isGameStarted)
this.env.DoString("main.fixedUpdate()");
}
private void LateUpdate()
{
if (this.isGameStarted)
this.env.DoString("main.lateUpdate()");
}
}
main.lua.txt
--定义全局模块
main = {}
local GameApp = require("Game.GameApp")
local function init()
--print("init")
--初始化lua框架:自定义时间、网络、日志...
--end
--end进入游戏逻辑
GameApp.EnterGame()
end
main.init = init
local function update()
--每帧执行
--print("update")
end
main.update = update
local function fixedUpdate()
--每帧执行
--print("fixedUpdate")
end
main.fixedUpdate = fixedUpdate
local function lateUpdate()
--每帧执行
--print("lateUpdate")
end
main.lateUpdate = lateUpdate
如何热更新代码与资源
注意:.lua无法打ab包,后缀必须改为.lua.byte才能打包。(有待考证,可能和框架有关)
利用assetbundle,将lua脚本打包为lua.assetsbundle,下载到本地;
加载lua脚本时,从ab包中加载最新的lua代码,加载完后执行。
和热更新其他资源的步骤是一样的。
参考资料
Tencent/xLua (github.com)
smilehao/xlua-framework (github.com)
【Unity框架】基于XLua框架搭建与AssetBundle的使用流程
Unity进阶教程:Xlua热更框架开发游戏【热更新视频教程】
ILRuntime.index