【Unity - xLua 热更新入门】从代码到发布的完整流程,创建最简单的热更新框架——提供用 Go 写的服务器端更新程序(图文教程)

【Unity - xLua 热更新入门】从代码到发布的完整流程,创建最简单的热更新框架——提供用 Go 写的服务器端更新程序(图文教程)
今天看博客发现一篇博客 分享一下也算记录一下
原文连接
感谢博主分享 写的很详细
本文的最终效果将通过修改服务器上的lua代码,在客户端创建一个球和两个方块,如下:
手机运行效果
手机运行效果

  • 本文的流程是这样:
  • 原理
  • 准备
  • 项目导入xLua
  • 创建最简单的热更新框架
  • 开启服务器端的热更新服务
  • Unity中测试热更新
  • 发布到安卓手机上测试热更新
  • 结束
  • 下载

在本文开始之前,将认为读者已掌握Unity的基本知识,比如创建项目啊,写脚本啊,什么的;也认为读者已掌握Lua的基本知识,比如Lua代码里创建个GameObject啊什么的,另外本文选择的热更方案是xLua,读者需要知道这点。

服务器端我将用Go写个简单的服务器程序,用于客户端更新文件,这方面读者不用担心,在本文的最后提供了下载地址,并有简单的使用介绍,到时候下载后直接运行就行了。

本文在提到更新时将会写更新资源,而不是写更新lua代码,因为代码也是资源。

本文也提供了这个测试项目下载链接,如果需要的话,可以去本文最后去下载查看。

  • 本文的测试环境:
  • macOS Mojave 10.14.2 / MacBook Pro 2015
  • Unity 2018.3.8f1
  • Lua:xLua
  • Go 1.12.1

原理
热更新很简单,简单的话就一句话:获取最新的资源,然后重新运行代码。

因为xLua等一些热更方案已经做的挺好了,我们需要做的其实只剩下去下载东西,然后运行它。

其他的像lua文件的加密解密,还是要等到lua更新后才去执行的工作

所以整个热更框架也就是下载文件,仅此而已,没什么别的东西。

只不过过程中涉及服务器逻辑,我们只写客户端,觉得很难搞。通常同服务器交互还需要验证 token,不可能随便谁来请求,服务器都无条件的响应它。

此文旨在提供一种简单的测试框架,帮助读者了解、熟悉热更新并且能够快速实践测试它。整个框架我只写了一遍,然后小小的整理下代码就再也没动它了,所以难免会有些不完美的地方,读者如果发现有bug,或者其他什么需要改正的地方,可以告知我,我会及时更新这个程序。

提供的框架核心代码不到200行,加上Go代码总共也不到400行;读者如果喜欢,可以直接拿去修改去用,。

准备
准备工作主要准备以下的文件:

xLua-master

一切准备好后,用Unity创建一个名为xlua-test的项目,就可以开始我们的热更新了。
1.xlua-test项目已经准备好
2.项目中导入xLua
3.导入xLua非常简单:

解压 xLua-master
将 xLua-master/Assets 中所有内容复制到xlua-test/Assets中

创建个C#脚本迅速测试下刚导入的xLua是否能够正常工作:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MainController : MonoBehaviour
{
    void Start()
    {
        XLua.LuaEnv luaenv = new XLua.LuaEnv();
        luaenv.DoString("CS.UnityEngine.Debug.Log('hello world')");
        luaenv.Dispose();
    }

    void Update()
    {
        
    }
}

运行效果

创建最简单的热更新框架
文章开头已经提到,热更新很简单,因为像xLua等热更新方案把工作已经做完了,我们只需要去远端获取资源即可。

这样就涉及两个问题:

1.客户端什么时候应该更新Lua代码或资源(热更新的时机)
2.去哪里更新
1.如果有最新的资源,就立即更新,然后替换掉本地旧资源(或根据需求来决定其他的更新时机)
2.去服务器那里更新(本地搭建的服务器那里)

所以我们的热更新逻辑是这样的:

1.每次打开客户端,都会向服务器发送本地资源的特征,询问是否需要更新
2.服务器收到客户端发来的询问,对比文件差异,如果需要更新,将返回需要更新的文件列表url
3.客户端如果收到服务器发来的文件更新列表url,将立即前往下载,并存储在本地;如果收到字符串"0",表示无需更新资源,则直接进入第4步。
4.开始运行客户端的主逻辑

好啦,把刚才测试xLua是否能正常工作的代码删掉。

