1.什么是熱更新
熱更新是指在不需要重新編譯打包游戲的情況下,在線更新游戲中的一些非核心代碼和資源,比如活動(dòng)運(yùn)營和打補(bǔ)丁。熱更新分為資源熱更新和代碼熱更新兩種,代碼熱更新實(shí)際上也是把代碼當(dāng)成資源的一種熱更新,但通常所說的熱更新一般是指代碼熱更新。資源熱更新主要通過AssetBundle來實(shí)現(xiàn),在Unity編輯器內(nèi)為游戲中所用到的資源指定AB包的名稱和后綴,然后進(jìn)行打包并上傳服務(wù)器,待游戲運(yùn)行時(shí)動(dòng)態(tài)加載服務(wù)器上的AB資源包。代碼熱更新主要包括Lua熱更新、ILRuntime熱更新和C#直接反射熱更新等。由于ILRuntime熱更新還不成熟可能存在一些坑,而C#直接反射熱更新又不支持IOS平臺(tái),因此目前大多采用更成熟的、沒有平臺(tái)限制的Lua熱更新方案。
2.為什么要有熱更新
熱更新,能夠縮短用戶取得新版客戶端的流程,改善用戶體驗(yàn)。
沒有熱更新:
pc用戶:下載客戶端->等待下載->安裝客戶端->等待安裝->啟動(dòng)->等待加載->玩
手機(jī)用戶:商城下載APP->等待下載->等待安裝->啟動(dòng)->等待加載->玩
有了熱更新:
pc用戶:啟動(dòng)->等待熱更新->等待加載->玩
有獨(dú)立loader的pc用戶:啟動(dòng)loader->等待熱更新->啟動(dòng)游戲->等待加載->玩
手機(jī)用戶:啟動(dòng)->等待熱更新->等待加載->玩
通過對(duì)比就可以看出,有沒有熱更新對(duì)于用戶體驗(yàn)的影響還是挺大的,主要就是省去用戶自行更新客戶端的步驟。
3.如何使用熱更新,使用熱更新的不同方案比較
3.1.LUA熱更(LUA與C#綁定,方案成熟)
Lua熱更新解決方案是通過一個(gè)Lua熱更新插件(如ulua、slua、tolua、xlua等)來提供一個(gè)Lua的運(yùn)行環(huán)境以及和C#進(jìn)行交互。xLua是騰訊開源的熱更新插件,有大廠背書和專職人員維護(hù),插件的穩(wěn)定性和可持續(xù)性較強(qiáng)。?
由于Lua不需要編譯,因此Lua代碼可以直接在Lua虛擬機(jī)里運(yùn)行,Python和JavaScript等腳本語言也是同理。而xLua熱更新插件就是為Unity、.Net、Mono等C#環(huán)境提供一個(gè)Lua虛擬機(jī),使這些環(huán)境里也可以運(yùn)行Lua代碼,從而為它們?cè)黾覮ua腳本編程的能力。借助xLua,這些Lua代碼就可以方便的和C#相互調(diào)用。這樣平時(shí)開發(fā)時(shí)使用C#,等需要熱更新時(shí)再使用Lua,等下次版本更新時(shí)再把之前的Lua代碼轉(zhuǎn)換成C#代碼,從而保證游戲正常運(yùn)營。
其中的XLua下文會(huì)重點(diǎn)討論。
lua熱更原理:邏輯代碼轉(zhuǎn)化為腳本,腳本轉(zhuǎn)化為文本資源,以更新資源的形式更新程序
?一位老哥做的Xlua性能測(cè)試:
XLua 性能測(cè)試_tian2kong的專欄-CSDN博客_xlua性能優(yōu)化這個(gè)一段代碼,運(yùn)行的性能分析是這樣的這個(gè)代碼是我用lua寫的,其他的環(huán)境是一樣,就是這個(gè)代碼差別,一個(gè)用lua寫,一個(gè)C#寫,運(yùn)行的分析圖是這樣的從這個(gè)兩個(gè)分析圖,我們可以看出,就Translate這個(gè)api函數(shù),一個(gè)在C#調(diào)用,一個(gè)在xlua調(diào)用,如果數(shù)量級(jí)多的話,性能上還是差別很大的所以我個(gè)人覺得,如果是UI用xlua實(shí)現(xiàn)沒有大問題,如果是在戰(zhàn)斗中,盡量不要再Xlua的update中取實(shí)現(xiàn)功能...https://blog.csdn.net/tian2kong/article/details/79423848這位老哥,用一個(gè)同樣的函數(shù),用C#寫的運(yùn)行10.68ms,用Xlua寫的運(yùn)行37.29ms。可見Xlua性能上還是差別很大的。后文將繼續(xù)研究為什么Xlua性能更差。
3.2 .ILRuntime熱更
ILRuntime項(xiàng)目是掌趣科技開源的熱更新項(xiàng)目,它為基于C#的平臺(tái)(例如Unity)提供了一個(gè)純C#、快速、方便和可靠的IL運(yùn)行時(shí),使得能夠在不支持JIT的硬件環(huán)境(如iOS)能夠?qū)崿F(xiàn)代碼熱更新。ILRuntime項(xiàng)目的原理實(shí)際上就是先用VS把需要熱更新的C#代碼封裝成DLL(動(dòng)態(tài)鏈接庫)文件,然后通過Mono.Cecil庫讀取DLL信息并得到對(duì)應(yīng)的IL中間代碼(IL是.NET平臺(tái)上的C#、F#等高級(jí)語言編譯后產(chǎn)生的中間代碼,IL的具體形式為.NET平臺(tái)編譯后得到的.dll動(dòng)態(tài)鏈接庫文件或.exe可執(zhí)行文件),最后再用內(nèi)置的IL解譯執(zhí)行虛擬機(jī)來執(zhí)行DLL文件中的IL代碼。
由于ILRuntime項(xiàng)目是使用C#來完成熱更新,因此很多時(shí)候會(huì)用到反射來實(shí)現(xiàn)某些功能。而反射是.NET平臺(tái)在運(yùn)行時(shí)獲取類型(包括類、接口、結(jié)構(gòu)體、委托和枚舉等類型)信息的重要機(jī)制,即從對(duì)象外部獲取內(nèi)部的信息,包括字段、屬性、方法、構(gòu)造函數(shù)和特性等。我們可以使用反射動(dòng)態(tài)獲取類型的信息,并利用這些信息動(dòng)態(tài)創(chuàng)建對(duì)應(yīng)類型的對(duì)象。只不過ILRuntime中的反射有兩種:一種是在熱更新DLL中直接使用C#反射獲取到System.Type類對(duì)象;另一種是在Unity主工程中通過appdomain.LoadedTypes來獲取繼承自System.Type類的IType類對(duì)象,因?yàn)樵赨nity主工程中無法直接通過System.Type類來獲取熱更新DLL中的類。
ILRuntime下文也會(huì)重點(diǎn)討論。
ILRuntime項(xiàng)目為基于C#的平臺(tái)(例如Unity)提供了一個(gè)純C#實(shí)現(xiàn)
,快速
、方便
且可靠
的IL運(yùn)行時(shí)(后文詳細(xì)了解),使得能夠在不支持JIT的硬件環(huán)境(如iOS)(后文詳細(xì)了解)能夠?qū)崿F(xiàn)代碼的熱更新。
3.3.直接更dll(IOS不能用)
由于Android支持JIT(Just In Time)即時(shí)編譯(動(dòng)態(tài)編譯)的模式,即可以邊運(yùn)行邊編譯,支持在運(yùn)行時(shí)動(dòng)態(tài)生成代碼和類型。從Android N開始引入了一種同時(shí)使用JIT和AOT的混合編譯模式。JIT的優(yōu)點(diǎn)是支持在運(yùn)行時(shí)動(dòng)態(tài)生成代碼和類型,APP安裝快,不占用太多內(nèi)存。缺點(diǎn)是編譯時(shí)占用運(yùn)行時(shí)資源,執(zhí)行速度比AOT慢。比如,C#中的虛函數(shù)和反射都是在程序運(yùn)行時(shí)才確定對(duì)應(yīng)的重載方法和類。因此,Android平臺(tái)可以不借助任何第三方熱更新方案,直接使用C#反射執(zhí)行DLL文件。實(shí)際開發(fā)時(shí)通過System.Reflection.Assembly類加載程序集DLL文件,然后再利用System.Type類獲取程序集中某個(gè)類的信息,還可以通過Activator類來動(dòng)態(tài)創(chuàng)建實(shí)例對(duì)象。
而IOS平臺(tái)采用AOT(Ahead Of Time)預(yù)先編譯(靜態(tài)編譯)的模式,不支持JIT編譯模式,即程序運(yùn)行前就將代碼編譯成機(jī)器碼存儲(chǔ)在本地,然后運(yùn)行時(shí)直接執(zhí)行即可,因此AOT不能在運(yùn)行時(shí)動(dòng)態(tài)生成代碼和類型。AOT的優(yōu)點(diǎn)是執(zhí)行速度快,安全性更高。缺點(diǎn)是由于AOT需要提前編譯,所以APP的安裝時(shí)間長且占內(nèi)存。Mono在IOS平臺(tái)上采用Full AOT模式運(yùn)行,如果直接使用C#反射執(zhí)行DLL文件,就會(huì)觸發(fā)Mono的JIT編譯器,而Full AOT模式下又不允許JIT,于是Mono就會(huì)報(bào)錯(cuò)。因此,IOS平臺(tái)上不允許直接使用C#反射執(zhí)行DLL文件來實(shí)現(xiàn)熱更新。
將執(zhí)行代碼預(yù)編譯為assembly?dll。將代碼作為TextAsset打包進(jìn)Assetbundle。運(yùn)行時(shí),使用Reflection機(jī)制實(shí)現(xiàn)代碼的功能。更新相應(yīng)的Assetbundle即可實(shí)現(xiàn)熱更新。(具體技術(shù)細(xì)節(jié),后文討論)
4.Xlua源碼實(shí)現(xiàn)
4.0 Xlua的特性
xLua為C#環(huán)境增加Lua腳本編程的能力,借助xLua,這些Lua代碼可以方便的和C#相互調(diào)用。
Xlua可以:?
- 1.可以運(yùn)行時(shí)把C#實(shí)現(xiàn)(方法,操作符,屬性,事件等等)替換成lua實(shí)現(xiàn);
- 2.出色的GC優(yōu)化,自定義struct,枚舉在Lua和C#間傳遞無C# gc alloc;
- 3.編輯器下無需生成代碼,開發(fā)更輕量;
- 4.為很多C#實(shí)現(xiàn)打熱補(bǔ)丁
4.1 Xlua的使用
4.1.1?Lua文件加載
luaenv = new LuaEnv();
luaenv.DoString("print('hello world')");
4.1.2?加載Lua文件
luaenv = new LuaEnv();
luaenv.DoString("require 'byfile'");
require實(shí)際上是調(diào)一個(gè)個(gè)的原生的loader去加載,有一個(gè)成功就不再往下嘗試,全失敗則報(bào)文件找不到。 目前xLua除了原生的loader外,還添加了從Resource加載的loader(Xlua的一個(gè)特性,自定義loader),需要注意的是因?yàn)镽esource只支持有限的后綴,放Resources下的lua文件得加上txt后綴。
建議的加載Lua腳本方式是:整個(gè)程序就一個(gè)DoString("require 'main'"),然后在main.lua加載其它腳本(類似lua腳本的命令行執(zhí)行:lua main.lua)。
?4.1.3?自定義Loader
Loader運(yùn)行的時(shí)候會(huì)檢索所有的loader,這里給Loader列表,添加一個(gè)Loader,當(dāng)其他所有l(wèi)oader都找不到"InMemory"的時(shí)候,這個(gè)自定義loader就起作用了。檢測(cè)filename名稱并返回相應(yīng)的東西。
luaenv = new LuaEnv();
luaenv.AddLoader((ref string filename) =>
{
if (filename == "InMemory")
{
string script = "return {ccc = 9999}";
return System.Text.Encoding.UTF8.GetBytes(script);
}
return null;
});
luaenv.DoString("print('InMemory.ccc=', require('InMemory').ccc)");
?4.1.4?C#訪問Lua
這里指的是C#主動(dòng)發(fā)起對(duì)Lua數(shù)據(jù)結(jié)構(gòu)的訪問。
1.全局基本數(shù)據(jù)類型:(調(diào)用LuaEnv.Global)
luaenv.Global.Get<int>("a")
luaenv.Global.Get<string>("b")
luaenv.Global.Get<bool>("c")
2.訪問一個(gè)全局的table:
也是用上面的Get方法,類型則要特殊處理。
2.1 映射到普通class或struct:(其實(shí)就是Get<>中的參數(shù)填XXXClass)
DClass d = luaenv.Global.Get<DClass>("d");//映射到有對(duì)應(yīng)字段的class,by value
Debug.Log("_G.d = {f1=" + d.f1 + ", f2=" + d.f2 + "}");
對(duì)于{f1 = 100, f2 = 100}可以定義一個(gè)包含public int f1;public int f2;的class。 這種方式下xLua會(huì)幫你new一個(gè)實(shí)例,并把對(duì)應(yīng)的字段賦值過去。這個(gè)過程是值拷貝,如果class比較復(fù)雜代價(jià)會(huì)比較大。而且修改class的字段值不會(huì)同步到table,反過來也不會(huì)。
2.2?映射到一個(gè)interface:
這種方式依賴于生成代碼(如果沒生成代碼會(huì)拋InvalidCastException異常),代碼生成器會(huì)生成這個(gè)interface的實(shí)例,如果get一個(gè)屬性,生成代碼會(huì)get對(duì)應(yīng)的table字段,如果set屬性也會(huì)設(shè)置對(duì)應(yīng)的字段。甚至可以通過interface的方法訪問lua的函數(shù)。(這個(gè)要求interface加到生成列表,否則會(huì)返回null)
本來interface不能直接生成實(shí)例的,這里給直接生成實(shí)例了。
ItfD d3 = luaenv.Global.Get<ItfD>("d"); //映射到interface實(shí)例,by ref,這個(gè)要求interface加到生成列表,否則會(huì)返回null,建議用法
d3.f2 = 1000;
Debug.Log("_G.d = {f1=" + d3.f1 + ", f2=" + d3.f2 + "}");
Debug.Log("_G.d:add(1, 2)=" + d3.add(1, 2));
2.3?更輕量級(jí)的by value方式:映射到Dictionary<>,List<>
不想定義class或者interface的話,可以考慮用這個(gè),前提t(yī)able下key和value的類型都是一致的。
Dictionary<string, double> d1 = luaenv.Global.Get<Dictionary<string, double>>("d");//映射到Dictionary<string, double>,by value
Debug.Log("_G.d = {f1=" + d1["f1"] + ", f2=" + d1["f2"] + "}, d.Count=" + d1.Count);
2.4?另外一種by ref方式:映射到LuaTable類
這種方式好處是不需要生成代碼,但也有一些問題,比如慢,比方式2要慢一個(gè)數(shù)量級(jí),比如沒有類型檢查。
LuaTable d4 = luaenv.Global.Get<LuaTable>("d");//映射到LuaTable,by ref
Debug.Log("_G.d = {f1=" + d4.Get<int>("f1") + ", f2=" + d4.Get<int>("f2") + "}");
3.訪問一個(gè)全局的function:
3.1.映射到delegate:
這種是建議的方式,性能好很多,而且類型安全。缺點(diǎn)是要生成代碼(如果沒生成代碼會(huì)拋InvalidCastException異常)。
[CSharpCallLua]
public delegate int FDelegate(int a, string b, out DClass c);
Action e = luaenv.Global.Get<Action>("e");//映射到一個(gè)delgate,要求delegate加到生成列表,否則返回null,建議用法
e();
FDelegate f = luaenv.Global.Get<FDelegate>("f");
DClass d_ret;
int f_ret = f(100, "John", out d_ret);//lua的多返回值映射:從左往右映射到c#的輸出參數(shù),輸出參數(shù)包括返回值,out參數(shù),ref參數(shù)
Debug.Log("ret.d = {f1=" + d_ret.f1 + ", f2=" + d_ret.f2 + "}, ret=" + f_ret);
?或者:
[CSharpCallLua]
public delegate Action GetE();
GetE ret_e = luaenv.Global.Get<GetE>("ret_e");//delegate可以返回更復(fù)雜的類型,甚至是另外一個(gè)delegate
e = ret_e();
e();
?3.2.映射到LuaFunction
這種方式的優(yōu)缺點(diǎn)剛好和第一種相反。性能差類型不安全,但是不用生成代碼。 使用也簡單,LuaFunction上有個(gè)變參的Call函數(shù),可以傳任意類型,任意個(gè)數(shù)的參數(shù),返回值是object的數(shù)組,對(duì)應(yīng)于lua的多返回值。
LuaFunction d_e = luaenv.Global.Get<LuaFunction>("e");
d_e.Call();
會(huì)返回[object,object,object] 的列表,對(duì)應(yīng)lua函數(shù)返回多個(gè)值
4.注意事項(xiàng)
4.1 訪問lua全局?jǐn)?shù)據(jù),特別是table以及function,代價(jià)比較大,建議盡量少做,比如在初始化時(shí)把要調(diào)用的lua function獲取一次(映射到delegate)后,保存下來,后續(xù)直接調(diào)用該delegate即可。table也類似。
4.2如果lua側(cè)的實(shí)現(xiàn)的部分都以delegate和interface的方式提供,使用方可以完全和xLua解耦:由一個(gè)專門的模塊負(fù)責(zé)xlua的初始化以及delegate、interface的映射,然后把這些delegate和interface設(shè)置到要用到它們的地方。
4.1.5 Lua調(diào)用C#
1.new C#對(duì)象
local newGameObj = CS.UnityEngine.GameObject()
?2.訪問C#靜態(tài)屬性,方法
讀寫靜態(tài)屬性
CS.UnityEngine.Time.deltaTime
CS.UnityEngine.Time.timeScale = 0.5
調(diào)用靜態(tài)方法
CS.UnityEngine.GameObject.Find('helloworld')
//小技巧:如果需要經(jīng)常訪問的類,可以先用局部變量引用后訪問,除了減少敲代碼的時(shí)間,還能提高性能:
local GameObject = CS.UnityEngine.GameObject
GameObject.Find('helloworld')
3.訪問C#成員屬性,方法
讀寫成員屬性
testobj.DMF
testobj.DMF = 1024
調(diào)用成員方法
注意:調(diào)用成員方法,第一個(gè)參數(shù)需要傳該對(duì)象,建議用冒號(hào)語法糖,如下
testobj:DMFunc()
//等同于
testobj.DMFunc(testobj)
參數(shù)的輸入輸出屬性(out,ref)
Lua調(diào)用側(cè)的參數(shù)處理規(guī)則:C#的普通參數(shù)算一個(gè)輸入形參,ref修飾的算一個(gè)輸入形參,out不算,然后從左往右對(duì)應(yīng)lua 調(diào)用側(cè)的實(shí)參列表;
Lua調(diào)用側(cè)的返回值處理規(guī)則:C#函數(shù)的返回值(如果有的話)算一個(gè)返回值,out算一個(gè)返回值,ref算一個(gè)返回值,然后從左往右對(duì)應(yīng)lua的多返回值。
public struct Param1
{
public int x;
public string y;
}
public double ComplexFunc(Param1 p1, ref int p2, out string p3, Action luafunc, out Action csfunc)
{
Debug.Log("P1 = {x=" + p1.x + ",y=" + p1.y + "},p2 = " + p2);
luafunc();
p2 = p2 * p1.x;
p3 = "hello " + p1.y;
csfunc = () =>
{
Debug.Log("csharp callback invoked!");
};
return 1.23;
}
--復(fù)雜方法調(diào)用
local ret, p2, p3, csfunc = testobj:ComplexFunc({x=3, y = 'john'}, 100, function()
print('i am lua callback')
end)
print('ComplexFunc ret:', ret, p2, p3, csfunc)
csfunc()
4.C#復(fù)雜類型和table的自動(dòng)轉(zhuǎn)換
對(duì)于一個(gè)有無參構(gòu)造函數(shù)的C#復(fù)雜類型,在lua側(cè)可以直接用一個(gè)table來代替,該table對(duì)應(yīng)復(fù)雜類型的public字段有相應(yīng)字段即可,支持函數(shù)參數(shù)傳遞,屬性賦值等,例如: C#下B結(jié)構(gòu)體(class也支持)定義如下:
public struct A
{
public int a;
}
public struct B
{
public A b;
public double c;
}
某個(gè)類有成員函數(shù)如下:
void Foo(B b)
在lua可以這么調(diào)用
obj:Foo({b = {a = 100}, c = 200})
5.獲取類型(相當(dāng)于C#的typeof)
typeof(CS.UnityEngine.ParticleSystem)
6.“強(qiáng)”轉(zhuǎn)
lua沒類型,所以不會(huì)有強(qiáng)類型語言的“強(qiáng)轉(zhuǎn)”,但有個(gè)有點(diǎn)像的東西:告訴xlua要用指定的生成代碼去調(diào)用一個(gè)對(duì)象,這在什么情況下能用到呢?有的時(shí)候第三方庫對(duì)外暴露的是一個(gè)interface或者抽象類,實(shí)現(xiàn)類是隱藏的,這樣我們無法對(duì)實(shí)現(xiàn)類進(jìn)行代碼生成。該實(shí)現(xiàn)類將會(huì)被xlua識(shí)別為未生成代碼而用反射來訪問,如果這個(gè)調(diào)用是很頻繁的話還是很影響性能的,這時(shí)我們就可以把這個(gè)interface或者抽象類加到生成代碼,然后指定用該生成代碼來訪問:
cast(calc, typeof(CS.Tutorial.Calc))
上面就是指定用CS.Tutorial.Calc的生成代碼來訪問calc對(duì)象。
//通過反射來調(diào)用函數(shù)
local calc = testobj:GetCalc()
print('assess instance of InnerCalc via reflection', calc:add(1, 2))
assert(calc.id == 100)
//把這個(gè)interface或者抽象類加到生成代碼,然后指定用該生成代碼來訪問
cast(calc, typeof(CS.Tutorial.ICalc))
print('cast to interface ICalc', calc:add(1, 2))
assert(calc.id == nil)
4.2 Xlua具體代碼的實(shí)現(xiàn)與原理
4.2.1 Xlua中?lua調(diào)用c#的原理:
如果一個(gè)C#類型添加了[LuaCallCSharp]標(biāo)簽,xLua會(huì)生成這個(gè)類型的XXXXXWrap.cs適配代碼(包括構(gòu)造該類型實(shí)例,訪問其成員屬性、方法,靜態(tài)屬性、方法),并用luaAPI放到lua的堆棧里面。如果沒有提前生成適配代碼,等程序運(yùn)行,lua調(diào)用c#的時(shí)候,就會(huì)用性能較低的反射方式來訪問,把c#類用反射全部遍歷一遍,然后再放入lua的堆棧中。(反射比普通方法慢10000倍)
而且在IL2CPP下還有可能因?yàn)榇a剪裁而導(dǎo)致無法訪問。IL2CPP是Unity推出的用來替代Mono VM的編譯器。
初始化過程:
(DelegatesGensBridge.cs :Xlua生成這個(gè)文件,是用來指向 “hotfix熱更新中,lua替代掉c#的方法函數(shù)?!? EnumWrap.cs: 把枚舉都遍歷一遍,存到EnumWrap.cs中,之后放入lua堆棧中。 XXXXXWrap.cs適配代碼:把lua需要調(diào)用的方法存到適配代碼中,之后放入Lua堆棧中。)
lua調(diào)用C#的過程:?
?4.2.2 Xlua中 c#調(diào)用lua的原理
如果C#想要訪問Lua中函數(shù)或Table,就要在C#中對(duì)應(yīng)的Delegate或Interface添加?[CSharpCallLua]標(biāo)簽。Xlua會(huì)生成相應(yīng)的XXXXXBridge.cs文件,會(huì)把lua中相應(yīng)的函數(shù)方法,映射到c#中相應(yīng)的Delegate或Interface中,然后通過luaenv.Global來調(diào)用。盡管還有其他映射方式,但最好通過Delegate來映射Lua中的函數(shù),通過Interface來映射Lua中的Table。
?4.2.3 Xlua中 Hotfix原理
生成XXXXXWrap.cs適配器代碼后,執(zhí)行XLua/Hotfix inject in Editor后,xLua會(huì)使用Mono.Cecil庫對(duì)當(dāng)前工程下的Assembly-CSharp.dll程序集進(jìn)行IL注入。由于移動(dòng)平臺(tái)無法把C#代碼編譯成IL中間代碼,所以絕大多數(shù)熱更新方案都會(huì)涉及到IL注入(啥意思?理解不了這句,直接跳過。),只有這樣Unity內(nèi)置的VM才能對(duì)熱更新的代碼進(jìn)行處理。xLua進(jìn)行IL注入時(shí)會(huì)為打上[Hotfix]標(biāo)簽的類的所有函數(shù)創(chuàng)建一個(gè)DelegateBridge變量,同時(shí)添加對(duì)應(yīng)的判斷條件,有hotfix代碼就執(zhí)行l(wèi)ua的hotfix代碼,沒有hotfix代碼就執(zhí)行原來的c#代碼。
4.3 Xlua中 其他問題
1.luaenv的原理是什么?
luaenv是Xlua中的一個(gè)Lua虛擬機(jī)類,用來執(zhí)行l(wèi)ua代碼,或者管理Lua堆棧。
Lua初學(xué)者(四)--Lua調(diào)用原理展示(lua的堆棧)_豬豬俠的點(diǎn)滴-CSDN博客_lua棧本文較詳細(xì)的描述了 宿主語言(這里拿C++實(shí)例) 調(diào)用 Lua 時(shí)過程中的 棧的使用情況,最后附圖 動(dòng)態(tài) 展示整個(gè)過程。希望對(duì)大家提供幫助。https://blog.csdn.net/zhuzhuyule/article/details/41086745?utm_medium=distribute.pc_relevant_t0.none-task-blog-2~default~BlogCommendFromMachineLearnPai2~default-1.control&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-2~default~BlogCommendFromMachineLearnPai2~default-1.control
2.DoStrnig的原理是什么?
讓Xlua中的luaenv虛擬機(jī),執(zhí)行DoString中的代碼。
3.自定義Loader的原理是什么?
通過LuaEnv.AddLoader以及LuaAPI注冊(cè)require的回調(diào),lua代碼里頭調(diào)用require時(shí),參數(shù)將會(huì)透傳給回調(diào),回調(diào)中就可以根據(jù)這個(gè)參數(shù)去加載指定文件。
4.Lua映射到C#的class原理是什么?
通過為每個(gè)CShapeCallLua生成對(duì)應(yīng)的XXXXXBridge.cs文件,然后通過LuaAPI對(duì)對(duì)應(yīng)的lua函數(shù)具體部分進(jìn)行操作,讓C#中的函數(shù)和lua中的函數(shù)對(duì)應(yīng)起來。
5.代碼生成器是啥概念?
Xlua中的Generator.cs類,能根據(jù)模板,例如LuaClassWrap.tpl.txt,來生成具體的.cs或者.lua或者.xml文件。
6.為啥Lua映射到C#的LuaTable類會(huì)比映射到一個(gè)interface代碼生成器慢一個(gè)數(shù)量級(jí)?
這種泛而全的LuaTable類,要把Lua映射上去,必定要對(duì)lua方法進(jìn)行反射操作,反射又是十分耗時(shí)的。不如直接用代碼生成器,提前生成好XXXXBridge.cs,直接照著Bridge.cs中的結(jié)構(gòu),用LuaAPI填數(shù)據(jù)進(jìn)去,這樣速度快非常多。
7 by ref方式映射又是啥?
by ref就是by reference,引用傳遞的意思。Xlua官方源碼案例中,直接搜by ref能看到案例。
8.為什么訪問lua全局?jǐn)?shù)據(jù),特別是table以及function,代價(jià)比較大
這個(gè)問題網(wǎng)上的答案也是眾說紛紜,短期內(nèi)我也找不到好的解答。問題肯定出在存全局和存局部的數(shù)據(jù)結(jié)構(gòu),堆棧中的配置方法不一樣導(dǎo)致的。可能是全局?jǐn)?shù)據(jù)需要掃描所有堆棧,而局部數(shù)據(jù)知道了入口以及數(shù)據(jù)量大小,只要掃描局部小部分。
9.lua調(diào)用C#中的CS.是什么原理?
生成XXXXBridge.cs,然后用luaAPI進(jìn)行映射。前文有具體說明。
10.為什么先用局部變量引用后訪問,能提高性能?
因?yàn)槿肿兞吭L問慢,用局部引用后,訪問局部就快了。
11.語法糖的冒號(hào)testobj:DMFunc()是怎么實(shí)現(xiàn)的?
冒號(hào)這個(gè),是Lua自身的特性,不是Xlua特有的東西。
1、定義的時(shí)候:Class:test()與 Class.test(self)是等價(jià)的,點(diǎn)號(hào)(.)要達(dá)到冒號(hào)(:)的效果要加一個(gè)self參數(shù)到第一個(gè)參數(shù);
2、調(diào)用的時(shí)候:object:test() 與object.test(object)等價(jià),點(diǎn)號(hào)(.)要添加對(duì)象自身到第一個(gè)參數(shù)。
總結(jié):可以把點(diǎn)號(hào)(.)作為靜態(tài)方法來看待,冒號(hào)(:)作為成員方法來看待。
12 泛化(模版)方法 是什么?Extension methods功能進(jìn)行封裝又是什么?
泛化編程,和模板是一個(gè)編程的概念。泛型編程:編寫與類型無關(guān)的通用代碼,是代碼復(fù)用的一種手段。模板是泛型編程的基礎(chǔ)。
泛化編程和模板的使用_ice_玖聞?dòng)谑镭嫉牟┛?CSDN博客_編程泛化在學(xué)習(xí)的過程中,我們?cè)趯懘a的時(shí)候會(huì)使用一些形式相同,參數(shù)相同,但參數(shù)類型和返回值類型不同的一些函數(shù)。當(dāng)初我們學(xué)過函數(shù)重載,但函數(shù)重載存在以下一些不好的地方。-例如:重載的函數(shù)僅僅只是類型不同,代碼的復(fù)用率比較低,只要有新類型出現(xiàn)時(shí),就需要增加對(duì)應(yīng)的函數(shù)。代碼的可維護(hù)性比較低,一個(gè)出錯(cuò)可能所有的重載均出錯(cuò)。泛型編程:編寫與類型無關(guān)的通用代碼,是代碼復(fù)用的一種手段。模板是泛型編程的...https://blog.csdn.net/weixin_42357849/article/details/105492332?用泛化編程的理念,來對(duì)Extension methods擴(kuò)展功能,進(jìn)行一下封裝,簡化了擴(kuò)展功能的實(shí)現(xiàn)。
13.il2cpp是什么?
IL2CPP 是 Unity一種新的腳本后處理(Scripting Backend)方式,針對(duì).Net平臺(tái)編譯輸出的IL(中間語言)進(jìn)行處理。
IL2CPP主要由兩部分組成:
- AOT靜態(tài)編譯編譯器(il2cpp.exe)
- 運(yùn)行時(shí)庫(libil2cpp)
其中AOT將IL轉(zhuǎn)換為C++源碼,再交給各平臺(tái)的C++編譯器進(jìn)行編譯,達(dá)到平臺(tái)兼容的目的;運(yùn)行時(shí)庫則會(huì)提供諸如垃圾回收、線程/文件獲取、內(nèi)部調(diào)用直接修改托管數(shù)據(jù)結(jié)構(gòu)的原生代的服務(wù)與抽象。
Unity之IL2CPP - 知乎作者:羅鵬背景在Unity4.6.1 p5以后版本中,在PlayerSettings—>Other Settings—>Scripting Backend有mono和il2cpp兩個(gè)選項(xiàng),它們是Unity腳本后處理(Scripting Backend)的兩種方式。 概念I(lǐng)L2CPP 是 Unity…https://zhuanlan.zhihu.com/p/141748334
16.c#的標(biāo)簽
[CSharpCallLua] 這種標(biāo)簽,其實(shí)是
c#中的attribute的一種應(yīng)用
C# 特性(Attribute) | 菜鳥教程C# 特性(Attribute) 特性(Attribute)是用于在運(yùn)行時(shí)傳遞程序中各種元素(比如類、方法、結(jié)構(gòu)、枚舉、組件等)的行為信息的聲明性標(biāo)簽。您可以通過使用特性向程序添加聲明性信息。一個(gè)聲明性標(biāo)簽是通過放置在它所應(yīng)用的元素前面的方括號(hào)([ ])來描述的。 特性(Attribute)用于添加元數(shù)據(jù),如編譯器指令和注釋、描述、方法、類等其他信息。.Net 框架提供了兩種類型的特性:預(yù)定義特性和自定義特性。 規(guī)定特性(Att..https://www.runoob.com/csharp/csharp-attribute.html
17.Lua調(diào)用c#,使用反射方法的時(shí)候,l2cpp下還有可能因?yàn)榇a剪裁,這是什么?link.xml阻止il2cpp的代碼剪裁怎么做到的?
什么是代碼裁剪?
關(guān)于代碼裁剪的說明: Unity - Manual: Managed code stripping
勾選代碼裁剪,構(gòu)建時(shí)Unity代碼裁剪工具會(huì)分析項(xiàng)目中的程序集,查找和刪除未使用的代碼. 裁剪掉沒有使用到的代碼.比如,一款2D游戲只用到了Sprite, 2D物理組件, 就可以把沒有用到的3D物理代碼部分裁剪掉. 使用裁剪功能可以顯著減小包體大小, 也是目前Unity游戲包體優(yōu)化的一個(gè)重要環(huán)節(jié).
看起來確實(shí)是一個(gè)非常牛掰又實(shí)用的功能, 然而還有不少問題,Unity貌似只能正確裁剪那些構(gòu)建時(shí)自動(dòng)打包進(jìn)apk的預(yù)制體和場(chǎng)景中的腳本. 而AssetBundle上的腳本就就不會(huì)被處理到, 也就是說如果Prefab被打包成AssetBundle, 這個(gè)Prefab上的代碼可能會(huì)被裁剪掉, 導(dǎo)致運(yùn)行的時(shí)候報(bào)錯(cuò)閃退, 這無疑是致命的。所以Unity提供了裁剪等級(jí)的設(shè)置, 以及通過配置link.xml告訴Unity需要保留哪些代碼.
Unity IL2CPP發(fā)布64位,以及代碼裁剪Strip Engine Code_空空如我-CSDN博客_unity 代碼裁剪關(guān)于此方面我也是最近遇到問題才剛剛接觸,有理解有誤的地方還請(qǐng)路過的看官大佬不吝賜教.Google Play要求從2019年8月1日起apk必須支持64位CPU, 否則就下架或不讓上. 使apk支持ARM64就需要把Scripting Backend由Mono切換為IL2CPP那么問題來了, 通過IL2CPP打出的包往往不能正常運(yùn)行(閃退,報(bào)錯(cuò)).其原因就是, BuildSettin...https://blog.csdn.net/final5788/article/details/100183528
18.XLua.GCOptimize,C#枚舉值加上了這個(gè)配置。xLua會(huì)為該類型生成gc優(yōu)化代碼,效果是該值類型在lua和c#間傳遞不產(chǎn)生(C#)gc alloc,該類型的數(shù)組訪問也不產(chǎn)生gc,這段話是什么意思?
傳遞引用類型需要boxing和unboxing,產(chǎn)生gc,而傳遞值類型則不需要產(chǎn)生gc,所以xlua生成XXXbridge.cs模板,把struct里的各字段拷貝到一塊非托管內(nèi)存(Pack)(托管內(nèi)存由垃圾收集器GC)清理),以及從非托管內(nèi)存拷貝出各字段的值(UnPack),傳遞的都是值類型,所以不產(chǎn)生gc。
XLua官方demo5-避免c#和lua間值類型的GC分析_Troubledealer/uestc-CSDN博客_xlua 無gc? 代碼里的幾個(gè)標(biāo)簽解釋:? ? ? ? ?· XLua.GCOptimize: gc代碼優(yōu)化。對(duì)于一個(gè)c#純值類型(官網(wǎng)指一個(gè)只包含值類型的struct,可以嵌套其它只包含值類型的struct)或者c#枚舉值加上了這個(gè)配置,會(huì)使得該類型在lua和c#間傳遞不產(chǎn)生gc alloc,該類型的數(shù)組訪問也不會(huì)產(chǎn)生gc。? ? ? ? ?(除枚舉之外,包含無參構(gòu)造函數(shù)的復(fù)雜類型,都會(huì)生成lua table...https://blog.csdn.net/qq_31915745/article/details/79635076
20.luajit是什么?
LuaJIT即采用C語言寫的Lua代碼的解釋器。
LuaJIT is a?Just-In-Time Compiler?for the Lua* programming language.
21.如何復(fù)現(xiàn)XLua的配置
直接在/Editor中新建一個(gè)靜態(tài)列表,打上LuaCallCSharp標(biāo)簽。里面的typeof(xxx)內(nèi)容就是要Lua調(diào)用Cs的函數(shù)。
22.如何復(fù)現(xiàn)熱補(bǔ)丁操作指南
24.如何復(fù)現(xiàn)XLua增加刪除第三方lua庫
參考這篇文章,照做就行。
xlua 集成rapidjson_塵封的羽翼-CSDN博客_lua-rapidjson1、結(jié)合https://blog.csdn.net/wanzhihui0000/article/details/105603317
25.如何復(fù)現(xiàn)生成引擎二次開發(fā)指南
根據(jù)模板生成link.xml文件
目錄Xlua/Src/Editor/LinkXmlGen/LinkXmlGen.cs(功能文件)和
Xlua/Src/Editor/LinkXmlGen/LinkXmlGen.tpl.txt(模板文件)
模板文件其中的ForEachCsList函數(shù),是個(gè)普通的lua函數(shù),編寫在TemplateCommon.lua.txt文件中。
而<%XXXXX%>中間的lua代碼,都是會(huì)直接執(zhí)行的。其余的則直接打印
?
?
?
點(diǎn)擊生成link.xml。?
26.由于ios的限制我們的lua虛擬機(jī)加載不了動(dòng)態(tài)庫,而是直接編譯進(jìn)進(jìn)程里頭。lua虛擬機(jī)是啥?加載動(dòng)態(tài)庫又是啥?編譯進(jìn)進(jìn)程頭又是啥?
虛擬機(jī):用于模擬計(jì)算機(jī)運(yùn)行的程序.是個(gè)中間層,它處于腳本語言和硬件之間的一個(gè)程序。
?VM就是虛擬機(jī)的意思。luaenv是Xlua中的一個(gè)Lua虛擬機(jī)類。
靜態(tài)庫特點(diǎn)(linux):
命名上是以 *.o 結(jié)尾
靜態(tài)庫在鏈接階段直接就加入到可執(zhí)行的文件中了,在執(zhí)行過程中無需該靜態(tài)庫
相對(duì)于動(dòng)態(tài)庫生成的文件,使用靜態(tài)庫生成的文件連接生成的可執(zhí)行文件較大
動(dòng)態(tài)庫的特點(diǎn)(linux)
命名上是以 *.so
目標(biāo)文件在鏈接階段只是指明鏈接的那個(gè)動(dòng)態(tài)庫,動(dòng)態(tài)庫與目標(biāo)文件保持獨(dú)立。在執(zhí)行過程中需要該動(dòng)態(tài)庫
使用動(dòng)態(tài)庫生成的目標(biāo)文件較小
對(duì)于工程中比較共通的源碼文件,比如多個(gè)進(jìn)程使用同一個(gè)模塊的源碼,我們最好將其制作成動(dòng)態(tài)庫,以節(jié)省系統(tǒng)空間。同時(shí)如果動(dòng)態(tài)庫出現(xiàn)bug,只需要重新生成一個(gè)動(dòng)態(tài)庫并將以前的替換即可。不需要重新編譯其他模塊。
動(dòng)態(tài)庫_xiaoxiongxiongshi的博客-CSDN博客_動(dòng)態(tài)庫總結(jié)一:動(dòng)態(tài)庫前言?我們知道程序編譯鏈接經(jīng)常使用動(dòng)態(tài),同時(shí)我們可能還知道,動(dòng)態(tài)庫時(shí)程序運(yùn)行時(shí)加載的。但是動(dòng)態(tài)庫到底有什么作用,如何生成、如何加載等,我們卻很少關(guān)注。接下來,我給大家做一個(gè)簡單的介紹。1.1 動(dòng)態(tài)庫和靜態(tài)庫的區(qū)別靜態(tài)庫特點(diǎn)(linux):命名上是以 *.o 結(jié)尾靜態(tài)庫在鏈接階段直接就加入到可執(zhí)行的文件中了,在執(zhí)行過程中無需該靜態(tài)庫相對(duì)于動(dòng)態(tài)庫生成的文件,使用靜態(tài)庫生...https://blog.csdn.net/xiaoxiongxiongshi/article/details/104520188直接編譯進(jìn)進(jìn)程里頭:用JIT即時(shí)編譯器,直接編譯到進(jìn)程里頭。
27.AOT和JIT的區(qū)別是什么?
目前,程序主要有兩種運(yùn)行方式:靜態(tài)編譯與動(dòng)態(tài)解釋。
- 靜態(tài)編譯的程序在執(zhí)行前全部被翻譯為機(jī)器碼,通常將這種類型稱為AOT (Ahead of time)即 “提前編譯”,典型代表是用C/C++開發(fā)的應(yīng)用,它們必須在執(zhí)行前編譯成機(jī)器碼
- 而解釋執(zhí)行的則是一句一句邊翻譯邊運(yùn)行,通常將這種類型稱為JIT(Just-in-time)即“即時(shí)編譯”,代表則非常多,如JavaScript、python
IOS不讓JIT,所以不能直接熱更DLL,所以得使用Mono.Cecil庫對(duì)當(dāng)前工程下的Assembly-CSharp.dll程序集進(jìn)行IL注入。該中間代碼IL再經(jīng).NET平臺(tái)中的CLR(類似于JVM)編譯成機(jī)器碼讓CPU執(zhí)行相關(guān)指令。
28.Assembly-CSharp.dll程序集是什么?
項(xiàng)目中的cs代碼在打包時(shí)都會(huì)被打進(jìn)Assembly-CSharp.dll中,通過Mono調(diào)用。
29.Mono.Cecil庫干嘛的?
Mono.Cecil:一個(gè)可加載并瀏覽現(xiàn)有程序集并進(jìn)行動(dòng)態(tài)修改并保存的.NET框架
30 IL是什么?
IL的全稱是Intermediate Language (IL)即將.NET代碼轉(zhuǎn)化為機(jī)器語言的一個(gè)中間語言的縮寫。在一定程度上,我們可以將其理解為偽匯編語言。我們?cè)谑褂?NET框架中的C#、VB.NET、F#等語言的時(shí)候,編譯過程并不是像C/C++一樣直接編譯出原生代碼,而是編譯成IL中間語言。通過IL中間語言這種方式,可以實(shí)現(xiàn)跨平臺(tái)、提高程序靈活性等多種優(yōu)點(diǎn)。
?31.CLR是什么,JVM又是什么?
公共語言運(yùn)行庫 (common language runtime,CLR) 是托管代碼執(zhí)行核心中的引擎。運(yùn)行庫為托管代碼提供各種服務(wù),如跨語言集成、代碼訪問安全性、對(duì)象生存期管理、調(diào)試和分析支持。它是整個(gè).NET框架的核心,它為.NET應(yīng)用程序提供了一個(gè)托管的代碼執(zhí)行環(huán)境。它實(shí)際上是駐留在內(nèi)存里的一段代理代碼,負(fù)責(zé)應(yīng)用程序在整個(gè)執(zhí)行期間的代碼管理工作。
公共語言運(yùn)行庫_百度百科公共語言運(yùn)行庫 (common language runtime,CLR) 是托管代碼執(zhí)行核心中的引擎。運(yùn)行庫為托管代碼提供各種服務(wù),如跨語言集成、代碼訪問安全性、對(duì)象生存期管理、調(diào)試和分析支持。它是整個(gè).NET框架的核心,它為.NET應(yīng)用程序提供了一個(gè)托管的代碼執(zhí)行環(huán)境。它實(shí)際上是駐留在內(nèi)存里的一段代理代碼,負(fù)責(zé)應(yīng)用程序在整個(gè)執(zhí)行期間的代碼管理工作。https://baike.baidu.com/item/%E5%85%AC%E5%85%B1%E8%AF%AD%E8%A8%80%E8%BF%90%E8%A1%8C%E5%BA%93/2882128?fromtitle=CLR&fromid=10567215&fr=aladdinJVM是Java Virtual Machine(Java虛擬機(jī))的縮寫。
5.ILRuntime源碼實(shí)現(xiàn)
5.1 ILRuntime的原理
(Runtime運(yùn)行時(shí)刻是指一個(gè)程序在運(yùn)行(cc或者在被執(zhí)行)的狀態(tài))
ILRuntime借助Mono.Cecil庫來讀取DLL的PE信息(Mono.Cecil庫也可以進(jìn)行IL注入),以及當(dāng)中類型的所有信息,最終得到方法的IL匯編碼,然后通過內(nèi)置的IL解譯執(zhí)行虛擬機(jī)來執(zhí)行DLL中的代碼。
?ILRuntime熱更流程:
ILRuntime的主要限制:
6.熱更新的關(guān)鍵點(diǎn),重要節(jié)點(diǎn),疑難雜癥場(chǎng)景
6.1 為什么Xlua性能更差?
lua調(diào)CS代碼,還需要把XXXWrap.cs中的代碼往lua堆棧里面?zhèn)髦?,如果unity調(diào)用原生接口,就沒有這個(gè)步驟了。
6.2 Xlua的幾個(gè)優(yōu)勢(shì)
1.用的人最多,性能最好的lua熱更新插件對(duì)應(yīng)的熱更新解決方案。
2.xLua是騰訊開源的熱更新插件,有大廠背書和專職人員維護(hù),插件的穩(wěn)定性和可持續(xù)性較強(qiáng)
6.3?Net 4.6編譯的DLL啥意思?
如何編譯生成dll_李青鋒的專欄-CSDN博客_編譯dll動(dòng)態(tài)鏈接庫是Windows的基石。所有的Win32?API函數(shù)都包含在DLL中。3個(gè)最重要的DLL是KERNEL32.DLL,它由管理內(nèi)存、進(jìn)程和線程的函數(shù)組成;USER32.DLL,它由執(zhí)行用戶界面的任務(wù)(如創(chuàng)建窗口和發(fā)送消息)的函數(shù)組成;GDI32.DLL,它由繪圖和顯示文本的函數(shù)組成。在此,我們主要用實(shí)際的操作過程,簡要的說明如何創(chuàng)建自己的?Win32?DLL。一、創(chuàng)建DLL工程https://blog.csdn.net/qianchenglenger/article/details/21599235
6.4?CLR綁定使跨域調(diào)用是啥?
這里特指的是ILRuntime方案里面的主dll和熱更dll中間的跨域調(diào)用問題。
Unity C#熱更新方案 ILRuntime學(xué)習(xí)筆記(二) 代碼跨域調(diào)用https://segmentfault.com/a/1190000023290547
6.5 跨域繼承是啥?
同上的答案。
6.6?IL運(yùn)行時(shí)是啥?
6.7 不支持JIT的硬件環(huán)境(如iOS),為什么IOS不支持JIT ?
IOS不讓JIT,所以不能直接熱更DLL,所以得使用Mono.Cecil庫對(duì)當(dāng)前工程下的Assembly-CSharp.dll程序集進(jìn)行IL注入。該中間代碼IL再經(jīng).NET平臺(tái)中的CLR(類似于JVM)編譯成機(jī)器碼讓CPU執(zhí)行相關(guān)指令。
?為什么IOS不支持JIT,我也不知道。不過最新的IOS14.2又支持JIT模式了,難不成?以后基于DLL的熱更....又能用啦?那Xlua豈不是要被淘汰了?
6.8? 我的DLL怎么才能獲取到UnityEngine命名空間類的引用?
unity 使用C#反射獲取dll中的類、調(diào)用類中的字段、方法_被代碼折磨的狗子的博客-CSDN博客_c#獲取dll中的方法一、什么是反射?反射是.NET中的重要機(jī)制,通過反射,可以在運(yùn)行時(shí)獲得程序或程序集中每一個(gè)類型(包括類、結(jié)構(gòu)、委托、接口和枚舉等)的成員和成員的信息。有了反射,即可對(duì)每一個(gè)類型了如指掌。另外我還可以直接創(chuàng)建對(duì)象,即使這個(gè)對(duì)象的類型在編譯時(shí)還不知道。二、反射的使用平時(shí)我們的寫法是先引用命名空間(dll),然后new一個(gè)對(duì)象,通過這個(gè)對(duì)象調(diào)用其中的字段或方法,通過反射,我們可以不用添加dll來實(shí)現(xiàn)效果。1.首先我們?cè)贑#中創(chuàng)建一個(gè)Testdll類 打包dll,內(nèi)容如下using Sy.https://blog.csdn.net/qq_42345116/article/details/121695595反射assembly-csharp.dll中的類。
6.9 我的DLL需要如何打成AssetBundle?
資源怎么打包,它就怎么打包。
6.10 程序下載AssetBundle如何讀取里面DLL?
調(diào)JIT相應(yīng)的方法。反射熱更下來的 .dll中的類。
6.11如何測(cè)試各個(gè)熱更新方案的性能
Unity Profiler性能分析全解析_Tokyo_2024的博客-CSDN博客_unityprofilerProfiler概述打開Unity Profiler1. Window->Analysis->Profiler。https://blog.csdn.net/Tokyo_2024/article/details/105388523
6.參考資料
xlua擴(kuò)展第三方庫記錄_流彩飛霞的專欄-CSDN博客_xlua 第三方
如何評(píng)價(jià)騰訊在Unity下的xLua(開源)熱更方案?
Unity 游戲用XLua的HotFix實(shí)現(xiàn)熱更原理揭秘
騰訊開源手游熱更新方案,Unity3D下的Lua編程
[Unity]基于IL代碼注入的Lua補(bǔ)丁方案
另類Unity熱更新大法:代碼注入式補(bǔ)丁熱更新
unity dll實(shí)現(xiàn)熱更新_baidu_28955655的博客-CSDN博客_unity 熱更新
ILRuntime的實(shí)現(xiàn)原理 — ILRuntime文章來源:http://www.zghlxwxcb.cn/news/detail-406125.html
深入理解xLua熱更新原理 - 鋼與鐵 - 博客園文章來源地址http://www.zghlxwxcb.cn/news/detail-406125.html
到了這里,關(guān)于Unity中的熱更新的基礎(chǔ)知識(shí),Xlua與ILRuntime基礎(chǔ)知識(shí)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!