Unity3D 使用xLua實(shí)現(xiàn)熱更新補(bǔ)丁修復(fù)

版權(quán)聲明:本文為Jumbo原創(chuàng)文章,采用[知識(shí)共享 署名-非商業(yè)性使用-禁止演繹 4.0 國際 許可協(xié)議],轉(zhuǎn)載前請保證理解此協(xié)議
原文出處:http://www.itdecent.cn/p/dc4de5612d9e

在Unity3D項(xiàng)目中,邏輯代碼熱更新這一塊,現(xiàn)在有很多實(shí)現(xiàn)解決方案,基本都是借助Lua來實(shí)現(xiàn)的,在這眾多之中,最后還是選擇xLua,最早了解xLua是在騰訊的手游項(xiàng)目中,騰訊Apollo通用組件中,只是涉及的項(xiàng)目中,并沒有xLua的源碼及文檔相關(guān)的說明??赡墚?dāng)時(shí)xLua還在改進(jìn)當(dāng)中。直到2017年1月3日,騰訊Github開源項(xiàng)目中,出現(xiàn)xLua的身影。xLua優(yōu)勢:1、集成快 (幾乎不用改變原本項(xiàng)目的代碼) 2、專職人員負(fù)責(zé)維護(hù)(企鵝還是有這個(gè)實(shí)力·~~·),說再多也不如自己切身體驗(yàn)一番。下面就幫大家,快速入門下。
一、xLua源碼: https://github.com/Tencent/xLua/

二、獲取到源碼后,首先認(rèn)真閱讀README.md內(nèi)容,可以更好的幫大家了解xLua,是否項(xiàng)目中遇到的問題,可以通過xLua來幫忙處理?;

三、根據(jù)提供的示例:
01_Helloworld: 快速入門的例子。
02_U3DScripting: 展示怎么用lua來寫MonoBehaviour。
03_UIEvent: 展示怎么用lua來寫UI邏輯。
04_LuaObjectOrented: 展示lua面向?qū)ο蠛虲#的配合。
05_NoGc: 展示怎么去避免值類型的GC。
06_Coroutine: 展示lua協(xié)程怎么和Unity協(xié)程相配合。
07_AsyncTest: 展示怎么用lua協(xié)程來把異步邏輯同步化。
08_Hotfix: 熱補(bǔ)丁的示例(需要開啟熱補(bǔ)丁特性,如何開啟請看指南)。
配合文檔:
XLua教程.doc:教程,其配套代碼這里。
XLua的配置.doc:介紹如何配置xLua。
XLua增加刪除第三方lua庫.doc:如何增刪第三方lua擴(kuò)展庫。
XLua API.doc:API文檔。
可以更好的了解xLua具備那些特性,如何便捷使用。

四、最重要的功能:熱補(bǔ)丁
xLua支持熱補(bǔ)丁,這意味著你可以:
1、開發(fā)只用C#;
2、運(yùn)行也是C#,性能可以秒殺lua;
3、出問題了才用Lua來改掉C#出問題的部位,下次整體更新時(shí)換回正確的C#;能做到用戶不重啟程序fix bug;

如果你僅僅希望用熱更新來fix bug,這是強(qiáng)烈建議的做法。這里是使用指南。
這個(gè)文檔說明一定要仔細(xì)閱讀https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/hotfix.md

很多剛開始接觸的同學(xué),因?yàn)闆]有認(rèn)真看文檔說明,容易出現(xiàn)一些問題,這邊列舉下:
注意:a、HOTFIX_ENABLE宏是否在Unity3D的File->Build Setting->Scripting Define Symbols下添加
b、Mono.Cecil.是否拷貝:OSX命令行 cp /Applications/Unity/Unity.app/Contents/Managed/Mono.Cecil. Project/Assets/XLua/Src/Editor/

Win命令行 copy UnityPath\Editor\Data\Managed\Mono.Cecil.* Project\Assets\XLua\Src\Editor\

c、菜單欄中,執(zhí)行xLua/Generat Code, 會(huì)在新建Gen文件夾,下面生成一些wrap文件


具體生成那些類型對(duì)應(yīng)的wrap文件,需要自己配置,詳見:https://github.com/Tencent/xLua/blob/master/Assets/XLua/Examples/ExampleGenConfig.cs
在項(xiàng)目中集成,最好自己寫一份配置,替換ExampleGenConfig,比如取名CustomGenConfig.cs,加入了[Hotfix]標(biāo)簽

  //需要熱更新的類型
    [Hotfix]
    public static List<Type> by_field = new List<Type>()
    {

    };

d、要等打印了hotfix inject finish!后才運(yùn)行例子,否則會(huì)類似xlua.access, no field __Hitfix0_Update的錯(cuò)誤

五、整合xLua進(jìn)項(xiàng)目
1、

image

2、

image

3、

image

先貼上代碼(myHotfix.lua 編寫fixbug邏輯):

using UnityEngine;
using System.Collections;
using XLua;

/// <summary>
/// 先判斷更新熱補(bǔ)丁文件,然后再執(zhí)行Fixing
/// </summary>
public class xLuaInstance : MonoSingleton<xLuaInstance>{

    LuaEnv luaenv;

    const string fixFile = "myHotfix.lua";

    public string FixFilePath
    {
        get
        {
            return FileUtils.GetFullPath(null, fixFile, FileUtils.StorageType.Persistent);
        }
    }