我们开始完成客户端的相关代码,创建一个名叫UpdateController的C#脚本,(代码一百多行,就不贴出来了,就贴个图吧,想看细节可以去文章的最后去下载 xlua-test,很简单的代码,就是两个网络请求。),我使用md5来简单计算文件的特征,server 程序也使用 md5 来进行文件对比:
UpdateController

UpdateController中摘选的代码片段:

 // 向服务器询问时候需要更新
    IEnumerator _request(System.Action<string> resp)
    {

        WWWForm form = new WWWForm();
        var md5list = GetResourcesMd5(ResourcesPath);
        Debug.Log("md5list => " + md5list);
        form.AddField("need-to-update-files", md5list);
        UnityEngine.Networking.UnityWebRequest unityWebRequest = UnityEngine.Networking.UnityWebRequest.Post(RequestUrl, form);
        unityWebRequest.timeout = Timeout;
        using (unityWebRequest)
        {
            yield return unityWebRequest.SendWebRequest();
            if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
            {
                Debug.Log(unityWebRequest.error);
                UpdateCompleteAction?.Invoke();
            }
            else
            {
                Debug.Log("Form upload complete!");
                resp?.Invoke(unityWebRequest.downloadHandler.text);
            }
        }
    }
    // 下载文件
   IEnumerator _downloadFile(string url, string filename, int allcount, System.Action complete)
    {
        Debug.Log("共有"+allcount+"个文件需要下载,开始下载=>" + url);
        UnityEngine.Networking.UnityWebRequest unityWebRequest = UnityEngine.Networking.UnityWebRequest.Get(url);
        unityWebRequest.timeout = Timeout;
        using (unityWebRequest)
        {
            yield return unityWebRequest.SendWebRequest();
            if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
            {
                Debug.Log(url + " -- " + unityWebRequest.error);
                completeUpdateFiles++;
            }
            else
            {
                Debug.Log("Form upload complete!\n\n" + unityWebRequest.downloadHandler.text);

                // 写入或覆盖本地资源
                if (!System.IO.Directory.Exists(ResourcesPath))
                    System.IO.Directory.CreateDirectory(ResourcesPath);
                createDirIfNeed(filename);
                var fs = new System.IO.FileStream(ResourcesPath + "/" + filename, System.IO.FileMode.Create, System.IO.FileAccess.Write);
                var sw = new System.IO.StreamWriter(fs, System.Text.Encoding.Default);
                sw.Write(unityWebRequest.downloadHandler.text);
                sw.Close();
                fs.Close();

                completeUpdateFiles++;
                // 完成后,立即开始主逻辑
                if (completeUpdateFiles == allcount) complete?.Invoke();
            }
        }
    }

然后再创建一个名叫MainController当做我们的主逻辑脚本,可以看到更新完成后的主逻辑就是去执行一个 main.lua.txt 文件,当然,在xLua中我们需要先把本地的资源路径添加到xLua中,这样执行 lua 文件的时候就能找到它:

using UnityEngine;

public class MainController : MonoBehaviour
{

    public XLua.LuaEnv LuaEnv;
    void Start()
    {
        // 创建lua环境
        LuaEnv = new XLua.LuaEnv();

        // 给xLua添加个lua文件加载路径
        LuaEnv.AddLoader((ref string filepath) => {
            filepath = UpdateController.ResourcesPath + "/" + filepath;
            if (System.IO.File.Exists(filepath))
                return System.Text.Encoding.UTF8.GetBytes(System.IO.File.ReadAllText(filepath));
            else
                return System.Text.Encoding.UTF8.GetBytes("");
            
        });

        // 开启/关闭热更新
        UpdateController.EnableHotUpdates = true;

        // 更新完成后主逻辑
        UpdateController.UpdateCompleteAction = () => {
            // 主逻辑
            LuaEnv.DoString("require 'main.lua.txt'");
        };
        // 开始热更新
        UpdateController.StartUpdate();

    }

    private void OnDestroy() => LuaEnv.Dispose();
}

把它们挂到对象上,客户端的工作已完成,如下:

运行效果
将更新脚本和主逻辑脚本挂在对象上,客户端工作已完成
此时我们运行一下Unity,将得到一个本地资源存储的位置和一个不能连接host的提示,如果你也是如此,不用慌,则表明现在一切正常,可以进行下一步操作:
运行效果

得到一个本地资源存储的位置和一个不能链接host的提示,如果你也是如此,则表明现在一切正常,可以进行下一步操作…
开启服务器端的热更新服务
你可以去本文的最后下载 xlua-test-server,解压后打开查看,找到并运行适合你操作系统的服务器程序,比如说我的是macOS 64位的,我就打开终端,cd 到该目录下,然后 ./xlua_test_server__darwin_amd64(下图中展示的运行界面已过时, server 程序已有新版本,它将会打印出本地 ip 地址和端口号,方便读者在手机上设置代理。):
在这里插入图片描述

