【Unity】浅尝xlua热更新插件

前言

之前的学习中了解到了一些热更新的知识,本想系统地学习基于xLua的热更新框架,但时间紧迫,遂浅尝辄止。在此记录一下相关知识。

什么是热更新

这里有一篇博客讲得很好,指路->【Unity框架】基于XLua框架搭建与AssetBundle的使用流程

从云端下载资源包,这些新资源包会被自动整合进现有软件中,达到动态更新软件的目的,而无需重新下载整个软件。而支持热更新的脚本可以打包成AB包上传到服务器,这就能够实现热更新。

最主流的Unity热更新方式的有三种:

  1. XLua
  2. ILRuntime
  3. huatuo

前者是腾讯开源,网上教程很多、十分成熟、好评如潮,但要熟悉新语言lua;
中者用C#来作为热更语言,最大的优势是项目可以用同一个语言来进行开发;
后者是Unity全平台原生c#热更新方案,据说它特性完整、零成本、高性能、低内存、近乎完美。

xLua是什么

Tencent/xLua: xLua is a lua programming solution for C# ( Unity, .Net, Mono) , it supports android, ios, windows, linux, osx, etc. (github.com)

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功能特性:

  1. 可以运行时把C#实现(方法,操作符,属性,事件等等)替换成lua实现;
  2. 出色的GC优化,自定义struct,枚举在Lua和C#间传递无C# gc alloc;
  3. 编辑器下无需生成代码,开发更轻量;
    详细说明见此处

如何向项目中引入xLua

github中下载ZIP文件后,解压,进入Assets文件夹,看到有如下四个文件。
image
把这四个文件直接复制进Unity项目中的Asset文件夹下,注意不要错放到Asset的子文件中。等待Unity编译完就可以了。

基于xLua的纯Lua开发框架的基本原则

  1. 游戏场景中不放任何游戏对象。避免冲突,用代码生成对象。

  2. 运行时仅有一个场景,不涉及场景切换。策划、美术、程序分离。

  3. 不手动向预制体上挂载代码。去编辑器化,便于维护代码。

  4. 以纯AssetsBundle代替Resources做资源管理。

    a:方便更新 :更新代码(lua->脚本代码ab包)+更新资源(AssetsBundle)

    b:方便打包:场景+场景依赖的资源;Resources文件夹下的所有资源打入安装包

  5. 制定一个纯Lua开发的组件化框架,方便编写业务逻辑与运算,尽可能减少Lua调用C#提升性能。

  6. 建立调试模式以加载Lua代码与资源:AssetsDatabase加载资源,供Editor模式下使用。

基本框架

目录结构

  1. AssetsPackage:用于存放所有游戏资源。
  2. Scenes:用于存放游戏场景。
  3. Scripts:用于存放框架性质的C#代码。
  4. LuaScripts:用于存放Lua代码。
  5. StreammingAssets:用于存放ab包。若不打空包,则将ab包放入该文件夹内,和安装包一起打包。
  6. Editor:用于扩展编辑器。

框架启动全流程

  1. 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脚本开发

  1. 内置Lua虚拟机。Managers->xLuaManager

  2. 将Lua代码脱离于C#代码,虚拟机加载我们的文本代码并执行。

    虚拟机装载执行第一个Lua脚本,main.lua

  3. 自定义一个Lua代码装载器(LuaScriptLoader),当我们请求装载代码时,到项目指定的路径下装载Lua代码。

  4. 装载方式:调试模式直接在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

posted @ 2022-09-29 22:59  AshScops  阅读(295)  评论(0编辑  收藏  举报