版權(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、

2、

3、

先貼上代碼(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()

,不然會(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)系(菜鳥)刪除