此时本地的服务器已经开启了。

Unity 中测试热更新
我们运行Unity 编辑器试试看结果:
在这里插入图片描述
热更新已完成,程序运行正常。
在这里插入图片描述
此时我们去看本地的资源目录,就会发现服务器上的资源已经被更新到本地了:

服务器上的资源已经被更新到本地

我们再次运行Unity 编辑器,发现客户端已经不再从服务器中下载文件了,因为本地的资源已经和服务器上的一模一样了,不必更新:
在这里插入图片描述
再次运行Unity客户端程序,客户端已经不再从服务器中下载文件了

我们去把服务器上的resources/createObj/bg.lua.txt 文件修改下,使之再创建一个方块,并且修改一下方块的位置:
在这里插入图片描述

再次运行Unity编辑器,看看有什么变化,此时只有一个文件需要下载,并且加载lua后,方块也出现了,一切合乎逻辑:
再次运行Unity客户端程序,一切合乎逻辑。

发布到安卓手机上测试热更新
server 程序已有新版本,输出本地ip和开放的端口的提示信息,方便设置代理,图片上面的ip地址和端口仅为示例,具体要看自己运行时输出的内容。

先别急着打包apk,如果你是在本地的电脑上运行 server ,则需要首先把手机的网络设置好(server 程序已有新版本,运行本文最后提供的程序,将会看到输出的本地 ip 地址和端口,方便读者设置代理。):
1.手机和server运行的电脑处于同一个网络环境中。也就是说wifi要连一样的wifi;如果手机连的wifi,电脑是网线,则wifi和网线连的都是同一个路由也行。
2.手机设置代理。(看你的手机品牌是怎么设置代理的,我写的可能不适合你的手机。)——wifi 的高级选项中把你的服务器ip地址(如果在本地运行的 server,你填自己的电脑的ip就好):
如果在本地运行的 server,你填自己的电脑的ip

