目錄:
1)URP關於多個攝相機的性能優化
2)Unity Addressables打包的時候如何設置BuildAssetBundleOptions.DisableWriteTypeTree
3)Unreal可以用於商業化游戲的熱更新方案
4)UGUI SpriteAtlas在使用中回調實例化,AtlasRequested和Start的順序顛倒
5)從AssetBundle中動態加載渲染管線,后期渲染異常問題
UWA 問答社區:answer.uwa4d.com
UWA QQ群2:793972859(原群已滿員)
Rendering
Q1:URP7.4.3,除開主相機外,還有一個子相機,用於將照到的模型渲到游戲主界面UI上。在Profiler中看到以下情況:
可以看到,在子相機中也進行了包括對LOD的計算,但子相機的CullingMask只開了一個名為RTModel的Layer,在這一層里只有一個3D對象。按說子相機CullScriptable這塊開銷不應該有才對。
目前懷疑可能的原因是URP會對每個Base Camera都進行這部分的計算,但如果用Overlay相機,又無法用原來的方式將相機的targetTexture渲到一張RawImage上了。
有人遇到過么?
A:題主的疑惑是:子相機的CullScriptable這塊的開銷不應該有那么大對吧(畢竟我只有一個物件)?
這里有兩個問題:
1.Culling到底做了什么,只有一個物件為啥要Culling那么久(難道只有一個物件也要做很多的准備工作)?
2.你在Profiler里面看到的數據真的是真實數據嗎?也就是說,子相機的Culling真的做了1.68ms嗎?拋開這兩個問題,也可以有更好的做法:
我們一共兩個相機,主相機和UI相機,那么UI上顯示的3D物件怎么辦呢?
我們有個虛擬相機,所謂相機,其實就是做一個VP矩陣,做一個RT,繪制可見的物件就可以了。那么使用Unity的SRP,隨便在什么地方,設置一下VP矩陣,設置RT,接着繪制指定的物件(UI中所有的3D物件都會掛在這個物件下面),然后這個RT就可以隨意使用了。假如一個UI上有兩個3D物件,盡量都放在一個RT上,如果不行,就放在兩個或更多的RT上。只是會多幾個繪制命令,幾個RT(還不需要是全屏的),而且會多幾個Swap RT的操作。由於我們項目沒需求需要若干RT,所以假設一下,在這種需要若干RT的情況下,也可以用一個RT加多個Viewport來解決的。這個代碼都是現成的,參考一下Cascade Shadow Map的做法,這樣Swap RT也就省了。
綜上所述,既然你都知道自己要繪制什么,就不要給Unity Culling的機會了。
感謝王爍@UWA問答社區提供了回答
Q2:數據是在Development Build中連真機看到的性能數據, 目前在使用類似於HLOD的方式來減少掉這個LOD的巨大開銷。樓上說的“設置一下VP矩陣,設置RT”,請問這個VP矩陣的操作具體是什么?可否詳解下或者有相關資料嗎?
A:一切皆有可能!但不過這個不重要。然后你提的HLOD和LOD和上面的Culling沒關系。VP矩陣就是view矩陣和projection矩陣。相機的作用就是提供這倆矩陣的。
如果你在管線里面設置了相應的矩陣,然后繪制指定的物件,就可以完全不用多一個相機,畢竟多一個相機就多一個Culling。
如果你對VP矩陣不熟悉,不清楚怎么實現,也簡單。依然用一個額外相機,關上這個相機的Culling,然后在渲染pass中,不要繪制cullingresult.visibleobject,而直接用Graphics.DrawMesh或者CommandBuffer.DrawMesh繪制你要顯示的那個3D Object的物件就好了。
感謝王爍@UWA問答社區提供了回答
Addressable
Q:原來的AssetBunlde打包的時候可以設置BuildAssetBundleOptions.DisableWriteTypeTree,使得包體小好多。今天項目嘗試升級使用Addressables,請問該如何設置BuildAssetBundleOptions.DisableWriteTypeTree?
A1:首先Addressable打Bundle包主要的腳本是BuildScriptPackedMode,在DoBuild函數內可以看到用了AddressableAssetsBundleBuildParameters這個類,這個類繼承於BuildParameters,而BuildParameters有一個成員變量是public ContentBuildFlags ContentBuildFlags { get; set; },這個ContentBuildFlags就是用於設置DisableWriteTypeTree的,BuildParameters是屬於ScriptableBuildPipline的。
參考SBP的CompatibilityBuildPipline有以下操作:
if ((options & BuildAssetBundleOptions.DisableWriteTypeTree) != 0) parameters.ContentBuildFlags |= ContentBuildFlags.DisableWriteTypeTree; IBundleBuildResults results; ReturnCode exitCode = ContentPipeline.BuildAssetBundles(parameters, content, out results);我們在BuildScriptPackedMode.cs里也可以加上:
var buildParams = new AddressableAssetsBundleBuildParameters( aaContext.Settings, aaContext.bundleToAssetGroup, buildTarget, buildTargetGroup, aaContext.Settings.buildSettings.bundleBuildPath); buildParams.ContentBuildFlags = UnityEditor.Build.Content.ContentBuildFlags.DisableWriteTypeTree;但是我在這兒測試了一下,並沒有什么用,不知道是最底層沒有支持還是我的測試用例有問題。建議用同樣的方法,測試一下。
另外說一下,我看了一圈代碼,在打包和壓縮部分SBP並沒有用到這個參數。而在處理場景依賴的地方,這個參數會被合到BuildSettings結構體提供給C++部分調用。所以這個參數應該最終傳到C++層起作用。
感謝黃程@UWA問答社區提供了回答
A2:Addressable似乎沒有給外部的接口直接來修改這個設置項,應該是需要自己寫代碼來修改打包方式了。需要自己寫一個BuildMode,可以參考以下這個帖子中第五個問題的回答:https://answer.uwa4d.com/question/5e649911438f7d0db495c724#5e64a4e8438f7d0db495c725
核心的代碼是要DoBuild中修改buildParams的設置項,buildParams.ContentBuildFlags |= UnityEditor.Build.Content.ContentBuildFlags.DisableWriteTypeTree;如下圖:
![]()
做了一下測試,發現是有效果的:
![]()
上面是默認的打包,修改后重新打包,變成下面的結果:
![]()
整體都變小了一點點,而且使用解開AssetBundle后,可以看到里面的內容變得很簡潔。
![]()
感謝Xuan@UWA問答社區提供了回答
Unreal
Q:Unreal方面,目前有什么可以用於商業化游戲的熱更方案嗎?
A1:因為我們公司是自己研發的一套框架,所以我也沒有太在意是否有開源的Unreal熱更新方案,印象中好像是沒有的。
但基於Unity引擎的開源方案倒是蠻多,你們可以按照Unity的開源方案的思路實現一套,基本思路就是使用反射預先生成引擎的導出Lua接口。
如何利用反射呢?簡單來說就是根據反射得到引擎接口的函數名,返回值類型、參數名和參數類型,由工具生成解析參數類型以及它的個數和順序,返回正確結果的Lua導出函數int xxx(lua_State *L) 函數。對於類成員函數需要導出到Table里,根據C++類層級關系利用Metatable可以找到父類的Lua導出成員函數。
Unity開源方案如此,Unreal也可以如此,區別只是Unreal使用UClass UProperty這套,而Unity使用C#而已。你們可以嘗試着實現一下。
感謝王遠明@UWA問答社區提供了回答
A2:可以參考以下這個鏈接:
https://github.com/Tencent/puerts
感謝lanyt@UWA問答社區提供了回答
A3:騰訊有兩款Lua熱更新的框架可供使用:
sLuaUnreal
據說是《和平精英》手游采用的框架;UnLua
較sLua后推出的一個框架。
感謝Vest@UWA問答社區提供了回答
Script
Q:請問UGUI SpriteAtlas使用中,AtlasRequested和Start的順序顛倒,如下圖:
測試工程可戳原問答獲取,測試環境Unity 2019.4。
A:可以使用Timeline看到具體的執行時機,在Main.Update里面進行實例化的時候(我測試的時候把Main.Update里面的interval去掉了),Atlas的回調跑到第二幀了,而TestAtlasSprite的Awake、OnEnable和Start都在第一幀。在Main.Start里面實例化的時候,這些打印都在第一幀,因為Atlas的回調是在EarlyUpdate.SpriteAtlasManagerUpdate里面,而實例化出來的TestSpriteAtlas.Start是在FixedUpdate.ScriptRunDelayedFixedFrameRate下面的,所以就跑到了Atlas回調的后面。
![]()
這個圖是在Update里面執行的情況。
![]()
這個是在Start里面執行的情況,可以看到都是在第一幀打印的。
![]()
這是在Main.Start里面執行實例化的Timeline的圖,可以看到TestSpriteAtlas.Start是在Atlas的回調后面執行的。
![]()
這是在Main.Update里面執行實例化的Timeline。
EarlyUpdate,FixedUpdate這些回調的執行順序可以參考:
https://medium.com/@thebeardphantom/unity-2018-and-playerloop-5c46a12a677
感謝Xuan@UWA問答社區提供了回答
Rendering
Q:有人遇到動態切換管線,顯示錯誤的問題嗎?
現有UBP項目需求在進入新的場景之后,切換到指定的渲染管線。在打包場景的時候將渲染管線一並打包到場景中,並通過以下代碼來切換渲染管線。
GraphicsSettings.renderPipelineAsset = targetAsset;
QualitySettings.renderPipeline = targetAsset;
但是通過AssetBundle加載出來的渲染流程:在后處理中,UberPost 缺少兩個Keyword。
造成如下的圖像。
在Project Setting -》 Quality里面手動設置成項目的hero_show管線則正常顯示。
通過斷點調試發現:在Render過程中,Keywords其實都是有賦值的。二者的流程均一致。
在PostProcessPass.cs文件中:
void Render(CommandBuffer cmd, ref RenderingData renderingData)
但就是在Frame Profiler中抓幀顯示沒有Keywords。附上簡單的項目測試:
默認是Windows平台。直接運行defaultScene即可。
如不是,按下面流程處理:
1. AssetBundle->build
2. 運行defaultScene.
鏈接:https://pan.baidu.com/s/1q3s6mAUwE723wTJ3VmS81g
提取碼:8kwn
A1:雖然沒遇到過,但是能猜到原因。
先總結一下樓主的問題:
Project Setting -》 Quality里面手動設置渲染管線,顯示正常;
以AssetBundle的形式加載渲染管線,顯示不正常,原因是Keyword丟失。那么答案就很簡單了。
因為這兩種模式Unity對渲染管線這個資源的處理方式是不同的:
以第一種方式,Unity會認為渲染管線是個默認資源,將其全部打包進包體,並進行使用。
以第二種方式,Unity會將渲染管線,特別是渲染管線關聯的Shader,判斷其Keyword是否被使用,如果沒使用,就優化掉了。項目還沒有看,我猜測樓主是在代碼中開啟的這兩個Keyword,這種代碼開啟Keyword的方式,Unity檢測不到,所以就丟掉了。
解決方法也簡單,對付Keyword常用的方案,創建一個沒用的材質球,使用相應的Shader,在材質球中開啟對這些Keyword的使用打進AssetBundle,這樣Unity就知道,這個Shader的這些Keyword是有用的,就不會Skip掉了(其實這個方法常被用於以防instance_on這個Keyword丟失)。
感謝王爍@UWA問答社區提供了回答
A2:想嘗試樓上的方法,發現材質球並不能直接引用到UberPost這個Shader,因為這個Shader是放在Package目錄下,於是參考了Packages目錄下Shader打包這個帖子中的方法進行打包。使用編輯器的SVC采集方法采集了一下SVC,SVC里面的UberPost是有那兩個Keyword的。將UberPost和這個SVC一起打包,拆包后可以看到ubqScene場景是引用了剛剛這個SVC中的Shader的,切場景后在Awake里面加載SVC並Warmup,然而也還是沒有解決問題。最后我把題主的原始工程打了一個exe,運行發現並沒有Keyword丟失的問題,渲染一切正常。另外打包成APK測試,在小米9上測試也是正常渲染的。
可能還是Unity編輯器的問題吧,類似於這個帖子:
UnityEditor下加載AssetBundle,材質球Keywords正常,但是某些屬性不存在)。
感謝Xuan@UWA問答社區提供了回答
封面圖來源於網絡
今天的分享就到這里。當然,生有涯而知無涯。在漫漫的開發周期中,您看到的這些問題也許都只是冰山一角,我們早已在UWA問答網站上准備了更多的技術話題等你一起來探索和分享。歡迎熱愛進步的你加入,也許你的方法恰能解別人的燃眉之急;而他山之“石”,也能攻你之“玉”。
官方技術QQ群:793972859(原群已滿員)