    /// <summary>
    /// 熱補(bǔ)丁修復(fù)中。。。
    /// </summary>
    public void Fixing()
    {

        byte[] bytes = System.IO.File.ReadAllBytes(FixFilePath);

        if (bytes != null)
        {
            luaenv = new LuaEnv();
            luaenv.AddLoader((ref string filename) =>
            {
                if (filename == "myHotfix")
                {
                    filename = FixFilePath;
                    return bytes;
                }

                return null;
            });

            luaenv.DoString(@"require 'myHotfix'");
        }
    }

    void Update()
    {
        if (luaenv != null)
            luaenv.Tick();
    }

    void OnDestroy()
    {
        if (luaenv != null)
            luaenv.Dispose();
    }
}

4、大致流程
i、游戲一開始啟動(dòng)時(shí),檢查遠(yuǎn)程服務(wù)器上是否有新的myHotfix.lua文件(例如:md5比對(duì),自己加解密),有的話,下載下來,放在指定目錄,沒有的話,讀取本地已有的myHotfix.lua文件,若文件不存在,則說明不需要熱修復(fù),一般這種情況是在項(xiàng)目剛發(fā)布的早起,沒有進(jìn)行打補(bǔ)?。?/p>

ii、項(xiàng)目發(fā)布版本前,需要在CustomGenConfig.cs中 加入需要添加[Hotfix]標(biāo)簽的類型,想要更靈活使用的話,可以自己寫個(gè)配置表,讀取配置中的類型,自動(dòng)添加,***只有加入[Hotfix]標(biāo)簽的類型,才可以調(diào)用xlua.hotfix()

image

,不然會(huì)報(bào)錯(cuò),切記?。。?/p>

iii、如果你的項(xiàng)目是自動(dòng)化發(fā)布版本,需要在調(diào)用打包之前,生成wrap, 調(diào)用這個(gè)CSObjectWrapEditor.Generator.GenAll();

MonoSingleton.cs

//非線程安全
using UnityEngine;

    /// <summary>
    ///     基類繼承樹中有MonoBehavrour類的單件實(shí)現(xiàn),這種單件實(shí)現(xiàn)有利于減少對(duì)場景樹的查詢操作
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class MonoSingleton<T> : MonoBehaviour where T : Component
    {
        // 單件子類實(shí)例
        private static T _instance;

        // 在單件中,每個(gè)物件的destroyed標(biāo)志設(shè)計(jì)上應(yīng)該分割在不同的存儲(chǔ)個(gè)空間中,因此,忽略R#的這個(gè)提示
        // ReSharper disable once StaticFieldInGenericType
        private static bool _destroyed;

        /// <summary>
        ///     獲得單件實(shí)例,查詢場景中是否有該種類型,如果有存儲(chǔ)靜態(tài)變量,如果沒有,構(gòu)建一個(gè)帶有這個(gè)component的gameobject
        ///     這種單件實(shí)例的GameObject直接掛接在bootroot節(jié)點(diǎn)下,在場景中的生命周期和游戲生命周期相同,創(chuàng)建這個(gè)單件實(shí)例的模塊
        ///     必須通過DestroyInstance自行管理單件的生命周期
        /// </summary>
        /// <returns>返回單件實(shí)例</returns>
        public static T Instance
        {
            get{

                 if (_instance == null && !_destroyed)
                 {
                     _instance = (T) FindObjectOfType(typeof (T));
                     if (_instance == null)
                    {
                       GameObject go = new GameObject(typeof (T).Name);
                       _instance =  go.AddComponent<T>();

                        DontDestroyOnLoad(_instance);

                       var singletonRootGo = GameObject.Find("MonoSingletonRoot");
                       if (singletonRootGo != null)
                       {
                           go.transform.parent = singletonRootGo.transform;
                         }
                    }
                 }
                return _instance;
            }

         }

        /// <summary>
        ///     刪除單件實(shí)例,這種繼承關(guān)系的單件生命周期應(yīng)該由模塊顯示管理
        /// </summary>
        public static void DestroyInstance()
        {
            if (_instance != null)
                Destroy(_instance.gameObject);

            _destroyed = true;
            _instance = null;
        }

        /// <summary>
        ///     Awake消息,確保單件實(shí)例的唯一性
        /// </summary>
        protected virtual void Awake()
        {
            if (_instance != null && _instance.gameObject != gameObject) Destroy(gameObject);
            else if(_instance == null)
                _instance = GetComponent<T>();

            DontDestroyOnLoad(_instance);
        }

        /// <summary>
        ///     OnDestroy消息,確保單件的靜態(tài)實(shí)例會(huì)隨著GameObject銷毀
        /// </summary>
        protected virtual void OnDestroy()
        {
            if (_instance != null && _instance.gameObject == gameObject)
            {
                _instance = null;                
            }
        }

        /// <summary>
        ///     Have Instance
        /// </summary>
        /// <returns></returns>
        public static bool HaveInstance()
        {
            return _instance != null;
        }
    }

如何調(diào)試Lua腳步,接下來,JumboWu會(huì)寫一篇關(guān)于ZeroBrane這個(gè)工具。

在此感謝xLua作者:車雄生 chexiongsheng?。?!
期待大家的支持!?。?/p>

作者:JumboWu
原文鏈接:http://www.itdecent.cn/p/dc4de5612d9e
來源:簡書
簡書著作權(quán)歸作者所有,任何形式的轉(zhuǎn)載都請聯(lián)系作者獲得授權(quán)并注明出處。

轉(zhuǎn)載者留言:文章內(nèi)容本人未作修改
轉(zhuǎn)載:菜鳥
轉(zhuǎn)載鏈接:http://www.itdecent.cn/p/3ebf7d7dd3ff
如涉及侵權(quán)問題請聯(lián)系(菜鳥)刪除

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容