端口是 12345,12345 如果你修改了 conf.txt 文件,则填你修改的端口,然后保存。这时候 wifi 显示已连接表示手机配置成功(不过,你的 server 要先打开啊!!! 手机设置的代理端口跟你server开放的端口要一致,不然手机上哪里连接,是吧,server 的端口介绍本文最后写的有:
手机设置代理,保存,wifi 连接成功表示设置成功。
打包
好啦,现在我们将xlua-test项目打包成apk,安装到安卓手机上运行一下试试:
看到一个球一个方块

立即在手机上运行下这个客户端:

紫色? 这不是我们程序的问题,这是因为程序中的Shader没有用到(我们的客户端里其实是个空壳子嘛),打包apk时,Unity就把Shader给剥离了。我们可以在项目随便找个地方中把默认材质添加一下就好了。
在这里插入图片描述
在项目随便找个地方中把默认材质添加一下就好了
然后再重新打包apk,运行:
在这里插入图片描述
手机上材质渲染正常
我们此时在服务器中再次把服务器上的resources/createObj/bg.lua.txt 文件修改下,让它在左边再创建一个方块:
在这里插入图片描述
在左边再创建一个方块
重新运行手机上的客户端:
在这里插入图片描述
一个球,两个方块。
电脑上的服务器关掉,然后再此重新运行手机上的客户端,等待三四秒后(UpdateController 中写的超时时间,你可以修改)或者没有等待(程序不能连接 Host,立即开始主逻辑,合乎逻辑),方块和小球又出现了:
在这里插入图片描述
服务器关掉,然后再此重新运行手机上的客户端,方块和小球又出现了
在去手机里找找下载下来的资源:

在这里插入图片描述
手机上存储的资源
在这里插入图片描述
手机上存储的资源
在这里插入图片描述
查看 gb.lua.txt 的内容
你可以随意在服务器上修改lua代码,这样客户端每次启动就会更新新的东西;甚至你可以修改客户端的热更逻辑,不用每次启动才热更,类似每隔一段时间的轮询去更新,这样,客户端运行时就能及时更新到服务器的最新资源了。
在这里插入图片描述
再次修改lua代码,使中间的球显示成黑色。
很好,一切完美。

结束
这个测试程序没有提示信息,Unity 编辑器里面运行还好,能看到输出,手机上就啥也看不到了,因为没有写界面,这点就有点那个了,希望读者尝试的时候一切顺利。

另外就是本文的程序只在安卓手机上测试通过了,iPhone上没有试过,因为我没有iPhone啊 。

整个框架中,你可以看到客户端的热更逻辑与服务器端热更逻辑其实是一个整体。具体来说,客户端发起请求时的参数以及之后服务器返回的数据,都需要提前约定好格式,因为服务器需要根据参数执行处理逻辑,客户端也需要解析返回的数据,然后实现热更新。

如果前后端都是你一个人写的,那你爽了,就像本文一样,参数和数据格式自己一个人定了;但是在真实的项目中不可能这样的,还是需要经过商讨来确定接口,然后写程序测试接口是否能正常工作。

就这样了,xLua把主要功能做的蛮完整的,剩下的问题就是怎么去远端下载资源而已,xLua还有个热补丁本文中没有涉及到的。我只是把整个流程走通一遍,读者根据这个应该能够顺利的学习其他内容了。

下载
我们在文中创建的 xlua-test 项目,里面包含了 UpdateController 和 MainController : 微云:xlua-test
xlua-test
在这里插入图片描述
我们要使用的服务器程序里面也包含 Linux、darwin、freebsd、windows 平台的可执行程序: 微云:xlua-test-server
在这里插入图片描述
xlua-test-server
如果程序无法运行,里面的 main.go 文件就是整个server的源代码,可以自行安装Go,然后编译运行它即可。

如果你Go已装好,并且使用 VSCode 打开了文件夹xlua–test–server,则你只需要点击终端/运行任务…,然后在弹出的窗上点run,就可以运行server
在这里插入图片描述
点击终端/运行任务...
在这里插入图片描述
然后再弹出的窗上点run,就可以运行server
在这里插入图片描述
macOS 允许它就 server 就可以了
或许你会好奇,run 下面的 build all platform 是干嘛用的,它就是用来生成所有平台可执行程序的任务。

server 的简单的用法
1.运行server之前,你还能看到一个conf.txt文件,打开它:
conf.txt
在这里插入图片描述
里面就一行这个:
port=12345
意思是 server 开放的端口是12345,这样运行 server 之后,server 将在端口 12345 上监听消息,所以客户端里的 host 也应该使用 12345端口。

如果运行后发生了端口占用(就是此端口已经被别的程序使用了),可以修改这个端口;如果你把 conf.txt 删掉了,server 将固定使用端口 12345 来给客户端提供更新服务:
在这里插入图片描述
如果 server 以端口 12345 在本地运行,则客户端 Host 也需要设置为 “http://localhost:12345”
2.另外是服务器上的资源根目录是默认的resources。
意思是server运行目录下的 resources 文件夹,里面放的都是我们的需要热更新的资源,包括 lua 代码, 可以看到里面已经有了几个文件,你可以在这个目录下随意增删内容,它们将被更新到客户端中。

3.如果你把 server 的某可执行文件复制另外的目录,然后想让它在该目录下运行。那么,如果你想更新资源或热更lua代码,则你需要在该目录下创建个 resources 文件夹,然后把你资源或lua代码放在里面。

4.lua文件的保存格式为UTF-8,切记!!!切记!!!敲黑板了!!! lua文件的保存格式为UTF-8,其他格式如 UTF-8 with BOM或许也行,但是不带 BOM 的 UTF-8 最保险。如果出现类似 …main.lua.txt:1: unexpected symbol near ‘<\239>’…的报错信息,多半就是 lua 文件的编码格式的问题;尤其是在 Windows 平台上创建 lua 文本的人需要注意这点。
lua文件的保存格式为UTF-8
在这里插入图片描述
5.我应该在server启动后输出下本地的ip地址,有空再写吧。 server 程序已更新,输出本地ip和端口的提示信息:
在这里插入图片描述
server 程序已更新,输出本地ip和开放的端口的提示信息。
6.运行 server 要看你的平台,比如说我是macOS 64位,我就打开终端, cd 到 xlua-test-server 文件夹,然后 ./xlua_test_server__darwin_amd64 (这个好像得给这个文件运行权限后才能直接双击运行,相应的 Linux 上估计也是这样,我没测试 Linux 的)。 386 意思是32位系统吧,如果是 Windows 64位的,可以直接双击 xlua_test_server__windows_amd64.exe,如下是我测试时的样子(Windows端server程序已有新版本,看第二个图样,输出本地的 ip 和端口):
在这里插入图片描述
Windows 64位:xlua_test_server__windows_amd64.exe
在这里插入图片描述
已更新的Windows端server

作者:大刀和长剑
链接:再次感谢楼主分享
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值